diff -u --recursive --new-file v2.1.91/linux/CREDITS linux/CREDITS
--- v2.1.91/linux/CREDITS	Tue Mar 17 22:18:13 1998
+++ linux/CREDITS	Wed Apr  1 14:57:58 1998
@@ -438,8 +438,8 @@
 W: http://www.pi.se/blox/
 D: Extended support for loadable modules
 D: D-Link pocket adapter drivers
-S: Myrstuguv. 83
-S: S-143 32 VARBY
+S: Grevgatan 11
+S: S-114 53 Stockholm
 S: Sweden
 
 N: Paal-Kristian Engstad
@@ -1625,11 +1625,13 @@
 S: France
 
 N: Jon Tombs
-E: jon@gtex02.us.es
+E: jon@gte.esi.us.es
+W: http://www.esi.us.es/~jon
 D: NFS mmap()
 D: XF86_S3
 D: Kernel modules
-S: C/ Carlos de Cepeda 36 2-5
+D: Parts of varios other programs (xfig, open, ...)
+S: C/ Federico Garcia Lorca 1 10-A
 S: Sevilla 41005
 S: Spain
 
diff -u --recursive --new-file v2.1.91/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.1.91/linux/Documentation/Configure.help	Thu Mar 26 15:57:02 1998
+++ linux/Documentation/Configure.help	Wed Apr  1 16:20:56 1998
@@ -7066,6 +7066,12 @@
   is the only voice-supporting driver. See
   Documentation/isdn/README.audio for more information.
 
+X.25 PLP on top of ISDN (EXPERIMENTAL)
+CONFIG_ISDN_X25
+  This experimental feature provides X.25 over ISDN. See
+  Documentation/isdn/README.x25 for more information about how to
+  configure and what other options must be enabled for using X.25.
+
 ICN 2B and 4B support
 CONFIG_ISDN_DRV_ICN
   This enables support for two kinds of ISDN-cards made by a German
@@ -7079,6 +7085,14 @@
   want), say M here and read Documentation/modules.txt. The module
   will be called icn.o.
 
+isdnloop support
+CONFIG_ISDN_DRV_LOOP
+  This driver provides a virtual ISDN card. It's primary purpose is
+  testing of linklevel features or configuration without getting
+  charged by your service-provider for lots of phone calls.
+  You need will need the loopctrl utility from the latest isdn4k-utils
+  package to set up this driver.
+
 HiSax SiemensChipSet driver support
 CONFIG_ISDN_DRV_HISAX
   This is a driver supporting the Siemens chipset on various
@@ -7104,36 +7118,95 @@
   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.
+  non-standard irq/port settings.
+
+HiSax Support for Teles 16.3c
+CONFIG_HISAX_TELES3C
+  This enables HiSax support for the Teles ISDN-cards 16.3c.
+  See Documentation/isdn/README.HiSax on how to configure it 
+  using the different cards, a different D-channel protocol, or
+  non-standard irq/port settings.
 
 HiSax Support for AVM 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.
+  non-standard irq/port settings.
 
 HiSax Support for Elsa ISA cards
-CONFIG_HISAX_ELSA_PCC
-  This enables HiSax support for the Elsa Mircolink ISA 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 Elsa PCMCIA card
-CONFIG_HISAX_ELSA_PCMCIA
-  This enables HiSax support for the Elsa PCMCIA cards.
+CONFIG_HISAX_ELSA
+  This enables HiSax support for the Elsa Mircolink ISA cards,
+  for the Elsa Quickstep series cards and Elsa 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.
+  non-standard irq/port settings.
 
 HiSax Support for ITK ix1-micro Revision 2
 CONFIG_HISAX_IX1MICROR2
   This enables HiSax support for the ITK ix1-micro Revision 2 card.
   See Documentation/isdn/README.HiSax on how to configure it 
   using the different cards, a different D-channel protocol, or
-  non-standard IRQ/port/shmem settings.
+  non-standard irq/port settings.
+
+HiSax Support for Eicon.Diehl Diva cards
+CONFIG_HISAX_DIEHLDIVA
+  This enables HiSax support for the Eicon.Diehl Diva none PRO versions
+  passive ISDN cards.
+  See Documentation/isdn/README.HiSax on how to configure it 
+  using the different cards, a different D-channel protocol, or
+  non-standard irq/port settings.
+
+HiSax Support for ASUSCOM cards
+CONFIG_HISAX_ASUSCOM
+  This enables HiSax support for the AsusCom and their OEM versions
+  passive ISDN cards.
+  See Documentation/isdn/README.HiSax on how to configure it 
+  using the different cards, a different D-channel protocol, or
+  non-standard irq/port settings.
+
+HiSax Support for TELEINT cards
+CONFIG_HISAX_TELEINT
+  This enables HiSax support for the TELEINT SA1 semiactiv ISDN 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 settings.
+
+HiSax Support for Sedlbauer speed card/win-star
+CONFIG_HISAX_SEDLBAUER
+  This enables HiSax support for the Sedlbauer passive ISDN cards.
+  See Documentation/isdn/README.HiSax on how to configure it 
+  using the different cards, a different D-channel protocol, or
+  non-standard irq/port settings.
+
+HiSax Support for USR Sportster internal TA
+CONFIG_HISAX_SPORTSTER
+  This enables HiSax support for the USR Sportster internal TA card.
+  See Documentation/isdn/README.HiSax on how to configure it 
+  using a different D-channel protocol, or non-standard irq/port settings.
+
+HiSax Support for MIC card
+CONFIG_HISAX_MIC
+  This enables HiSax support for the ITH MIC card.
+  See Documentation/isdn/README.HiSax on how to configure it 
+  using a different D-channel protocol, or non-standard irq/port settings.
+
+HiSax Support for NETjet card
+CONFIG_HISAX_NETJET
+  This enables HiSax support for the NetJet from Traverse Technologies.
+  See Documentation/isdn/README.HiSax on how to configure it 
+  using a different D-channel protocol, or non-standard irq/port settings.
+
+HiSax Support for Niccy PnP/PCI card
+CONFIG_HISAX_NICCY
+  This enables HiSax support for the Dr. Neuhaus Niccy PnP or PCI.
+  See Documentation/isdn/README.HiSax on how to configure it 
+  using a different D-channel protocol, or non-standard irq/port settings.
+
+HiSax Support for Am7930 (EXPERIMENTAL)
+CONFIG_HISAX_AMD7930
+  This enables HiSax support for the AMD7930 chips on some sparcs.
+  This code is not finished yet.
 
 HiSax Support for EURO/DSS1
 CONFIG_HISAX_EURO
@@ -7142,7 +7215,18 @@
   NOTE: This is mutually exclusive with HiSax Support for
   German 1TR6 if you have only one ISDN card installed.
 
-HiSax Support for US/NI-1
+Support for german tarifinfo
+CONFIG_DE_AOC
+  If you want, that HiSax send messages to the linklevel on each
+  AOCD/AOCE, enable this. This works only in Germany.
+
+Support for australian Microlink service (not for std. EURO)
+CONFIG_HISAX_ML
+  If you are in Australia and connected on the Microlink telephone
+  network enable this, because here are little differences in protocol.
+  Please don't enable this in other countries.
+
+HiSax Support for US/NI-1 (not released yet)
 CONFIG_HISAX_NI1
   You should choose the D-channel protocol your local
   telephone service provider uses here by saying Y or N.
@@ -7198,6 +7282,14 @@
   If you say Y here, the AVM B1 driver will give verbose reasons for
   disconnecting. This will increase the size of the kernel by 7K. If
   unsure, say Y.
+
+IBM Active 2000 support (EXPERIMENTAL)
+CONFIG_ISDN_DRV_ACT2000
+  This enables support for IBM Active 2000 ISDN card. In order to use
+  this card, additional firmware is necessary, which has to be loaded
+  into the card using a utility which is part of the latest isdn4k-utils
+  package. Please read the file Documentation/isdn/README.act2000 for
+  more information.
 
 Support for AP1000 multicomputer
 CONFIG_AP1000
diff -u --recursive --new-file v2.1.91/linux/Documentation/isdn/00-INDEX linux/Documentation/isdn/00-INDEX
--- v2.1.91/linux/Documentation/isdn/00-INDEX	Thu May 29 21:53:03 1997
+++ linux/Documentation/isdn/00-INDEX	Wed Apr  1 16:20:56 1998
@@ -19,4 +19,12 @@
 syncPPP.FAQ
 	- frequently asked questions about running PPP over ISDN.
 README.avmb1
-	- info on driver for AVM-B1 ISDN card
+	- info on driver for AVM-B1 ISDN card.
+README.act2000
+	- info on driver for IBM ACT-2000 card.
+README.concap
+	- info on "CONCAP" ecapsulation protocol interface used for X.25.
+README.sc
+	- info on driver for Spellcaster cards.
+README.x25
+    _ info for running X.25 over ISDN.
diff -u --recursive --new-file v2.1.91/linux/Documentation/isdn/CREDITS linux/Documentation/isdn/CREDITS
--- v2.1.91/linux/Documentation/isdn/CREDITS	Thu May 29 21:53:03 1997
+++ linux/Documentation/isdn/CREDITS	Wed Apr  1 16:20:56 1998
@@ -8,6 +8,9 @@
 Alan Cox (alan@cymru.net)
   For help getting into standard-kernel.
 
+Henner Eisen (eis@baty.hanse.de)
+  For X.25 implementation.
+
 Volker Götz (volker@oops.franken.de)
   For contribution of man-pages, the imontty-tool and a perfect
   maintaining of the mailing-list at hub-wue.
@@ -37,18 +40,24 @@
 Eberhard Moenkeberg (emoenke@gwdg.de)
   For testing and help to get into kernel.
 
+Thomas Neumann (tn@ruhr.de)
+  For help with Cisco-SLARP and keepalive
+
 Jan den Ouden (denouden@groovin.xs4all.nl)
-  For contribution of the teles-driver
+  For contribution of the original teles-driver
+
+Carsten Paeth (calle@calle.in-berlin.de)
+  For the AVM-B1-CAPI2.0 driver
+
+Thomas Pfeiffer (pfeiffer@pds.de)
+  For V.110, extended T.70 and Hylafax extensions in isdn_tty.c
 
 Max Riegel (riegel@max.franken.de)
   For making the ICN hardware-documentation and test-equipment available.
 
-Gerhard 'Fido' Schneider (fido@wuff.franken.de)
+Gerhard 'Fido' Schneider (fido@wuff.mayn.de)
   For heavy-duty-beta-testing with his BBS ;)
 
 Thomas Uhl (uhl@think.de)
   For distributing the cards.
   For pushing me to work ;-)
-
-Carsten Paeth (calle@calle.in-berlin.de)
-  For the AVM-B1-CAPI2.0 driver
diff -u --recursive --new-file v2.1.91/linux/Documentation/isdn/INTERFACE linux/Documentation/isdn/INTERFACE
--- v2.1.91/linux/Documentation/isdn/INTERFACE	Thu Feb 27 10:57:29 1997
+++ linux/Documentation/isdn/INTERFACE	Wed Apr  1 16:20:56 1998
@@ -1,4 +1,4 @@
-$Id: INTERFACE,v 1.6 1997/02/10 22:40:57 fritz Exp $
+$Id: INTERFACE,v 1.8 1998/02/20 17:38:20 fritz Exp $
 
 Description of the Interface between Linklevel and Hardwarelevel
   of isdn4linux:
@@ -22,7 +22,7 @@
   got a separate version number. These numbers are shown at initialization,
   separated by slashes:
 
-     c.c/t.t/n.n/p.p/a.a
+     c.c/t.t/n.n/p.p/a.a/v.v
 
   where
 
@@ -31,11 +31,13 @@
    n.n is the revision of the network related code.
    p.p is the revision of the ppp related code.
    a.a is the revision of the audio related code.
+   v.v is the revision of the V.110 related code.
 
   Changes in this document are marked with '***CHANGEx' where x representing
   the version number. If that number starts with 0, it refers to the old,
   separately distributed package. If it starts with one of the letters
   above, it refers to the revision of the corresponding module. 
+  ***CHANGEIx refers to the revision number of the isdnif.h  
 
 1. Description of the fields of isdn_if:
 
@@ -65,32 +67,16 @@
 
   unsigned short hl_hdrlen;
 
-    ***CHANGED.7.4: New field.
+    ***CHANGE0.7.4: New field.
 
     To be preset by the HL-driver, if it supports sk_buff's. The driver
     should put here the amount of additional space needed in sk-buff's for
     its internal purposes. Drivers not supporting sk_buff's should put
     initialize this field to 0.
 
-  void (*rcvcallb)(int, int, u_char*, int);
-
-    ***CHANGEc1.14: Declared obsolete. Do NOT use this field/function
-                    anymore, since it will be removed when all current
-                    LL drivers have been changed accordingly. Use
-                    rcvcallb_skb instead.
-
-    This field will be set by LL. The HL-driver delivers received data-
-    packets by calling this function.
-
-    Parameter:
-      int    driver-Id
-      int    Channel-number locally to the driver. (starting with 0)
-      u_char Pointer to received data. (in kernel-space)
-      int    length of data-packet.
-
   void (*rcvcallb_skb)(int, int, struct sk_buff *)
 
-    ***CHANGED.7.4: New field.
+    ***CHANGE0.7.4: New field.
 
     This field will be set by LL. The HL-driver delivers received data-
     packets by calling this function. Upon calling, the HL-driver must
@@ -138,41 +124,22 @@
     Returnvalue:
       >=0 on success, else error-code (-ENODEV etc.)
 
-  int (*writebuf)(int, int, u_char*, int, int);
+  int (*writebuf_skb)(int, int, int, struct sk_buff *)
 
-    ***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.
-
-    This field has to be preset by the HL-driver. The given function will
-    be called by the LL for delivering data to be send via B-Channel.
-
-    Parameter:
-      int     driver-Id ***CHANGE.7.4: New parameter.
-      int     channel-number locally to the HL-driver. (starts with 0)
-      u_char* pointer to data.
-      int     length of data-packet.
-      int     flag: 0 = call from within kernel-space. (HL-driver must use
-                        memcpy, may NOT use schedule())
-                    1 = call from user-space. (HL-driver must use
-                        memcpy_fromfs, use of schedule() allowed)
-
-    Returnvalue:
-      Length of data accepted on success, else error-code (-EINVAL on
-      oversized packets etc.)
-
-  int (*writebuf_skb)(int, int, struct sk_buff *)
-
-    ***CHANGED.7.4: New field.
+    ***CHANGE0.7.4: New field.
+    ***CHANGEI.1.21: New field.
 
     This field has to be preset by the HL-driver. The given function will
     be called by the LL for delivering data to be send via B-Channel.
 
  
     Parameter:
-      int              driver-Id ***CHANGE.7.4: New parameter.
+      int              driver-Id ***CHANGE0.7.4: New parameter.
       int              channel-number locally to the HL-driver. (starts with 0)
+      int	       ack ***ChangeI1.21: New parameter
+		       If this is !0, the driver has to signal the delivery
+		       by sending an ISDN_STAT_BSENT. If this is 0, the driver
+		       MUST NOT send an ISDN_STAT_BSENT.
       struct sk_buff * Pointer to sk_buff containing data to be send via
                        B-channel.
 
@@ -199,7 +166,7 @@
       int     driver-Id.
       int     channel-number locally to the HL-driver. (starts with 0)
 
-***CHANGED1.14: The driver-Id and channel-number are new since this revision.
+***CHANGEI1.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 +190,7 @@
       int     driver-Id.
       int     channel-number locally to the HL-driver. (starts with 0)
 
-***CHANGED1.14: The driver-Id and channel-number are new since this revision.
+***CHANGEI1.14: The driver-Id and channel-number are new since this revision.
 
     Returnvalue:
       Length of data on success, else error-code (-EINVAL etc.)
@@ -249,7 +216,7 @@
 
    Until now, the following commands are defined:
 
-***CHANGED1.34: The parameter "num" has been replaced by a union "para" containing
+***CHANGEI1.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.
 
@@ -552,6 +519,9 @@
    a B-Channel-connection. (Response to ISDN_CMD_ACCEPTB or because the
    remote-station has initiated establishment)
 
+   The HL driver should call this when the logical l2/l3 protocol 
+   connection on top of the physical B-channel is esatblished .
+
     Parameter:
       driver      = driver-Id
       command     = ISDN_STAT_BCONN
@@ -577,6 +547,9 @@
    B-Channel-connection. This could be a response to a prior ISDN_CMD_HANGUP,
    or caused by a remote-hangup.
 
+   The HL driver should call this as soon as the logical l2/l3 protocol 
+   connection on top of the physical B-channel is released.
+
     Parameter:
       driver      = driver-Id
       command     = ISDN_STAT_BHUP
@@ -617,7 +590,9 @@
       driver      = driver-Id
       command     = ISDN_STAT_BSENT
       arg         = channel-number, locally to the driver. (starting with 0)
-      para        = unused.
+      para.length = ***CHANGEI.1.21: New field.
+		    the driver has to set this to the original length
+		    of the skb at the time of receiving it from the linklevel.
 
   ISDN_STAT_NODCH:
 
@@ -657,3 +632,16 @@
       arg         = channel-number, locally to the driver. (starting with 0)
       para.num    = ASCII string containing CAUSE-message.
 
+  ISDN_STAT_L1ERR:
+
+    ***CHANGEI1.21 new status message.
+    A signal can be sent to the linklevel if an Layer1-error results in
+    packet-loss on receive or send. The field errcode of the cmd.parm
+    union describes the error more precisely.
+
+    Parameter:
+      driver      = driver-Id
+      command     = ISDN_STAT_L1ERR
+      arg         = channel-number, locally to the driver. (starting with 0)
+      para.errcode= ISDN_STAT_L1ERR_SEND:     Packet lost while sending.
+		    ISDN_STAT_L1ERR_RECV:     Packet lost while receiving.
diff -u --recursive --new-file v2.1.91/linux/Documentation/isdn/README linux/Documentation/isdn/README
--- v2.1.91/linux/Documentation/isdn/README	Tue Mar 10 10:03:30 1998
+++ linux/Documentation/isdn/README	Wed Apr  1 16:20:56 1998
@@ -120,12 +120,35 @@
                AT&D3    Same as AT&D2 but also resets all registers.
 	       AT&Ex    Set the EAZ/MSN for this channel to x.
 	       AT&F     Reset all registers and profile to "factory-defaults"
+	       AT&Rx    Select V.110 bitrate adaption.
+                        This command enables V.110 protocol with 9600 baud
+                        (x=9600), 19200 baud (x=19200) or 38400 baud
+                        (x=38400). A value of x=0 disables V.110 switching
+                        back to default X.75. This command sets the following
+                        Registers:
+                          Reg 14 (Layer-2 protocol):
+                            x = 0:     0
+                            x = 9600:  7
+                            x = 19200: 8
+                            x = 38400: 9
+                          Reg 18.2 = 1
+                          Reg 19 (Additional Service Indicator):
+                            x = 0:       0
+                            x = 9600:  197
+                            x = 19200: 199
+                            x = 38400: 198
+                          Note on value in Reg 19:
+                            There is _NO_ common convention for 38400 baud.
+                            The value 198 is choosen arbitrarily. Users
+                            _MUST_ negotiate this value before establishing
+                            a connection.
 	       AT&Sx    Set window-size (x = 1..8) (not yet implemented)
 	       AT&V     Show all settings.
                AT&W0    Write registers and EAZ/MSN to profile. See also
                         iprofd (5.c in this README).
-               AT&X0	BTX-mode off (default)
-	       AT&X1    BTX-mode on. (S13.1=1, S14=0, S16=7, S18=7, S19=0)
+               AT&X0	BTX-mode and T.70-mode off (default)
+	       AT&X1    BTX-mode on. (S13.1=1, S13.5=0 S14=0, S16=7, S18=7, S19=0)
+	       AT&X2    T.70-mode on. (S13.1=1, S13.5=1, S14=0, S16=7, S18=7, S19=0)
 
            For voice-mode commands refer to README.audio
 
@@ -184,12 +207,23 @@
 				      1 = Extended response messages
                             Bit 4:    0 = CALLER NUMBER before every RING.
                                       1 = CALLER NUMBER after first RING.
+                            Bit 5:    0 = T.70 extended protocol off
+                                      1 = T.70 extended protocol on
+                            Bit 6:    0 = Special RUNG Message off
+                                      1 = Special RUNG Message on
+                                          "RUNG" is delivered on a ttyI, if
+                                          an incoming call happened (RING) and
+                                          the remote party hung up before any
+                                          local ATA was given.
 	     14   0         Layer-2 protocol:
 				      0 = X75/LAPB with I-frames
 				      1 = X75/LAPB with UI-frames
                                       2 = X75/LAPB with BUI-frames
                                       3 = HDLC
                                       4 = Transparent (audio)
+                                      7 = V.110, 9600 baud
+                                      8 = V.110, 19200 baud
+                                      9 = V.110, 38400 baud
              15   0         Layer-3 protocol: (at the moment always 0)
                                       0 = transparent
 	     16   250       Send-Packet-size/16
@@ -406,6 +440,9 @@
             are stripped off.  
    ip       IP with type-field. Same as IP but the type-field of the MAC-header
             is preserved.
+   x25iface x25 interface encapsulation (first byte semantics as defined in
+	    ../networking/x25-iface.txt). Use this for running the linux
+	    x25 network protocol stack (AF_X25 sockets) on top of isdn.
    cisco-h  A special-mode for communicating with a Cisco, which is configured
             to do "hdlc"
    ethernet No stripping. Packets are sent with full MAC-header.
@@ -415,6 +452,11 @@
 
    uihdlc   HDLC with UI-frame-header (for use with DOS ISPA, option -h1)
 
+
+   NOTE:    x25iface encapsulation is currently experimental. Please
+	    read README.x25 for further details    
+
+
    Watching packets, using standard-tcpdump will fail for all encapsulations
    except ethernet because tcpdump does not know how to handle packets
    without MAC-header. A patch for tcpdump is included in the utility-package
@@ -423,7 +465,8 @@
    "isdnctrl l2_prot <InterfaceName> <L2-ProtocolName>" 
    Selects a layer-2-protocol. 
    (With the ICN-driver and the HiSax-driver, "x75i" and "hdlc" is available.
-   With other drivers, "x75ui", "x75bui" may be possible too.)
+   With other drivers, "x75ui", "x75bui", "x25dte", "x25dce" may be
+   possible too. See README.x25 for x25 related l2 protocols.)
 
    isdnctrl l3_prot <InterfaceName> <L3-ProtocolName> 
    The same for layer-3. (At the moment only "trans" is allowed)
diff -u --recursive --new-file v2.1.91/linux/Documentation/isdn/README.HiSax linux/Documentation/isdn/README.HiSax
--- v2.1.91/linux/Documentation/isdn/README.HiSax	Thu May 29 21:53:03 1997
+++ linux/Documentation/isdn/README.HiSax	Wed Apr  1 16:20:56 1998
@@ -23,24 +23,39 @@
 ---------------
 
 Teles 8.0/16.0/16.3 and compatible ones
+Teles 16.3c
 Teles S0/PCMCIA
 Creatix PnP S0 
-AVM A1 (Fritz)
+Compaq ISDN S0 ISA card
+AVM A1 (Fritz, Teledat 150)
 ELSA Microlink PCC-16, PCF, PCF-Pro, PCC-8
 ELSA Quickstep 1000
+ELSA Quickstep 1000PCI
+ELSA Quickstep 3000 (same settings as QS1000)
 ELSA PCMCIA
 ITK ix1-micro Rev.2
+Eicon.Diehl Diva 2.0 ISA and PCI (S0 and U interface, no PRO version)
+Eicon.Diehl Diva Piccola
+ASUSCOM NETWORK INC. ISDNLink 128K PC adapter (order code I-IN100-ST-D)
+Dynalink IS64PH (OEM version of ASUSCOM NETWORK INC. ISDNLink 128K adapter)
+HFC-2BS0 based cards (TeleInt SA1)
+Sedlbauer Speed Card (Speed Win, Teledat 100)
+Sedlbauer Speed Star (PCMCIA)
+USR Sportster internal TA (compatible Stollmann tina-pp V3)
+ith Kommunikationstechnik GmbH MIC 16 ISA card 
+Traverse Technologie NETjet PCI S0 card
+Dr. Neuhaus Niccy PnP/PCI
 
 Note: PCF, PCF-Pro: up to now, only the ISDN part is supported
       PCC-8: not tested yet
       Teles PCMCIA is EXPERIMENTAL
+      Teles 16.3c is EXPERIMENTAL
+      Eicon.Diehl Diva U interface not tested
 
 If you know other passive cards with the Siemens chipset, please let me know.
 To use the PNP cards you need the isapnptools.
 You can combine any card, if there is no conflict between the ressources
-(io, mem, irq), with one exception: The ELSA PCMCIA cannot work with an other
-non PCMCIA ELSA card at the same time. You cannot select ELSA ISA and ELSA
-PCMCIA support at the same time during kernel config.
+(io, mem, irq).
 
 
 Configuring the driver
@@ -113,10 +128,26 @@
     6   ELSA PCC/PCF cards       io or nothing for autodetect (the iobase is
                                  required only if you have more than one ELSA
                                  card in your PC)
-    7   ELSA Quickstep 1000	 irq, io  (from isapnp setup)
-    7   ELSA PCMCIA		 irq, io  (set with card manager)
+    7   ELSA Quickstep 1000      irq, io  (from isapnp setup)
     8   Teles 16.3 PCMCIA     	 irq, io
     9   ITK ix1-micro Rev.2      irq, io
+   10   ELSA PCMCIA		 irq, io  (set with card manager)
+   11   Eicon.Diehl Diva ISA PnP irq, io
+   11   Eicon.Diehl Diva PCI     no parameter
+   12   ASUS COM ISDNLink        irq, io  (from isapnp setup)
+   13   HFC-2BS0 based cards     irq, io  
+   14   Teles 16.3c PnP          irq, io
+   15   Sedlbauer Speed Card     irq, io  
+   16   USR Sportster internal   irq, io  
+   17   MIC card                 irq, io  
+   18   ELSA Quickstep 1000PCI   no parameter
+   19   Compaq ISDN S0 ISA card  irq, io0, io1, io (from isapnp setup io=IO2)
+   20   NETjet PCI card          no parameter
+   22   Sedlbauer Speed Star (PCMCIA) irq, io (set with card manager)
+   24   Dr. Neuhaus Niccy PnP    irq, io0, io1 (from isapnp setup)
+   24   Dr. Neuhaus Niccy PCI    no parameter
+
+
         
 At the moment IRQ sharing is not possible. Please make sure that your IRQ
 is free and enabled for ISA use.
@@ -181,17 +212,28 @@
 Card types:
        
   type  
-    1 	Teles 16.0     		pa=irq	pb=membase	pc=iobase
-    2 	Teles  8.0      	pa=irq	pb=membase
-    3 	Teles 16.3      	pa=irq	pb=iobase
+    1 	Teles 16.0     	        pa=irq  pb=membase  pc=iobase
+    2 	Teles  8.0              pa=irq  pb=membase
+    3 	Teles 16.3              pa=irq  pb=iobase
     4 	Creatix/Teles PNP     	ONLY WORKS AS A MODULE !
-    5 	AVM A1 (Fritz)  	pa=irq	pb=iobase
+    5 	AVM A1 (Fritz)          pa=irq  pb=iobase
     6 	ELSA PCC/PCF cards      pa=iobase or nothing for autodetect
-    7   ELSA Quickstep 1000	ONLY WORKS AS A MODULE !
-    7   ELSA PCMCIA		irq, io  (set with card manager)
-    8   Teles S0 PCMCIA    	pa=irq  pb=iobase
+    7   ELSA Quickstep 1000     ONLY WORKS AS A MODULE !
+    8   Teles S0 PCMCIA         pa=irq  pb=iobase
     9   ITK ix1-micro Rev.2     pa=irq  pb=iobase
-
+   10   ELSA PCMCIA             pa=irq, pb=io  (set with card manager)
+   11   Eicon.Diehl Diva ISAPnP ONLY WORKS AS A MODULE !
+   11   Eicon.Diehl Diva PCI    no parameter
+   12   ASUS COM ISDNLink       ONLY WORKS AS A MODULE !
+   13	HFC-2BS0 based cards    pa=irq  pb=io
+   14   Teles 16.3c PnP         ONLY WORKS AS A MODULE !
+   15	Sedlbauer Speed Card    pa=irq  pb=io (Speed Win only as module !)  
+   16   USR Sportster internal  pa=irq  pb=io
+   17   MIC card                pa=irq  pb=io
+   18   ELSA Quickstep 1000PCI  no parameter
+   19   Compaq ISDN S0 ISA card ONLY WORKS AS A MODULE !
+   20   NETjet PCI card         no parameter
+   21   Sedlbauer Speed Star (PCMCIA)  pa=irq, pb=io  (set with card manager)
 
 Running the driver
 ------------------
@@ -215,8 +257,8 @@
 Apr 13 21:01:59 kke01 kernel: HiSax: 2 channels added
 
 This means that the card is ready for use.
-Cabling problems or line-downs are not detected, and only ELSA cards can detect
-the S0 power.
+Cabling problems or line-downs are not detected, and only some ELSA cards can 
+detect the S0 power.
 
 Remember that, according to the new strategy for accessing low-level drivers
 from within isdn4linux, you should also define a driver ID while doing
@@ -226,9 +268,9 @@
 At this point you can run a 'cat /dev/isdnctrl0' and view debugging 
 messages. 
 
-At the moment, debugging messages are enabled with the telesctrl tool:
+At the moment, debugging messages are enabled with the hisaxctrl tool:
 
-    telesctrl <DriverId> DebugCmd <debugging_flags>
+    hisaxctrl <DriverId> DebugCmd <debugging_flags>
 
 <DriverId> default is HiSax, if you didn't specified one.
 
@@ -271,7 +313,14 @@
          4  l3 state machine
          8  charge info debugging (1TR6)
 
-For example, 'telesctrl HiSax 1 0x3ff' enables full generic debugging.
+For example, 'hisaxctrl HiSax 1 0x3ff' enables full generic debugging.
+
+Because of some obscure problems with some switch equipment, the delay
+between CONNECT message and sending the first data on th B-channel is now
+configurable with 
+
+hisaxctrl <DriverId> 2 <delay>
+<delay> in ms Value between 50 an 800 ms are recommended.
 
 
 Warning
@@ -284,7 +333,7 @@
 Limitations
 -----------
 At this time, HiSax only works on Euro ISDN lines and German 1TR6 lines.
-
+For leased lines see appendix. 
 
 Bugs 
 ----
@@ -301,10 +350,20 @@
         Andreas Kool, Pekka Sarnila, Sim Yskes, Johan Myrre'en,
 	Klaus-Peter Nischke (ITK AG), Christof Petig, Werner Fehn (ELSA GmbH),
 	Volker Schmidt
+	Edgar Toernig and Marcus Niemann for the Sedlbauer driver
+	Stephan von Krawczynski
+	Juergen Quade for the Leased Line part
+	Klaus Lichtenwalder (Klaus.Lichtenwalder@WebForum.DE), for ELSA PCMCIA support
         and more people who are hunting bugs. (If I forgot somebody, please
 	send me a mail). 
 
         Firma ELSA GmbH
+        Firma Eicon.Diehl GmbH
+        Firma Dynalink NL
+	Firma ASUSCOM NETWORK INC. Taiwan
+	Firma S.u.S.E
+	Firma ith Kommunikationstechnik GmbH
+	Firma Traverse Technologie Australia
         
         My girl friend and partner in life Ute for her patience with me.
 
@@ -321,3 +380,171 @@
 See 
    http://www.stud.uni-wuppertal.de/~ea0141/pcmcia.html
 for instructions.
+
+Appendix: Linux and ISDN-leased lines
+-------------------------------------
+
+Original from Juergen Quade, new version KKe.
+
+Attention NEW VERSION, the old leased line syntax won't work !!!
+
+You can use HiSax to connect your Linux-Box via an ISDN leased line
+to i.e. the internet:
+
+1. Build a kernel which includes the HiSax driver either as a module
+   or as part of the kernel.
+     cd /usr/src/linux
+     make menuconfig
+     <ISDN subsystem - ISDN support -- HiSax>
+     make clean; make dep; make zImage; make modules; make modules_install
+2. Install the new kernel
+     cp /usr/src/linux/arch/i386/boot/zImage /etc/kernel/linux.isdn
+     vi /etc/lilo.conf
+     <add new kernel in the bootable image section>
+     lilo
+3. in case the hisax driver is a "fixed" part of the kernel, configure
+   the driver with lilo:
+     vi /etc/lilo.conf
+     <add HiSax driver parameter in the global section (see below)>
+     lilo
+   Your lilo.conf _might_ look as the following:
+
+	# LILO configuration-file
+	# global section
+    # teles 16.0 on IRQ=5, MEM=0xd8000, PORT=0xd80
+	append="hisax=1,3,5,0xd8000,0xd80,HiSax"
+    # teles 16.3 (non pnp) on IRQ=15, PORT=0xd80
+	# append="hisax=3,3,5,0xd8000,0xd80,HiSax"
+	boot=/dev/sda
+	compact        # faster, but won't work on all systems.
+	linear
+	read-only
+	prompt
+	timeout=100
+	vga = normal    # force sane state
+	# Linux bootable partition config begins
+	image = /etc/kernel/linux.isdn
+	root = /dev/sda1
+	label = linux.isdn
+	#
+	image = /etc/kernel/linux-2.0.30
+	root = /dev/sda1
+	label = linux.secure
+
+   In the line starting with "append" you have to adapt the parameters
+   according to your card (see above in this file)
+
+3. boot the new linux.isdn kernel
+4. start the ISDN subsystem:
+   a) load - if necessary - the modules (depends, whether you compiled
+      the ISDN driver as module or not)
+      According to the type of card you have to specify the necessary
+      driver parameter (irq, io, mem, type, protocol).
+      For the leased line the protocol is "3". See the table above for
+      the parameters, which you have to specify depending on your card.
+   b) configure i4l
+      /sbin/isdnctrl addif isdn0
+      # EAZ  1 -- B1 channel   2 --B2 channel
+      /sbin/isdnctrl eaz isdn0 1
+      /sbin/isdnctrl secure isdn0 on
+      /sbin/isdnctrl huptimeout isdn0 0
+      /sbin/isdnctrl l2_prot isdn0 hdlc
+      # Attention you must not set a outgoing number !!! This won't work !!!
+      # The incomming number is LEASED0 for the first card, LEASED1 for the
+      # second and so on. 
+      /sbin/isdnctrl addphone isdn0 in LEASED0
+      # Here is no need to bind the channel.
+   c) in case the remote partner is a CISCO:
+      /sbin/isdnctrl encap isdn0 cisco-h
+   d) configure the interface
+      /sbin/ifconfig isdn0 ${LOCAL_IP} pointopoint ${REMOTE_IP}
+   e) set the routes
+      /sbin/route add -host ${REMOTE_IP} isdn0
+      /sbin/route add default gw ${REMOTE_IP}
+   f) switch the card into leased mode for each used B-channel 
+      /sbin/hisaxctrl HiSax 5 1
+ 
+Remarks:
+a) If you have a CISCO don´t forget to switch off the KEEP ALIVE option!
+
+Here an example script:
+#!/bin/sh
+# Start/Stop ISDN lesaed line connection
+
+I4L_AS_MODULE=yes
+I4L_REMOTE_IS_CISCO=no
+I4L_MODULE_PARAMS="type=16 io=0x268 irq=7 "
+I4L_DEBUG=no
+I4L_LEASED_128K=yes
+LOCAL_IP=192.168.1.1
+REMOTE_IP=192.168.2.1
+
+case "$1" in
+    start)
+	echo "Starting ISDN ..."
+        if [ ${I4L_AS_MODULE} = "yes" ]; then
+		echo "loading modules..."
+		/sbin/modprobe hisax ${I4L_MODULE_PARAMS}
+	fi
+	# configure interface
+	/sbin/isdnctrl addif isdn0
+	/sbin/isdnctrl secure isdn0 on
+	if [ ${I4L_DEBUG} = "yes" ]; then
+		/sbin/isdnctrl verbose 7
+		/sbin/hisaxctrl HiSax 1 0xffff
+		/sbin/hisaxctrl HiSax 11 0xff
+		cat  /dev/isdnctrl >/tmp/lea.log &
+	fi
+	if [ ${I4L_REMOTE_IS_CISCO} = "yes" ]; then
+		/sbin/isdnctrl encap isdn0 cisco-h
+	fi
+	/sbin/isdnctrl huptimeout isdn0 0
+	# B-CHANNEL 1
+	/sbin/isdnctrl eaz isdn0 1
+	/sbin/isdnctrl l2_prot isdn0 hdlc
+	# 1. card
+	/sbin/isdnctrl addphone isdn0 in LEASED0
+        if [ ${I4L_LEASED_128K} = "yes" ]; then
+		/sbin/isdnctrl addslave isdn0 isdn0s
+		/sbin/isdnctrl secure isdn0s on
+		/sbin/isdnctrl huptimeout isdn0s 0
+		# B-CHANNEL 2
+		/sbin/isdnctrl eaz isdn0s 2
+		/sbin/isdnctrl l2_prot isdn0s hdlc
+		# 1. card
+		/sbin/isdnctrl addphone isdn0s in LEASED0
+		if [ ${I4L_REMOTE_IS_CISCO} = "yes" ]; then
+			/sbin/isdnctrl encap isdn0s cisco-h
+		fi
+	fi
+	# configure tcp/ip
+	/sbin/ifconfig isdn0 ${LOCAL_IP} pointopoint ${REMOTE_IP}
+	/sbin/route add -host ${REMOTE_IP} isdn0
+	/sbin/route add default gw ${REMOTE_IP}
+	# switch to leased mode
+	# B-CHANNEL 1
+	/sbin/hisaxctrl HiSax 5 1
+        if [ ${I4L_LEASED_128K} = "yes" ]; then
+		# B-CHANNEL 2
+		/sbin/hisaxctrl HiSax 5 2
+	fi
+	;;
+    stop)
+	/sbin/ifconfig isdn0 down
+	/sbin/isdnctrl delif isdn0
+	if [ ${I4L_DEBUG} = "yes" ]; then
+		killall cat
+	fi
+	if [ ${I4L_AS_MODULE} = "yes" ]; then
+		/sbin/rmmod hisax
+		/sbin/rmmod isdn
+		/sbin/rmmod ppp
+		/sbin/rmmod slhc
+	fi
+	;;
+    *)
+	echo "Usage: $0 {start|stop}"
+	exit 1
+esac
+exit 0
+
diff -u --recursive --new-file v2.1.91/linux/Documentation/isdn/README.act2000 linux/Documentation/isdn/README.act2000
--- v2.1.91/linux/Documentation/isdn/README.act2000	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/isdn/README.act2000	Wed Apr  1 16:20:56 1998
@@ -0,0 +1,104 @@
+$Id: README.act2000,v 1.1 1997/09/24 23:50:16 fritz Exp $
+
+This document describes the ACT2000 driver for the
+IBM Active 2000 ISDN card.
+
+There are 3 Types of this card available. A ISA-, MCA-, and PCMCIA-Bus
+Version. Currently, only the ISA-Bus version of the card is supported.
+However MCA and PCMCIA will follow soon.
+
+The ISA-Bus Version uses 8 IO-ports. The base port adress has to be set
+manually using the DIP switches.
+
+Setting up the DIP switches for the IBM Active 2000 ISDN card:
+
+	 Note: S5 and S6 always set off!
+
+     S1  S2  S3  S4  Base-port
+     on  on  on  on  0x0200 (Factory default)
+     off on  on  on  0x0240 
+     on  off on  on  0x0280 
+     off off on  on  0x02c0 
+     on  on  off on  0x0300 
+     off on  off on  0x0340 
+     on  off off on  0x0380 
+     on  on  on  off 0xcfe0
+     off on  on  off 0xcfa0 
+     on  off on  off 0xcf60 
+     off off on  off 0xcf20 
+     on  on  off off 0xcee0 
+     off on  off off 0xcea0 
+     on  off off off 0xce60 
+     off off off off Card disabled 
+
+IRQ is configured by software. Possible values are:
+
+  3, 5, 7, 10, 11, 12, 15 and none (polled mode)
+
+
+The ACT2000 driver either may be build into kernel or as a module.
+Initialization depends on how the driver is built:
+
+Driver built into the kernel:
+
+  The ACT2000 driver can be configured using the commandline-feature while
+  loading the kernel with LILO or LOADLIN. It accepts the following syntax:
+
+  act2000=b,p,i[,idstring]
+
+  where
+
+    b = Bus-Type      (1=ISA, 2=MCA, 3=PCMCIA)
+    p = portbase      (-1 means autoprobe)
+    i = Interrupt     (-1 means use next free IRQ, 0 means polled mode)
+
+  The idstring is an arbitrary string used for referencing the card
+  by the actctrl tool later.
+
+  Defaults used, when no parameters given at all:
+
+    1,-1,-1,""
+
+  which means: Autoprobe for an ISA card, use next free IRQ, let the
+  ISDN linklevel fill the IdString (usually "line0" for the first card).
+ 
+  If you like to use more than one card, you can use the program
+  "actctrl" from the utility-package to configure additional cards.
+
+  Using the "actctrl"-utility, portbase and irq can also be changed
+  during runtime. The D-channel protocol is configured by the "dproto"
+  option of the "actctrl"-utility after loading the firmware into the
+  card's memory using the "actctrl"-utility.
+
+Driver built as module:
+
+  The module act2000.o can be configured during modprobe (insmod) by
+  appending its parameters to the modprobe resp. insmod commandline.
+  The following syntax is accepted:
+
+    act_bus=b act_port=p act_irq=i act_id=idstring
+
+  where b, p, i and idstring have the same meanings like parameters
+  described for the builtin version above.
+
+  Using the "actctrl"-utility, the same features apply to the modularized
+  version like to the kernel-builtin one. (i.e. loading of firmware and
+  configuring the D-channel protocol)
+
+Loading the firmware into the card:
+
+  The firmware is supplied together with the isdn4k-utils package. It
+  can be found in the subdirectory act2000/firmware/
+
+  Assumed you have installed the utility-package correctly, the firmware
+  will be downloaded into the card using the following command:
+
+    actctrl -d idstring load /etc/isdn/bip11.btl
+
+  where idstring is the Name of the card, given during insmod-time or
+  (for kernel-builtin driver) on the kernel commandline. If only one
+  ISDN card is used, the -d isdstrin may be omitted.
+
+  For further documentation (adding more IBM Active 2000 cards), refer to
+  the manpage actctrl.8 which is included in the isdn4k-utils package.
+
diff -u --recursive --new-file v2.1.91/linux/Documentation/isdn/README.concap linux/Documentation/isdn/README.concap
--- v2.1.91/linux/Documentation/isdn/README.concap	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/isdn/README.concap	Wed Apr  1 16:20:56 1998
@@ -0,0 +1,258 @@
+Description of the "concap" encapsulation protocol interface
+============================================================
+
+The "concap" interface is intended to be used by network device
+drivers that need to process an encapsulation protocol. 
+It is assumed that the protocol interacts with a linux network device by
+- data transmission
+- connection control (establish, release)
+Thus, the mnemonic: "CONnection CONtrolling eNCAPsulation Protocol".
+
+This is currently only used inside the isdn subsystem. But it might
+also be useful to other kinds of network devices. Thus, if you want
+to suggest changes that improve usability or performace of the
+interface, please let me know. I'm willing to include them in future
+releases (even if I needed to adapt the current isdn code to the
+changed interface).
+
+
+Why is this useful?
+===================
+
+The encapsulation protocol used on top of WAN connections or permanent
+point-to-point links are frequently chosen upon bilateral agreement.
+Thus, a device driver for a certain type of hardware must support
+several different encapsulation protocols at once.
+
+The isdn device driver did already support several different
+encapsulation protocols. The encapsulation protocol is configuered by a
+user space utility (isdnctrl). The isdn network interface code then
+uses several case statements which select appropriate actions
+depending on the currently configuered encapsulation protocol.
+
+In contrast, LAN network interfaces always used a single encapsulation
+protocol which is unique to the hardware type of the interface. The LAN
+encapsulation is usually done by just sticking a header at the data. Thus,
+traditional linux network device drivers used to process the
+encapsulation protocol directly (usually by just providing a hard_header()
+method in the device structure) using some hardware type specific support
+functions. This is simple, direct and efficient. But it doesn't fit all
+the requirements for complex WAN encapsulations. 
+
+
+   The configurability of the encapsulation protocol to be used
+   makes isdn network interfaces more flexible, but also much more
+   complex than traditional lan network interfaces.
+
+
+Many Encapsulation protocols used on top of WAN connections will not just
+stick a header at the data. They also might need to set up or release
+the WAN connection. They also might want to send other data for their
+private purpose over the wire. I.e. ppp does a lot of link level
+negotiation before the first peace of user data can be transmitted.
+Such encapsulation protocols for WAN devices are typically more complex
+than encapsulation protocols for lan devices. Thus, network interfaces
+code for typical WAN devices also tends to be more more complex.
+
+
+In order to support Linux' x25 PLP implementation on top of
+isdn network interfaces I could have introduced yet another branch to
+the various case statements inside drivers/isdn/isdn_net.c.
+This eventually made isdn_net.c even more complex. In addition, it made
+isdn_net.c harder to maintain. Thus, by identifying an abstract
+interface between the network interface code and the encapsulation
+protocol, complexity could be reduced and maintainability could be
+increased.
+
+
+Likewise, a same encapsulation protocol will frequently be needed by
+several different interfaces of even different hardware type. I.e. the
+synchronous ppp implementaion used by the isdn driver and the
+asyncronous ppp implemntation used by the ppp driver have a lot of
+similar code in them. By cleanly separating the encapsulation protocol
+from the hardware specific interface stuff such code could be shared
+better in future.
+
+
+When operating over dial-up-connections (i.e. telephone lines via modem,
+non-permanent virtual circuits of wide area networks, ISDN) many
+encapsulation protocols will need to control the connection. Therfore,
+some basic connection control primitives are supported. The type and
+semantics of the connection (i.e the ISO layer where connection service
+is provided) is outside our scope and might be different depending on
+the encapsulation protocol used. I.e. for a ppp module using our service
+on top of a modem connection a connect_request will result in dialing
+a (somewhere else configured) remote phone number. For an X25-interface
+module (LAPB semantics, as defined in Documentation/networking/x25-iface.txt)
+a connect_request will ask for establishing a reliable lapb
+datalink connection.
+
+
+The encapsulation protocol currently provides the follwing
+service primitives to the network device.
+
+- create a new encapsulation protocol instance
+- delete encapsulation protocol instance and free all its resources
+- initialize (open) the encapsulation protocol instance for use.
+- deactivate (close) an encapsulation protocol instance.
+- process (xmit) data handed down by upper protocol layer
+- receive data from lower (hardware) layer
+- process connect indication from lower (hardware) layer
+- process disconnect indication from lower (hardware) layer
+
+
+The network interface driver accesses those primitives via callbacks
+provided by the encapsulation protocol instance within a
+struct concap_proto_ops.
+
+struct concap_proto_ops{
+
+	/* create a new encapsulation protocol instance of same type */
+	struct concap_proto *  (*proto_new) (void);
+
+	/* delete encapsulation protocol instance and free all its resources.
+	   cprot may no loger be referenced after calling this */
+	void (*proto_del)(struct concap_proto *cprot);
+
+	/* initialize the protocol's data. To be called at interface startup
+	   or when the device driver resets the interface. All services of the
+	   encapsulation protocol may be used after this*/
+	int (*restart)(struct concap_proto *cprot, 
+		       struct device *ndev,
+		       struct concap_device_ops *dops);
+
+	/* inactivate an encapsulation protocol instance. The encapsulation
+	   protocol may not call any *dops methods after this. */
+	int (*close)(struct concap_proto *cprot);
+
+	/* process a frame handed down to us by upper layer */
+	int (*encap_and_xmit)(struct concap_proto *cprot, struct sk_buff *skb);
+
+	/* to be called for each data entity received from lower layer*/ 
+	int (*data_ind)(struct concap_proto *cprot, struct sk_buff *skb);
+
+	/* to be called when a connection was set up/down.
+	   Protocols that don't process these primitives might fill in
+	   dummy methods here */
+	int (*connect_ind)(struct concap_proto *cprot);
+	int (*disconn_ind)(struct concap_proto *cprot);
+};
+
+
+The data structures are defined in the header file include/linux/concap.h.
+
+
+A Network interface using encapsulation protocols must also provide
+some service primitives to the encapsulation protocol:
+
+- request data beeing submitted by lower layer (device hardware) 
+- request a connection beeing set up by lower layer 
+- request a connection beeing released by lower layer
+
+The encapsulations protocol accesses those primitives via callbacks
+provided by the network interface within a struct concap_device_ops.
+
+struct concap_device_ops{
+
+	/* to request data is submitted by device*/ 
+	int (*data_req)(struct concap_proto *, struct sk_buff *);
+
+	/* Control methods must be set to NULL by devices which do not
+	   support connection control.*/
+	/* to request a connection is set up */ 
+	int (*connect_req)(struct concap_proto *);
+
+	/* to request a connection is released */
+	int (*disconn_req)(struct concap_proto *);	
+};
+
+The network interface does not explicitly provide a receive service
+because the encapsulation protocol directly calls netif_rx(). 
+
+
+
+
+An encapsulation protocol itsself is actually the
+struct concap_proto{
+	struct device *net_dev;		/* net device using our service  */
+	struct concap_device_ops *dops; /* callbacks provided by device */
+ 	struct concap_proto_ops  *pops; /* callbacks provided by us */
+	int flags;
+	void *proto_data;               /* protocol specific private data, to
+					   be accessed via *pops methods only*/
+	/*
+	  :
+	  whatever 
+	  :
+	  */
+};
+
+Most of this is filled in when the device requests the protocol to 
+be reset (opend). The network interface must provide the net_dev and
+dops pointers. Other concap_proto members should be considerd private
+data that are only accessed by the pops callback functions. Likewise,
+a concap proto should access the network device's private data
+only by means of the callbacks referred to by the dops pointer.
+
+
+A possible extended device structure which uses the connection controlling
+encapsulation services could look like this:
+
+struct concap_device{
+	struct device net_dev;
+	struct my_priv  /* device->local stuff */
+			/* the my_priv struct might contain a 
+			   struct concap_device_ops *dops;
+	                   to provide the device specific callbacks
+			*/
+	struct concap_proto *cprot;        /* callbacks provided by protocol */
+};
+
+
+
+Misc Thoughts
+=============
+
+The concept of the concap proto might help to reuse protocol code and
+reduce the complexity of certain network interface implementations.
+The trade off is that it introduces yet another procedure call layer
+when processing the protocol. This has of course some impact on
+performace. However, typically the concap interface will be used by
+devices attached to slow lines (like telephone, isdn, leased synchronous
+lines). For such slow lines, the overhead is probably neglectable.
+This might no longer hold for certain high speed WAN links (like
+ATM).
+
+
+If general linux network interfaces explicitly supported concap
+protocols (i.e. by a member struct concap_proto* in struct device)
+then the interface of the service function could be changed
+by passing a pointer of type (struct device*) instead of
+type (struct concap_proto*). Doing so would make many of the service
+functions compatible to network device support fuctions. i.e.
+
+i.e. instead of the concap protocol's service function
+
+  int (*encap_and_xmit)(struct concap_proto *cprot, struct sk_buff *skb);
+
+we could have
+
+  int (*encap_and_xmit)(struct device *ndev, struct sk_buff *skb);
+
+As this is compatible to the dev->hard_start_xmit() method, the device
+driver could directly register the concap protocol's encap_and_xmit()
+fuction as its hard_start_xmit() method. This would eliminate one
+procedure call layer.
+
+
+The device's data request function could also be defined as
+ 
+  int (*data_req)(struct device *ndev, struct sk_buff *skb);
+
+This might even allow for some protocol stacking. And the network
+interface might even register the same data_req() function directly
+as its hard_start_xmit() method when a zero layer encapsulation
+protocol is configured. Thus, eliminating the performace penalty
+of the concap interface when a trivial concap protocol is used.
+Nevertheless, the device remains able to support encapsulation
+protocol configuration.
diff -u --recursive --new-file v2.1.91/linux/Documentation/isdn/README.x25 linux/Documentation/isdn/README.x25
--- v2.1.91/linux/Documentation/isdn/README.x25	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/isdn/README.x25	Wed Apr  1 16:20:56 1998
@@ -0,0 +1,217 @@
+ 
+X25 support within isdn4linux
+
+
+This is experimental code and should be used with linux version 2.1.72.
+or later. Use it completely on your own risk.
+
+
+As new versions appear, the stuff described here might suddenly change
+or become invalid without notice.
+
+Keep in mind:
+
+You are using an experimental kernel (2.1.x series) with an experimental
+x25 protocol implementation and experimental x25-on-top-of-isdn extensions.
+Thus, be prepared to face problems related therefrom.
+
+- If you connect to an x25 neighbour not operated by yourself, ASK the
+  other side first. Be prepared that bugs in the protocol implementation
+  might result in problems (even crashing the peer, however such ugly events
+  should only happen if your peer's protocol implementation has serious bugs).
+
+- This implementation has never wiped out my whole hard disk yet. But as
+  this is experimental code, don't blame me if that happened to you. Take
+  appropriate actions (such as backing up important data) before
+  trying this code.
+
+- Monitor your isdn connections while using this software. This should
+  prevent you from undesired phone bills in case of driver problems.
+  
+ 
+
+
+How to configure the kernel
+
+ 
+The ITU-T (former CCITT) X.25 network protocol layer has been implemented
+in the Linux source tree since version 2.1.16. The isdn subsystem might be 
+useful to run X.25 on top of ISDN. If you want to try it, select
+
+   "CCITT X.25 Packet Layer"
+
+from the networking options as well as
+
+   "ISDN Support" and "X.25 PLP on Top of ISDN"
+
+from the ISDN subsystem options when you configure your kernel for
+compilation. You currently also need to enable
+"Prompt for development and/or incomplete code/drivers" from the
+"Code maturity level options" menu. For the x25trace utility to work
+you also need to enable "Packet socket" (I recommend to choose "y",
+not "m" for testing) from the networking options.
+
+
+For testing you should also select the isdnloop driver from the
+isdn subsystem's configuration menu.
+
+
+
+What's it for? How to use it?
+
+
+X25 on top of isdn might be useful with two different scenarios:
+
+- You might want to access a public X.25 data network from your Linux box.
+  You can use i4l if you were physically connected to the X.25 switch
+  by an ISDN line (leased line as well as dial up connection should work,
+  but connecting to x.25 network switches is currently untested. Testing
+  needs to be done by somebody with access to such a switch.) 
+
+- Or you might want to operate certain ISDN teleservices on 
+  your linux box. A lot of those teleservices run on top of the ISO-8208 
+  network layer protocol. ISO-8208 is essentially the same as ITU-T X.25.
+
+  Popular candidates of such teleservices are EUROFILE transfer or any
+  teleservice applying ITU-T recommendation T.90 (i.e., AFAIK, G4 Fax).
+
+To use the X.25 protocol on top of isdn, just create an isdn network
+interface as usual, configure your own and/or peer's ISDN numbers,
+and choose x25iface encapsulation by
+
+   isdnctrl encap <iface-name> x25iface.
+
+Once encap is set like this, the device can be used by the x25 packet layer.
+
+All the stuff needed for x25 is implemented inside the isdn link
+level (mainly isdn_net.c and some new source files). Thus, it should
+work with every existing HL driver. I was able to successfully open x25
+connections on top of the isdnloop driver and the hisax driver.
+"x25iface"-encapsulation bypasses demand dialing. Dialing will be
+initiated when the upper (x25 packet) layer requests the lapb datalink to
+be established. But hangup timeout is still active. The connection
+will not automatically be re-established by the isdn_net module
+itself when new data arrives after the hangup timeout. But
+the x25 network code will re-establish the datalink connection
+(resulting in re-dialing and an x25 protocol reset) when new data is
+to be transmitted. (This currently does not work properly with the
+isdnloop driver, see "known problems" below)
+
+
+In order to set up a conforming protocol stack you also need to
+specify the proper l2_prot parameter:
+
+To operate in ISO-8208  X.25 DTE-DTE mode, use
+
+   isdnctrl l2_prot <iface-name> x75i
+
+To access an X.25 network switch via isdn (your linux box is the DTE), use
+
+   isdnctrl l2_prot <iface-name> x25dte
+
+To mimic an X.25 network switch (DCE side of the connection), use
+
+   isdnctrl l2_prot <iface-name> x25dce
+
+However, x25dte or x25dce is currently not supported by any real HL
+level driver. The main difference between x75 and x25dte/dce is that
+x25d[tc]e uses fixed lap_b addresses. With x75i, the side which
+initiates the isdn connection uses the DTE's lap_b address while the
+called side used the DCE's lap_b address. Thus, l2_prot x75i will
+probably work if you access a public x25 network as long as the
+corresponding isdn connection is set up by you. However, I've never
+tested this.
+
+
+
+How to use the test installation?
+
+
+To test x25 on top of isdn, you need to get
+
+- a patched version of the "isdnctrl" program that supports setting the new
+  x25 specific parameters.
+
+- the x25-utils-2.1.x package from ftp.pspt.fi/pub/ham/linux/ax25
+  or any mirror site (i.e. ftp://ftp.gwdg.de/pub/linux/misc/ax25/).
+
+- a kernel patch that enhances isdn4linux to provide x25 network
+  interface support. (This file is part of that kernel patch).
+
+- an application that uses linux AF_X25 sockets program. 
+
+Before compiling the user level utilities make sure that the compiler/
+preprocessor will fetch the proper (patched) kernel header files. Either make
+/usr/include/linux a symbolic link pointing to your developer kernel's
+include/linux directory or set the appropriate compiler flags.
+
+It is recommended that all isdn drivers and the x25 PLP protocol 
+are compiled as loadable modules. Like this, you can recover
+from certain errors by simply unloading and reloading the modules.
+
+When all drivers and interfaces are loaded and configured you need to
+ifconfig the network interfaces up and add x25-routes to them. Use
+the usual ifconfig tool.
+
+ifconfig <iface-name> up
+
+But a special x25route tool (distributed with the x25-util package)
+is needed to set up x25 routes. I.e. 
+
+x25route add 01 <iface-name>
+
+will cause all x.25 connections to the destination x.25-address
+"01" beeing routed to your created isdn network interface.
+
+
+There are currently no real x25 applications available. However, for
+tests, the x25-utils package contains a modified version of telnet
+and telnetd that uses x25 sockets instead of tcp/ip sockets. Use
+this for your first tests. Furthermore, there is an x25.echod and a client
+named "eftp" (which contains some experimental code to download files
+from a remote eft server using the EUROfile transfer protocol).
+It available at ftp://ftp.hamburg.pop.de/pub/LOCAL/linux/i4l-eft/eftp4linux-*
+
+The x25-utility package also contains an x25trace tool that can be
+used to monitor x25 packets received by the network interfaces.
+The /proc/net/x25* files also contain useful information. 
+
+The eftp4linux test release also contains an "ix25test" script that can
+be used for testing x25 on top of isdn4linux. Edit
+this script according to your local needs and then call it as
+
+ix25test start
+
+This will set up a sample configuration using the isdnloop and hisax
+driver and create some isdn network interfaces.
+It is recommended that all other isdn drivers and the
+x25 module is unloaded before calling this script.
+
+
+
+Known problems and deficiencies:
+
+The isdnloop HL driver apparently has problems to re-establish a
+connection that has been hang up from the outgoing device. You have to
+unload the isdnloop driver after the faked isdn-connection is closed
+and insmod it again. With the Hisax driver, this problem is not present.
+
+Sometimes the x25 module cannot be unloaded (decrementation of its use
+count seems to get lost occasionally).
+
+Using the x25 based telnet and telnetd programm to establish connection
+from your own to your own computer repeatedly sometimes totally locked
+up my system. However, this kernel patch also modifies
+net/x25/af_x25.c to include a workaround. With this workaround
+enabled, my system is stable. (If you want to disable the
+workaround, just undefine ISDN_X25_FIXES in af_x25.c).
+
+The latter problem could be reproduced by using hisax as well as the
+isdnloop driver. It seems that it is not caused by the isdn code.
+Somehow, the inode of a socket is freed while a process still refers
+the socket's wait queue. This causes problems when the process tries to
+remove itsself from the wait queue (refered by the dangling
+sock->sleep pointer) before returning from a select() system call.
+
+- Henner
+
diff -u --recursive --new-file v2.1.91/linux/Documentation/serial-console.txt linux/Documentation/serial-console.txt
--- v2.1.91/linux/Documentation/serial-console.txt	Wed Dec  3 15:21:57 1997
+++ linux/Documentation/serial-console.txt	Wed Apr  1 14:58:54 1998
@@ -19,10 +19,10 @@
 			9600n8. The maximum baudrate is 115200.
 
 You can specify multiple console= options on the kernel command line.
-Output will appear on all of them. The first device will be used when
+Output will appear on all of them. The last device will be used when
 you open /dev/console. So, for example:
 
-	console=tty0 console=ttyS1,9600
+	console=ttyS1,9600 console=tty0
 
 defines that opening /dev/console will get you the current foreground
 virtual console, and kernel messages will appear on both the VGA
@@ -91,4 +91,4 @@
    for porting the patches from 2.1.4x to 2.1.6x for taking care of
    the integration of these patches into m68k, ppc and alpha.
 
-Miquel van Smoorenburg <miquels@cistron.nl>, 03-Dec-1997
+Miquel van Smoorenburg <miquels@cistron.nl>, 21-Mar-1998
diff -u --recursive --new-file v2.1.91/linux/Makefile linux/Makefile
--- v2.1.91/linux/Makefile	Thu Mar 26 15:57:02 1998
+++ linux/Makefile	Thu Mar 26 15:58:18 1998
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 1
-SUBLEVEL = 91
+SUBLEVEL = 92
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/)
 
diff -u --recursive --new-file v2.1.91/linux/arch/alpha/Makefile linux/arch/alpha/Makefile
--- v2.1.91/linux/arch/alpha/Makefile	Tue Feb 17 13:12:43 1998
+++ linux/arch/alpha/Makefile	Mon Mar 30 00:21:39 1998
@@ -10,8 +10,40 @@
 
 NM := nm -B
 
+#LINKFLAGS = -static -T arch/alpha/vmlinux.lds
+#CFLAGS := $(CFLAGS) -pipe -mno-fp-regs -ffixed-8
+
+ifdef CONFIG_CROSSCOMPILE
+# enable this for linking under OSF/1:
+LINKFLAGS = -non_shared -T 0xfffffc0000310000 -N
+else
+ elf=$(shell if $(LD) --help | grep elf64alpha >/dev/null; then echo yes; fi)
+ ifeq ($(elf),yes)
+#   LINKFLAGS = -static -Ttext 0xfffffc0000310000 -N
 LINKFLAGS = -static -T arch/alpha/vmlinux.lds
-CFLAGS := $(CFLAGS) -pipe -mno-fp-regs -ffixed-8
+ else
+   LINKFLAGS = -static -T arch/alpha/vmlinux.lds -N
+ endif
+# GNU gcc/cc1/as can use pipes instead of temporary files
+CFLAGS := $(CFLAGS) -pipe
+endif
+
+CFLAGS := $(CFLAGS) -mno-fp-regs -ffixed-8 -Wno-uninitialized
+
+# determine if we can use the BWX instructions with GAS
+$(shell rm -f ./GAS_VER)
+$(shell $(AS) --version >& ./GAS_VER)
+OLD_GAS := $(shell if cat ./GAS_VER | grep 'version 2.7' > /dev/null; then echo yes; else echo no; fi)
+$(shell rm -f ./GAS_VER)
+
+ifneq ($(OLD_GAS),yes)
+ CFLAGS := $(CFLAGS) -Wa,-m21164a -DBWX_USABLE
+
+# if PYXIS, then enable use of BWIO space
+ ifeq ($(CONFIG_ALPHA_PYXIS),y)
+  CFLAGS := $(CFLAGS) -DBWIO_ENABLED
+ endif
+endif
 
 HEAD := arch/alpha/kernel/head.o
 
@@ -23,7 +55,7 @@
   CORE_FILES := $(CORE_FILES) arch/alpha/math-emu/math-emu.o
 endif
 
-LIBS := arch/alpha/lib/lib.a $(LIBS) arch/alpha/lib/lib.a
+LIBS := $(TOPDIR)/arch/alpha/lib/lib.a $(LIBS) $(TOPDIR)/arch/alpha/lib/lib.a
 
 MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot
 
diff -u --recursive --new-file v2.1.91/linux/arch/alpha/boot/bootp.c linux/arch/alpha/boot/bootp.c
--- v2.1.91/linux/arch/alpha/boot/bootp.c	Mon Jan 12 14:51:14 1998
+++ linux/arch/alpha/boot/bootp.c	Mon Mar 30 00:21:39 1998
@@ -11,11 +11,13 @@
 #include <linux/string.h>
 #include <linux/version.h>
 #include <linux/mm.h>
+#include <linux/config.h>
 
 #include <asm/system.h>
 #include <asm/console.h>
 #include <asm/hwrpb.h>
 #include <asm/pgtable.h>
+#include <asm/io.h>
 
 #include <stdarg.h>
 
@@ -156,7 +158,8 @@
 	printk("Ok (rev %lx)\n", rev);
 	/* remove the old virtual page-table mapping */
 	L1[1] = 0;
-	flush_tlb_all();
+
+	tbia(); /* do it directly in case we are SMP */
 }
 
 static inline long load(unsigned long dst,
@@ -189,30 +192,59 @@
 
 void start_kernel(void)
 {
-	long i;
-	int nbytes;
-	char envval[256];
+	static long i;
+	static int nbytes;
+	/*
+	 * note that this crufty stuff with static and envval and envbuf
+	 * is because:
+	 *
+	 * 1. frequently, the stack is is short, and we don't want to overrun;
+	 * 2. frequently the stack is where we are going to copy the kernel to;
+	 * 3. a certain SRM console required the GET_ENV output to stack.
+	 */
+	static char envval[256];
+	char envbuf[256];
 
 	printk("Linux/AXP bootp loader for Linux " UTS_RELEASE "\n");
 	if (hwrpb.pagesize != 8192) {
-		printk("Expected 8kB pages, got %ldkB\n", hwrpb.pagesize >> 10);
+		printk("Expected 8kB pages, got %ldkB\n",
+		       hwrpb.pagesize >> 10);
 		return;
 	}
 	pal_init();
 
 	nbytes = dispatch(CCB_GET_ENV, ENV_BOOTED_OSFLAGS,
-			  envval, sizeof(envval));
-	if (nbytes < 0) {
+			  envbuf, sizeof(envbuf));
+	if (nbytes < 0 || nbytes >= sizeof(envbuf)) {
 		nbytes = 0;
 	}
-	envval[nbytes] = '\0';
-	strcpy((char*)ZERO_PAGE, envval);
-
-	printk("Loading the kernel ...\n");
+	envbuf[nbytes] = '\0';
+	memcpy(envval, envbuf, nbytes+1);
+	printk("Loading the kernel...'%s'\n", envval);
 
 	/* NOTE: *no* callbacks or printouts from here on out!!! */
 
+#if 1
+	/*
+	 * this is a hack, as some consoles seem to get virtual 20000000
+	 * (ie where the SRM console puts the kernel bootp image) memory
+	 * overlapping physical 310000 memory, which causes real problems
+	 * when attempting to copy the former to the latter... :-(
+	 *
+	 * so, we first move the kernel virtual-to-physical way above where
+	 * we physically want the kernel to end up, then copy it from there
+	 * to its final resting place... ;-}
+	 *
+	 * sigh...
+	 */
+
+        i = load(START_ADDR+(4*KERNEL_SIZE), KERNEL_ORIGIN, KERNEL_SIZE);
+        i = load(START_ADDR, START_ADDR+(4*KERNEL_SIZE), KERNEL_SIZE);
+#else
 	i = load(START_ADDR, KERNEL_ORIGIN, KERNEL_SIZE);
+#endif
+
+	strcpy((char*)ZERO_PAGE, envval);
 
 	runkernel();
 
diff -u --recursive --new-file v2.1.91/linux/arch/alpha/config.in linux/arch/alpha/config.in
--- v2.1.91/linux/arch/alpha/config.in	Tue Mar 17 22:18:13 1998
+++ linux/arch/alpha/config.in	Mon Mar 30 00:21:39 1998
@@ -6,11 +6,13 @@
 
 # clear all implied options (don't want default values for those):
 unset CONFIG_CROSSCOMPILE CONFIG_NATIVE
-unset CONFIG_ALPHA_EV4 CONFIG_ALPHA_EV5
+unset CONFIG_ALPHA_EV4 CONFIG_ALPHA_EV5 CONFIG_ALPHA_EV6
 unset CONFIG_PCI CONFIG_ALPHA_EISA
 unset CONFIG_ALPHA_LCA CONFIG_ALPHA_APECS CONFIG_ALPHA_CIA
 unset CONFIG_ALPHA_T2 CONFIG_ALPHA_PYXIS
+unset CONFIG_ALPHA_TSUNAMI CONFIG_ALPHA_MCPCIA
 unset CONFIG_ALPHA_NEED_ROUNDING_EMULATION
+unset CONFIG_ALPHA_SRM CONFIG_ALPHA_SRM_SETUP
 
 mainmenu_option next_comment
 comment 'Code maturity level options'
@@ -48,13 +50,16 @@
 	 PC164		CONFIG_ALPHA_PC164		\
 	 LX164		CONFIG_ALPHA_LX164		\
 	 SX164		CONFIG_ALPHA_SX164		\
+	 DP264		CONFIG_ALPHA_DP264		\
 	 Jensen		CONFIG_ALPHA_JENSEN		\
 	 Noname		CONFIG_ALPHA_NONAME		\
+	 Takara		CONFIG_ALPHA_TAKARA		\
 	 Mikasa		CONFIG_ALPHA_MIKASA		\
 	 Noritake	CONFIG_ALPHA_NORITAKE		\
 	 Alcor		CONFIG_ALPHA_ALCOR		\
 	 Miata		CONFIG_ALPHA_MIATA		\
 	 Sable		CONFIG_ALPHA_SABLE		\
+	 Rawhide	CONFIG_ALPHA_RAWHIDE		\
 	 AlphaBook1	CONFIG_ALPHA_BOOK1		\
 	 Ruffian	CONFIG_ALPHA_RUFFIAN		\
 	 Platform2000	CONFIG_ALPHA_P2K" Cabriolet
@@ -78,7 +83,8 @@
 	define_bool CONFIG_ALPHA_APECS y
 fi
 if [ "$CONFIG_ALPHA_EB164" = "y" -o "$CONFIG_ALPHA_PC164" = "y" \
-	-o "$CONFIG_ALPHA_ALCOR" = "y" -o "$CONFIG_ALPHA_XLT" = "y" ]
+	-o "$CONFIG_ALPHA_ALCOR" = "y" -o "$CONFIG_ALPHA_XLT" = "y" \
+	-o "$CONFIG_ALPHA_TAKARA" = "y" ]
 then
 	define_bool CONFIG_PCI y
 	define_bool CONFIG_ALPHA_EV5 y
@@ -86,9 +92,7 @@
 fi
 if [ "$CONFIG_ALPHA_MIKASA" = "y" -o "$CONFIG_ALPHA_NORITAKE" = "y" ]
 then
-	choice 'CPU daughtercard' \
-		"Pinnacle	CONFIG_ALPHA_PINNACLE \
-		Primo		CONFIG_ALPHA_PRIMO" Primo
+	bool 'EV5 CPU daughtercard (model 5/xxx)?' CONFIG_ALPHA_PRIMO
 	if [ "$CONFIG_ALPHA_PRIMO" = "y" ]
 	then
 		define_bool CONFIG_ALPHA_EV5 y
@@ -102,7 +106,13 @@
 if [ "$CONFIG_ALPHA_SABLE" = "y" ]
 then
 	define_bool CONFIG_PCI y
+	bool 'EV5 CPU(s) (model 5/xxx)?' CONFIG_ALPHA_GAMMA
+	if [ "$CONFIG_ALPHA_GAMMA" = "y" ]
+	then
+		define_bool CONFIG_ALPHA_EV5 y
+	else
 	define_bool CONFIG_ALPHA_EV4 y
+	fi
 	define_bool CONFIG_ALPHA_T2 y
 fi
 if [ "$CONFIG_ALPHA_MIATA" = "y" -o "$CONFIG_ALPHA_LX164" = "y" \
@@ -112,6 +122,18 @@
 	define_bool CONFIG_ALPHA_EV5 y
 	define_bool CONFIG_ALPHA_PYXIS y
 fi
+if [ "$CONFIG_ALPHA_DP264" = "y" ]
+then
+	define_bool CONFIG_PCI y
+	define_bool CONFIG_ALPHA_EV6 y
+	define_bool CONFIG_ALPHA_TSUNAMI y
+fi
+if [ "$CONFIG_ALPHA_RAWHIDE" = "y" ]
+then
+	define_bool CONFIG_PCI y
+	define_bool CONFIG_ALPHA_EV5 y
+	define_bool CONFIG_ALPHA_MCPCIA y
+fi
 if [ "$CONFIG_ALPHA_JENSEN" = "y" ]
 then
 	define_bool CONFIG_ALPHA_EV4 y
@@ -127,12 +149,19 @@
 	-o "$CONFIG_ALPHA_MIKASA" = "y" -o "$CONFIG_ALPHA_ALCOR" = "y" \
 	-o "$CONFIG_ALPHA_SABLE" = "y" -o "$CONFIG_ALPHA_MIATA" = "y" \
 	-o "$CONFIG_ALPHA_NORITAKE" = "y" -o "$CONFIG_ALPHA_PC164" = "y" \
-	-o "$CONFIG_ALPHA_LX164" = "y" -o "$CONFIG_ALPHA_SX164" = "y" ]
+	-o "$CONFIG_ALPHA_LX164" = "y" -o "$CONFIG_ALPHA_SX164" = "y" \
+	-o "$CONFIG_ALPHA_DP264" = "y" -o "$CONFIG_ALPHA_RAWHIDE" = "y" ]
 then
-	bool 'Using SRM as bootloader' CONFIG_ALPHA_SRM
+  bool 'Use SRM as bootloader' CONFIG_ALPHA_SRM
+  if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    if [ "$CONFIG_ALPHA_SRM" = "y" ]; then
+      bool '  Use SRM PCI setup' CONFIG_ALPHA_SRM_SETUP
+    fi
+  fi
 fi
 if [ "$CONFIG_ALPHA_ALCOR" = "y" -o "$CONFIG_ALPHA_MIKASA" = "y" \
-	-o "$CONFIG_ALPHA_SABLE" = "y" -o "$CONFIG_ALPHA_NORITAKE" = "y" ]
+	-o "$CONFIG_ALPHA_SABLE" = "y" -o "$CONFIG_ALPHA_NORITAKE" = "y" \
+	-o "$CONFIG_ALPHA_RAWHIDE" = "y" ]
 then
 	define_bool CONFIG_ALPHA_EISA y
 fi
@@ -141,8 +170,13 @@
 	define_bool CONFIG_ALPHA_AVANTI y
 fi
 
+#bool 'Echo console messages on /dev/ttyS0 (COM1)' CONFIG_SERIAL_ECHO
+
 if [ "$CONFIG_PCI" = "y" ]; then
   bool 'TGA Console Support' CONFIG_TGA_CONSOLE
+#  if [ "$CONFIG_TGA_CONSOLE" = "y" ]; then
+#    bool 'VGA Console Support' CONFIG_VGA_CONSOLE
+#  fi
   if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
     bool 'PCI bridge optimization (experimental)' CONFIG_PCI_OPTIMIZE
   fi
@@ -215,17 +249,6 @@
   source drivers/cdrom/Config.in
 fi
 endmenu
-
-# Conditionally compile in the Uniform CD-ROM driver
-if [ "$CONFIG_BLK_DEV_IDECD" = "y" -o "$CONFIG_BLK_DEV_SR" = "y" -o "$CONFIG_SBPCD" = "y" -o "$CONFIG_MCD" = "y" -o "$CONFIG_CM206" = "y" -o "$CONFIG_CDU31A" = "y" ]; then
-  define_bool CONFIG_CDROM y
-else
-  if [ "$CONFIG_BLK_DEV_IDECD" = "m" -o "$CONFIG_BLK_DEV_SR" = "m" -o "$CONFIG_SBPCD" = "m" -o "$CONFIG_MCD" = "m" -o "$CONFIG_CM206" = "m" -o "$CONFIG_CDU31A" = "m" ]; then
-    define_bool CONFIG_CDROM m
-  else
-    define_bool CONFIG_CDROM n
-  fi
-fi
 
 source fs/Config.in
 
diff -u --recursive --new-file v2.1.91/linux/arch/alpha/defconfig linux/arch/alpha/defconfig
--- v2.1.91/linux/arch/alpha/defconfig	Tue Mar 17 22:18:13 1998
+++ linux/arch/alpha/defconfig	Mon Mar 30 00:21:39 1998
@@ -27,14 +27,20 @@
 # CONFIG_ALPHA_EB64P is not set
 # CONFIG_ALPHA_EB164 is not set
 # CONFIG_ALPHA_PC164 is not set
+# CONFIG_ALPHA_LX164 is not set
+# CONFIG_ALPHA_SX164 is not set
+# CONFIG_ALPHA_DP264 is not set
 # CONFIG_ALPHA_JENSEN is not set
 # CONFIG_ALPHA_NONAME is not set
+# CONFIG_ALPHA_TAKARA is not set
 # CONFIG_ALPHA_MIKASA is not set
 # CONFIG_ALPHA_NORITAKE is not set
 CONFIG_ALPHA_ALCOR=y
 # CONFIG_ALPHA_MIATA is not set
 # CONFIG_ALPHA_SABLE is not set
+# CONFIG_ALPHA_RAWHIDE is not set
 # CONFIG_ALPHA_BOOK1 is not set
+# CONFIG_ALPHA_RUFFIAN is not set
 # CONFIG_ALPHA_P2K is not set
 CONFIG_PCI=y
 CONFIG_ALPHA_EV5=y
@@ -214,7 +220,6 @@
 # CD-ROM drivers (not for SCSI or IDE/ATAPI drives)
 #
 # CONFIG_CD_NO_IDESCSI is not set
-CONFIG_CDROM=y
 
 #
 # Filesystems
diff -u --recursive --new-file v2.1.91/linux/arch/alpha/kernel/Makefile linux/arch/alpha/kernel/Makefile
--- v2.1.91/linux/arch/alpha/kernel/Makefile	Tue Mar 10 10:03:30 1998
+++ linux/arch/alpha/kernel/Makefile	Mon Mar 30 00:21:39 1998
@@ -8,9 +8,9 @@
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
 .S.s:
-	$(CPP) -D__ASSEMBLY__ -traditional $< -o $*.s
+	$(CPP) -D__ASSEMBLY__ $(AFLAGS) -traditional    $< -o $*.s
 .S.o:
-	$(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o
+	$(CC)  -D__ASSEMBLY__ $(AFLAGS) -traditional -c $< -o $*.o
 
 all: kernel.o head.o
 
@@ -35,19 +35,29 @@
 ifdef CONFIG_ALPHA_T2
 O_OBJS   += t2.o
 endif
+ifdef CONFIG_ALPHA_TSUNAMI
+O_OBJS   += tsunami.o
+endif
+ifdef CONFIG_ALPHA_MCPCIA
+O_OBJS   += mcpcia.o
+endif
+
 ifneq ($(CONFIG_ALPHA_PC164)$(CONFIG_ALPHA_LX164),nn)
 O_OBJS   += smc37c93x.o
 endif
-ifdef CONFIG_ALPHA_SX164
+ifneq ($(CONFIG_ALPHA_SX164)$(CONFIG_ALPHA_MIATA)$(CONFIG_ALPHA_DP264),nnn)
 O_OBJS   += smc37c669.o
 endif
 
+ifdef SMP
+O_OBJS   += smp.o
+endif
 
 all: kernel.o head.o
 
 head.o: head.s
 
 head.s: head.S $(TOPDIR)/include/asm-alpha/system.h
-	$(CPP) -traditional -o $*.s $<
+	$(CPP) -traditional $(AFLAGS) -o $*.s $<
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.91/linux/arch/alpha/kernel/apecs.c linux/arch/alpha/kernel/apecs.c
--- v2.1.91/linux/arch/alpha/kernel/apecs.c	Fri Jan 30 11:28:05 1998
+++ linux/arch/alpha/kernel/apecs.c	Mon Mar 30 00:21:39 1998
@@ -18,13 +18,14 @@
 #include <asm/hwrpb.h>
 #include <asm/ptrace.h>
 
-/* NOTE: Herein are back-to-back mb insns.  They are magic. 
-   A plausible explanation is that the i/o controler does not properly
-   handle the system transaction.  Another involves timing.  Ho hum.  */
+/*
+ * NOTE: Herein lie back-to-back mb instructions.  They are magic. 
+ * One plausible explanation is that the i/o controller does not properly
+ * handle the system transaction.  Another involves timing.  Ho hum.
+ */
 
 extern struct hwrpb_struct *hwrpb;
 extern asmlinkage void wrmces(unsigned long mces);
-extern int alpha_sys_type;
 
 /*
  * BIOS32-style PCI interface:
@@ -36,13 +37,16 @@
 # define DBG(args)
 #endif
 
-#define vulp	volatile unsigned long *
 #define vuip	volatile unsigned int  *
 
 static volatile unsigned int apecs_mcheck_expected = 0;
 static volatile unsigned int apecs_mcheck_taken = 0;
-static unsigned long apecs_jd, apecs_jd1, apecs_jd2;
+static unsigned int apecs_jd, apecs_jd1, apecs_jd2;
 
+#ifdef CONFIG_ALPHA_SRM_SETUP
+unsigned int APECS_DMA_WIN_BASE = APECS_DMA_WIN_BASE_DEFAULT;
+unsigned int APECS_DMA_WIN_SIZE = APECS_DMA_WIN_SIZE_DEFAULT;
+#endif /* SRM_SETUP */
 
 /*
  * Given a bus, device, and function number, compute resulting
@@ -194,7 +198,7 @@
 		}
 
 		/* reset error status: */
-		*(vulp)APECS_IOC_DCSR = stat0;
+		*(vuip)APECS_IOC_DCSR = stat0;
 		mb();
 		wrmces(0x7);			/* reset machine check */
 		value = 0xffffffff;
@@ -269,7 +273,7 @@
 		}
 
 		/* reset error status: */
-		*(vulp)APECS_IOC_DCSR = stat0;
+		*(vuip)APECS_IOC_DCSR = stat0;
 		mb();
 		wrmces(0x7);			/* reset machine check */
 	}
@@ -424,6 +428,38 @@
 	*(vuip)APECS_IOC_TB2R = 0;
 
 #else  /* CONFIG_ALPHA_XL */
+#ifdef CONFIG_ALPHA_SRM_SETUP
+	/* check window 1 for enabled and mapped to 0 */
+	if ((*(vuip)APECS_IOC_PB1R & (1U<<19)) && (*(vuip)APECS_IOC_TB1R == 0))
+	{
+	  APECS_DMA_WIN_BASE = *(vuip)APECS_IOC_PB1R & 0xfff00000U;
+	  APECS_DMA_WIN_SIZE = *(vuip)APECS_IOC_PM1R & 0xfff00000U;
+	  APECS_DMA_WIN_SIZE += 0x00100000U;
+#if 0
+	  printk("apecs_init: using Window 1 settings\n");
+	  printk("apecs_init: PB1R 0x%x PM1R 0x%x TB1R 0x%x\n",
+     		 *(vuip)APECS_IOC_PB1R,
+		 *(vuip)APECS_IOC_PM1R,
+		 *(vuip)APECS_IOC_TB1R);
+#endif
+     	}
+	else	/* check window 2 for enabled and mapped to 0 */
+	if ((*(vuip)APECS_IOC_PB2R & (1U<<19)) && (*(vuip)APECS_IOC_TB2R == 0))
+	{
+	  APECS_DMA_WIN_BASE = *(vuip)APECS_IOC_PB2R & 0xfff00000U;
+	  APECS_DMA_WIN_SIZE = *(vuip)APECS_IOC_PM2R & 0xfff00000U;
+	  APECS_DMA_WIN_SIZE += 0x00100000U;
+#if 0
+     	  printk("apecs_init: using Window 2 settings\n");
+	  printk("apecs_init: PB2R 0x%x PM2R 0x%x TB2R 0x%x\n",
+		 *(vuip)APECS_IOC_PB2R,
+		 *(vuip)APECS_IOC_PM2R,
+		 *(vuip)APECS_IOC_TB2R);
+#endif
+     	}
+	else /* we must use our defaults... */
+#endif /* SRM_SETUP */
+	{
 	/*
 	 * Set up the PCI->physical memory translation windows.
 	 * For now, window 2 is disabled.  In the future, we may
@@ -435,9 +471,11 @@
 	*(vuip)APECS_IOC_PB1R  = 1U<<19 | (APECS_DMA_WIN_BASE & 0xfff00000U);
 	*(vuip)APECS_IOC_PM1R  = (APECS_DMA_WIN_SIZE - 1) & 0xfff00000U;
 	*(vuip)APECS_IOC_TB1R  = 0;
+	}
 #endif /* CONFIG_ALPHA_XL */
 
 #ifdef CONFIG_ALPHA_CABRIOLET
+#ifdef NO_LONGER_NEEDED_I_HOPE
 	/*
 	 * JAE: HACK!!! for now, hardwire if configured...
 	 * davidm: Older miniloader versions don't set the clock frequency
@@ -461,6 +499,7 @@
 	      sum += *l;
 	    hwrpb->chksum = sum;
 	}
+#endif /* NO_LONGER_NEEDED_I_HOPE */
 #endif /* CONFIG_ALPHA_CABRIOLET */
 
        /*
@@ -483,15 +522,15 @@
 
 int apecs_pci_clr_err(void)
 {
-	apecs_jd = *(vulp)APECS_IOC_DCSR;
+	apecs_jd = *(vuip)APECS_IOC_DCSR;
 	if (apecs_jd & 0xffe0L) {
-		apecs_jd1 = *(vulp)APECS_IOC_SEAR;
-		*(vulp)APECS_IOC_DCSR = apecs_jd | 0xffe1L;
-		apecs_jd = *(vulp)APECS_IOC_DCSR;
+		apecs_jd1 = *(vuip)APECS_IOC_SEAR;
+		*(vuip)APECS_IOC_DCSR = apecs_jd | 0xffe1L;
+		apecs_jd = *(vuip)APECS_IOC_DCSR;
 		mb();
 	}
-	*(vulp)APECS_IOC_TBIA = APECS_IOC_TBIA;
-	apecs_jd2 = *(vulp)APECS_IOC_TBIA;
+	*(vuip)APECS_IOC_TBIA = (unsigned int)APECS_IOC_TBIA;
+	apecs_jd2 = *(vuip)APECS_IOC_TBIA;
 	mb();
 	return 0;
 }
diff -u --recursive --new-file v2.1.91/linux/arch/alpha/kernel/bios32.c linux/arch/alpha/kernel/bios32.c
--- v2.1.91/linux/arch/alpha/kernel/bios32.c	Thu Mar 26 15:57:02 1998
+++ linux/arch/alpha/kernel/bios32.c	Mon Mar 30 00:21:39 1998
@@ -25,6 +25,7 @@
  */
 #include <linux/config.h>
 #include <linux/kernel.h>
+#include <linux/tasks.h>
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
 #include <linux/init.h>
@@ -63,6 +64,8 @@
 #include <asm/hwrpb.h>
 #include <asm/io.h>
 #include <asm/uaccess.h>
+#include <asm/segment.h>
+#include <asm/system.h>
 
 
 #define KB		1024
@@ -70,7 +73,9 @@
 #define GB		(1024*MB)
 
 #define MAJOR_REV	0
-#define MINOR_REV	3
+
+/* minor revision 4, add multi-PCI handling */
+#define MINOR_REV	4
 
 /*
  * Align VAL to ALIGN, which must be a power of two.
@@ -78,7 +83,20 @@
 #define ALIGN(val,align)	(((val) + ((align) - 1)) & ~((align) - 1))
 
 
+#if defined(CONFIG_ALPHA_MCPCIA) || defined(CONFIG_ALPHA_TSUNAMI)
+/* multiple PCI bus machines */
+/* make handle from bus number */
+extern struct linux_hose_info *bus2hose[256];
+#define HANDLE(b) (((unsigned long)(bus2hose[(b)]->pci_hose_index)&3)<<32)
+#define DEV_IS_ON_PRIMARY(dev) \
+	(bus2hose[(dev)->bus->number]->pci_first_busno == (dev)->bus->number)
+#else /* MCPCIA || TSUNAMI */
+#define HANDLE(b) (0)
+#define DEV_IS_ON_PRIMARY(dev) ((dev)->bus->number == 0)
+#endif /* MCPCIA || TSUNAMI */
 /*
+ * PCI_MODIFY
+ *
  * Temporary internal macro.  If this 0, then do not write to any of
  * the PCI registers, merely read them (i.e., use configuration as
  * determined by SRM).  The SRM seem do be doing a less than perfect
@@ -95,7 +113,18 @@
  * the graphics card---there have been some rumor that the #9 BIOS
  * incorrectly resets that address to 0...).
  */
+#ifdef CONFIG_ALPHA_SRM_SETUP
+#define PCI_MODIFY		0
+static struct pci_dev *irq_dev_to_reset[16];
+static unsigned char irq_to_reset[16];
+static int irq_reset_count = 0;
+static struct pci_dev *io_dev_to_reset[16];
+static unsigned char io_reg_to_reset[16];
+static unsigned int io_to_reset[16];
+static int io_reset_count = 0;
+#else /* SRM_SETUP */
 #define PCI_MODIFY		1
+#endif /* SRM_SETUP */
 
 extern struct hwrpb_struct *hwrpb;
 
@@ -103,9 +132,7 @@
 #if defined(CONFIG_ALPHA_PC164) || defined(CONFIG_ALPHA_LX164)
 extern int SMC93x_Init(void);
 #endif
-#ifdef CONFIG_ALPHA_SX164
 extern int SMC669_Init(void);
-#endif
 #ifdef CONFIG_ALPHA_MIATA
 static int es1888_init(void);
 #endif
@@ -115,7 +142,7 @@
 /*
  * NOTE: we can't just blindly use 64K for machines with EISA busses; they
  * may also have PCI-PCI bridges present, and then we'd configure the bridge
- * incorrectly
+ * incorrectly.
  *
  * Also, we start at 0x8000 or 0x9000, in hopes to get all devices'
  * IO space areas allocated *before* 0xC000; this is because certain
@@ -123,12 +150,17 @@
  * accesses to probe the bus. If a device's registers appear at 0xC000,
  * it may see an INx/OUTx at that address during BIOS emulation of the
  * VGA BIOS, and some cards, notably Adaptec 2940UW, take mortal offense.
+ *
+ * Note that we may need this stuff for SRM_SETUP also, since certain
+ * SRM consoles screw up and allocate I/O space addresses > 64K behind
+ * PCI-to_PCI bridges, which can't pass I/O addresses larger than 64K, AFAIK.
  */
 #if defined(CONFIG_ALPHA_EISA)
-static unsigned int    io_base  = 0x9000;      /* start above 8th slot */
+#define DEFAULT_IO_BASE 0x9000 /* start above 8th slot */
 #else
-static unsigned int    io_base  = 0x8000;
+#define DEFAULT_IO_BASE 0x8000 /* start at 8th slot */
 #endif
+static unsigned int    io_base;
 
 #if defined(CONFIG_ALPHA_XL)
 /*
@@ -142,7 +174,7 @@
  * We accept the risk that a broken Myrinet card will be put into a true XL
  * and thus can more easily run into the problem described below.
  */
-static unsigned int mem_base = 16*MB + 2*MB; /* 16M to 64M-1 is avail */
+#define DEFAULT_MEM_BASE (16*MB + 2*MB) /* 16M to 64M-1 is avail */
 
 #elif defined(CONFIG_ALPHA_LCA) || defined(CONFIG_ALPHA_APECS)
 /*
@@ -154,7 +186,7 @@
  * However, APECS and LCA have only 34 bits for physical addresses, thus
  * limiting PCI bus memory addresses for SPARSE access to be less than 128Mb.
  */
-static unsigned int mem_base = 64*MB + 2*MB;
+#define DEFAULT_MEM_BASE (64*MB + 2*MB)
 
 #else
 /*
@@ -166,9 +198,10 @@
  * Because CIA and PYXIS and T2 have more bits for physical addresses,
  * they support an expanded range of SPARSE memory addresses.
  */
-static unsigned int	mem_base = 128*MB + 16*MB;
+#define DEFAULT_MEM_BASE (128*MB + 16*MB)
 
 #endif
+static unsigned int mem_base; 
 
 /*
  * Disable PCI device DEV so that it does not respond to I/O or memory
@@ -179,7 +212,6 @@
 	struct pci_bus *bus;
 	unsigned short cmd;
 
-#ifdef CONFIG_ALPHA_EISA
 	/*
 	 * HACK: the PCI-to-EISA bridge does not seem to identify
 	 *       itself as a bridge... :-(
@@ -189,15 +221,17 @@
 		DBG_DEVS(("disable_dev: ignoring PCEB...\n"));
 		return;
 	}
-#endif
-#ifdef CONFIG_ALPHA_SX164
+
+	/*
+	 * we don't have code that will init the CYPRESS bridge correctly
+	 * so we do the next best thing, and depend on the previous
+	 * console code to do the right thing, and ignore it here... :-\
+	 */
 	if (dev->vendor == PCI_VENDOR_ID_CONTAQ &&
-	    /* FIXME: We want a symbolic device name here.  */
-	    dev->device == 0xc693) {
+	    dev->device == PCI_DEVICE_ID_CONTAQ_82C693) {
 		DBG_DEVS(("disable_dev: ignoring CYPRESS bridge...\n"));
 		return;
 	}
-#endif
 
 	bus = dev->bus;
 	pcibios_read_config_word(bus->number, dev->devfn, PCI_COMMAND, &cmd);
@@ -211,16 +245,16 @@
 /*
  * Layout memory and I/O for a device:
  */
-#define MAX(val1, val2) ((val1) > (val2) ? val1 : val2)
+#define MAX(val1, val2) ((val1) > (val2) ? (val1) : (val2))
 
 static void layout_dev(struct pci_dev *dev)
 {
 	struct pci_bus *bus;
 	unsigned short cmd;
-	unsigned int base, mask, size, reg;
+	unsigned int base, mask, size, off;
 	unsigned int alignto;
+	unsigned long handle;
 
-#ifdef CONFIG_ALPHA_EISA
 	/*
 	 * HACK: the PCI-to-EISA bridge does not seem to identify
 	 *       itself as a bridge... :-(
@@ -230,32 +264,39 @@
 		DBG_DEVS(("layout_dev: ignoring PCEB...\n"));
 		return;
 	}
-#endif
-#ifdef CONFIG_ALPHA_SX164
+
+	/*
+	 * we don't have code that will init the CYPRESS bridge correctly
+	 * so we do the next best thing, and depend on the previous
+	 * console code to do the right thing, and ignore it here... :-\
+	 */
 	if (dev->vendor == PCI_VENDOR_ID_CONTAQ &&
-	    dev->device == 0xc693) {
+	    dev->device == PCI_DEVICE_ID_CONTAQ_82C693) {
 		DBG_DEVS(("layout_dev: ignoring CYPRESS bridge...\n"));
 		return;
 	}
-#endif
 
 	bus = dev->bus;
 	pcibios_read_config_word(bus->number, dev->devfn, PCI_COMMAND, &cmd);
 
-	for (reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg += 4) {
+	for (off = PCI_BASE_ADDRESS_0; off <= PCI_BASE_ADDRESS_5; off += 4) {
 		/*
 		 * Figure out how much space and of what type this
 		 * device wants.
 		 */
-		pcibios_write_config_dword(bus->number, dev->devfn, reg,
+		pcibios_write_config_dword(bus->number, dev->devfn, off,
 					   0xffffffff);
-		pcibios_read_config_dword(bus->number, dev->devfn, reg, &base);
+		pcibios_read_config_dword(bus->number, dev->devfn, off, &base);
 		if (!base) {
 			/* this base-address register is unused */
-			dev->base_address[(reg - PCI_BASE_ADDRESS_0)>>2] = 0;
+			dev->base_address[PCI_BASE_INDEX(off)] = 0;
 			continue;
 		}
 
+		DBG_DEVS(("layout_dev: slot %d fn %d off 0x%x base 0x%x\n",
+			  PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
+			  off, base));
+
 		/*
 		 * We've read the base address register back after
 		 * writing all ones and so now we must decode it.
@@ -281,11 +322,13 @@
 			base = ALIGN(io_base, alignto);
 			io_base = base + size;
 			pcibios_write_config_dword(bus->number, dev->devfn, 
-						   reg, base | 0x1);
-			dev->base_address[(reg - PCI_BASE_ADDRESS_0)>>2]
-				= base | 0x1;
-			DBG_DEVS(("layout_dev: dev 0x%x IO @ 0x%x (0x%x)\n",
-				  dev->device, base, size));
+						   off, base | 0x1);
+
+			handle = HANDLE(bus->number) | base | 1;
+			dev->base_address[PCI_BASE_INDEX(off)] = handle;
+
+			DBG_DEVS(("layout_dev: dev 0x%x IO @ 0x%lx (0x%x)\n",
+				  dev->device, handle, size));
 		} else {
 			unsigned int type;
 			/*
@@ -306,7 +349,7 @@
 				       "slot %d, function %d: \n",
 				       PCI_SLOT(dev->devfn),
 				       PCI_FUNC(dev->devfn));
-				reg += 4;	/* skip extra 4 bytes */
+				off += 4;	/* skip extra 4 bytes */
 				continue;
 
 			case PCI_BASE_ADDRESS_MEM_TYPE_1M:
@@ -365,10 +408,11 @@
 			}
 			mem_base = base + size;
 			pcibios_write_config_dword(bus->number, dev->devfn,
-						   reg, base);
-			dev->base_address[(reg-PCI_BASE_ADDRESS_0)>>2] = base;
-			DBG_DEVS(("layout_dev: dev 0x%x MEM @ 0x%x (0x%x)\n",
-				  dev->device, base, size));
+						   off, base);
+			handle = HANDLE(bus->number) | base;
+			dev->base_address[PCI_BASE_INDEX(off)] = handle;
+			DBG_DEVS(("layout_dev: dev 0x%x MEM @ 0x%lx (0x%x)\n",
+				  dev->device, handle, size));
 		}
 	}
 
@@ -397,16 +441,17 @@
 }
 
 
-static void layout_bus(struct pci_bus *bus)
+static int layout_bus(struct pci_bus *bus)
 {
 	unsigned int l, tio, bio, tmem, bmem;
 	struct pci_bus *child;
 	struct pci_dev *dev;
+	int found_vga = 0;
 
 	DBG_DEVS(("layout_bus: starting bus %d\n", bus->number));
 
 	if (!bus->devices && !bus->children)
-		return;
+		return 0;
 
 	/*
 	 * Align the current bases on appropriate boundaries (4K for
@@ -424,6 +469,8 @@
 	 * devices.  They'll be re-enabled only once all address
 	 * decoders are programmed consistently.
 	 */
+	DBG_DEVS(("layout_bus: disable_dev for bus %d\n", bus->number));
+
 	for (dev = bus->devices; dev; dev = dev->sibling) {
 		if ((dev->class >> 16 != PCI_BASE_CLASS_BRIDGE) ||
 		    (dev->class >> 8 == PCI_CLASS_BRIDGE_PCMCIA)) {
@@ -441,6 +488,8 @@
 		    (dev->class >> 8 == PCI_CLASS_BRIDGE_PCMCIA)) {
 			layout_dev(dev);
 		}
+		if ((dev->class >> 8) == PCI_CLASS_DISPLAY_VGA)
+			found_vga = 1;
 	}
 	/*
 	 * Recursively allocate space for all of the sub-buses:
@@ -448,7 +497,7 @@
 	DBG_DEVS(("layout_bus: starting bus %d children\n", bus->number));
 
     	for (child = bus->children; child; child = child->next) {
-		layout_bus(child);
+		found_vga += layout_bus(child);
 	}
 	/*
 	 * Align the current bases on 4K and 1MB boundaries:
@@ -458,6 +507,8 @@
 
 	if (bus->self) {
 		struct pci_dev *bridge = bus->self;
+
+		DBG_DEVS(("layout_bus: config bus %d bridge\n", bus->number));
 		/*
 		 * Set up the top and bottom of the PCI I/O segment
 		 * for this bus.
@@ -481,10 +532,13 @@
 		pcibios_write_config_dword(bridge->bus->number, bridge->devfn,
 					   0x24, 0x0000ffff);
 		/*
-		 * Tell bridge that there is an ISA bus in the system:
+		 * Tell bridge that there is an ISA bus in the system,
+		 * and (possibly) a VGA as well.
 		 */
+		l = 0x00040000; /* ISA present */
+		if (found_vga) l |= 0x00080000; /* VGA present */
 		pcibios_write_config_dword(bridge->bus->number, bridge->devfn,
-					   0x3c, 0x00040000);
+					   0x3c, l);
 		/*
 		 * Clear status bits, enable I/O (for downstream I/O),
 		 * turn on master enable (for upstream I/O), turn on
@@ -494,6 +548,8 @@
 		pcibios_write_config_dword(bridge->bus->number, bridge->devfn,
 					   0x4, 0xffff0007);
 	}
+	DBG_DEVS(("layout_bus: bus %d finished\n", bus->number));
+	return found_vga;
 }
 
 #endif /* !PCI_MODIFY */
@@ -633,6 +689,76 @@
 	return (((pin-1) + slot) % 4) + 1;
 }
 
+#ifdef CONFIG_ALPHA_SRM_SETUP
+/* look for mis-configured devices' I/O space addresses behind bridges */
+static void check_behind_io(struct pci_dev *dev)
+{
+	struct pci_bus *bus = dev->bus;
+	unsigned int reg, orig_base, new_base, found_one = 0;
+
+	for (reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg += 4) {
+		/* read the current setting, check for I/O space and >= 64K */
+		pcibios_read_config_dword(bus->number, dev->devfn, reg, &orig_base);
+		if (!orig_base || !(orig_base & PCI_BASE_ADDRESS_SPACE_IO))
+			continue; /* unused or non-IO */
+		if (orig_base < 64*1024) {
+#if 1
+printk("check_behind_io: ALREADY OK! bus %d slot %d base 0x%x\n",
+       bus->number, PCI_SLOT(dev->devfn), orig_base);
+#endif
+			if (orig_base & ~1)
+			  continue; /* OK! */
+			orig_base = 0x12001; /* HACK! FIXME!! */
+		}
+
+		/* HACK ALERT! for now, just subtract 32K from the
+		   original address, which should give us addresses
+		   in the range 0x8000 and up */
+		new_base = orig_base - 0x8000;
+#if 1
+printk("check_behind_io: ALERT! bus %d slot %d old 0x%x new 0x%x\n",
+       bus->number, PCI_SLOT(dev->devfn), orig_base, new_base);
+#endif
+		pcibios_write_config_dword(bus->number, dev->devfn,
+					   reg, new_base);
+
+		io_dev_to_reset[io_reset_count] = dev;
+		io_reg_to_reset[io_reset_count] = reg;
+		io_to_reset[io_reset_count] = orig_base;
+		io_reset_count++;
+		found_one++;
+	} /* end for-loop */
+
+	/* if any were modified, gotta hack the bridge IO limits too... */
+	if (found_one) {
+	    if (bus->self) {
+		struct pci_dev *bridge = bus->self;
+		unsigned int l;
+		/*
+		 * Set up the top and bottom of the PCI I/O segment
+		 * for this bus.
+		 */
+		pcibios_read_config_dword(bridge->bus->number,
+					  bridge->devfn, 0x1c, &l);
+#if 1
+printk("check_behind_io: ALERT! bus %d slot %d oldLIM 0x%x\n",
+       bus->number, PCI_SLOT(bridge->devfn), l);
+#endif
+		l = (l & 0xffff0000U) | 0xf080U; /* give it ALL */
+		pcibios_write_config_dword(bridge->bus->number,
+					   bridge->devfn, 0x1c, l);
+		pcibios_write_config_dword(bridge->bus->number,
+					   bridge->devfn,
+					   0x3c, 0x00040000);
+		pcibios_write_config_dword(bridge->bus->number,
+					   bridge->devfn,
+					   0x4, 0xffff0007);
+	    } else
+	        printk("check_behind_io: WARNING! bus->self NULL\n");
+	}
+}
+#endif /* CONFIG_ALPHA_SRM_SETUP */
+
 /*
  * Most evaluation boards share most of the fixup code, which is isolated
  * here.  This function is declared "inline" as only one platform will ever
@@ -644,7 +770,7 @@
 	     char irq_tab[max_idsel - min_idsel + 1][irqs_per_slot],
 	     long ide_base)
 {
-	struct pci_dev *dev;
+	struct pci_dev *dev, *curr;
 	unsigned char pin;
 	unsigned char slot;
 
@@ -652,12 +778,18 @@
 	 * Go through all devices, fixing up irqs as we see fit:
 	 */
 	for (dev = pci_devices; dev; dev = dev->next) {
-		if ((dev->class >> 16 != PCI_BASE_CLASS_BRIDGE
-		     /* PCEB (PCI to EISA bridge) does not identify
-		        itself as a bridge... :-P */
-		     && !(dev->vendor == PCI_VENDOR_ID_INTEL &&
-			  dev->device == PCI_DEVICE_ID_INTEL_82375))
-                    || dev->class >> 8 == PCI_CLASS_BRIDGE_PCMCIA) {
+		if (dev->class >> 16 != PCI_BASE_CLASS_BRIDGE ||
+		    dev->class >> 8 == PCI_CLASS_BRIDGE_PCMCIA) {
+			/*
+			 * HACK: the PCI-to-EISA bridge appears not to identify
+			 *       itself as a bridge... :-(
+			 */
+			if (dev->vendor == PCI_VENDOR_ID_INTEL &&
+			    dev->device == PCI_DEVICE_ID_INTEL_82375) {
+				DBG_DEVS(("common_fixup: ignoring PCEB...\n"));
+				continue;
+			}
+
 			/*
 			 * This device is not on the primary bus, we need
 			 * to figure out which interrupt pin it will come
@@ -668,45 +800,100 @@
 			 * the inline static routine above).
 			 */
 			dev->irq = 0;
-			if (dev->bus->number != 0) {
-				struct pci_dev *curr = dev;
+			if (!DEV_IS_ON_PRIMARY(dev)) {
 				/* read the pin and do the PCI-PCI bridge
 				   interrupt pin swizzle */
 				pcibios_read_config_byte(dev->bus->number,
 							 dev->devfn,
 							 PCI_INTERRUPT_PIN,
 							 &pin);
-				/* cope with 0 */
-				if (pin == 0)
+				/* cope with 0 and illegal */
+				if (pin == 0 || pin > 4)
 					pin = 1;
 				/* follow the chain of bridges, swizzling
 				   as we go */
+				curr = dev;
 #if defined(CONFIG_ALPHA_MIATA)
+				/* check first for the built-in bridge */
+				if ((PCI_SLOT(dev->bus->self->devfn) == 8) ||
+				    (PCI_SLOT(dev->bus->self->devfn) == 20)) {
 				slot = PCI_SLOT(dev->devfn) + 5;
 				DBG_DEVS(("MIATA: bus 1 slot %d pin %d"
 					  " irq %d min_idsel %d\n",
 					  PCI_SLOT(dev->devfn), pin,
 					  irq_tab[slot - min_idsel][pin],
 					  min_idsel));
+				}
+				else /* must be a card-based bridge */
+				{
+				do {
+				  if ((PCI_SLOT(curr->bus->self->devfn) == 8) ||
+				      (PCI_SLOT(curr->bus->self->devfn) == 20))
+				    {
+				      slot = PCI_SLOT(curr->devfn) + 5;
+				      break;
+				    }
+				        /* swizzle */
+				    pin = bridge_swizzle(
+						  pin, PCI_SLOT(curr->devfn)) ;
+				    /* move up the chain of bridges */
+				    curr = curr->bus->self ;
+				    /* slot of the next bridge. */
+				    slot = PCI_SLOT(curr->devfn);
+				  } while (curr->bus->self) ;
+				}
 #elif defined(CONFIG_ALPHA_NORITAKE)
-				/* WAG Alert! */
-				slot = PCI_SLOT(dev->devfn) + 14;
+				/* check first for the built-in bridge */
+				if (PCI_SLOT(dev->bus->self->devfn) == 8) {
+				  slot = PCI_SLOT(dev->devfn) + 15; /* WAG! */
 				DBG_DEVS(("NORITAKE: bus 1 slot %d pin %d"
-					  " irq %d min_idsel %d\n",
+					    "irq %d min_idsel %ld\n",
 					  PCI_SLOT(dev->devfn), pin,
 					  irq_tab[slot - min_idsel][pin],
 					  min_idsel));
-#else
+				}
+				else /* must be a card-based bridge */
+				{
 				do {
+				    if (PCI_SLOT(curr->bus->self->devfn) == 8) {
+				      slot = PCI_SLOT(curr->devfn) + 15;
+				      break;
+				    }
 					/* swizzle */
-					pin = bridge_swizzle(pin, PCI_SLOT(curr->devfn));
+				    pin = bridge_swizzle(
+						pin, PCI_SLOT(curr->devfn)) ;
+				    /* move up the chain of bridges */
+				    curr = curr->bus->self ;
+				    /* slot of the next bridge. */
+				    slot = PCI_SLOT(curr->devfn);
+				  } while (curr->bus->self) ;
+				}
+#else /* everyone but MIATA and NORITAKE */
+				DBG_DEVS(("common_fixup: bus %d slot %d pin %d "
+					  "irq %d min_idsel %ld\n",
+					  curr->bus->number,
+					  PCI_SLOT(dev->devfn), pin,
+					  irq_tab[slot - min_idsel][pin],
+					  min_idsel));
+				do {
+				  /* swizzle */
+				  pin =
+				    bridge_swizzle(pin, PCI_SLOT(curr->devfn));
 					/* move up the chain of bridges */
 					curr = curr->bus->self;
 				} while (curr->bus->self);
 				/* The slot is the slot of the last bridge. */
 				slot = PCI_SLOT(curr->devfn);
-#endif /* MIATA */
-			} else {
+#endif
+#ifdef CONFIG_ALPHA_SRM_SETUP
+				/*
+				 * must make sure that SRM didn't screw up
+				 * and allocate an address > 64K for I/O
+				 * space behind a PCI-PCI bridge
+				*/
+				check_behind_io(dev);
+#endif /* CONFIG_ALPHA_SRM_SETUP */
+			} else { /* just a device on a primary bus */
 				/* work out the slot */
 				slot = PCI_SLOT(dev->devfn);
 				/* read the pin */
@@ -714,16 +901,48 @@
 							 dev->devfn,
 							 PCI_INTERRUPT_PIN,
 							 &pin);
+				DBG_DEVS(("common_fixup: bus %d slot %d"
+					  " pin %d irq %d min_idsel %ld\n",
+					  dev->bus->number, slot, pin,
+					  irq_tab[slot - min_idsel][pin],
+					  min_idsel));
+				/* cope with 0 and illegal */
+				if (pin == 0 || pin > 4)
+					pin = 1;
 			}
 			if (irq_tab[slot - min_idsel][pin] != -1)
 				dev->irq = irq_tab[slot - min_idsel][pin];
-#if PCI_MODIFY
-			/* tell the device: */
-			pcibios_write_config_byte(dev->bus->number,
+#ifdef CONFIG_ALPHA_RAWHIDE
+			dev->irq +=
+			    24 * bus2hose[dev->bus->number]->pci_hose_index;
+#endif /* RAWHIDE */
+#ifdef CONFIG_ALPHA_SRM
+			{
+			  unsigned char irq_orig;
+			  /* read the original SRM-set IRQ and tell */
+			  pcibios_read_config_byte(dev->bus->number,
 						  dev->devfn,
 						  PCI_INTERRUPT_LINE,
-						  dev->irq);
-#endif
+						    &irq_orig);
+			  if (irq_orig != dev->irq) {
+			    DBG_DEVS(("common_fixup: bus %d slot 0x%x "
+				      "SRM IRQ 0x%x changed to 0x%x\n",
+				      dev->bus->number,PCI_SLOT(dev->devfn),
+				      irq_orig, dev->irq));
+#ifdef CONFIG_ALPHA_SRM_SETUP
+			    irq_dev_to_reset[irq_reset_count] = dev;
+			    irq_to_reset[irq_reset_count] = irq_orig;
+			    irq_reset_count++;
+#endif /* CONFIG_ALPHA_SRM_SETUP */
+			  }
+			}
+#endif /* SRM */
+
+			/* always tell the device, so the driver knows what is
+			 * the real IRQ to use; the device does not use it.
+			 */
+			pcibios_write_config_byte(dev->bus->number, dev->devfn,
+						  PCI_INTERRUPT_LINE, dev->irq);
 
 			DBG_DEVS(("common_fixup: bus %d slot 0x%x"
 				  " VID 0x%x DID 0x%x\n"
@@ -737,11 +956,24 @@
 			 * if it's a VGA, enable its BIOS ROM at C0000
 			 */
 			if ((dev->class >> 8) == PCI_CLASS_DISPLAY_VGA) {
-				pcibios_write_config_dword(dev->bus->number,
+			  /* but if its a Cirrus 543x/544x DISABLE it, */
+			  /* since enabling ROM disables the memory... */
+			  if ((dev->vendor == PCI_VENDOR_ID_CIRRUS) &&
+			      (dev->device >= 0x00a0) &&
+			      (dev->device <= 0x00ac)) {
+				  pcibios_write_config_dword(
+					dev->bus->number,
+					dev->devfn,
+					PCI_ROM_ADDRESS,
+					0x00000000);
+			  } else {
+				  pcibios_write_config_dword(
+					dev->bus->number,
 							   dev->devfn,
 							   PCI_ROM_ADDRESS,
 							   0x000c0000 | PCI_ROM_ADDRESS_ENABLE);
 			}
+			}
 			/*
 			 * if it's a SCSI, disable its BIOS ROM
 			 */
@@ -752,46 +984,6 @@
 							   0x0000000);
 			}
 		}
-#ifdef CONFIG_ALPHA_SX164
-		/* If it the CYPRESS PCI-ISA bridge, disable IDE
-		   interrupt routing through PCI (ie do through PIC).  */
-		else if (dev->vendor == PCI_VENDOR_ID_CONTAQ &&
-			 dev->device == 0xc693 &&
-			 PCI_FUNC(dev->devfn) == 0) {
-			pcibios_write_config_word(dev->bus->number,
-						  dev->devfn, 0x04, 0x0007);
-
-			pcibios_write_config_byte(dev->bus->number,
-						  dev->devfn, 0x40, 0x80);
-			pcibios_write_config_byte(dev->bus->number,
-						  dev->devfn, 0x41, 0x80);
-			pcibios_write_config_byte(dev->bus->number,
-						  dev->devfn, 0x42, 0x80);
-			pcibios_write_config_byte(dev->bus->number,
-						  dev->devfn, 0x43, 0x80);
-			pcibios_write_config_byte(dev->bus->number,
-						  dev->devfn, 0x44, 0x27);
-			pcibios_write_config_byte(dev->bus->number,
-						  dev->devfn, 0x45, 0xe0);
-			pcibios_write_config_byte(dev->bus->number,
-						  dev->devfn, 0x48, 0xf0);
-			pcibios_write_config_byte(dev->bus->number,
-						  dev->devfn, 0x49, 0x40);
-			pcibios_write_config_byte(dev->bus->number,
-						  dev->devfn, 0x4a, 0x00);
-			pcibios_write_config_byte(dev->bus->number,
-						  dev->devfn, 0x4b, 0x80);
-			pcibios_write_config_byte(dev->bus->number,
-						  dev->devfn, 0x4c, 0x80);
-			pcibios_write_config_byte(dev->bus->number,
-						  dev->devfn, 0x4d, 0x70);
-
-			outb(0, DMA1_RESET_REG);
-			outb(0, DMA2_RESET_REG);
-			outb(DMA_MODE_CASCADE, DMA2_MODE_REG);
-			outb(0, DMA2_MASK_REG);
-		}
-#endif /* SX164 */
 	}
 	if (ide_base) {
 		enable_ide(ide_base);
@@ -814,6 +1006,7 @@
 static inline void eb66p_fixup(void)
 {
 	static char irq_tab[5][5] __initlocaldata = {
+		/*INT  INTA  INTB  INTC   INTD */
 		{16+0, 16+0, 16+5,  16+9, 16+13},  /* IdSel 6,  slot 0, J25 */
 		{16+1, 16+1, 16+6, 16+10, 16+14},  /* IdSel 7,  slot 1, J26 */
 		{  -1,   -1,   -1,    -1,    -1},  /* IdSel 8,  SIO         */
@@ -825,8 +1018,8 @@
 
 
 /*
- * The PC164/LX164 has 19 PCI interrupts, four from each of the four PCI
- * slots, the SIO, PCI/IDE, and USB.
+ * The PC164 and LX164 have 19 PCI interrupts, four from each of the four
+ * PCI slots, the SIO, PCI/IDE, and USB.
  * 
  * Each of the interrupts can be individually masked. This is
  * accomplished by setting the appropriate bit in the mask register.
@@ -901,6 +1094,7 @@
 static inline void cabriolet_fixup(void)
 {
 	static char irq_tab[5][5] __initlocaldata = {
+		/*INT   INTA  INTB  INTC   INTD */
 		{ 16+2, 16+2, 16+7, 16+11, 16+15}, /* IdSel 5,  slot 2, J21 */
 		{ 16+0, 16+0, 16+5,  16+9, 16+13}, /* IdSel 6,  slot 0, J19 */
 		{ 16+1, 16+1, 16+6, 16+10, 16+14}, /* IdSel 7,  slot 1, J20 */
@@ -957,6 +1151,7 @@
 static inline void eb66_and_eb64p_fixup(void)
 {
 	static char irq_tab[5][5] __initlocaldata = {
+		/*INT  INTA  INTB  INTC   INTD */
 		{16+7, 16+7, 16+7, 16+7,  16+7},  /* IdSel 5,  slot ?, ?? */
 		{16+0, 16+0, 16+2, 16+4,  16+9},  /* IdSel 6,  slot ?, ?? */
 		{16+1, 16+1, 16+3, 16+8, 16+10},  /* IdSel 7,  slot ?, ?? */
@@ -968,7 +1163,7 @@
 
 
 /*
- * Fixup configuration for MIKASA (NORITAKE is different)
+ * Fixup configuration for MIKASA (AlphaServer 1000)
  *
  * Summary @ 0x536:
  * Bit      Meaning
@@ -1020,7 +1215,10 @@
 }
 
 /*
- * Fixup configuration for NORITAKE (MIKASA is different)
+ * Fixup configuration for NORITAKE (AlphaServer 1000A)
+ *
+ * This is also used for CORELLE (AlphaServer 800)
+ * and ALCOR Primo (AlphaStation 600A).
  *
  * Summary @ 0x542, summary register #1:
  * Bit      Meaning
@@ -1076,8 +1274,11 @@
  */
 static inline void noritake_fixup(void)
 {
-	static char irq_tab[13][5] __initlocaldata = {
+	static char irq_tab[15][5] __initlocaldata = {
 		/*INT    INTA   INTB   INTC   INTD */
+	  /* note: IDSELs 16, 17, and 25 are CORELLE only */
+          { 16+1,  16+1,  16+1,  16+1,  16+1},  /* IdSel 16,  QLOGIC */
+	  {   -1,    -1,    -1,    -1,    -1},	/* IdSel 17,  S3 Trio64 */
 		{   -1,    -1,    -1,    -1,    -1},  /* IdSel 18,  PCEB */
 		{   -1,    -1,    -1,    -1,    -1},  /* IdSel 19,  PPB  */
 		{   -1,    -1,    -1,    -1,    -1},  /* IdSel 20,  ???? */
@@ -1085,18 +1286,20 @@
 		{ 16+2,  16+2,  16+3,  32+2,  32+3},  /* IdSel 22,  slot 0 */
 		{ 16+4,  16+4,  16+5,  32+4,  32+5},  /* IdSel 23,  slot 1 */
 		{ 16+6,  16+6,  16+7,  32+6,  32+7},  /* IdSel 24,  slot 2 */
-		/* The following are actually on bus 1, across the bridge */
+	  { 16+8,  16+8,  16+9,  32+8,  32+9},	/* IdSel 25,  slot 3 */
+	  /* the following 5 are actually on PCI bus 1, which is */
+	  /* across the built-in bridge of the NORITAKE only */
 		{ 16+1,  16+1,  16+1,  16+1,  16+1},  /* IdSel 16,  QLOGIC */
 		{ 16+8,  16+8,  16+9,  32+8,  32+9},  /* IdSel 17,  slot 3 */
 		{16+10, 16+10, 16+11, 32+10, 32+11},  /* IdSel 18,  slot 4 */
 		{16+12, 16+12, 16+13, 32+12, 32+13},  /* IdSel 19,  slot 5 */
 		{16+14, 16+14, 16+15, 32+14, 32+15},  /* IdSel 20,  slot 6 */
 	};
-	common_fixup(7, 18, 5, irq_tab, 0);
+	common_fixup(5, 19, 5, irq_tab, 0);
 }
 
 /*
- * Fixup configuration for ALCOR
+ * Fixup configuration for ALCOR and XLT (XL-300/366/433)
  *
  * Summary @ GRU_INT_REQ:
  * Bit      Meaning
@@ -1126,6 +1329,7 @@
  * The device to slot mapping looks like:
  *
  * Slot     Device
+ *  6       built-in TULIP (XLT only)
  *  7       PCI on board slot 0
  *  8       PCI on board slot 3
  *  9       PCI on board slot 4
@@ -1140,8 +1344,10 @@
  */
 static inline void alcor_fixup(void)
 {
-	static char irq_tab[6][5] __initlocaldata = {
+	static char irq_tab[7][5] __initlocaldata = {
 		/*INT    INTA   INTB   INTC   INTD */
+	  /* note: IDSEL 17 is XLT only */
+	  {16+13, 16+13, 16+13, 16+13, 16+13},	/* IdSel 17,  TULIP  */
 		{ 16+8,  16+8,  16+9, 16+10, 16+11},	/* IdSel 18,  slot 0 */
 		{16+16, 16+16, 16+17, 16+18, 16+19},	/* IdSel 19,  slot 3 */
 		{16+12, 16+12, 16+13, 16+14, 16+15},	/* IdSel 20,  slot 4 */
@@ -1149,62 +1355,6 @@
 		{ 16+0,  16+0,  16+1,  16+2,  16+3},	/* IdSel 22,  slot 2 */
 		{ 16+4,  16+4,  16+5,  16+6,  16+7},	/* IdSel 23,  slot 1 */
 	};
-	common_fixup(7, 12, 5, irq_tab, 0);
-}
-
-/*
- * Fixup configuration for ALPHA XLT (EV5/EV56)
- *
- * Summary @ GRU_INT_REQ:
- * Bit      Meaning
- * 0        Interrupt Line A from slot 2
- * 1        Interrupt Line B from slot 2
- * 2        Interrupt Line C from slot 2
- * 3        Interrupt Line D from slot 2
- * 4        Interrupt Line A from slot 1
- * 5        Interrupt line B from slot 1
- * 6        Interrupt Line C from slot 1
- * 7        Interrupt Line D from slot 1
- * 8        Interrupt Line A from slot 0
- * 9        Interrupt Line B from slot 0
- *10        Interrupt Line C from slot 0
- *11        Interrupt Line D from slot 0
- *12        NCR810 SCSI in slot 9
- *13        DC-21040 (TULIP) in slot 6
- *14-19     Reserved
- *20-23     Jumpers (interrupt)
- *24-27     Module revision
- *28-30     Reserved
- *31        EISA interrupt
- *
- * The device to slot mapping looks like:
- *
- * Slot     Device
- *  6       TULIP
- *  7       PCI on board slot 0
- *  8       none
- *  9       SCSI
- * 10       PCI-ISA bridge
- * 11       PCI on board slot 2
- * 12       PCI on board slot 1
- *   
- *
- * This two layered interrupt approach means that we allocate IRQ 16 and 
- * above for PCI interrupts.  The IRQ relates to which bit the interrupt
- * comes in on.  This makes interrupt processing much easier.
- */
-static inline void xlt_fixup(void)
-{
-	static char irq_tab[7][5] __initlocaldata = {
-		/*INT    INTA   INTB   INTC   INTD */
-		{16+13, 16+13, 16+13, 16+13, 16+13},	/* IdSel 17,  TULIP  */
-		{ 16+8,  16+8,  16+9, 16+10, 16+11},	/* IdSel 18,  slot 0 */
-		{   -1,    -1,    -1,    -1,    -1},	/* IdSel 19,  none   */
-		{16+12, 16+12, 16+12, 16+12, 16+12},	/* IdSel 20,  SCSI   */
-		{   -1,    -1,    -1,    -1,    -1},	/* IdSel 21,  SIO    */
-		{ 16+0,  16+0,  16+1,  16+2,  16+3},	/* IdSel 22,  slot 2 */
-		{ 16+4,  16+4,  16+5,  16+6,  16+7},	/* IdSel 23,  slot 1 */
-	};
 	common_fixup(6, 12, 5, irq_tab, 0);
 }
 
@@ -1262,8 +1412,6 @@
  * with the values in the sable_irq_to_mask[] and sable_mask_to_irq[] tables
  * in irq.c
  */
-
-#ifdef CONFIG_ALPHA_SABLE
 static inline void sable_fixup(void)
 {
         static char irq_tab[9][5] __initlocaldata = {
@@ -1280,7 +1428,6 @@
         };
         common_fixup(0, 8, 5, irq_tab, 0);
 }
-#endif
 
 /*
  * Fixup configuration for MIATA (EV56+PYXIS)
@@ -1362,7 +1509,8 @@
 		{   -1,    -1,    -1,    -1,    -1},  /* IdSel 21,  none    */
 		{16+12, 16+12, 16+13, 16+14, 16+15},  /* IdSel 22,  slot 4 */
 		{16+16, 16+16, 16+17, 16+18, 16+19},  /* IdSel 23,  slot 5 */
-		/* The following are actually on bus 1, across the bridge */
+		/* The following are actually on bus 1, which is */
+		/* across the builtin PCI-PCI bridge */
 		{16+20, 16+20, 16+21, 16+22, 16+23},  /* IdSel 24,  slot 1 */
 		{16+24, 16+24, 16+25, 16+26, 16+27},  /* IdSel 25,  slot 2 */
 		{16+28, 16+28, 16+29, 16+30, 16+31},  /* IdSel 26,  slot 3 */
@@ -1373,6 +1521,7 @@
 		{   -1,    -1,    -1,    -1,    -1},  /* IdSel 31,  PCI-PCI */
         };
 	common_fixup(3, 20, 5, irq_tab, 0);
+	SMC669_Init(); /* it might be a GL (fails harmlessly if not) */
 	es1888_init();
 }
 #endif
@@ -1399,7 +1548,6 @@
  *14        Interrupt Line B from slot 1
  *15        Interrupt line B from slot 0
  *16        Interrupt Line C from slot 3
-
  *17        Interrupt Line C from slot 2
  *18        Interrupt Line C from slot 1
  *19        Interrupt Line C from slot 0
@@ -1417,7 +1565,6 @@
  * 
  */
 
-#ifdef CONFIG_ALPHA_SX164
 static inline void sx164_fixup(void)
 {
 	static char irq_tab[5][5] __initlocaldata = {
@@ -1428,12 +1575,154 @@
 		{    -1,    -1,    -1,	  -1,    -1}, /* IdSel 8 SIO        */
 		{ 16+ 8, 16+ 8, 16+12, 16+16, 16+20}  /* IdSel 9 slot 3 J15 */
 	};
-
 	common_fixup(5, 9, 5, irq_tab, 0);
+	SMC669_Init();
+}
 
+/*
+ * Fixup configuration for DP264 (EV6+TSUNAMI)
+ *
+ * Summary @ TSUNAMI_CSR_DIM0:
+ * Bit      Meaning
+ * 0-17     Unused
+ *18        Interrupt SCSI B (Adaptec 7895 builtin)
+ *19        Interrupt SCSI A (Adaptec 7895 builtin)
+ *20        Interrupt Line D from slot 2 PCI0
+ *21        Interrupt Line C from slot 2 PCI0
+ *22        Interrupt Line B from slot 2 PCI0
+ *23        Interrupt Line A from slot 2 PCI0
+ *24        Interrupt Line D from slot 1 PCI0
+ *25        Interrupt Line C from slot 1 PCI0
+ *26        Interrupt Line B from slot 1 PCI0
+ *27        Interrupt Line A from slot 1 PCI0
+ *28        Interrupt Line D from slot 0 PCI0
+ *29        Interrupt Line C from slot 0 PCI0
+ *30        Interrupt Line B from slot 0 PCI0
+ *31        Interrupt Line A from slot 0 PCI0
+ *
+ *32        Interrupt Line D from slot 3 PCI1
+ *33        Interrupt Line C from slot 3 PCI1
+ *34        Interrupt Line B from slot 3 PCI1
+ *35        Interrupt Line A from slot 3 PCI1
+ *36        Interrupt Line D from slot 2 PCI1
+ *37        Interrupt Line C from slot 2 PCI1
+ *38        Interrupt Line B from slot 2 PCI1
+ *39        Interrupt Line A from slot 2 PCI1
+ *40        Interrupt Line D from slot 1 PCI1
+ *41        Interrupt Line C from slot 1 PCI1
+ *42        Interrupt Line B from slot 1 PCI1
+ *43        Interrupt Line A from slot 1 PCI1
+ *44        Interrupt Line D from slot 0 PCI1
+ *45        Interrupt Line C from slot 0 PCI1
+ *46        Interrupt Line B from slot 0 PCI1
+ *47        Interrupt Line A from slot 0 PCI1
+ *48-52     Unused
+ *53        PCI0 NMI (from Cypress)
+ *54        PCI0 SMI INT (from Cypress)
+ *55        PCI0 ISA Interrupt (from Cypress)
+ *56-60     Unused
+ *61        PCI1 Bus Error
+ *62        PCI0 Bus Error
+ *63        Reserved
+ *
+ * IdSel	
+ *   5	 Cypress Bridge I/O
+ *   6	 SCSI Adaptec builtin
+ *   7	 64 bit PCI option slot 0
+ *   8	 64 bit PCI option slot 1
+ *   9	 64 bit PCI option slot 2
+ * 
+ */
+
+static inline void dp264_fixup(void)
+{
+	static char irq_tab[5][5] __initlocaldata = {
+          /*INT    INTA   INTB   INTC   INTD */
+	  {    -1,    -1,    -1,    -1,    -1},	/* IdSel 5 ISA Bridge */
+	  { 16+ 2, 16+ 2, 16+ 2, 16+ 2, 16+ 2},	/* IdSel 6 SCSI builtin */
+	  { 16+15, 16+15, 16+14, 16+13, 16+12},	/* IdSel 7 slot 0 */
+	  { 16+11, 16+11, 16+10, 16+ 9, 16+ 8},	/* IdSel 8 slot 1 */
+	  { 16+ 7, 16+ 7, 16+ 6, 16+ 5, 16+ 4}	/* IdSel 9 slot 2 */
+	};
+	common_fixup(5, 9, 5, irq_tab, 0);
 	SMC669_Init();
 }
-#endif
+
+/*
+ * Fixup configuration for RAWHIDE
+ *
+ * Summary @ MCPCIA_PCI0_INT_REQ:
+ * Bit      Meaning
+ *0         Interrupt Line A from slot 2 PCI0
+ *1         Interrupt Line B from slot 2 PCI0
+ *2         Interrupt Line C from slot 2 PCI0
+ *3         Interrupt Line D from slot 2 PCI0
+ *4         Interrupt Line A from slot 3 PCI0
+ *5         Interrupt Line B from slot 3 PCI0
+ *6         Interrupt Line C from slot 3 PCI0
+ *7         Interrupt Line D from slot 3 PCI0
+ *8         Interrupt Line A from slot 4 PCI0
+ *9         Interrupt Line B from slot 4 PCI0
+ *10        Interrupt Line C from slot 4 PCI0
+ *11        Interrupt Line D from slot 4 PCI0
+ *12        Interrupt Line A from slot 5 PCI0
+ *13        Interrupt Line B from slot 5 PCI0
+ *14        Interrupt Line C from slot 5 PCI0
+ *15        Interrupt Line D from slot 5 PCI0
+ *16        EISA interrupt (PCI 0) or SCSI interrupt (PCI 1)
+ *17-23     NA
+ *
+ * IdSel	
+ *   1	 EISA bridge (PCI bus 0 only)
+ *   2 	 PCI option slot 2
+ *   3	 PCI option slot 3
+ *   4   PCI option slot 4
+ *   5   PCI option slot 5
+ * 
+ */
+
+static inline void rawhide_fixup(void)
+{
+	static char irq_tab[5][5] __initlocaldata = {
+          /*INT    INTA   INTB   INTC   INTD */
+	  { 16+16, 16+16, 16+16, 16+16, 16+16},	/* IdSel 1 SCSI PCI 1 only */
+	  { 16+ 0, 16+ 0, 16+ 1, 16+ 2, 16+ 3},	/* IdSel 2 slot 2 */
+	  { 16+ 4, 16+ 4, 16+ 5, 16+ 6, 16+ 7},	/* IdSel 3 slot 3 */
+	  { 16+ 8, 16+ 8, 16+ 9, 16+10, 16+11},	/* IdSel 4 slot 4 */
+	  { 16+12, 16+12, 16+13, 16+14, 16+15}	/* IdSel 5 slot 5 */
+	};
+	common_fixup(1, 5, 5, irq_tab, 0);
+}
+
+/*
+ * The Takara has PCI devices 1, 2, and 3 configured to slots 20,
+ * 19, and 18 respectively, in the default configuration. They can
+ * also be jumpered to slots 8, 7, and 6 respectively, which is fun
+ * because the SIO ISA bridge can also be slot 7. However, the SIO
+ * doesn't explicitly generate PCI-type interrupts, so we can
+ * assign it whatever the hell IRQ we like and it doesn't matter.
+ */
+static inline void takara_fixup(void)
+{
+	static char irq_tab[15][5] __initlocaldata = {
+	    { 16+3, 16+3, 16+3, 16+3, 16+3},   /* slot  6 == device 3 */
+	    { 16+2, 16+2, 16+2, 16+2, 16+2},   /* slot  7 == device 2 */
+	    { 16+1, 16+1, 16+1, 16+1, 16+1},   /* slot  8 == device 1 */
+	    {   -1,   -1,   -1,   -1,   -1},   /* slot  9 == nothing */
+	    {   -1,   -1,   -1,   -1,   -1},   /* slot 10 == nothing */
+	    {   -1,   -1,   -1,   -1,   -1},   /* slot 11 == nothing */
+	    {   -1,   -1,   -1,   -1,   -1},   /* slot 12 == nothing */
+	    {   -1,   -1,   -1,   -1,   -1},   /* slot 13 == nothing */
+	    {   -1,   -1,   -1,   -1,   -1},   /* slot 14 == nothing */
+	    {   -1,   -1,   -1,   -1,   -1},   /* slot 15 == nothing */
+	    {   -1,   -1,   -1,   -1,   -1},   /* slot 16 == nothing */
+	    {   -1,   -1,   -1,   -1,   -1},   /* slot 17 == nothing */
+	    { 16+3, 16+3, 16+3, 16+3, 16+3},   /* slot 18 == device 3 */
+	    { 16+2, 16+2, 16+2, 16+2, 16+2},   /* slot 19 == device 2 */
+	    { 16+1, 16+1, 16+1, 16+1, 16+1},   /* slot 20 == device 1 */
+	};
+	common_fixup(6, 20, 5, irq_tab, 0x26e);
+}
 
 /*
  * Fixup configuration for all boards that route the PCI interrupts
@@ -1462,6 +1751,7 @@
 	 * driven at all).
 	 */
 	static const char pirq_tab[][5] __initlocaldata = {
+	      /*INT   A   B   C   D */
 #ifdef CONFIG_ALPHA_P2K
 		{ 0,  0, -1, -1, -1}, /* idsel  6 (53c810) */
 		{-1, -1, -1, -1, -1}, /* idsel  7 (SIO: PCI/ISA bridge) */
@@ -1497,7 +1787,7 @@
 
 #if defined(CONFIG_ALPHA_BOOK1)
         /* for the AlphaBook1, NCR810 SCSI is 14, PCMCIA controller is 15 */
-        const unsigned int route_tab = 0x0e0f0a0a;
+        const unsigned int new_route_tab = 0x0e0f0a0a;
 
 #elif defined(CONFIG_ALPHA_NONAME)
 	/*
@@ -1510,16 +1800,24 @@
 	 *  they are co-indicated when the platform type "Noname" is
 	 *  selected... :-(
 	 */
-	const unsigned int route_tab = 0x0b0a0f09;
+	const unsigned int new_route_tab = 0x0b0a0f09;
 #else
-	const unsigned int route_tab = 0x0b0a090f;
+	const unsigned int new_route_tab = 0x0b0a090f;
 #endif
-
-	unsigned int level_bits;
+        unsigned int route_tab, old_route_tab;
+	unsigned int level_bits, old_level_bits;
 	unsigned char pin, slot;
 	int pirq;
 
+        pcibios_read_config_dword(0, PCI_DEVFN(7, 0), 0x60, &old_route_tab);
+	DBG_DEVS(("sio_fixup: old pirq route table: 0x%08x\n",
+                  old_route_tab));
+#if PCI_MODIFY
+	route_tab = new_route_tab;
 	pcibios_write_config_dword(0, PCI_DEVFN(7, 0), 0x60, route_tab);
+#else
+	route_tab = old_route_tab;
+#endif
 
 	/*
 	 * Go through all devices, fixing up irqs as we see fit:
@@ -1576,20 +1874,33 @@
 		 * if it's a VGA, enable its BIOS ROM at C0000
 		 */
 		if ((dev->class >> 8) == PCI_CLASS_DISPLAY_VGA) {
-			pcibios_write_config_dword(dev->bus->number,
+			/* but if its a Cirrus 543x/544x DISABLE it, */
+			/* since enabling ROM disables the memory... */
+			if ((dev->vendor == PCI_VENDOR_ID_CIRRUS) &&
+			    (dev->device >= 0x00a0) &&
+			    (dev->device <= 0x00ac)) {
+				pcibios_write_config_dword(
+					dev->bus->number,
+					dev->devfn,
+					PCI_ROM_ADDRESS,
+					0x00000000);
+			} else {
+				pcibios_write_config_dword(
+					dev->bus->number,
 						   dev->devfn,
 						   PCI_ROM_ADDRESS,
 						   0x000c0000 | PCI_ROM_ADDRESS_ENABLE);
 		}
+		}
 		if ((dev->class >> 16) == PCI_BASE_CLASS_DISPLAY) {
 			continue; /* for now, displays get no IRQ */
 		}
 
 		if (pirq < 0) {
-			printk("bios32.sio_fixup: "
+			DBG_DEVS(("bios32.sio_fixup: "
 			       "weird, device %04x:%04x coming in on"
 			       " slot %d has no irq line!!\n",
-			       dev->vendor, dev->device, slot);
+			       dev->vendor, dev->device, slot));
 			continue;
 		}
 
@@ -1653,7 +1964,12 @@
 	 *
 	 * Note: we at least preserve any level-set bits on AlphaBook1
 	 */
-	level_bits |= ((inb(0x4d0) | (inb(0x4d1) << 8)) & 0x71ff);
+	old_level_bits = inb(0x4d0) | (inb(0x4d1) << 8);
+	DBG_DEVS(("sio_fixup: old irq level bits: 0x%04x\n",
+                  old_level_bits));
+	level_bits |= (old_level_bits & 0x71ff);
+	DBG_DEVS(("sio_fixup: new irq level bits: 0x%04x\n",
+                  level_bits));
 	outb((level_bits >> 0) & 0xff, 0x4d0);
 	outb((level_bits >> 8) & 0xff, 0x4d1);
 
@@ -1688,11 +2004,35 @@
 unsigned long __init
 pcibios_fixup(unsigned long mem_start, unsigned long mem_end)
 {
+	  struct pci_bus *cur;
+
+#ifdef CONFIG_ALPHA_MCPCIA
+	/* must do massive setup for multiple PCI busses here... */
+	DBG_DEVS(("pcibios_fixup: calling mcpcia_fixup()...\n"));
+	mem_start = mcpcia_fixup(mem_start, mem_end);
+#endif /* MCPCIA */
+
+#ifdef CONFIG_ALPHA_TSUNAMI
+	/* must do massive setup for multiple PCI busses here... */
+	/*	mem_start = tsunami_fixup(mem_start, mem_end); */
+#endif /* TSUNAMI */
+
 #if PCI_MODIFY && !defined(CONFIG_ALPHA_RUFFIAN)
 	/*
 	 * Scan the tree, allocating PCI memory and I/O space.
 	 */
-	layout_bus(&pci_root);
+	/*
+	 * Sigh; check_region() will need changing to accept a HANDLE,
+	 * if we allocate I/O space addresses on a per-bus basis.
+	 * For now, make the I/O bases unique across all busses, so
+	 * that check_region() will not get confused... ;-}
+	 */
+	io_base = DEFAULT_IO_BASE;
+	for (cur = &pci_root; cur; cur = cur->next) {
+		mem_base = DEFAULT_MEM_BASE;
+		DBG_DEVS(("pcibios_fixup: calling layout_bus()\n"));
+		layout_bus(cur);
+	}
 #endif
 	
 	/*
@@ -1713,10 +2053,8 @@
 	eb66_and_eb64p_fixup();
 #elif defined(CONFIG_ALPHA_MIKASA)
 	mikasa_fixup();
-#elif defined(CONFIG_ALPHA_ALCOR)
+#elif defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT)
 	alcor_fixup();
-#elif defined(CONFIG_ALPHA_XLT)
-	xlt_fixup();
 #elif defined(CONFIG_ALPHA_SABLE)
 	sable_fixup();
 #elif defined(CONFIG_ALPHA_MIATA)
@@ -1725,6 +2063,12 @@
 	noritake_fixup();
 #elif defined(CONFIG_ALPHA_SX164)
 	sx164_fixup();
+#elif defined(CONFIG_ALPHA_DP264)
+	dp264_fixup();
+#elif defined(CONFIG_ALPHA_RAWHIDE)
+	rawhide_fixup();
+#elif defined(CONFIG_ALPHA_TAKARA)
+	takara_fixup();
 #elif defined(CONFIG_ALPHA_RUFFIAN)
 	/* no fixup needed */
 #else
@@ -1831,6 +2175,33 @@
 	return err;
 }
 
+#if (defined(CONFIG_ALPHA_PC164) || \
+     defined(CONFIG_ALPHA_LX164) || \
+     defined(CONFIG_ALPHA_SX164) || \
+     defined(CONFIG_ALPHA_EB164) || \
+     defined(CONFIG_ALPHA_EB66P) || \
+     defined(CONFIG_ALPHA_CABRIOLET)) && defined(CONFIG_ALPHA_SRM)
+
+/*
+  on the above machines, under SRM console, we must use the CSERVE PALcode
+  routine to manage the interrupt mask for us, otherwise, the kernel/HW get
+  out of sync with what the PALcode thinks it needs to deliver/ignore
+ */
+void
+cserve_update_hw(unsigned long irq, unsigned long mask)
+{
+    extern void cserve_ena(unsigned long);
+    extern void cserve_dis(unsigned long);
+
+    if (mask & (1UL << irq))
+	/* disable */
+	cserve_dis(irq - 16);
+    else
+      /* enable */
+	cserve_ena(irq - 16);
+    return;
+}
+#endif /* (PC164 || LX164 || SX164 || EB164 || CABRIO) && SRM */
 
 #ifdef CONFIG_ALPHA_MIATA
 /*
@@ -1876,5 +2247,45 @@
 	return 0;
 }
 #endif /* CONFIG_ALPHA_MIATA */
+
+#ifdef CONFIG_ALPHA_SRM_SETUP
+void reset_for_srm(void)
+{
+	extern void scrreset(void);
+	struct pci_dev *dev;
+	int i;
+
+	/* reset any IRQs that we changed */
+	for (i = 0; i < irq_reset_count; i++) {
+	    dev = irq_dev_to_reset[i];
+
+	    pcibios_write_config_byte(dev->bus->number, dev->devfn,
+				      PCI_INTERRUPT_LINE, irq_to_reset[i]);
+#if 1
+	    printk("reset_for_srm: bus %d slot 0x%x "
+		   "SRM IRQ 0x%x changed back from 0x%x\n",
+		   dev->bus->number, PCI_SLOT(dev->devfn),
+		   irq_to_reset[i], dev->irq);
+#endif
+	}
+
+	/* reset any IO addresses that we changed */
+	for (i = 0; i < io_reset_count; i++) {
+	    dev = io_dev_to_reset[i];
+
+	    pcibios_write_config_byte(dev->bus->number, dev->devfn,
+				      io_reg_to_reset[i], io_to_reset[i]);
+#if 1
+	    printk("reset_for_srm: bus %d slot 0x%x "
+		   "SRM IO restored to 0x%x\n",
+		   dev->bus->number, PCI_SLOT(dev->devfn),
+		   io_to_reset[i]);
+#endif
+}
+
+	/* reset the visible screen to the top of display memory */
+	scrreset();
+}
+#endif /* CONFIG_ALPHA_SRM_SETUP */
 
 #endif /* CONFIG_PCI */
diff -u --recursive --new-file v2.1.91/linux/arch/alpha/kernel/cia.c linux/arch/alpha/kernel/cia.c
--- v2.1.91/linux/arch/alpha/kernel/cia.c	Fri Jan 30 11:28:05 1998
+++ linux/arch/alpha/kernel/cia.c	Mon Mar 30 00:21:39 1998
@@ -6,6 +6,7 @@
  *
  */
 #include <linux/kernel.h>
+#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/bios32.h>
 #include <linux/pci.h>
@@ -17,13 +18,14 @@
 #include <asm/ptrace.h>
 #include <asm/mmu_context.h>
 
-/* NOTE: Herein are back-to-back mb insns.  They are magic.
-   A plausible explanation is that the i/o controler does not properly
-   handle the system transaction.  Another involves timing.  Ho hum.  */
+/*
+ * NOTE: Herein lie back-to-back mb instructions.  They are magic. 
+ * One plausible explanation is that the i/o controller does not properly
+ * handle the system transaction.  Another involves timing.  Ho hum.
+ */
 
 extern struct hwrpb_struct *hwrpb;
 extern asmlinkage void wrmces(unsigned long mces);
-extern int alpha_sys_type;
 
 /*
  * Machine check reasons.  Defined according to PALcode sources
@@ -56,13 +58,17 @@
 # define DBGC(args)
 #endif
 
-#define vulp	volatile unsigned long *
 #define vuip	volatile unsigned int  *
 
 static volatile unsigned int CIA_mcheck_expected = 0;
 static volatile unsigned int CIA_mcheck_taken = 0;
 static unsigned int CIA_jd;
 
+#ifdef CONFIG_ALPHA_SRM_SETUP
+unsigned int CIA_DMA_WIN_BASE = CIA_DMA_WIN_BASE_DEFAULT;
+unsigned int CIA_DMA_WIN_SIZE = CIA_DMA_WIN_SIZE_DEFAULT;
+unsigned long cia_sm_base_r1, cia_sm_base_r2, cia_sm_base_r3;
+#endif /* SRM_SETUP */
 
 /*
  * Given a bus, device, and function number, compute resulting
@@ -271,7 +277,7 @@
 		}
 
 		/* reset error status: */
-		*(vulp)CIA_IOC_CIA_ERR = stat0;
+		*(vuip)CIA_IOC_CIA_ERR = stat0;
 		mb();
 		wrmces(0x7);			/* reset machine check */
 		value = 0xffffffff;
@@ -442,6 +448,18 @@
 		printk("CIA_init: CIA_STAT was 0x%x\n", temp);
 		temp = *(vuip)CIA_IOC_MCR; mb();
 		printk("CIA_init: CIA_MCR was 0x%x\n", temp);
+		temp = *(vuip)CIA_IOC_CIA_CTRL; mb();
+		printk("CIA_init: CIA_CTRL was 0x%x\n", temp);
+		temp = *(vuip)CIA_IOC_ERR_MASK; mb();
+		printk("CIA_init: CIA_ERR_MASK was 0x%x\n", temp);
+		temp = *((vuip)CIA_IOC_PCI_W0_BASE); mb();
+		printk("CIA_init: W0_BASE was 0x%x\n", temp);
+		temp = *((vuip)CIA_IOC_PCI_W1_BASE); mb();
+		printk("CIA_init: W1_BASE was 0x%x\n", temp);
+		temp = *((vuip)CIA_IOC_PCI_W2_BASE); mb();
+		printk("CIA_init: W2_BASE was 0x%x\n", temp);
+		temp = *((vuip)CIA_IOC_PCI_W3_BASE); mb();
+		printk("CIA_init: W3_BASE was 0x%x\n", temp);
 	}
 #endif /* DEBUG_DUMP_REGS */
 
@@ -458,6 +476,70 @@
 	*(vuip)CIA_IOC_CIA_CTRL = cia_tmp;
 	mb();
 
+#ifdef CONFIG_ALPHA_SRM_SETUP
+	/* check window 0 for enabled and mapped to 0 */
+	if (((*(vuip)CIA_IOC_PCI_W0_BASE & 3) == 1) &&
+	    (*(vuip)CIA_IOC_PCI_T0_BASE == 0))
+	{
+	  CIA_DMA_WIN_BASE = *(vuip)CIA_IOC_PCI_W0_BASE & 0xfff00000U;
+	  CIA_DMA_WIN_SIZE = *(vuip)CIA_IOC_PCI_W0_MASK & 0xfff00000U;
+	  CIA_DMA_WIN_SIZE += 0x00100000U;
+#if 1
+	  printk("cia_init: using Window 0 settings\n");
+	  printk("cia_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n",
+		 *(vuip)CIA_IOC_PCI_W0_BASE,
+		 *(vuip)CIA_IOC_PCI_W0_MASK,
+		 *(vuip)CIA_IOC_PCI_T0_BASE);
+#endif
+	}
+	else  /* check window 1 for enabled and mapped to 0 */
+	if (((*(vuip)CIA_IOC_PCI_W1_BASE & 3) == 1) &&
+	    (*(vuip)CIA_IOC_PCI_T1_BASE == 0))
+	{
+	  CIA_DMA_WIN_BASE = *(vuip)CIA_IOC_PCI_W1_BASE & 0xfff00000U;
+	  CIA_DMA_WIN_SIZE = *(vuip)CIA_IOC_PCI_W1_MASK & 0xfff00000U;
+	  CIA_DMA_WIN_SIZE += 0x00100000U;
+#if 1
+	  printk("cia_init: using Window 1 settings\n");
+	  printk("cia_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n",
+		 *(vuip)CIA_IOC_PCI_W1_BASE,
+		 *(vuip)CIA_IOC_PCI_W1_MASK,
+		 *(vuip)CIA_IOC_PCI_T1_BASE);
+#endif
+	}
+	else  /* check window 2 for enabled and mapped to 0 */
+	if (((*(vuip)CIA_IOC_PCI_W2_BASE & 3) == 1) &&
+	    (*(vuip)CIA_IOC_PCI_T2_BASE == 0))
+	{
+	  CIA_DMA_WIN_BASE = *(vuip)CIA_IOC_PCI_W2_BASE & 0xfff00000U;
+	  CIA_DMA_WIN_SIZE = *(vuip)CIA_IOC_PCI_W2_MASK & 0xfff00000U;
+	  CIA_DMA_WIN_SIZE += 0x00100000U;
+#if 1
+	  printk("cia_init: using Window 2 settings\n");
+	  printk("cia_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n",
+		 *(vuip)CIA_IOC_PCI_W2_BASE,
+		 *(vuip)CIA_IOC_PCI_W2_MASK,
+		 *(vuip)CIA_IOC_PCI_T2_BASE);
+#endif
+	}
+	else  /* check window 3 for enabled and mapped to 0 */
+	if (((*(vuip)CIA_IOC_PCI_W3_BASE & 3) == 1) &&
+	    (*(vuip)CIA_IOC_PCI_T3_BASE == 0))
+	{
+	  CIA_DMA_WIN_BASE = *(vuip)CIA_IOC_PCI_W3_BASE & 0xfff00000U;
+	  CIA_DMA_WIN_SIZE = *(vuip)CIA_IOC_PCI_W3_MASK & 0xfff00000U;
+	  CIA_DMA_WIN_SIZE += 0x00100000U;
+#if 1
+	  printk("cia_init: using Window 3 settings\n");
+	  printk("cia_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n",
+		 *(vuip)CIA_IOC_PCI_W3_BASE,
+		 *(vuip)CIA_IOC_PCI_W3_MASK,
+		 *(vuip)CIA_IOC_PCI_T3_BASE);
+#endif
+	}
+	else  /* we must use our defaults which were pre-initialized... */
+#endif /* SRM_SETUP */
+	{
 	/*
 	 * Set up the PCI->physical memory translation windows.
 	 * For now, windows 1,2 and 3 are disabled.  In the future, we may
@@ -472,6 +554,7 @@
 	*(vuip)CIA_IOC_PCI_W1_BASE = 0x0;
 	*(vuip)CIA_IOC_PCI_W2_BASE = 0x0;
 	*(vuip)CIA_IOC_PCI_W3_BASE = 0x0;
+	}
 
 	/*
 	 * check ASN in HWRPB for validity, report if bad
@@ -483,28 +566,54 @@
 	}
 
         /*
-         * Finally, clear the CIA_CFG register, which gets used
+         * Next, clear the CIA_CFG register, which gets used
          *  for PCI Config Space accesses. That is the way
          *  we want to use it, and we do not want to depend on
          *  what ARC or SRM might have left behind...
          */
         {
-#if 0
-		unsigned int cia_cfg = *(vuip)CIA_IOC_CFG; mb();
-		if (cia_cfg) printk("CIA_init: CFG was 0x%x\n", cia_cfg);
-#endif
-		*(vuip)CIA_IOC_CFG = 0; mb();
+          unsigned int cia_cfg = *((vuip)CIA_IOC_CFG); mb();
+          if (cia_cfg) {
+	      printk("CIA_init: CFG was 0x%x\n", cia_cfg);
+	      *((vuip)CIA_IOC_CFG) = 0; mb();
+	  }
         }
  
-#if 0
 	{
-		unsigned int temp;
-		temp = *(vuip)CIA_IOC_CIA_CTRL; mb();
-		printk("CIA_init: CIA_CTRL was 0x%x\n", temp);
-		temp = *(vuip)CIA_IOC_ERR_MASK; mb();
-		printk("CIA_init: CIA_ERR_MASK was 0x%x\n", temp);
-	}
+          unsigned int cia_hae_mem = *((vuip)CIA_IOC_HAE_MEM);
+          unsigned int cia_hae_io = *((vuip)CIA_IOC_HAE_IO);
+#if 0
+	  printk("CIA_init: HAE_MEM was 0x%x\n", cia_hae_mem);
+	  printk("CIA_init: HAE_IO was 0x%x\n", cia_hae_io);
 #endif
+#ifdef CONFIG_ALPHA_SRM_SETUP
+	  /*
+	    sigh... For the SRM setup, unless we know apriori what the HAE
+	    contents will be, we need to setup the arbitrary region bases
+	    so we can test against the range of addresses and tailor the
+	    region chosen for the SPARSE memory access.
+
+	    see include/asm-alpha/cia.h for the SPARSE mem read/write
+	  */
+	  cia_sm_base_r1 = (cia_hae_mem      ) & 0xe0000000UL; /* region 1 */
+	  cia_sm_base_r2 = (cia_hae_mem << 16) & 0xf8000000UL; /* region 2 */
+	  cia_sm_base_r3 = (cia_hae_mem << 24) & 0xfc000000UL; /* region 3 */
+
+	  /*
+	    Set the HAE cache, so that setup_arch() code
+	    will use the SRM setting always. Our readb/writeb
+	    code in cia.h expects never to have to change
+	    the contents of the HAE.
+	   */
+	  hae.cache = cia_hae_mem;
+#else /* SRM_SETUP */
+	  *((vuip)CIA_IOC_HAE_MEM) = 0; mb();
+	  cia_hae_mem = *((vuip)CIA_IOC_HAE_MEM);
+	  *((vuip)CIA_IOC_HAE_IO) = 0; mb();
+	  cia_hae_io = *((vuip)CIA_IOC_HAE_IO);
+#endif /* SRM_SETUP */
+        }
+ 
 	return mem_start;
 }
 
@@ -512,7 +621,7 @@
 {
 	CIA_jd = *(vuip)CIA_IOC_CIA_ERR;
 	DBGM(("CIA_pci_clr_err: CIA ERR after read 0x%x\n", CIA_jd));
-	*(vulp)CIA_IOC_CIA_ERR = 0x0180;
+	*(vuip)CIA_IOC_CIA_ERR = 0x0180;
 	mb();
 	return 0;
 }
diff -u --recursive --new-file v2.1.91/linux/arch/alpha/kernel/entry.S linux/arch/alpha/kernel/entry.S
--- v2.1.91/linux/arch/alpha/kernel/entry.S	Tue Mar 10 10:03:30 1998
+++ linux/arch/alpha/kernel/entry.S	Mon Mar 30 00:21:39 1998
@@ -4,6 +4,7 @@
  * kernel entry-points
  */
 
+#include <linux/config.h>
 #include <asm/system.h>
 
 #define halt	.long PAL_halt
@@ -48,6 +49,8 @@
  * JRP - Save regs 16-18 in a special area of the stack, so that
  * the palcode-provided values are available to the signal handler.
  */
+#if defined(CONFIG_ALPHA_TSUNAMI)
+/* TSUNAMI has no HAE register to save/restore */
 #define SAVE_ALL			\
 	subq	$30,184,$30;		\
 	stq	$0,0($30);		\
@@ -55,6 +58,55 @@
 	stq	$2,16($30);		\
 	stq	$3,24($30);		\
 	stq	$4,32($30);		\
+	stq	$5,40($30);		\
+	stq	$6,48($30);		\
+	stq	$7,56($30);		\
+	stq	$8,64($30);		\
+	stq	$19,72($30);		\
+	stq	$20,80($30);		\
+	stq	$21,88($30);		\
+	stq	$22,96($30);		\
+	stq	$23,104($30);		\
+	stq	$24,112($30);		\
+	stq	$25,120($30);		\
+	stq	$26,128($30);		\
+	stq	$27,136($30);		\
+	stq	$28,144($30);		\
+	stq	$16,160($30);		\
+	stq	$17,168($30);		\
+	stq	$18,176($30)
+
+#define RESTORE_ALL			\
+	ldq	$0,0($30);		\
+	ldq	$1,8($30);		\
+	ldq	$2,16($30);		\
+	ldq	$3,24($30);		\
+	ldq	$4,32($30);		\
+	ldq	$5,40($30);		\
+	ldq	$6,48($30);		\
+	ldq	$7,56($30);		\
+	ldq	$8,64($30);		\
+	ldq	$19,72($30);		\
+	ldq	$20,80($30);		\
+	ldq	$21,88($30);		\
+	ldq	$22,96($30);		\
+	ldq	$23,104($30);		\
+	ldq	$24,112($30);		\
+	ldq	$25,120($30);		\
+	ldq	$26,128($30);		\
+	ldq	$27,136($30);		\
+	ldq	$28,144($30);		\
+	addq	$30,184,$30
+
+#else /* TSUNAMI */
+#define SAVE_ALL			\
+	subq	$30,184,$30;		\
+	stq	$0,0($30);		\
+	stq	$1,8($30);		\
+	stq	$2,16($30);		\
+	stq	$3,24($30);		\
+	stq	$4,32($30);		\
+	stq	$28,144($30);		\
 	lda	$2,hae;			\
 	stq	$5,40($30);		\
 	stq	$6,48($30);		\
@@ -70,7 +122,6 @@
 	stq	$25,120($30);		\
 	stq	$26,128($30);		\
 	stq	$27,136($30);		\
-	stq	$28,144($30);		\
 	stq	$2,152($30);		\
 	stq	$16,160($30);		\
 	stq	$17,168($30);		\
@@ -113,6 +164,8 @@
 	ldq	$28,144($30);		\
 	addq	$30,184,$30
 
+#endif /* TSUNAMI */
+
 .text
 .set noat
 #if defined(__linux__) && !defined(__ELF__)
@@ -508,6 +561,8 @@
 alpha_switch_to:
 	bsr $1,do_switch_stack
 	call_pal PAL_swpctx
+	lda $16,-2($31)
+	call_pal PAL_tbi
 	bsr $1,undo_switch_stack
 	ret $31,($26),1
 .end alpha_switch_to
@@ -680,6 +735,19 @@
 	lda	$30,SWITCH_STACK_SIZE($30)
 	br	$31,restore_all
 .end entSys
+
+#ifdef __SMP__
+        .globl  ret_from_smpfork
+.align 3
+.ent ret_from_smpfork
+ret_from_smpfork:
+	.set at
+	stq	$31,scheduler_lock	
+	mb	/* ?????????????????? */
+	br	ret_from_sys_call
+	.set noat
+.end ret_from_smpfork
+#endif /* __SMP__ */
 
 .align 3
 .ent reschedule
diff -u --recursive --new-file v2.1.91/linux/arch/alpha/kernel/head.S linux/arch/alpha/kernel/head.S
--- v2.1.91/linux/arch/alpha/kernel/head.S	Tue Feb 17 13:12:44 1998
+++ linux/arch/alpha/kernel/head.S	Mon Mar 30 00:21:39 1998
@@ -8,6 +8,7 @@
  */
 
 #define __ASSEMBLY__
+#include <linux/config.h>
 #include <asm/system.h>
 
 #define halt call_pal PAL_halt
@@ -31,6 +32,27 @@
 	jsr	$26,start_kernel
 	halt
 	.end __start
+
+#ifdef __SMP__
+	.align 3
+	.globl	__start_cpu
+	.ent	__start_cpu
+	/* on entry here from SRM console, the HWPCB of this processor */
+	/* has been loaded, and $27 contains the task pointer */
+__start_cpu:
+	/* first order of business, load the GP */
+	br	$26,1f
+1:	ldgp	$29,0($26)
+	/* We need to get current loaded up with our first task...  */
+	lda	$8,0($27)
+	/* set FEN */
+	lda	$16,1($31)
+	call_pal PAL_wrfen
+	/* ... and then we can start the processor.  */
+	jsr	$26,start_secondary
+	halt
+	.end __start_cpu
+#endif /* __SMP__ */
 
 	.align 3
 	.globl	wrent
diff -u --recursive --new-file v2.1.91/linux/arch/alpha/kernel/irq.c linux/arch/alpha/kernel/irq.c
--- v2.1.91/linux/arch/alpha/kernel/irq.c	Tue Mar 10 10:03:30 1998
+++ linux/arch/alpha/kernel/irq.c	Mon Mar 30 00:21:39 1998
@@ -30,6 +30,10 @@
 #define vulp	volatile unsigned long *
 #define vuip	volatile unsigned int *
 
+extern void timer_interrupt(struct pt_regs * regs);
+extern void cserve_update_hw(unsigned long, unsigned long);
+extern void handle_ipi(struct pt_regs *);
+
 #define RTC_IRQ    8
 #ifdef CONFIG_RTC
 #define TIMER_IRQ  0        /* timer is the pit */
@@ -45,12 +49,15 @@
 #if defined(CONFIG_ALPHA_P2K)
   /* always mask out unused timer irq 0 and RTC irq 8 */
 # define PROBE_MASK (((1UL << NR_IRQS) - 1) & ~0x101UL)
-#elif defined(CONFIG_ALPHA_ALCOR)
+#elif defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT)
   /* always mask out unused timer irq 0, "irqs" 20-30, and the EISA cascade: */
 # define PROBE_MASK (((1UL << NR_IRQS) - 1) & ~0xfff000000001UL)
 #elif defined(CONFIG_ALPHA_RUFFIAN)
   /* must leave timer irq 0 in the mask */
 # define PROBE_MASK ((1UL << NR_IRQS) - 1)
+#elif NR_IRQS == 64
+  /* always mask out unused timer irq 0: */
+# define PROBE_MASK (~1UL)
 #else
   /* always mask out unused timer irq 0: */
 # define PROBE_MASK (((1UL << NR_IRQS) - 1) & ~1UL)
@@ -119,7 +126,7 @@
 {
 	/* The "irq" argument is really the mask bit number */
 	switch (irq) {
-	default: /* 16 ... 23 */
+	case 16 ... 23:
 		outb(mask >> 16, 0x53d);
 		break;
 	case 8 ... 15:
@@ -135,7 +142,7 @@
 noritake_update_hw(unsigned long irq, unsigned long mask)
 {
 	switch (irq) {
-	default: /* 32 ... 47 */
+	case 32 ... 47:
 		outw(~(mask >> 32), 0x54c);
 		break;
 	case 16 ... 31:
@@ -155,7 +162,7 @@
 miata_update_hw(unsigned long irq, unsigned long mask)
 {
 	switch (irq) {
-	default: /* 16 ... 47 */
+	case 16 ... 47:
 		/* Make CERTAIN none of the bogus ints get enabled... */
 		*(vulp)PYXIS_INT_MASK =
 			~((long)mask >> 16) & ~0x4000000000000e3bUL;
@@ -178,7 +185,7 @@
 alcor_and_xlt_update_hw(unsigned long irq, unsigned long mask)
 {
 	switch (irq) {
-	default: /* 16 ... 47 */
+	case 16 ... 47:
 		/* On Alcor, at least, lines 20..30 are not connected and can
 		   generate spurrious interrupts if we turn them on while IRQ
 		   probing.  So explicitly mask them out. */
@@ -202,7 +209,7 @@
 mikasa_update_hw(unsigned long irq, unsigned long mask)
 {
 	switch (irq) {
-	default: /* 16 ... 31 */
+	case 16 ... 31:
 		outw(~(mask >> 16), 0x536); /* note invert */
 		break;
 	case  8 ... 15:	/* ISA PIC2 */
@@ -214,7 +221,7 @@
 	}
 }
 
-#ifdef CONFIG_ALPHA_RUFFIAN
+#if defined(CONFIG_ALPHA_RUFFIAN)
 static inline void
 ruffian_update_hw(unsigned long irq, unsigned long mask)
 {
@@ -223,8 +230,7 @@
 		/* Note inverted sense of mask bits: */
 		/* Make CERTAIN none of the bogus ints get enabled... */
 		*(vulp)PYXIS_INT_MASK =
-			~((long)mask >> 16) & 0x00000000ffffffbfUL;
-		mb();
+		      ~((long)mask >> 16) & 0x00000000ffffffbfUL; mb();
 		/* ... and read it back to make sure it got written.  */
 		*(vulp)PYXIS_INT_MASK;
 		break;
@@ -236,20 +242,23 @@
 		break;
 	}
 }
-#endif
+#endif /* RUFFIAN */
 
-#ifdef CONFIG_ALPHA_SX164
+#if defined(CONFIG_ALPHA_SX164)
 static inline void
 sx164_update_hw(unsigned long irq, unsigned long mask)
 {
 	switch (irq) {
 	case 16 ... 39:
-		/* Make CERTAIN none of the bogus ints get enabled */
+#if defined(CONFIG_ALPHA_SRM)
+		cserve_update_hw(irq, mask);
+#else
+		/* make CERTAIN none of the bogus ints get enabled */
 		*(vulp)PYXIS_INT_MASK =
-			~((long)mask >> 16) & ~0x000000000000003bUL;
-		mb();
+		    ~((long)mask >> 16) & ~0x000000000000003bUL; mb();
 		/* ... and read it back to make sure it got written.  */
 		*(vulp)PYXIS_INT_MASK;
+#endif /* SRM */
 		break;
 	case  8 ... 15: /* ISA PIC2 */
 		outb(mask >> 8, 0xA1);
@@ -258,20 +267,23 @@
 		outb(mask, 0x21);
 		break;
 	}
-}
-#endif
 
-/* Unlabeled mechanisms based on the number of irqs.  Someone should
-   probably document and name these.  */
+}
+#endif /* SX164 */
 
+#if defined(CONFIG_ALPHA_DP264)
 static inline void
-update_hw_33(unsigned long irq, unsigned long mask)
+dp264_update_hw(unsigned long irq, unsigned long mask)
 {
 	switch (irq) {
-	default: /* 16 ... 32 */
-		outl(mask >> 16, 0x804);
+	case 16 ... 63:
+		/* make CERTAIN none of the bogus ints get enabled */
+		/* HACK ALERT! only CPU#0 is used currently */
+		*(vulp)TSUNAMI_CSR_DIM0 =
+		    ~(mask) & ~0x0000000000000000UL; mb();
+		/* ... and read it back to make sure it got written.  */
+		*(vulp)TSUNAMI_CSR_DIM0;
 		break;
-
 	case  8 ... 15:	/* ISA PIC2 */
 		outb(mask >> 8, 0xA1);
 		break;
@@ -280,16 +292,24 @@
 		break;
 	}
 }
+#endif /* DP264 */
 
+#if defined(CONFIG_ALPHA_RAWHIDE)
 static inline void
-update_hw_32(unsigned long irq, unsigned long mask)
+rawhide_update_hw(unsigned long irq, unsigned long mask)
 {
 	switch (irq) {
-	default: /* 24 ... 31 */
-		outb(mask >> 24, 0x27);
+	case 16 ... 39: /* PCI bus 0 with EISA bridge */
+		*(vuip)MCPCIA_INT_MASK0(0) =
+		    (~((mask) >> 16) & 0x00ffffffU) | 0x00ff0000U; mb();
+		/* ... and read it back to make sure it got written.  */
+	  	*(vuip)MCPCIA_INT_MASK0(0);
 		break;
-	case 16 ... 23:
-		outb(mask >> 16, 0x26);
+	case 40 ... 63: /* PCI bus 1 with builtin NCR810 SCSI */
+		*(vuip)MCPCIA_INT_MASK0(1) =
+		    (~((mask) >> 40) & 0x00ffffffU) | 0x00fe0000U; mb();
+		/* ... and read it back to make sure it got written.  */
+	  	*(vuip)MCPCIA_INT_MASK0(1);
 		break;
 	case  8 ... 15:	/* ISA PIC2 */
 		outb(mask >> 8, 0xA1);
@@ -299,12 +319,29 @@
 		break;
 	}
 }
+#endif /* RAWHIDE */
 
+/*
+ * HW update code for the following platforms:
+ *
+ *	CABRIOLET (AlphaPC64)
+ *	EB66P
+ *	EB164
+ *	PC164
+ *	LX164
+ */
 static inline void
-update_hw_16(unsigned long irq, unsigned long mask)
+update_hw_35(unsigned long irq, unsigned long mask)
 {
 	switch (irq) {
-	default: /* 8 ... 15, ISA PIC2 */
+	case 16 ... 34:
+#if defined(CONFIG_ALPHA_SRM)
+		cserve_update_hw(irq, mask);
+#else /* SRM */
+		outl(irq_mask >> 16, 0x804);
+#endif /* SRM */
+		break;
+	case  8 ... 15:	/* ISA PIC2 */
 		outb(mask >> 8, 0xA1);
 		break;
 	case  0 ... 7: /* ISA PIC1 */
@@ -313,42 +350,38 @@
 	}
 }
 
-#if (defined(CONFIG_ALPHA_PC164) || defined(CONFIG_ALPHA_LX164)) \
-	&& defined(CONFIG_ALPHA_SRM)
-/*
- * On the pc164, we cannot take over the IRQs from the SRM, 
- * so we call down to do our dirty work.  Too bad the SRM
- * isn't consistent across platforms otherwise we could do
- * this always.
- */
-
-extern void cserve_ena(unsigned long);
-extern void cserve_dis(unsigned long);
-
-static inline void mask_irq(unsigned long irq)
+static inline void
+update_hw_32(unsigned long irq, unsigned long mask)
 {
-	irq_mask |= (1UL << irq);
-	cserve_dis(irq - 16);
+	switch (irq) {
+	case 24 ... 31:
+		outb(mask >> 24, 0x27);
+		break;
+	case 16 ... 23:
+		outb(mask >> 16, 0x26);
+		break;
+	case  8 ... 15:	/* ISA PIC2 */
+		outb(mask >> 8, 0xA1);
+		break;
+	case  0 ... 7:	/* ISA PIC1 */
+		outb(mask, 0x21);
+		break;
 }
-
-static inline void unmask_irq(unsigned long irq)
-{
-	irq_mask &= ~(1UL << irq);
-	cserve_ena(irq - 16);
 }
 
-/* Since we are calling down to PALcode, no need to diddle IPL.  */
-void disable_irq(unsigned int irq_nr)
+static inline void
+update_hw_16(unsigned long irq, unsigned long mask)
 {
-	mask_irq(IRQ_TO_MASK(irq_nr));
+	switch (irq) {
+	case 8 ... 15: /* ISA PIC2 */
+		outb(mask >> 8, 0xA1);
+		break;
+	case  0 ... 7: /* ISA PIC1 */
+		outb(mask, 0x21);
+		break;
 }
-
-void enable_irq(unsigned int irq_nr)
-{
-	unmask_irq(IRQ_TO_MASK(irq_nr));
 }
 
-#else
 /*
  * We manipulate the hardware ourselves.
  */
@@ -369,9 +402,18 @@
 	sx164_update_hw(irq, mask);
 #elif defined(CONFIG_ALPHA_RUFFIAN)
 	ruffian_update_hw(irq, mask);
-#elif NR_IRQS == 33
-	update_hw_33(irq, mask);
-#elif NR_IRQS == 32
+#elif defined(CONFIG_ALPHA_DP264)
+	dp264_update_hw(irq, mask);
+#elif defined(CONFIG_ALPHA_RAWHIDE)
+	rawhide_update_hw(irq, mask);
+#elif defined(CONFIG_ALPHA_CABRIOLET) || \
+      defined(CONFIG_ALPHA_EB66P)     || \
+      defined(CONFIG_ALPHA_EB164)     || \
+      defined(CONFIG_ALPHA_PC164)     || \
+      defined(CONFIG_ALPHA_LX164)
+	update_hw_35(irq, mask);
+#elif defined(CONFIG_ALPHA_EB66) || \
+      defined(CONFIG_ALPHA_EB64P)
 	update_hw_32(irq, mask);
 #elif NR_IRQS == 16
 	update_hw_16(irq, mask);
@@ -396,8 +438,7 @@
 {
 	unsigned long flags;
 
-	save_flags(flags);
-	cli();
+	save_and_cli(flags);
 	mask_irq(IRQ_TO_MASK(irq_nr));
 	restore_flags(flags);
 }
@@ -406,12 +447,10 @@
 {
 	unsigned long flags;
 
-	save_flags(flags);
-	cli();
+	save_and_cli(flags);
 	unmask_irq(IRQ_TO_MASK(irq_nr));
 	restore_flags(flags);
 }
-#endif /* (PC164 || LX164) && SRM */
 
 /*
  * Initial irq handlers.
@@ -423,13 +462,14 @@
 {
 	int i, len = 0;
 	struct irqaction * action;
+	int cpu = smp_processor_id();
 
 	for (i = 0; i < NR_IRQS; i++) {
 		action = irq_action[i];
 		if (!action) 
 			continue;
 		len += sprintf(buf+len, "%2d: %10u %c %s",
-			       i, kstat.irqs[0][i],
+			       i, kstat.irqs[cpu][i],
 			       (action->flags & SA_INTERRUPT) ? '+' : ' ',
 			       action->name);
 		for (action=action->next; action; action = action->next) {
@@ -463,16 +503,18 @@
 #elif defined(CONFIG_ALPHA_RUFFIAN)
 	if (irq < 16) {
 		/* Ack PYXIS ISA interrupt.  */
-		*(vulp)PYXIS_INT_REQ = 1 << 7;
-		mb();
+		*(vulp)PYXIS_INT_REQ = 1L << 7; mb();
+		/* ... and read it back to make sure it got written.  */
+		*(vulp)PYXIS_INT_REQ;
 		if (irq > 7) {
 			outb(0x20, 0xa0);
 		}
 		outb(0x20, 0x20);
 	} else {
-		/* Ack PYXIS interrupt.  */
+		/* Ack PYXIS PCI interrupt.  */
 		*(vulp)PYXIS_INT_REQ = (1UL << (irq - 16));
-		mb();
+		/* ... and read it back to make sure it got written.  */
+		*(vulp)PYXIS_INT_REQ;
 	}
 #else
 	if (irq < 16) {
@@ -488,7 +530,7 @@
 		/* on ALCOR/XLT, need to dismiss interrupt via GRU */
 		*(vuip)GRU_INT_CLEAR = 0x80000000; mb();
 		*(vuip)GRU_INT_CLEAR = 0x00000000; mb();
-#endif
+#endif /* ALCOR || XLT */
 	}
 #endif
 }
@@ -556,8 +598,7 @@
 	action->next = NULL;
 	action->dev_id = dev_id;
 
-	save_flags(flags);
-	cli();
+	save_and_cli(flags);
 	*p = action;
 
 	if (!shared)
@@ -585,8 +626,7 @@
 			continue;
 
 		/* Found it - now free it */
-		save_flags(flags);
-		cli();
+		save_and_cli(flags);
 		*p = action->next;
 		if (!irq[irq_action])
 			mask_irq(IRQ_TO_MASK(irq));
@@ -607,7 +647,277 @@
 unsigned int local_bh_count[NR_CPUS];
 
 #ifdef __SMP__
-#error "Me no hablo Alpha SMP"
+/* Who has global_irq_lock. */
+unsigned char global_irq_holder = NO_PROC_ID;
+
+/* This protects IRQ's. */
+spinlock_t global_irq_lock = SPIN_LOCK_UNLOCKED;
+
+/* Global IRQ locking depth. */
+atomic_t global_irq_count = ATOMIC_INIT(0);
+
+/* This protects BH software state (masks, things like that). */
+atomic_t global_bh_lock = ATOMIC_INIT(0);
+atomic_t global_bh_count = ATOMIC_INIT(0);
+
+static unsigned long previous_irqholder = NO_PROC_ID;
+
+#undef INIT_STUCK
+#define INIT_STUCK 100000000
+
+#undef STUCK
+#define STUCK \
+if (!--stuck) {printk("wait_on_irq CPU#%d stuck at %08lx, waiting for %08lx (local=%d, global=%d)\n", cpu, where, previous_irqholder, local_count, atomic_read(&global_irq_count)); stuck = INIT_STUCK; }
+
+static inline void wait_on_irq(int cpu, unsigned long where)
+{
+	int stuck = INIT_STUCK;
+	int local_count = local_irq_count[cpu];
+
+	/* Are we the only one in an interrupt context? */
+	while (local_count != atomic_read(&global_irq_count)) {
+		/*
+		 * No such luck. Now we need to release the lock,
+		 * _and_ release our interrupt context, because
+		 * otherwise we'd have dead-locks and live-locks
+		 * and other fun things.
+		 */
+		atomic_sub(local_count, &global_irq_count);
+		spin_unlock(&global_irq_lock);
+
+		/*
+		 * Wait for everybody else to go away and release
+		 * their things before trying to get the lock again.
+		 */
+		for (;;) {
+			STUCK;
+			if (atomic_read(&global_irq_count))
+				continue;
+			if (global_irq_lock.lock)
+				continue;
+			if (spin_trylock(&global_irq_lock))
+				break;
+		}
+		atomic_add(local_count, &global_irq_count);
+	}
+}
+
+#undef INIT_STUCK
+#define INIT_STUCK 10000000
+
+#undef STUCK
+#define STUCK \
+if (!--stuck) {printk("get_irqlock stuck at %08lx, waiting for %08lx\n", where, previous_irqholder); stuck = INIT_STUCK;}
+
+static inline void get_irqlock(int cpu, unsigned long where)
+{
+	int stuck = INIT_STUCK;
+
+	if (!spin_trylock(&global_irq_lock)) {
+		/* do we already hold the lock? */
+		if ((unsigned char) cpu == global_irq_holder) {
+#if 0
+			printk("get_irqlock: already held at %08lx\n",
+			       previous_irqholder);
+#endif
+			return;
+		}
+		/* Uhhuh.. Somebody else got it. Wait.. */
+		do {
+			do {
+				STUCK;
+				barrier();
+			} while (global_irq_lock.lock);
+		} while (!spin_trylock(&global_irq_lock));
+	}
+	/*
+	 * Ok, we got the lock bit.
+	 * But that's actually just the easy part.. Now
+	 * we need to make sure that nobody else is running
+	 * in an interrupt context. 
+	 */
+	wait_on_irq(cpu, where);
+
+	/*
+	 * Finally.
+	 */
+	global_irq_holder = cpu;
+	previous_irqholder = where;
+}
+
+void __global_cli(void)
+{
+	int cpu = smp_processor_id();
+	unsigned long where;
+
+        __asm__("mov $26, %0" : "=r" (where));
+	__cli();
+
+	if (!local_irq_count[cpu])
+	  get_irqlock(smp_processor_id(), where);
+}
+
+void __global_sti(void)
+{
+        int cpu = smp_processor_id();
+
+        if (!local_irq_count[cpu])
+	  release_irqlock(smp_processor_id());
+
+	__sti();
+}
+
+#if 0
+unsigned long __global_save_flags(void)
+{
+	return global_irq_holder == (unsigned char) smp_processor_id();
+}
+#endif
+
+void __global_restore_flags(unsigned long flags)
+{
+	if (flags & 1) {
+		__global_cli();
+	} else {
+		/* release_irqlock() */
+		if (global_irq_holder == smp_processor_id()) {
+			global_irq_holder = NO_PROC_ID;
+			spin_unlock(&global_irq_lock);
+		}
+		if (!(flags & 2))
+			__sti();
+	}
+}
+
+#undef INIT_STUCK
+#define INIT_STUCK 200000000
+
+#undef STUCK
+#define STUCK \
+if (!--stuck) {printk("irq_enter stuck (irq=%d, cpu=%d, global=%d)\n",irq,cpu,global_irq_holder); stuck = INIT_STUCK;}
+
+#undef VERBOSE_IRQLOCK_DEBUGGING
+
+void irq_enter(int cpu, int irq)
+{
+#ifdef VERBOSE_IRQLOCK_DEBUGGING
+	extern void smp_show_backtrace_all_cpus(void);
+#endif
+	int stuck = INIT_STUCK;
+
+	hardirq_enter(cpu);
+	barrier();
+	while (global_irq_lock.lock) {
+		if ((unsigned char) cpu == global_irq_holder) {
+			int globl_locked = global_irq_lock.lock;
+			int globl_icount = atomic_read(&global_irq_count);
+			int local_count = local_irq_count[cpu];
+
+			/* It is very important that we load the state variables
+			 * before we do the first call to printk() as printk()
+			 * could end up changing them...
+			 */
+
+#if 0
+			printk("CPU[%d]: BAD! Local IRQ's enabled,"
+			       " global disabled interrupt\n", cpu);
+#endif
+			printk("CPU[%d]: where [%08lx] glocked[%d] gicnt[%d]"
+			       " licnt[%d]\n",
+			       cpu, previous_irqholder, globl_locked,
+			       globl_icount, local_count);
+#ifdef VERBOSE_IRQLOCK_DEBUGGING
+			printk("Performing backtrace on all cpus,"
+			       " write this down!\n");
+			smp_show_backtrace_all_cpus();
+#endif
+			break;
+		}
+		STUCK;
+		barrier();
+	}
+}
+
+void irq_exit(int cpu, int irq)
+{
+	hardirq_exit(cpu);
+	release_irqlock(cpu);
+}
+
+static void show(char * str)
+{
+#if 0
+	int i;
+        unsigned long *stack;
+#endif
+        int cpu = smp_processor_id();
+
+        printk("\n%s, CPU %d:\n", str, cpu);
+        printk("irq:  %d [%d %d]\n",
+	       atomic_read(&global_irq_count), local_irq_count[0],
+	       local_irq_count[1]);
+        printk("bh:   %d [%d %d]\n",
+	       atomic_read(&global_bh_count), local_bh_count[0],
+	       local_bh_count[1]);
+#if 0
+        stack = (unsigned long *) &str;
+        for (i = 40; i ; i--) {
+		unsigned long x = *++stack;
+                if (x > (unsigned long) &init_task_union &&
+		    x < (unsigned long) &vsprintf) {
+			printk("<[%08lx]> ", x);
+                }
+        }
+#endif
+}
+        
+#define MAXCOUNT 100000000
+
+static inline void wait_on_bh(void)
+{
+	int count = MAXCOUNT;
+        do {
+		if (!--count) {
+			show("wait_on_bh");
+                        count = ~0;
+                }
+                /* nothing .. wait for the other bh's to go away */
+        } while (atomic_read(&global_bh_count) != 0);
+}
+
+/*
+ * This is called when we want to synchronize with
+ * bottom half handlers. We need to wait until
+ * no other CPU is executing any bottom half handler.
+ *
+ * Don't wait if we're already running in an interrupt
+ * context or are inside a bh handler.
+ */
+void synchronize_bh(void)
+{
+	if (atomic_read(&global_bh_count)) {
+		int cpu = smp_processor_id();
+                if (!local_irq_count[cpu] && !local_bh_count[cpu]) {
+			wait_on_bh();
+		}
+        }
+}
+
+/* There has to be a better way. */
+void synchronize_irq(void)
+{
+	int cpu = smp_processor_id();
+	int local_count = local_irq_count[cpu];
+
+	if (local_count != atomic_read(&global_irq_count)) {
+		unsigned long flags;
+
+		/* An infamously unpopular approach. */
+		save_and_cli(flags);
+		restore_flags(flags);
+	}
+}
+
 #else
 #define irq_enter(cpu, irq)	(++local_irq_count[cpu])
 #define irq_exit(cpu, irq)	(--local_irq_count[cpu])
@@ -647,7 +957,7 @@
 	int cpu = smp_processor_id();
 
 	irq_enter(cpu, irq);
-	kstat.irqs[0][irq] += 1;
+	kstat.irqs[cpu][irq] += 1;
 	if (!action) {
 		unexpected_irq(irq, regs);
 	} else {
@@ -670,8 +980,9 @@
 	}
 
 	irq_enter(cpu, irq);
-	kstat.irqs[0][irq] += 1;
+	kstat.irqs[cpu][irq] += 1;
 	action = irq_action[irq];
+
 	/*
 	 * For normal interrupts, we mask it out, and then ACK it.
 	 * This way another (more timing-critical) interrupt can
@@ -691,6 +1002,10 @@
 			action = action->next;
 		} while (action);
 		unmask_irq(ack);
+	} else {
+#if 1
+		printk("device_interrupt: unexpected interrupt %d\n", irq);
+#endif
 	}
 	irq_exit(cpu, irq);
 }
@@ -711,6 +1026,8 @@
 #	define IACK_SC	CIA_IACK_SC
 #elif defined(CONFIG_ALPHA_PYXIS)
 #	define IACK_SC	PYXIS_IACK_SC
+#elif defined(CONFIG_ALPHA_TSUNAMI)
+#	define IACK_SC	TSUNAMI_PCI0_IACK_SC
 #else
 	/*
 	 * This is bogus but necessary to get it to compile
@@ -729,7 +1046,7 @@
 	 * interrupt that is pending.  The PALcode sets up the
 	 * interrupts vectors such that irq level L generates vector L.
 	 */
-	j = *(volatile int *) IACK_SC;
+	j = *(vuip) IACK_SC;
 	j &= 0xff;
 	if (j == 7) {
 		if (!(inb(0x20) & 0x80)) {
@@ -775,10 +1092,9 @@
 	unsigned int i;
 	unsigned long flags;
 
-	save_flags(flags);
-	cli();
+	save_and_cli(flags);
 
-	/* read the interrupt summary register of the GRU */
+	/* Read the interrupt summary register of the GRU */
 	pld = (*(vuip)GRU_INT_REQ) & GRU_INT_REQ_BITS;
 
 #if 0
@@ -810,10 +1126,9 @@
 	unsigned int i;
 	unsigned long flags;
 
-	save_flags(flags);
-	cli();
+	save_and_cli(flags);
 
-	/* read the interrupt summary registers */
+	/* Read the interrupt summary registers */
 	pld = inb(0x804) | (inb(0x805) << 8) | (inb(0x806) << 16);
 
 #if 0
@@ -843,10 +1158,9 @@
 	unsigned int i;
 	unsigned long flags;
 
-	save_flags(flags);
-	cli();
+	save_and_cli(flags);
 
-	/* read the interrupt summary registers */
+	/* Read the interrupt summary registers */
 	pld = (((unsigned long) (~inw(0x534)) & 0x0000ffffUL) << 16) |
 		(((unsigned long) inb(0xa0))  <<  8) |
 		((unsigned long) inb(0x20));
@@ -878,10 +1192,9 @@
 	unsigned int i;
 	unsigned long flags;
 
-	save_flags(flags);
-	cli();
+	save_and_cli(flags);
 
-	/* read the interrupt summary registers */
+	/* Read the interrupt summary registers */
 	pld = inb(0x26) | (inb(0x27) << 8);
 	/*
 	 * Now, for every possible bit set, work through
@@ -909,30 +1222,34 @@
 	unsigned int i;
 	unsigned long flags;
 
-	save_flags(flags);
-	cli();
+	save_and_cli(flags);
 
-	/* read the interrupt summary register of PYXIS */
-	pld = (*(vulp)PYXIS_INT_REQ);
+	/* Read the interrupt summary register of PYXIS */
+	pld = *(vulp)PYXIS_INT_REQ;
 
 #if 0
 	printk("[0x%08lx/0x%08lx/0x%04x]", pld,
 	       *(vulp)PYXIS_INT_MASK, inb(0x20) | (inb(0xA0) << 8));
 #endif
 
-	/* For now, AND off any bits we are not interested in.  */
-#if defined(CONFIG_ALPHA_MIATA)
-	/* HALT (2), timer (6), ISA Bridge (7), 21142/3 (8),
-	   then all the PCI slots/INTXs (12-31).  */
+#ifdef CONFIG_ALPHA_MIATA
+	/*
+	 * For now, AND off any bits we are not interested in:
+	 *   HALT (2), timer (6), ISA Bridge (7), 21142/3 (8)
+	 * then all the PCI slots/INTXs (12-31).
+	 */
 	/* Maybe HALT should only be used for SRM console boots? */
 	pld &= 0x00000000fffff1c4UL;
 #endif
-#if defined(CONFIG_ALPHA_SX164)
-	/* HALT (2), timer (6), ISA Bridge (7),
-	   then all the PCI slots/INTXs (8-23).  */
-	/* HALT should only be used for SRM console boots.  */
+#ifdef CONFIG_ALPHA_SX164
+	/*
+	 * For now, AND off any bits we are not interested in:
+	 *  HALT (2), timer (6), ISA Bridge (7)
+	 * then all the PCI slots/INTXs (8-23)
+	 */
+	/* Maybe HALT should only be used for SRM console boots? */
 	pld &= 0x0000000000ffffc0UL;
-#endif
+#endif /* SX164 */
 
 	/*
 	 * Now for every possible bit set, work through them and call
@@ -962,10 +1279,9 @@
 	unsigned int i;
 	unsigned long flags;
 
-	save_flags(flags);
-	cli();
+	save_and_cli(flags);
 
-	/* read the interrupt summary registers of NORITAKE */
+	/* Read the interrupt summary registers of NORITAKE */
 	pld = ((unsigned long) inw(0x54c) << 32) |
 		((unsigned long) inw(0x54a) << 16) |
 		((unsigned long) inb(0xa0)  <<  8) |
@@ -991,16 +1307,76 @@
 	restore_flags(flags);
 }
 
+#if defined(CONFIG_ALPHA_DP264)
+/* we have to conditionally compile this because of TSUNAMI_xxx symbols */
+static inline void dp264_device_interrupt(unsigned long vector,
+                                                  struct pt_regs * regs)
+{
+        unsigned long pld, tmp;
+        unsigned int i;
+        unsigned long flags;
+
+	__save_and_cli(flags);
+
+        /* Read the interrupt summary register of TSUNAMI */
+        pld = (*(vulp)TSUNAMI_CSR_DIR0);
+
+#if 0
+        printk("[0x%08lx/0x%08lx/0x%04x]", pld,
+	       *(vulp)TSUNAMI_CSR_DIM0,
+	       inb(0x20) | (inb(0xA0) << 8));
+#endif
+
+        /*
+         * Now for every possible bit set, work through them and call
+         * the appropriate interrupt handler.
+         */
+        while (pld) {
+                i = ffz(~pld);
+                pld &= pld - 1; /* clear least bit set */
+                if (i == 55) {
+                        isa_device_interrupt(vector, regs);
+		} else { /* if not timer int */
+                        device_interrupt(16 + i, 16 + i, regs);
+                }
+#if 0
+		*(vulp)TSUNAMI_CSR_DIR0 = 1UL << i; mb();
+		tmp = *(vulp)TSUNAMI_CSR_DIR0;
+#endif
+        }
+	__restore_flags(flags);
+}
+#endif /* DP264 */
+
+#if defined(CONFIG_ALPHA_RAWHIDE)
+/* we have to conditionally compile this because of MCPCIA_xxx symbols */
+static inline void rawhide_device_interrupt(unsigned long vector,
+                                                  struct pt_regs * regs)
+{
+#if 0
+        unsigned long pld;
+        unsigned int i;
+        unsigned long flags;
+
+	__save_and_cli(flags);
+
+	/* PLACEHOLDER, perhaps never used if we always do SRM */
+
+	__restore_flags(flags);
+#endif
+}
+#endif /* RAWHIDE */
+
 #if defined(CONFIG_ALPHA_RUFFIAN)
 static inline void
 ruffian_device_interrupt(unsigned long vector, struct pt_regs *regs)
+
 {
 	unsigned long pld;
 	unsigned int i;
 	unsigned long flags;
 
-	save_flags(flags);
-	cli();
+        save_and_cli(flags);
 
 	/* Read the interrupt summary register of PYXIS */
 	pld = *(vulp)PYXIS_INT_REQ;
@@ -1010,16 +1386,16 @@
 	 * then all the PCI slots/INTXs (12-31) 
 	 * flash(5) :DWH:
 	 */
-	pld &= 0x00000000ffffff9fUL;
+        pld &= 0x00000000ffffff9fUL;/* was ffff7f */
 
 	/*
 	 * Now for every possible bit set, work through them and call
 	 * the appropriate interrupt handler.
 	 */
+
 	while (pld) {
 		i = ffz(~pld);
 		pld &= pld - 1; /* clear least bit set */
-
 		if (i == 7) {
 			/* Copy this bit from isa_device_interrupt cause
 			   we need to hook into int 0 for the timer.  I
@@ -1041,19 +1417,57 @@
 			} else {
 				device_interrupt(j, j, regs);
 			}
-		} else {
+                } else { /* if not timer int */
 			device_interrupt(16 + i, 16 + i, regs);
 		}
 
-		*(vulp)PYXIS_INT_REQ = 1UL << i;
-		mb();
-		*(vulp)PYXIS_INT_REQ;
+                *(vulp)PYXIS_INT_REQ = 1UL << i; mb();
+                *(vulp)PYXIS_INT_REQ; /* read to force the write */
 	}
-
 	restore_flags(flags);
 }
 #endif /* RUFFIAN */
 
+static inline void takara_device_interrupt(unsigned long vector,
+					   struct pt_regs * regs)
+{
+	unsigned long flags;
+	unsigned intstatus;
+
+	save_and_cli(flags);
+
+	/*
+	 * The PALcode will have passed us vectors 0x800 or 0x810,
+	 * which are fairly arbitrary values and serve only to tell
+	 * us whether an interrupt has come in on IRQ0 or IRQ1. If
+	 * it's IRQ1 it's a PCI interrupt; if it's IRQ0, it's
+	 * probably ISA, but PCI interrupts can come through IRQ0
+	 * as well if the interrupt controller isn't in accelerated
+	 * mode.
+	 *
+	 * OTOH, the accelerator thing doesn't seem to be working
+	 * overly well, so what we'll do instead is try directly
+	 * examining the Master Interrupt Register to see if it's a
+	 * PCI interrupt, and if _not_ then we'll pass it on to the
+	 * ISA handler.
+	 */
+
+	intstatus = inw(0x500) & 15;
+	if (intstatus) {
+		/*
+		 * This is a PCI interrupt. Check each bit and
+		 * despatch an interrupt if it's set.
+		 */
+		if (intstatus & 8) device_interrupt(16+3, 16+3, regs);
+		if (intstatus & 4) device_interrupt(16+2, 16+2, regs);
+		if (intstatus & 2) device_interrupt(16+1, 16+1, regs);
+		if (intstatus & 1) device_interrupt(16+0, 16+0, regs);
+	} else
+		isa_device_interrupt (vector, regs);
+
+	restore_flags(flags);
+}
+
 #endif /* CONFIG_PCI */
 
 /*
@@ -1085,9 +1499,11 @@
 	int irq, ack;
 	unsigned long flags;
 
-	save_flags(flags);
-	cli();
+	__save_and_cli(flags);
 
+#ifdef __SMP__
+if (smp_processor_id()) printk("srm_device_interrupt on other CPU\n");
+#endif
 
 	ack = irq = (vector - 0x800) >> 4;
 
@@ -1131,9 +1547,9 @@
 
 #ifdef CONFIG_ALPHA_NORITAKE
 	/*
-	 * I really hate to do this, but the NORITAKE SRM console reports
-	 *  PCI vectors *lower* than I expected from the bit numbering in
-	 *  the documentation.
+	 * I really hate to do this, too, but the NORITAKE SRM console also
+	 *  reports PCI vectors *lower* than I expected from the bit numbers
+	 *  in the documentation.
 	 * But I really don't want to change the fixup code for allocation
 	 *  of IRQs, nor the irq_mask maintenance stuff, both of which look
 	 *  nice and clean now.
@@ -1153,9 +1569,41 @@
 #endif
 #endif /* CONFIG_ALPHA_SABLE */
 
+#ifdef CONFIG_ALPHA_DP264
+        /*
+         * the DP264 SRM console reports PCI interrupts with a vector
+	 * 0x100 *higher* than one might expect, as PCI IRQ 0 (ie bit 0)
+	 * shows up as IRQ 16, etc, etc. We adjust it down by 16 to have
+	 * it line up with the actual bit numbers from the DIM registers,
+	 * which is how we manage the interrupts/mask. Sigh...
+         */
+        if (irq >= 32)
+                ack = irq = irq - 16;
+#endif /* DP264 */
+
+#ifdef CONFIG_ALPHA_RAWHIDE
+        /*
+         * the RAWHIDE SRM console reports PCI interrupts with a vector
+	 * 0x80 *higher* than one might expect, as PCI IRQ 0 (ie bit 0)
+	 * shows up as IRQ 24, etc, etc. We adjust it down by 8 to have
+	 * it line up with the actual bit numbers from the REQ registers,
+	 * which is how we manage the interrupts/mask. Sigh...
+	 *
+	 * also, PCI #1 interrupts are offset some more... :-(
+         */
+	if (irq == 52)
+	  ack = irq = 56; /* SCSI on PCI 1 is special */
+	else {
+	  if (irq >= 24) /* adjust all PCI interrupts down 8 */
+	    ack = irq = irq - 8;
+	  if (irq >= 48) /* adjust PCI bus 1 interrupts down another 8 */
+	    ack = irq = irq - 8;
+	}
+#endif /* RAWHIDE */
+
 	device_interrupt(irq, ack, regs);
 
-	restore_flags(flags);
+	__restore_flags(flags);
 }
 
 /*
@@ -1218,6 +1666,10 @@
 				struct pt_regs * regs);
 extern void t2_machine_check(unsigned long vector, unsigned long la,
 			     struct pt_regs * regs);
+extern void tsunami_machine_check(unsigned long vector, unsigned long la,
+				  struct pt_regs * regs);
+extern void mcpcia_machine_check(unsigned long vector, unsigned long la,
+				 struct pt_regs * regs);
 
 static void 
 machine_check(unsigned long vector, unsigned long la, struct pt_regs *regs)
@@ -1232,6 +1684,10 @@
 	pyxis_machine_check(vector, la, regs);
 #elif defined(CONFIG_ALPHA_T2)
 	t2_machine_check(vector, la, regs);
+#elif defined(CONFIG_ALPHA_TSUNAMI)
+	tsunami_machine_check(vector, la, regs);
+#elif defined(CONFIG_ALPHA_MCPCIA)
+	mcpcia_machine_check(vector, la, regs);
 #else
 	printk("Machine check\n");
 #endif
@@ -1244,7 +1700,14 @@
 {
 	switch (type) {
 	case 0:
+#ifdef __SMP__
+/*		irq_enter(smp_processor_id(), 0); ??????? */
+		handle_ipi(&regs);
+/*		irq_exit(smp_processor_id(), 0);  ??????? */
+		return;
+#else /* __SMP__ */
 		printk("Interprocessor interrupt? You must be kidding\n");
+#endif /* __SMP__ */
 		break;
 	case 1:
 		handle_irq(RTC_IRQ, &regs);
@@ -1253,23 +1716,38 @@
 		machine_check(vector, la_ptr, &regs);
 		return;
 	case 3:
-#if defined(CONFIG_ALPHA_JENSEN) || defined(CONFIG_ALPHA_NONAME) || \
-    defined(CONFIG_ALPHA_P2K) || defined(CONFIG_ALPHA_SRM)
+#if defined(CONFIG_ALPHA_JENSEN) || \
+    defined(CONFIG_ALPHA_NONAME) || \
+    defined(CONFIG_ALPHA_P2K)    || \
+    defined(CONFIG_ALPHA_SRM)
 		srm_device_interrupt(vector, &regs);
-#elif defined(CONFIG_ALPHA_MIATA) || defined(CONFIG_ALPHA_SX164)
+#elif defined(CONFIG_ALPHA_MIATA) || \
+    defined(CONFIG_ALPHA_SX164)
 		miata_device_interrupt(vector, &regs);
 #elif defined(CONFIG_ALPHA_NORITAKE)
 		noritake_device_interrupt(vector, &regs);
-#elif defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT)
+#elif defined(CONFIG_ALPHA_ALCOR) || \
+      defined(CONFIG_ALPHA_XLT)
 		alcor_and_xlt_device_interrupt(vector, &regs);
-#elif defined(CONFIG_ALPHA_RUFFIAN)
-		ruffian_device_interrupt(vector, &regs);
+#elif defined(CONFIG_ALPHA_CABRIOLET) || \
+      defined(CONFIG_ALPHA_EB66P)     || \
+      defined(CONFIG_ALPHA_EB164)     || \
+      defined(CONFIG_ALPHA_PC164)     || \
+      defined(CONFIG_ALPHA_LX164)
+		cabriolet_and_eb66p_device_interrupt(vector, &regs);
 #elif defined(CONFIG_ALPHA_MIKASA)
 		mikasa_device_interrupt(vector, &regs);
-#elif NR_IRQS == 33
-		cabriolet_and_eb66p_device_interrupt(vector, &regs);
-#elif NR_IRQS == 32
+#elif defined(CONFIG_ALPHA_EB66) || \
+      defined(CONFIG_ALPHA_EB64P)
 		eb66_and_eb64p_device_interrupt(vector, &regs);
+#elif defined(CONFIG_ALPHA_RUFFIAN)
+		ruffian_device_interrupt(vector, &regs);
+#elif defined(CONFIG_ALPHA_DP264)
+		dp264_device_interrupt(vector, &regs);
+#elif defined(CONFIG_ALPHA_RAWHIDE)
+		rawhide_device_interrupt(vector, &regs);
+#elif defined(CONFIG_ALPHA_TAKARA)
+		takara_device_interrupt(vector, &regs);
 #elif NR_IRQS == 16
 		isa_device_interrupt(vector, &regs);
 #endif
@@ -1293,22 +1771,21 @@
 	outb(0x44, 0x535);		/* enable cascades in master */
 }
 
-#ifdef CONFIG_ALPHA_SX164
+#if defined(CONFIG_ALPHA_SX164)
 static inline void sx164_init_IRQ(void)
 {
+#if !defined(CONFIG_ALPHA_SRM)
 	/* note invert on MASK bits */
 	*(vulp)PYXIS_INT_MASK  = ~((long)irq_mask >> 16); mb();
-#if 0
-	*(vulp)PYXIS_INT_HILO  = 0x000000B2UL; mb(); /* ISA/NMI HI */
-	*(vulp)PYXIS_RT_COUNT  = 0UL; mb(); /* clear count */
-#endif
+        *(vulp)PYXIS_INT_MASK;
+#endif /* !SRM */
 	enable_irq(16 + 6);	/* enable timer */
 	enable_irq(16 + 7);	/* enable ISA PIC cascade */
 	enable_irq(2);		/* enable cascade */
 }
 #endif /* SX164 */
 
-#ifdef CONFIG_ALPHA_RUFFIAN
+#if defined(CONFIG_ALPHA_RUFFIAN)
 static inline void ruffian_init_IRQ(void)
 {
 	/* invert 6&7 for i82371 */
@@ -1343,19 +1820,19 @@
 }
 #endif /* RUFFIAN */
 
-
 #ifdef CONFIG_ALPHA_MIATA
 static inline void miata_init_IRQ(void)
 {
 	/* note invert on MASK bits */
 	*(vulp)PYXIS_INT_MASK = ~((long)irq_mask >> 16); mb(); /* invert */
+#if 0
+	/* these break on MiataGL so we'll try not to do it at all */
 	*(vulp)PYXIS_INT_HILO = 0x000000B2UL; mb();	/* ISA/NMI HI */
 	*(vulp)PYXIS_RT_COUNT = 0UL; mb();		/* clear count */
-	*(vulp)PYXIS_INT_REQ  = 0x4000000000000000UL; mb(); /* clear upper timer */
-#if 0
-	*(vulp)PYXIS_INT_ROUTE = 0UL; mb();		/* all are level */
-	*(vulp)PYXIS_INT_CNFG  = 0UL; mb();		/* all clear */
 #endif
+	/* clear upper timer */
+	*(vulp)PYXIS_INT_REQ  = 0x4000000000000000UL; mb();
+
 	enable_irq(16 + 2);	/* enable HALT switch - SRM only? */
 	enable_irq(16 + 6);     /* enable timer */
 	enable_irq(16 + 7);     /* enable ISA PIC cascade */
@@ -1381,7 +1858,7 @@
 	enable_irq(16 + 31);		/* enable (E)ISA PIC cascade */
 	enable_irq(2);			/* enable cascade */
 }
-#endif
+#endif /* ALCOR || XLT */
 
 static inline void mikasa_init_IRQ(void)
 {
@@ -1389,9 +1866,56 @@
 	enable_irq(2);			/* enable cascade */
 }
 
-static inline void init_IRQ_33(void)
+#if defined(CONFIG_ALPHA_DP264)
+static inline void dp264_init_IRQ(void)
+{
+	/* note invert on MASK bits */
+        *(vulp)TSUNAMI_CSR_DIM0 =
+	      ~(irq_mask) & ~0x0000000000000000UL; mb();
+        *(vulp)TSUNAMI_CSR_DIM0;
+        enable_irq(55);     /* enable CYPRESS interrupt controller (ISA) */
+	enable_irq(2);
+}
+#endif /* DP264 */
+
+#if defined(CONFIG_ALPHA_RAWHIDE)
+static inline void rawhide_init_IRQ(void)
+{
+	/* HACK ALERT! only PCI busses 0 and 1 are used currently,
+	   and routing is only to CPU #1*/
+
+	*(vuip)MCPCIA_INT_MASK0(0) =
+	    (~((irq_mask) >> 16) & 0x00ffffffU) | 0x00ff0000U; mb();
+	/* ... and read it back to make sure it got written.  */
+	*(vuip)MCPCIA_INT_MASK0(0);
+
+	*(vuip)MCPCIA_INT_MASK0(1) =
+	    (~((irq_mask) >> 40) & 0x00ffffffU) | 0x00fe0000U; mb();
+	/* ... and read it back to make sure it got written.  */
+	*(vuip)MCPCIA_INT_MASK0(1);
+	enable_irq(2);
+}
+#endif /* RAWHIDE */
+
+static inline void takara_init_IRQ(void)
+{
+	unsigned int ctlreg = inl(0x500);
+
+	ctlreg &= ~0x8000;     /* return to non-accelerated mode */
+	outw(ctlreg >> 16, 0x502);
+	outw(ctlreg & 0xFFFF, 0x500);
+	ctlreg = 0x05107c00;   /* enable the PCI interrupt register */
+	printk("Setting to 0x%08x\n", ctlreg);
+	outw(ctlreg >> 16, 0x502);
+	outw(ctlreg & 0xFFFF, 0x500);
+	enable_irq(2);
+}
+
+static inline void init_IRQ_35(void)
 {
+#if !defined(CONFIG_ALPHA_SRM)
 	outl(irq_mask >> 16, 0x804);
+#endif /* !SRM */
 	enable_irq(16 + 4);		/* enable SIO cascade */
 	enable_irq(2);			/* enable cascade */
 }
@@ -1413,13 +1937,20 @@
 init_IRQ(void)
 {
 	wrent(entInt, 0);
-	dma_outb(0, DMA1_RESET_REG);
-	dma_outb(0, DMA2_RESET_REG);
-#ifndef CONFIG_ALPHA_SX164
-	dma_outb(0, DMA1_CLR_MASK_REG);
-	/* We need to figure out why this fails on the SX164.  */
-	dma_outb(0, DMA2_CLR_MASK_REG);
-#endif
+
+/* FIXME FIXME FIXME FIXME FIXME */
+#if !defined(CONFIG_ALPHA_DP264)
+	/* we need to figure out why these fail on the DP264 */
+	outb(0, DMA1_RESET_REG);
+	outb(0, DMA2_RESET_REG);
+#endif /* !DP264 */
+/* FIXME FIXME FIXME FIXME FIXME */
+#if !defined(CONFIG_ALPHA_SX164) && !defined(CONFIG_ALPHA_DP264)
+	outb(0, DMA1_CLR_MASK_REG);
+	/* we need to figure out why this fails on the SX164 */
+	outb(0, DMA2_CLR_MASK_REG);
+#endif /* !SX164 && !DP264 */
+/* end FIXMEs */
 
 #if defined(CONFIG_ALPHA_SABLE)
 	sable_init_IRQ();
@@ -1431,17 +1962,21 @@
 	noritake_init_IRQ();
 #elif defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT)
 	alcor_and_xlt_init_IRQ();
-#elif (defined(CONFIG_ALPHA_PC164) || defined(CONFIG_ALPHA_LX164)) \
-	&& defined(CONFIG_ALPHA_SRM)
-	/* Disable all the PCI interrupts?  Otherwise, everthing was
-	   done by SRM already.  */
 #elif defined(CONFIG_ALPHA_MIKASA)
 	mikasa_init_IRQ();
+#elif defined(CONFIG_ALPHA_CABRIOLET) || defined(CONFIG_ALPHA_EB66P) || \
+      defined(CONFIG_ALPHA_PC164)     || defined(CONFIG_ALPHA_LX164) || \
+      defined(CONFIG_ALPHA_EB164)
+	init_IRQ_35();
 #elif defined(CONFIG_ALPHA_RUFFIAN)
 	ruffian_init_IRQ();
-#elif NR_IRQS == 33
-	init_IRQ_33();
-#elif NR_IRQS == 32
+#elif defined(CONFIG_ALPHA_DP264)
+        dp264_init_IRQ();
+#elif defined(CONFIG_ALPHA_RAWHIDE)
+        rawhide_init_IRQ();
+#elif defined(CONFIG_ALPHA_TAKARA)
+        takara_init_IRQ();
+#elif defined(CONFIG_ALPHA_EB66) || defined(CONFIG_ALPHA_EB64P)
 	init_IRQ_32();
 #elif NR_IRQS == 16
 	init_IRQ_16();
diff -u --recursive --new-file v2.1.91/linux/arch/alpha/kernel/lca.c linux/arch/alpha/kernel/lca.c
--- v2.1.91/linux/arch/alpha/kernel/lca.c	Mon Jan 12 14:51:14 1998
+++ linux/arch/alpha/kernel/lca.c	Mon Mar 30 00:21:39 1998
@@ -6,6 +6,7 @@
  * bios code.
  */
 #include <linux/kernel.h>
+#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/bios32.h>
 #include <linux/pci.h>
@@ -44,6 +45,11 @@
 #define MCHK_K_SIO_IOCHK	0x206	/* all platforms so far */
 #define MCHK_K_DCSR		0x208	/* all but Noname */
 
+#ifdef CONFIG_ALPHA_SRM_SETUP
+unsigned int LCA_DMA_WIN_BASE = LCA_DMA_WIN_BASE_DEFAULT;
+unsigned int LCA_DMA_WIN_SIZE = LCA_DMA_WIN_SIZE_DEFAULT;
+#endif /* SRM_SETUP */
+
 /*
  * Given a bus, device, and function number, compute resulting
  * configuration space address and setup the LCA_IOC_CONF register
@@ -100,11 +106,11 @@
 			return -1;
 		}
 
-		*((vulp) LCA_IOC_CONF) = 0;
+		*(vulp)LCA_IOC_CONF = 0;
 		addr = (1 << (11 + device)) | (func << 8) | where;
 	} else {
 		/* type 1 configuration cycle: */
-		*((vulp) LCA_IOC_CONF) = 1;
+		*(vulp)LCA_IOC_CONF = 1;
 		addr = (bus << 16) | (device_fn << 8) | where;
 	}
 	*pci_addr = addr;
@@ -130,7 +136,7 @@
 	value = *(vuip)addr;
 	draina();
 
-	stat0 = *((unsigned long*)LCA_IOC_STAT0);
+	stat0 = *(vulp)LCA_IOC_STAT0;
 	if (stat0 & LCA_IOC_STAT0_ERR) {
 		code = ((stat0 >> LCA_IOC_STAT0_CODE_SHIFT)
 			& LCA_IOC_STAT0_CODE_MASK);
@@ -167,7 +173,7 @@
 	*(vuip)addr = value;
 	draina();
 
-	stat0 = *((unsigned long*)LCA_IOC_STAT0);
+	stat0 = *(vulp)LCA_IOC_STAT0;
 	if (stat0 & LCA_IOC_STAT0_ERR) {
 		code = ((stat0 >> LCA_IOC_STAT0_CODE_SHIFT)
 			& LCA_IOC_STAT0_CODE_MASK);
@@ -287,6 +293,40 @@
 
 unsigned long lca_init(unsigned long mem_start, unsigned long mem_end)
 {
+#ifdef CONFIG_ALPHA_SRM_SETUP
+	/* check window 0 for enabled and mapped to 0 */
+	if ((*(vulp)LCA_IOC_W_BASE0 & (1UL<<33)) &&
+	    (*(vulp)LCA_IOC_T_BASE0 == 0))
+	{
+	  LCA_DMA_WIN_BASE = *(vulp)LCA_IOC_W_BASE0 & 0xffffffffUL;
+	  LCA_DMA_WIN_SIZE = *(vulp)LCA_IOC_W_MASK0 & 0xffffffffUL;
+	  LCA_DMA_WIN_SIZE += 1;
+#if 1
+	  printk("lca_init: using Window 0 settings\n");
+	  printk("lca_init: BASE 0x%lx MASK 0x%lx TRANS 0x%lx\n",
+		 *(vulp)LCA_IOC_W_BASE0,
+		 *(vulp)LCA_IOC_W_MASK0,
+		 *(vulp)LCA_IOC_T_BASE0);
+#endif
+	}
+	else	/* check window 2 for enabled and mapped to 0 */
+	if ((*(vulp)LCA_IOC_W_BASE1 & (1UL<<33)) &&
+	    (*(vulp)LCA_IOC_T_BASE1 == 0))
+	{
+	  LCA_DMA_WIN_BASE = *(vulp)LCA_IOC_W_BASE1 & 0xffffffffUL;
+	  LCA_DMA_WIN_SIZE = *(vulp)LCA_IOC_W_MASK1 & 0xffffffffUL;
+	  LCA_DMA_WIN_SIZE += 1;
+#if 1
+	  printk("lca_init: using Window 1 settings\n");
+	  printk("lca_init: BASE 0x%lx MASK 0x%lx TRANS 0x%lx\n",
+		 *(vulp)LCA_IOC_W_BASE1,
+		 *(vulp)LCA_IOC_W_MASK1,
+		 *(vulp)LCA_IOC_T_BASE1);
+#endif
+	}
+	else /* we must use our defaults... */
+#endif /* SRM_SETUP */
+	{
 	/*
 	 * Set up the PCI->physical memory translation windows.
 	 * For now, window 1 is disabled.  In the future, we may
@@ -294,9 +334,11 @@
 	 * goes at 1 GB and is 1 GB large.
 	 */
 	*(vulp)LCA_IOC_W_BASE1 = 0UL<<33;
+
 	*(vulp)LCA_IOC_W_BASE0 = 1UL<<33 | LCA_DMA_WIN_BASE;
 	*(vulp)LCA_IOC_W_MASK0 = LCA_DMA_WIN_SIZE - 1;
 	*(vulp)LCA_IOC_T_BASE0 = 0;
+	}
 
 	/*
 	 * Disable PCI parity for now.  The NCR53c810 chip has
diff -u --recursive --new-file v2.1.91/linux/arch/alpha/kernel/mcpcia.c linux/arch/alpha/kernel/mcpcia.c
--- v2.1.91/linux/arch/alpha/kernel/mcpcia.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/alpha/kernel/mcpcia.c	Mon Mar 30 00:21:39 1998
@@ -0,0 +1,981 @@
+/*
+ * Code common to all MCbus-PCI adaptor chipsets
+ *
+ * Based on code written by David A Rusling (david.rusling@reo.mts.dec.com).
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/sched.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/hwrpb.h>
+#include <asm/ptrace.h>
+#include <asm/mmu_context.h>
+#include <asm/delay.h>
+
+/*
+ * NOTE: Herein lie back-to-back mb instructions.  They are magic. 
+ * One plausible explanation is that the i/o controller does not properly
+ * handle the system transaction.  Another involves timing.  Ho hum.
+ */
+
+extern struct hwrpb_struct *hwrpb;
+extern asmlinkage void wrmces(unsigned long mces);
+
+/*
+ * BIOS32-style PCI interface:
+ */
+
+#ifdef CONFIG_ALPHA_MCPCIA
+
+#undef DEBUG_CFG
+
+#ifdef DEBUG_CFG
+# define DBG_CFG(args)	printk args
+#else
+# define DBG_CFG(args)
+#endif
+
+#undef DEBUG_PCI
+
+#ifdef DEBUG_PCI
+# define DBG_PCI(args)	printk args
+#else
+# define DBG_PCI(args)
+#endif
+
+#define DEBUG_MCHECK
+
+#ifdef DEBUG_MCHECK
+# define DBG_MCK(args)	printk args
+# define DEBUG_MCHECK_DUMP
+#else
+# define DBG_MCK(args)
+#endif
+
+#define vuip	volatile unsigned int  *
+#define vulp	volatile unsigned long  *
+
+static volatile unsigned int MCPCIA_mcheck_expected[NR_CPUS];
+static volatile unsigned int MCPCIA_mcheck_taken[NR_CPUS];
+static unsigned int MCPCIA_jd[NR_CPUS];
+
+#define MCPCIA_MAX_HOSES 2
+static int mcpcia_num_hoses = 0;
+
+static int pci_probe_enabled = 0; /* disable to start */
+
+static struct linux_hose_info *mcpcia_root = NULL, *mcpcia_last_hose;
+
+struct linux_hose_info *bus2hose[256];
+
+static inline unsigned long long_align(unsigned long addr)
+{
+	return ((addr + (sizeof(unsigned long) - 1)) &
+		~(sizeof(unsigned long) - 1));
+}
+
+#ifdef CONFIG_ALPHA_SRM_SETUP
+unsigned int MCPCIA_DMA_WIN_BASE = MCPCIA_DMA_WIN_BASE_DEFAULT;
+unsigned int MCPCIA_DMA_WIN_SIZE = MCPCIA_DMA_WIN_SIZE_DEFAULT;
+unsigned long mcpcia_sm_base_r1, mcpcia_sm_base_r2, mcpcia_sm_base_r3;
+#endif /* SRM_SETUP */
+
+/*
+ * Given a bus, device, and function number, compute resulting
+ * configuration space address and setup the MCPCIA_HAXR2 register
+ * accordingly.  It is therefore not safe to have concurrent
+ * invocations to configuration space access routines, but there
+ * really shouldn't be any need for this.
+ *
+ * Type 0:
+ *
+ *  3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 
+ *  3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | | |D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|0|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ *	31:11	Device select bit.
+ * 	10:8	Function number
+ * 	 7:2	Register number
+ *
+ * Type 1:
+ *
+ *  3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 
+ *  3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | | | | | | | | | | |B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|1|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ *	31:24	reserved
+ *	23:16	bus number (8 bits = 128 possible buses)
+ *	15:11	Device number (5 bits)
+ *	10:8	function number
+ *	 7:2	register number
+ *  
+ * Notes:
+ *	The function number selects which function of a multi-function device 
+ *	(e.g., scsi and ethernet).
+ * 
+ *	The register selects a DWORD (32 bit) register offset.  Hence it
+ *	doesn't get shifted by 2 bits as we want to "drop" the bottom two
+ *	bits.
+ */
+
+static unsigned int
+conf_read(unsigned long addr, unsigned char type1,
+	  struct linux_hose_info *hose)
+{
+	unsigned long flags;
+	unsigned long hoseno = hose->pci_hose_index;
+	unsigned int stat0, value, temp, cpu;
+
+	cpu = smp_processor_id();
+
+	save_and_cli(flags);
+
+	DBG_CFG(("conf_read(addr=0x%lx, type1=%d, hose=%d)\n",
+	     addr, type1, hoseno));
+
+	/* reset status register to avoid losing errors: */
+	stat0 = *(vuip)MCPCIA_CAP_ERR(hoseno);
+	*(vuip)MCPCIA_CAP_ERR(hoseno) = stat0; mb();
+	temp = *(vuip)MCPCIA_CAP_ERR(hoseno);
+	DBG_CFG(("conf_read: MCPCIA CAP_ERR(%d) was 0x%x\n", hoseno, stat0));
+
+	mb();
+	draina();
+	MCPCIA_mcheck_expected[cpu] = 1;
+	MCPCIA_mcheck_taken[cpu] = 0;
+	mb();
+	/* access configuration space: */
+	value = *((vuip)addr);
+	mb();
+	mb();  /* magic */
+	if (MCPCIA_mcheck_taken[cpu]) {
+		MCPCIA_mcheck_taken[cpu] = 0;
+		value = 0xffffffffU;
+		mb();
+	}
+	MCPCIA_mcheck_expected[cpu] = 0;
+	mb();
+
+	DBG_CFG(("conf_read(): finished\n"));
+
+	restore_flags(flags);
+	return value;
+}
+
+
+static void
+conf_write(unsigned long addr, unsigned int value, unsigned char type1,
+	   struct linux_hose_info *hose)
+{
+	unsigned long flags;
+	unsigned long hoseno = hose->pci_hose_index;
+	unsigned int stat0, temp, cpu;
+
+	cpu = smp_processor_id();
+
+	save_and_cli(flags);	/* avoid getting hit by machine check */
+
+	/* reset status register to avoid losing errors: */
+	stat0 = *(vuip)MCPCIA_CAP_ERR(hoseno);
+	*(vuip)MCPCIA_CAP_ERR(hoseno) = stat0; mb();
+	temp = *(vuip)MCPCIA_CAP_ERR(hoseno);
+	DBG_CFG(("conf_write: MCPCIA CAP_ERR(%d) was 0x%x\n", hoseno, stat0));
+
+	draina();
+	MCPCIA_mcheck_expected[cpu] = 1;
+	mb();
+	/* access configuration space: */
+	*((vuip)addr) = value;
+	mb();
+	mb();  /* magic */
+	temp = *(vuip)MCPCIA_CAP_ERR(hoseno); /* read to force the write */
+	MCPCIA_mcheck_expected[cpu] = 0;
+	mb();
+
+	DBG_CFG(("conf_write(): finished\n"));
+	restore_flags(flags);
+}
+
+static int mk_conf_addr(struct linux_hose_info *hose,
+			unsigned char bus, unsigned char device_fn,
+			unsigned char where, unsigned long *pci_addr,
+			unsigned char *type1)
+{
+	unsigned long addr;
+
+	if (!pci_probe_enabled) /* if doing standard pci_init(), ignore */
+	    return -1;
+
+	DBG_CFG(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x,"
+	     " pci_addr=0x%p, type1=0x%p)\n",
+	     bus, device_fn, where, pci_addr, type1));
+
+	/* type 1 configuration cycle for *ALL* busses */
+	*type1 = 1;
+
+	if (hose->pci_first_busno == bus)
+	    bus = 0;
+	addr = (bus << 16) | (device_fn << 8) | (where);
+	addr <<= 5; /* swizzle for SPARSE */
+	addr |= hose->pci_config_space;
+
+	*pci_addr = addr;
+	DBG_CFG(("mk_conf_addr: returning pci_addr 0x%lx\n", addr));
+	return 0;
+}
+
+
+int hose_read_config_byte (struct linux_hose_info *hose,
+			   unsigned char bus, unsigned char device_fn,
+			   unsigned char where, unsigned char *value)
+{
+	unsigned long addr;
+	unsigned char type1;
+
+	*value = 0xff;
+
+	if (mk_conf_addr(hose, bus, device_fn, where, &addr, &type1) < 0) {
+		return PCIBIOS_SUCCESSFUL;
+	}
+
+	addr |= 0x00; /* or in length */
+
+	*value = conf_read(addr, type1, hose) >> ((where & 3) * 8);
+	return PCIBIOS_SUCCESSFUL;
+}
+
+
+int hose_read_config_word (struct linux_hose_info *hose,
+			   unsigned char bus, unsigned char device_fn,
+			   unsigned char where, unsigned short *value)
+{
+	unsigned long addr;
+	unsigned char type1;
+
+	*value = 0xffff;
+
+	if (where & 0x1) {
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+	}
+
+	if (mk_conf_addr(hose, bus, device_fn, where, &addr, &type1)) {
+		return PCIBIOS_SUCCESSFUL;
+	}
+
+	addr |= 0x08; /* or in length */
+
+	*value = conf_read(addr, type1, hose) >> ((where & 3) * 8);
+	return PCIBIOS_SUCCESSFUL;
+}
+
+
+int hose_read_config_dword (struct linux_hose_info *hose,
+			    unsigned char bus, unsigned char device_fn,
+			    unsigned char where, unsigned int *value)
+{
+	unsigned long addr;
+	unsigned char type1;
+
+	*value = 0xffffffff;
+
+	if (where & 0x3) {
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+	}
+
+	if (mk_conf_addr(hose, bus, device_fn, where, &addr, &type1)) {
+		return PCIBIOS_SUCCESSFUL;
+	}
+	addr |= 0x18; /* or in length */
+
+	*value = conf_read(addr, type1, hose);
+	return PCIBIOS_SUCCESSFUL;
+}
+
+
+int hose_write_config_byte (struct linux_hose_info *hose,
+			    unsigned char bus, unsigned char device_fn,
+			    unsigned char where, unsigned char value)
+{
+	unsigned long addr;
+	unsigned char type1;
+
+	if (mk_conf_addr(hose, bus, device_fn, where, &addr, &type1) < 0) {
+		return PCIBIOS_SUCCESSFUL;
+	}
+
+	addr |= 0x00; /* or in length */
+
+	conf_write(addr, value << ((where & 3) * 8), type1, hose);
+	return PCIBIOS_SUCCESSFUL;
+}
+
+
+int hose_write_config_word (struct linux_hose_info *hose,
+			    unsigned char bus, unsigned char device_fn,
+			    unsigned char where, unsigned short value)
+{
+	unsigned long addr;
+	unsigned char type1;
+
+	if (mk_conf_addr(hose, bus, device_fn, where, &addr, &type1) < 0) {
+		return PCIBIOS_SUCCESSFUL;
+	}
+
+	addr |= 0x08; /* or in length */
+
+	conf_write(addr, value << ((where & 3) * 8), type1, hose);
+	return PCIBIOS_SUCCESSFUL;
+}
+
+
+int hose_write_config_dword (struct linux_hose_info *hose,
+			     unsigned char bus, unsigned char device_fn,
+			     unsigned char where, unsigned int value)
+{
+	unsigned long addr;
+	unsigned char type1;
+
+	if (mk_conf_addr(hose, bus, device_fn, where, &addr, &type1) < 0) {
+		return PCIBIOS_SUCCESSFUL;
+	}
+
+	addr |= 0x18; /* or in length */
+
+	conf_write(addr, value << ((where & 3) * 8), type1, hose);
+	return PCIBIOS_SUCCESSFUL;
+}
+
+int pcibios_read_config_byte (unsigned char bus, unsigned char devfn,
+			      unsigned char where, unsigned char *value)
+{
+	return hose_read_config_byte(bus2hose[bus], bus, devfn, where, value);
+}
+
+int pcibios_read_config_word (unsigned char bus, unsigned char devfn,
+			      unsigned char where, unsigned short *value)
+{
+	return hose_read_config_word(bus2hose[bus], bus, devfn, where, value);
+}
+
+int pcibios_read_config_dword (unsigned char bus, unsigned char devfn,
+			       unsigned char where, unsigned int *value)
+{
+	return hose_read_config_dword(bus2hose[bus], bus, devfn, where, value);
+}
+
+int pcibios_write_config_byte (unsigned char bus, unsigned char devfn,
+			       unsigned char where, unsigned char value)
+{
+	return hose_write_config_byte(bus2hose[bus], bus, devfn, where, value);
+}
+
+int pcibios_write_config_word (unsigned char bus, unsigned char devfn,
+			       unsigned char where, unsigned short value)
+{
+	return hose_write_config_word(bus2hose[bus], bus, devfn, where, value);
+}
+
+int pcibios_write_config_dword (unsigned char bus, unsigned char devfn,
+			        unsigned char where, unsigned int value)
+{
+	return hose_write_config_dword(bus2hose[bus], bus, devfn, where, value);
+}
+
+unsigned long mcpcia_init(unsigned long mem_start, unsigned long mem_end)
+{
+    struct linux_hose_info *hose;
+    unsigned int mcpcia_err;
+    unsigned int pci_rev;
+    int h;
+
+    mem_start = long_align(mem_start);
+
+    for (h = 0; h < NR_CPUS; h++) {
+	MCPCIA_mcheck_expected[h] = 0;
+	MCPCIA_mcheck_taken[h] = 0;
+    }
+
+    /* first, find how many hoses we have */
+    for (h = 0; h < MCPCIA_MAX_HOSES; h++) {
+	pci_rev = *(vuip)MCPCIA_REV(h);
+#if 0
+	printk("mcpcia_init: got 0x%x for PCI_REV for hose %d\n",
+	       pci_rev, h);
+#endif
+	if ((pci_rev >> 16) == PCI_CLASS_BRIDGE_HOST) {
+	    mcpcia_num_hoses++;
+
+	    hose = (struct linux_hose_info *)mem_start;
+	    mem_start = long_align(mem_start + sizeof(*hose));
+
+	    memset(hose, 0, sizeof(*hose));
+
+	    if (mcpcia_root)
+	        mcpcia_last_hose->next = hose;
+	    else
+	        mcpcia_root = hose;
+	    mcpcia_last_hose = hose;
+
+	    hose->pci_io_space = MCPCIA_IO(h);
+	    hose->pci_mem_space = MCPCIA_DENSE(h);
+	    hose->pci_config_space = MCPCIA_CONF(h);
+	    hose->pci_sparse_space = MCPCIA_SPARSE(h);
+	    hose->pci_hose_index = h;
+	    hose->pci_first_busno = 255;
+	    hose->pci_last_busno = 0;
+	}
+    }
+
+#if 1
+    printk("mcpcia_init: found %d hoses\n", mcpcia_num_hoses);
+#endif
+
+    /* now do init for each hose */
+    for (hose = mcpcia_root; hose; hose = hose->next) {
+      h = hose->pci_hose_index;
+#if 0
+#define PRINTK printk
+PRINTK("mcpcia_init: -------- hose %d --------\n",h);
+PRINTK("mcpcia_init: MCPCIA_REV 0x%x\n", *(vuip)MCPCIA_REV(h));
+PRINTK("mcpcia_init: MCPCIA_WHOAMI 0x%x\n", *(vuip)MCPCIA_WHOAMI(h));
+PRINTK("mcpcia_init: MCPCIA_HAE_MEM 0x%x\n", *(vuip)MCPCIA_HAE_MEM(h));
+PRINTK("mcpcia_init: MCPCIA_HAE_IO 0x%x\n", *(vuip)MCPCIA_HAE_IO(h));
+PRINTK("mcpcia_init: MCPCIA_HAE_DENSE 0x%x\n", *(vuip)MCPCIA_HAE_DENSE(h));
+PRINTK("mcpcia_init: MCPCIA_INT_CTL 0x%x\n", *(vuip)MCPCIA_INT_CTL(h));
+PRINTK("mcpcia_init: MCPCIA_INT_REQ 0x%x\n", *(vuip)MCPCIA_INT_REQ(h));
+PRINTK("mcpcia_init: MCPCIA_INT_TARG 0x%x\n", *(vuip)MCPCIA_INT_TARG(h));
+PRINTK("mcpcia_init: MCPCIA_INT_ADR 0x%x\n", *(vuip)MCPCIA_INT_ADR(h));
+PRINTK("mcpcia_init: MCPCIA_INT_ADR_EXT 0x%x\n", *(vuip)MCPCIA_INT_ADR_EXT(h));
+PRINTK("mcpcia_init: MCPCIA_INT_MASK0 0x%x\n", *(vuip)MCPCIA_INT_MASK0(h));
+PRINTK("mcpcia_init: MCPCIA_INT_MASK1 0x%x\n", *(vuip)MCPCIA_INT_MASK1(h));
+PRINTK("mcpcia_init: MCPCIA_HBASE 0x%x\n", *(vuip)MCPCIA_HBASE(h));
+#endif
+
+        /* 
+	 * Set up error reporting. Make sure CPU_PE is OFF in the mask.
+	 */
+#if 0
+	mcpcia_err = *(vuip)MCPCIA_ERR_MASK(h);
+	mcpcia_err &= ~4;   
+	*(vuip)MCPCIA_ERR_MASK(h) = mcpcia_err;
+	mb();
+	mcpcia_err = *(vuip)MCPCIA_ERR_MASK;
+#endif
+
+	mcpcia_err = *(vuip)MCPCIA_CAP_ERR(h);
+	mcpcia_err |= 0x0006;   /* master/target abort */
+	*(vuip)MCPCIA_CAP_ERR(h) = mcpcia_err;
+	mb() ;
+	mcpcia_err = *(vuip)MCPCIA_CAP_ERR(h);
+
+#ifdef CONFIG_ALPHA_SRM_SETUP
+	/* check window 0 for enabled and mapped to 0 */
+	if (((*(vuip)MCPCIA_W0_BASE(h) & 3) == 1) &&
+	    (*(vuip)MCPCIA_T0_BASE(h) == 0) &&
+	    ((*(vuip)MCPCIA_W0_MASK(h) & 0xfff00000U) > 0x0ff00000U))
+	{
+	  MCPCIA_DMA_WIN_BASE = *(vuip)MCPCIA_W0_BASE(h) & 0xfff00000U;
+	  MCPCIA_DMA_WIN_SIZE = *(vuip)MCPCIA_W0_MASK(h) & 0xfff00000U;
+	  MCPCIA_DMA_WIN_SIZE += 0x00100000U;
+#if 1
+	  printk("mcpcia_init: using Window 0 settings\n");
+	  printk("mcpcia_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n",
+		 *(vuip)MCPCIA_W0_BASE(h),
+		 *(vuip)MCPCIA_W0_MASK(h),
+		 *(vuip)MCPCIA_T0_BASE(h));
+#endif
+	}
+	else  /* check window 1 for enabled and mapped to 0 */
+	if (((*(vuip)MCPCIA_W1_BASE(h) & 3) == 1) &&
+	    (*(vuip)MCPCIA_T1_BASE(h) == 0) &&
+	    ((*(vuip)MCPCIA_W1_MASK(h) & 0xfff00000U) > 0x0ff00000U))
+{
+	  MCPCIA_DMA_WIN_BASE = *(vuip)MCPCIA_W1_BASE(h) & 0xfff00000U;
+	  MCPCIA_DMA_WIN_SIZE = *(vuip)MCPCIA_W1_MASK(h) & 0xfff00000U;
+	  MCPCIA_DMA_WIN_SIZE += 0x00100000U;
+#if 1
+	  printk("mcpcia_init: using Window 1 settings\n");
+	  printk("mcpcia_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n",
+		 *(vuip)MCPCIA_W1_BASE(h),
+		 *(vuip)MCPCIA_W1_MASK(h),
+		 *(vuip)MCPCIA_T1_BASE(h));
+#endif
+	}
+	else  /* check window 2 for enabled and mapped to 0 */
+	if (((*(vuip)MCPCIA_W2_BASE(h) & 3) == 1) &&
+	    (*(vuip)MCPCIA_T2_BASE(h) == 0) &&
+	    ((*(vuip)MCPCIA_W2_MASK(h) & 0xfff00000U) > 0x0ff00000U))
+	{
+	  MCPCIA_DMA_WIN_BASE = *(vuip)MCPCIA_W2_BASE(h) & 0xfff00000U;
+	  MCPCIA_DMA_WIN_SIZE = *(vuip)MCPCIA_W2_MASK(h) & 0xfff00000U;
+	  MCPCIA_DMA_WIN_SIZE += 0x00100000U;
+#if 1
+	  printk("mcpcia_init: using Window 2 settings\n");
+	  printk("mcpcia_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n",
+		 *(vuip)MCPCIA_W2_BASE(h),
+		 *(vuip)MCPCIA_W2_MASK(h),
+		 *(vuip)MCPCIA_T2_BASE(h));
+#endif
+	}
+	else  /* check window 3 for enabled and mapped to 0 */
+	if (((*(vuip)MCPCIA_W3_BASE(h) & 3) == 1) &&
+	    (*(vuip)MCPCIA_T3_BASE(h) == 0) &&
+	    ((*(vuip)MCPCIA_W3_MASK(h) & 0xfff00000U) > 0x0ff00000U))
+	{
+	  MCPCIA_DMA_WIN_BASE = *(vuip)MCPCIA_W3_BASE(h) & 0xfff00000U;
+	  MCPCIA_DMA_WIN_SIZE = *(vuip)MCPCIA_W3_MASK(h) & 0xfff00000U;
+	  MCPCIA_DMA_WIN_SIZE += 0x00100000U;
+#if 1
+	  printk("mcpcia_init: using Window 3 settings\n");
+	  printk("mcpcia_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n",
+		 *(vuip)MCPCIA_W3_BASE(h),
+		 *(vuip)MCPCIA_W3_MASK(h),
+		 *(vuip)MCPCIA_T3_BASE(h));
+#endif
+	}
+	else  /* we must use our defaults which were pre-initialized... */
+#endif /* SRM_SETUP */
+	{
+	/*
+	 * Set up the PCI->physical memory translation windows.
+	 * For now, windows 1,2 and 3 are disabled.  In the future, we may
+	 * want to use them to do scatter/gather DMA.  Window 0
+	 * goes at 1 GB and is 1 GB large.
+	 */
+
+	*(vuip)MCPCIA_W0_BASE(h) = 1U | (MCPCIA_DMA_WIN_BASE & 0xfff00000U);
+ 	*(vuip)MCPCIA_W0_MASK(h) = (MCPCIA_DMA_WIN_SIZE - 1) & 0xfff00000U;
+	*(vuip)MCPCIA_T0_BASE(h) = 0;
+
+	*(vuip)MCPCIA_W1_BASE(h) = 0x0 ;
+	*(vuip)MCPCIA_W2_BASE(h) = 0x0 ;
+	*(vuip)MCPCIA_W3_BASE(h) = 0x0 ;
+
+	*(vuip)MCPCIA_HBASE(h) = 0x0 ;
+	mb();
+	}
+
+	/*
+	 * check ASN in HWRPB for validity, report if bad
+	 */
+	if (hwrpb->max_asn != MAX_ASN) {
+		printk("mcpcia_init: max ASN from HWRPB is bad (0x%lx)\n",
+			hwrpb->max_asn);
+		hwrpb->max_asn = MAX_ASN;
+	}
+
+#if 0
+        {
+          unsigned int mcpcia_int_ctl = *((vuip)MCPCIA_INT_CTL(h));
+	  printk("mcpcia_init: INT_CTL was 0x%x\n", mcpcia_int_ctl);
+          *(vuip)MCPCIA_INT_CTL(h) = 1U; mb();
+	  mcpcia_int_ctl = *(vuip)MCPCIA_INT_CTL(h);
+        }
+#endif
+
+        {
+          unsigned int mcpcia_hae_mem = *(vuip)MCPCIA_HAE_MEM(h);
+          unsigned int mcpcia_hae_io = *(vuip)MCPCIA_HAE_IO(h);
+#if 0
+	  printk("mcpcia_init: HAE_MEM was 0x%x\n", mcpcia_hae_mem);
+	  printk("mcpcia_init: HAE_IO was 0x%x\n", mcpcia_hae_io);
+#endif
+#ifdef CONFIG_ALPHA_SRM_SETUP
+	  /*
+	    sigh... For the SRM setup, unless we know apriori what the HAE
+	    contents will be, we need to setup the arbitrary region bases
+	    so we can test against the range of addresses and tailor the
+	    region chosen for the SPARSE memory access.
+
+	    see include/asm-alpha/mcpcia.h for the SPARSE mem read/write
+	  */
+	  mcpcia_sm_base_r1 = (mcpcia_hae_mem      ) & 0xe0000000UL;/* reg 1 */
+	  mcpcia_sm_base_r2 = (mcpcia_hae_mem << 16) & 0xf8000000UL;/* reg 2 */
+	  mcpcia_sm_base_r3 = (mcpcia_hae_mem << 24) & 0xfc000000UL;/* reg 3 */
+	  /*
+	    Set the HAE cache, so that setup_arch() code
+	    will use the SRM setting always. Our readb/writeb
+	    code in mcpcia.h expects never to have to change
+	    the contents of the HAE.
+	   */
+	  hae.cache = mcpcia_hae_mem;
+#else /* SRM_SETUP */
+          *(vuip)MCPCIA_HAE_MEM(h) = 0U; mb();
+	  mcpcia_hae_mem = *(vuip)MCPCIA_HAE_MEM(h);
+          *(vuip)MCPCIA_HAE_IO(h) = 0; mb();
+	  mcpcia_hae_io = *(vuip)MCPCIA_HAE_IO(h);
+#endif /* SRM_SETUP */
+        }
+    } /* end for-loop on hoses */
+    return mem_start;
+}
+
+int mcpcia_pci_clr_err(int h)
+{
+	unsigned int cpu = smp_processor_id();
+
+	MCPCIA_jd[cpu] = *(vuip)MCPCIA_CAP_ERR(h);
+#if 0
+	DBG_MCK(("MCPCIA_pci_clr_err: MCPCIA CAP_ERR(%d) after read 0x%x\n",
+	     h, MCPCIA_jd[cpu]));
+#endif
+	*(vuip)MCPCIA_CAP_ERR(h) = 0xffffffff; mb(); /* clear them all */
+	MCPCIA_jd[cpu] = *(vuip)MCPCIA_CAP_ERR(h);
+	return 0;
+}
+
+static void
+mcpcia_print_uncorrectable(struct el_MCPCIA_uncorrected_frame_mcheck *logout)
+{
+  struct el_common_EV5_uncorrectable_mcheck *frame;
+  int i;
+
+  frame = &logout->procdata;
+
+  /* Print PAL fields */
+  for (i = 0; i < 24; i += 2) {
+    printk("\tpal temp[%d-%d]\t\t= %16lx %16lx\n\r",
+	   i, i+1, frame->paltemp[i], frame->paltemp[i+1]);
+  }
+  for (i = 0; i < 8; i += 2) {
+    printk("\tshadow[%d-%d]\t\t= %16lx %16lx\n\r",
+	   i, i+1, frame->shadow[i], 
+	   frame->shadow[i+1]);
+  }
+  printk("\tAddr of excepting instruction\t= %16lx\n\r",
+	 frame->exc_addr);
+  printk("\tSummary of arithmetic traps\t= %16lx\n\r",
+	 frame->exc_sum);
+  printk("\tException mask\t\t\t= %16lx\n\r",
+	 frame->exc_mask);
+  printk("\tBase address for PALcode\t= %16lx\n\r",
+	 frame->pal_base);
+  printk("\tInterrupt Status Reg\t\t= %16lx\n\r",
+	 frame->isr);
+  printk("\tCURRENT SETUP OF EV5 IBOX\t= %16lx\n\r",
+	 frame->icsr);
+  printk("\tI-CACHE Reg %s parity error\t= %16lx\n\r",
+	 (frame->ic_perr_stat & 0x800L) ? 
+	 "Data" : "Tag", 
+	 frame->ic_perr_stat); 
+  printk("\tD-CACHE error Reg\t\t= %16lx\n\r",
+	 frame->dc_perr_stat);
+  if (frame->dc_perr_stat & 0x2) {
+    switch (frame->dc_perr_stat & 0x03c) {
+    case 8:
+      printk("\t\tData error in bank 1\n\r");
+      break;
+    case 4:
+      printk("\t\tData error in bank 0\n\r");
+      break;
+    case 20:
+      printk("\t\tTag error in bank 1\n\r");
+      break;
+    case 10:
+      printk("\t\tTag error in bank 0\n\r");
+      break;
+    }
+  }
+  printk("\tEffective VA\t\t\t= %16lx\n\r",
+	 frame->va);
+  printk("\tReason for D-stream\t\t= %16lx\n\r",
+	 frame->mm_stat);
+  printk("\tEV5 SCache address\t\t= %16lx\n\r",
+	 frame->sc_addr);
+  printk("\tEV5 SCache TAG/Data parity\t= %16lx\n\r",
+	 frame->sc_stat);
+  printk("\tEV5 BC_TAG_ADDR\t\t\t= %16lx\n\r",
+	 frame->bc_tag_addr);
+  printk("\tEV5 EI_ADDR: Phys addr of Xfer\t= %16lx\n\r",
+	 frame->ei_addr);
+  printk("\tFill Syndrome\t\t\t= %16lx\n\r",
+	 frame->fill_syndrome);
+  printk("\tEI_STAT reg\t\t\t= %16lx\n\r",
+	 frame->ei_stat);
+  printk("\tLD_LOCK\t\t\t\t= %16lx\n\r",
+	 frame->ld_lock);
+}
+
+void mcpcia_machine_check(unsigned long type, unsigned long la_ptr,
+			 struct pt_regs * regs)
+{
+#if 0
+        printk("mcpcia machine check ignored\n") ;
+#else
+	struct el_common *mchk_header;
+	struct el_MCPCIA_uncorrected_frame_mcheck *mchk_logout;
+	unsigned int cpu = smp_processor_id();
+	int h = 0;
+
+	mchk_header = (struct el_common *)la_ptr;
+	mchk_logout = (struct el_MCPCIA_uncorrected_frame_mcheck *)la_ptr;
+
+#if 0
+	DBG_MCK(("mcpcia_machine_check: type=0x%lx la_ptr=0x%lx\n",
+	     type, la_ptr));
+	DBG_MCK(("\t\t pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n",
+	     regs->pc, mchk_header->size, mchk_header->proc_offset,
+	     mchk_header->sys_offset));
+#endif
+	/*
+	 * Check if machine check is due to a badaddr() and if so,
+	 * ignore the machine check.
+	 */
+	mb();
+	mb();  /* magic */
+	if (MCPCIA_mcheck_expected[cpu]) {
+#if 0
+		DBG_MCK(("MCPCIA machine check expected\n"));
+#endif
+		MCPCIA_mcheck_expected[cpu] = 0;
+		MCPCIA_mcheck_taken[cpu] = 1;
+		mb();
+		mb();  /* magic */
+		draina();
+		mcpcia_pci_clr_err(h);
+		wrmces(0x7);
+		mb();
+	}
+#if 1
+	else {
+		printk("MCPCIA machine check NOT expected on CPU %d\n", cpu);
+		DBG_MCK(("mcpcia_machine_check: type=0x%lx pc=0x%lx"
+			 " code=0x%lx\n",
+			 type, regs->pc, mchk_header->code));
+
+		MCPCIA_mcheck_expected[cpu] = 0;
+		MCPCIA_mcheck_taken[cpu] = 1;
+		mb();
+		mb();  /* magic */
+		draina();
+		mcpcia_pci_clr_err(h);
+		wrmces(0x7);
+		mb();
+#ifdef DEBUG_MCHECK_DUMP
+		if (type == 0x620)
+		  printk("MCPCIA machine check: system CORRECTABLE!\n");
+		else if (type == 0x630)
+		  printk("MCPCIA machine check: processor CORRECTABLE!\n");
+		else
+		  mcpcia_print_uncorrectable(mchk_logout);
+#endif /* DEBUG_MCHECK_DUMP */
+	}
+#endif
+#endif
+}
+
+/*==========================================================================*/
+
+#define PRIMARY(b) ((b)&0xff)
+#define SECONDARY(b) (((b)>>8)&0xff)
+#define SUBORDINATE(b) (((b)>>16)&0xff)
+
+static int
+hose_scan_bridges(struct linux_hose_info *hose, unsigned char bus)
+{
+	unsigned int devfn, l, class;
+	unsigned char hdr_type = 0;
+	unsigned int found = 0;
+
+	for (devfn = 0; devfn < 0xff; ++devfn) {
+		if (PCI_FUNC(devfn) == 0) {
+			hose_read_config_byte(hose, bus, devfn,
+					      PCI_HEADER_TYPE, &hdr_type);
+		} else if (!(hdr_type & 0x80)) {
+			/* not a multi-function device */
+			continue;
+		}
+
+		/* Check if there is anything here. */
+		hose_read_config_dword(hose, bus, devfn, PCI_VENDOR_ID, &l);
+		if (l == 0xffffffff || l == 0x00000000) {
+			hdr_type = 0;
+			continue;
+		}
+
+		/* See if this is a bridge device. */
+		hose_read_config_dword(hose, bus, devfn,
+				       PCI_CLASS_REVISION, &class);
+
+		if ((class >> 16) == PCI_CLASS_BRIDGE_PCI) {
+			unsigned int busses;
+
+			found++;
+
+			hose_read_config_dword(hose, bus, devfn,
+					       PCI_PRIMARY_BUS, &busses);
+
+DBG_PCI(("hose_scan_bridges: hose %d bus %d slot %d busses 0x%x\n",
+       hose->pci_hose_index, bus, PCI_SLOT(devfn), busses));
+			/*
+			 * do something with first_busno and last_busno
+			 */
+			if (hose->pci_first_busno > PRIMARY(busses)) {
+				hose->pci_first_busno = PRIMARY(busses);
+DBG_PCI(("hose_scan_bridges: hose %d bus %d slot %d change first to %d\n",
+       hose->pci_hose_index, bus, PCI_SLOT(devfn), PRIMARY(busses)));
+			}
+			if (hose->pci_last_busno < SUBORDINATE(busses)) {
+				hose->pci_last_busno = SUBORDINATE(busses);
+DBG_PCI(("hose_scan_bridges: hose %d bus %d slot %d change last to %d\n",
+       hose->pci_hose_index, bus, PCI_SLOT(devfn), SUBORDINATE(busses)));
+			}
+			/*
+			 * Now scan everything underneath the bridge.
+			 */
+			hose_scan_bridges(hose, SECONDARY(busses));
+		}
+	}
+	return found;
+}
+
+static void
+hose_reconfigure_bridges(struct linux_hose_info *hose, unsigned char bus)
+{
+	unsigned int devfn, l, class;
+	unsigned char hdr_type = 0;
+
+	for (devfn = 0; devfn < 0xff; ++devfn) {
+		if (PCI_FUNC(devfn) == 0) {
+			hose_read_config_byte(hose, bus, devfn,
+					      PCI_HEADER_TYPE, &hdr_type);
+		} else if (!(hdr_type & 0x80)) {
+			/* not a multi-function device */
+			continue;
+		}
+
+		/* Check if there is anything here. */
+		hose_read_config_dword(hose, bus, devfn, PCI_VENDOR_ID, &l);
+		if (l == 0xffffffff || l == 0x00000000) {
+			hdr_type = 0;
+			continue;
+		}
+
+		/* See if this is a bridge device. */
+		hose_read_config_dword(hose, bus, devfn,
+				       PCI_CLASS_REVISION, &class);
+
+		if ((class >> 16) == PCI_CLASS_BRIDGE_PCI) {
+			unsigned int busses;
+
+			hose_read_config_dword(hose, bus, devfn,
+					       PCI_PRIMARY_BUS, &busses);
+
+			/*
+			 * First reconfigure everything underneath the bridge.
+			 */
+			hose_reconfigure_bridges(hose, (busses >> 8) & 0xff);
+
+			/*
+			 * Unconfigure this bridges bus numbers,
+			 * pci_scan_bus() will fix this up properly.
+			 */
+			busses &= 0xff000000;
+			hose_write_config_dword(hose, bus, devfn,
+						PCI_PRIMARY_BUS, busses);
+		}
+	}
+}
+
+static void mcpcia_fixup_busno(struct linux_hose_info *hose, unsigned char bus)
+{
+	unsigned int nbus;
+
+	/*
+	 * First, scan for all bridge devices underneath this hose,
+	 * to determine the first and last busnos.
+	 */
+	if (!hose_scan_bridges(hose, 0)) {
+		/* none found, exit */
+		hose->pci_first_busno = bus;
+		hose->pci_last_busno = bus;
+	} else {
+		/*
+		 * Reconfigure all bridge devices underneath this hose.
+		 */
+		hose_reconfigure_bridges(hose, hose->pci_first_busno);
+	}
+
+	/*
+	 * Now reconfigure the hose to it's new bus number and set up
+	 * our bus2hose mapping for this hose.
+	 */
+	nbus = hose->pci_last_busno - hose->pci_first_busno;
+
+	hose->pci_first_busno = bus;
+
+DBG_PCI(("mcpcia_fixup_busno: hose %d startbus %d nbus %d\n",
+       hose->pci_hose_index, bus, nbus));
+
+	do {
+		bus2hose[bus++] = hose;
+	} while (nbus-- > 0);
+}
+
+static void mcpcia_probe(struct linux_hose_info *hose,
+			 unsigned long *mem_start)
+{
+	static struct pci_bus *pchain = NULL;
+	struct pci_bus *pbus = &hose->pci_bus;
+	static unsigned char busno = 0;
+
+	/* Hoses include child PCI bridges in bus-range property,
+	 * but we don't scan each of those ourselves, Linux generic PCI
+	 * probing code will find child bridges and link them into this
+	 * hose's root PCI device hierarchy.
+	 */
+
+	pbus->number = pbus->secondary = busno;
+	pbus->sysdata = hose;
+
+	mcpcia_fixup_busno(hose, busno);
+
+	pbus->subordinate = pci_scan_bus(pbus, mem_start); /* the original! */
+
+	/*
+	 * Set the maximum subordinate bus of this hose.
+	 */
+	hose->pci_last_busno = pbus->subordinate;
+#if 0
+	hose_write_config_byte(hose, busno, 0, 0x41, hose->pci_last_busno);
+#endif
+	busno = pbus->subordinate + 1;
+
+	/*
+	 * Fixup the chain of primary PCI busses.
+	 */
+	if (pchain) {
+		pchain->next = &hose->pci_bus;
+		pchain = pchain->next;
+	} else {
+		pchain = &pci_root;
+		memcpy(pchain, &hose->pci_bus, sizeof(pci_root));
+	}
+}
+
+unsigned long mcpcia_fixup(unsigned long memory_start,
+			   unsigned long memory_end)
+{
+	struct linux_hose_info *hose;
+
+	/* turn on Config space access finally! */
+	pci_probe_enabled = 1;
+
+	/* for each hose, probe and setup the devices on the hose */
+	for (hose = mcpcia_root; hose; hose = hose->next) {
+		mcpcia_probe(hose, &memory_start);
+	}
+
+	return memory_start;
+}
+#endif /* CONFIG_ALPHA_MCPCIA */
diff -u --recursive --new-file v2.1.91/linux/arch/alpha/kernel/process.c linux/arch/alpha/kernel/process.c
--- v2.1.91/linux/arch/alpha/kernel/process.c	Tue Mar 17 22:18:13 1998
+++ linux/arch/alpha/kernel/process.c	Mon Mar 30 00:21:39 1998
@@ -66,10 +66,49 @@
 	unsigned long a3, unsigned long a4, unsigned long a5,
 	struct pt_regs regs)
 {
+#if !defined(CONFIG_ALPHA_TSUNAMI)
 	(&regs)->hae = hae;
+#endif
 	return 0;
 }
 
+#ifdef __SMP__
+/* This is being executed in task 0 'user space'. */
+#define resched_needed() 1
+int cpu_idle(void *unused)
+{
+	extern volatile int smp_commenced;
+
+	current->priority = -100;
+	while (1) {
+		/*
+		 * tq_scheduler currently assumes we're running in a process
+		 * context (ie that we hold the kernel lock..)
+		 */
+		if (tq_scheduler) {
+			lock_kernel();
+			run_task_queue(&tq_scheduler);
+			unlock_kernel();
+		}
+		/* endless idle loop with no priority at all */
+		current->counter = -100;
+		if (!smp_commenced || resched_needed()) {
+			schedule();
+		}
+	}
+}
+
+asmlinkage int sys_idle(void)
+{
+        if(current->pid != 0)
+                return -EPERM;
+
+        cpu_idle(NULL);
+        return 0;
+}
+
+#else /* __SMP__ */
+
 asmlinkage int sys_idle(void)
 {
 	int ret = -EPERM;
@@ -88,6 +127,12 @@
 	unlock_kernel();
 	return ret;
 }
+#endif /* __SMP__ */
+
+#if defined(CONFIG_ALPHA_SRM_SETUP)
+extern void reset_for_srm(void);	
+extern unsigned long srm_hae;
+#endif
 
 static void finish_shutdown(void)
 {
@@ -96,8 +141,8 @@
 	unsigned long flags;
 
 	/* i'm not sure if i really need to disable interrupts here */
-	save_flags(flags);
-	cli();
+	save_and_cli(flags);
+
  	/* reset periodic interrupt frequency */
         CMOS_WRITE(0x26, RTC_FREQ_SELECT);
 
@@ -131,6 +176,10 @@
 /*	flags |=  0x0000000000030000UL; *//* this is "warm bootstrap" */
 	cpup->flags = flags;					       
 	mb();						
+#if defined(CONFIG_ALPHA_SRM_SETUP)
+	reset_for_srm();
+	set_hae(srm_hae);
+#endif
 #endif /* SRM */                                        
 
 	finish_shutdown();
@@ -150,6 +199,10 @@
 	flags |=  0x0000000000040000UL; /* this is "remain halted" */
 	cpup->flags = flags;					       
 	mb();						
+#if defined(CONFIG_ALPHA_SRM_SETUP)
+	reset_for_srm();
+	set_hae(srm_hae);
+#endif
 
 	finish_shutdown();
 #endif /* SRM */                                        
@@ -228,6 +281,7 @@
 }
 
 extern void ret_from_sys_call(void);
+extern void ret_from_smpfork(void);
 /*
  * Copy an alpha thread..
  *
@@ -258,7 +312,11 @@
 	stack = ((struct switch_stack *) regs) - 1;
 	childstack = ((struct switch_stack *) childregs) - 1;
 	*childstack = *stack;
+#ifdef __SMP__
+	childstack->r26 = (unsigned long) ret_from_smpfork;
+#else
 	childstack->r26 = (unsigned long) ret_from_sys_call;
+#endif
 	p->tss.usp = usp;
 	p->tss.ksp = (unsigned long) childstack;
 	p->tss.pal_flags = 1;	/* set FEN, clear everything else */
diff -u --recursive --new-file v2.1.91/linux/arch/alpha/kernel/pyxis.c linux/arch/alpha/kernel/pyxis.c
--- v2.1.91/linux/arch/alpha/kernel/pyxis.c	Tue Mar 10 10:03:30 1998
+++ linux/arch/alpha/kernel/pyxis.c	Mon Mar 30 00:21:39 1998
@@ -17,13 +17,12 @@
 #include <asm/ptrace.h>
 #include <asm/mmu_context.h>
 
-/* NOTE: Herein are back-to-back mb insns.  They are magic.
-   A plausible explanation is that the i/o controler does not properly
+/* NOTE: Herein are back-to-back mb instructions.  They are magic.
+   One plausible explanation is that the I/O controller does not properly
    handle the system transaction.  Another involves timing.  Ho hum.  */
 
 extern struct hwrpb_struct *hwrpb;
 extern asmlinkage void wrmces(unsigned long mces);
-extern int alpha_sys_type;
 
 /*
  * BIOS32-style PCI interface:
@@ -38,6 +37,7 @@
 #define DEBUG_MCHECK
 #ifdef DEBUG_MCHECK
 # define DBG_MCK(args)	printk args
+#define DEBUG_MCHECK_DUMP
 #else
 # define DBG_MCK(args)
 #endif
@@ -49,6 +49,11 @@
 static volatile unsigned int PYXIS_mcheck_taken = 0;
 static unsigned int PYXIS_jd;
 
+#ifdef CONFIG_ALPHA_SRM_SETUP
+unsigned int PYXIS_DMA_WIN_BASE = PYXIS_DMA_WIN_BASE_DEFAULT;
+unsigned int PYXIS_DMA_WIN_SIZE = PYXIS_DMA_WIN_SIZE_DEFAULT;
+unsigned long pyxis_sm_base_r1, pyxis_sm_base_r2, pyxis_sm_base_r3;
+#endif /* SRM_SETUP */
 
 /*
  * Given a bus, device, and function number, compute resulting
@@ -129,24 +134,23 @@
 static unsigned int conf_read(unsigned long addr, unsigned char type1)
 {
 	unsigned long flags;
-	unsigned int stat0, value;
+	unsigned int stat0, value, temp;
 	unsigned int pyxis_cfg = 0; /* to keep gcc quiet */
 
-	save_flags(flags);	/* avoid getting hit by machine check */
-	cli();
+	save_and_cli(flags);	/* avoid getting hit by machine check */
 
 	DBG(("conf_read(addr=0x%lx, type1=%d)\n", addr, type1));
 
 	/* reset status register to avoid losing errors: */
 	stat0 = *(vuip)PYXIS_ERR;
-	*(vuip)PYXIS_ERR = stat0;
-	mb();
+	*(vuip)PYXIS_ERR = stat0; mb();
+	temp = *(vuip)PYXIS_ERR;  /* re-read to force write */
 	DBG(("conf_read: PYXIS ERR was 0x%x\n", stat0));
 	/* if Type1 access, must set PYXIS CFG */
 	if (type1) {
 		pyxis_cfg = *(vuip)PYXIS_CFG;
-		*(vuip)PYXIS_CFG = pyxis_cfg | 1;
-		mb();
+		*(vuip)PYXIS_CFG = pyxis_cfg | 1; mb();
+		temp = *(vuip)PYXIS_CFG;  /* re-read to force write */
 		DBG(("conf_read: TYPE1 access\n"));
 	}
 
@@ -166,36 +170,11 @@
 	}
 	PYXIS_mcheck_expected = 0;
 	mb();
-	/*
-	 * david.rusling@reo.mts.dec.com.  This code is needed for the
-	 * EB64+ as it does not generate a machine check (why I don't
-	 * know).  When we build kernels for one particular platform
-	 * then we can make this conditional on the type.
-	 */
-#if 0
-	draina();
-
-	/* now look for any errors */
-	stat0 = *(vuip)PYXIS_IOC_PYXIS_ERR;
-	DBG(("conf_read: PYXIS ERR after read 0x%x\n", stat0));
-	if (stat0 & 0x8280U) { /* is any error bit set? */
-		/* if not NDEV, print status */
-		if (!(stat0 & 0x0080)) {
-			printk("PYXIS.c:conf_read: got stat0=%x\n", stat0);
-		}
-
-		/* reset error status: */
-		*(vulp)PYXIS_IOC_PYXIS_ERR = stat0;
-		mb();
-		wrmces(0x7);			/* reset machine check */
-		value = 0xffffffff;
-	}
-#endif
 
 	/* if Type1 access, must reset IOC CFG so normal IO space ops work */
 	if (type1) {
-		*(vuip)PYXIS_CFG = pyxis_cfg & ~1;
-		mb();
+		*(vuip)PYXIS_CFG = pyxis_cfg & ~1; mb();
+		temp = *(vuip)PYXIS_CFG;  /* re-read to force write */
 	}
 
 	DBG(("conf_read(): finished\n"));
@@ -209,22 +188,21 @@
 		       unsigned char type1)
 {
 	unsigned long flags;
-	unsigned int stat0;
+	unsigned int stat0, temp;
 	unsigned int pyxis_cfg = 0; /* to keep gcc quiet */
 
-	save_flags(flags);	/* avoid getting hit by machine check */
-	cli();
+	save_and_cli(flags);	/* avoid getting hit by machine check */
 
 	/* reset status register to avoid losing errors: */
 	stat0 = *(vuip)PYXIS_ERR;
-	*(vuip)PYXIS_ERR = stat0;
-	mb();
+	*(vuip)PYXIS_ERR = stat0; mb();
+	temp = *(vuip)PYXIS_ERR;  /* re-read to force write */
 	DBG(("conf_write: PYXIS ERR was 0x%x\n", stat0));
 	/* if Type1 access, must set PYXIS CFG */
 	if (type1) {
 		pyxis_cfg = *(vuip)PYXIS_CFG;
-		*(vuip)PYXIS_CFG = pyxis_cfg | 1;
-		mb();
+		*(vuip)PYXIS_CFG = pyxis_cfg | 1; mb();
+		temp = *(vuip)PYXIS_CFG;  /* re-read to force write */
 		DBG(("conf_read: TYPE1 access\n"));
 	}
 
@@ -235,13 +213,14 @@
 	*(vuip)addr = value;
 	mb();
 	mb();  /* magic */
+	temp = *(vuip)PYXIS_ERR; /* do a PYXIS read to force the write */
 	PYXIS_mcheck_expected = 0;
 	mb();
 
 	/* if Type1 access, must reset IOC CFG so normal IO space ops work */
 	if (type1) {
-		*(vuip)PYXIS_CFG = pyxis_cfg & ~1;
-		mb();
+		*(vuip)PYXIS_CFG = pyxis_cfg & ~1; mb();
+		temp = *(vuip)PYXIS_CFG;  /* re-read to force write */
 	}
 
 	DBG(("conf_write(): finished\n"));
@@ -367,19 +346,105 @@
 {
 	unsigned int pyxis_err ;
 
+#if 0
+printk("pyxis_init: PYXIS_ERR_MASK 0x%x\n", *(vuip)PYXIS_ERR_MASK);
+printk("pyxis_init: PYXIS_ERR 0x%x\n", *(vuip)PYXIS_ERR);
+
+printk("pyxis_init: PYXIS_INT_REQ 0x%lx\n", *(vulp)PYXIS_INT_REQ);
+printk("pyxis_init: PYXIS_INT_MASK 0x%lx\n", *(vulp)PYXIS_INT_MASK);
+printk("pyxis_init: PYXIS_INT_ROUTE 0x%lx\n", *(vulp)PYXIS_INT_ROUTE);
+printk("pyxis_init: PYXIS_INT_HILO 0x%lx\n", *(vulp)PYXIS_INT_HILO);
+printk("pyxis_init: PYXIS_INT_CNFG 0x%x\n", *(vuip)PYXIS_INT_CNFG);
+printk("pyxis_init: PYXIS_RT_COUNT 0x%lx\n", *(vulp)PYXIS_RT_COUNT);
+#endif
+
 	/* 
-	 * Set up error reporting.
+	 * Set up error reporting. Make sure CPU_PE is OFF in the mask.
 	 */
+	pyxis_err = *(vuip)PYXIS_ERR_MASK;
+	pyxis_err &= ~4;   
+	*(vuip)PYXIS_ERR_MASK = pyxis_err; mb();
+	pyxis_err = *(vuip)PYXIS_ERR_MASK;  /* re-read to force write */
+
 	pyxis_err = *(vuip)PYXIS_ERR ;
 	pyxis_err |= 0x180;   /* master/target abort */
-	*(vuip)PYXIS_ERR = pyxis_err ;
-	mb() ;
-	pyxis_err = *(vuip)PYXIS_ERR ;
+	*(vuip)PYXIS_ERR = pyxis_err; mb();
+	pyxis_err = *(vuip)PYXIS_ERR;  /* re-read to force write */
 
-#ifdef CONFIG_ALPHA_RUFFIAN
-	printk("pyxis_init: Skipping window register rewrites --"
+#ifdef CONFIG_ALPHA_SRM_SETUP
+	/* check window 0 for enabled and mapped to 0 */
+	if (((*(vuip)PYXIS_W0_BASE & 3) == 1) &&
+	    (*(vuip)PYXIS_T0_BASE == 0) &&
+	    ((*(vuip)PYXIS_W0_MASK & 0xfff00000U) > 0x0ff00000U))
+	{
+	  PYXIS_DMA_WIN_BASE = *(vuip)PYXIS_W0_BASE & 0xfff00000U;
+	  PYXIS_DMA_WIN_SIZE = *(vuip)PYXIS_W0_MASK & 0xfff00000U;
+	  PYXIS_DMA_WIN_SIZE += 0x00100000U;
+#if 1
+	  printk("pyxis_init: using Window 0 settings\n");
+	  printk("pyxis_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n",
+		 *(vuip)PYXIS_W0_BASE,
+		 *(vuip)PYXIS_W0_MASK,
+		 *(vuip)PYXIS_T0_BASE);
+#endif
+	}
+	else  /* check window 1 for enabled and mapped to 0 */
+	if (((*(vuip)PYXIS_W1_BASE & 3) == 1) &&
+	    (*(vuip)PYXIS_T1_BASE == 0) &&
+	    ((*(vuip)PYXIS_W1_MASK & 0xfff00000U) > 0x0ff00000U))
+{
+	  PYXIS_DMA_WIN_BASE = *(vuip)PYXIS_W1_BASE & 0xfff00000U;
+	  PYXIS_DMA_WIN_SIZE = *(vuip)PYXIS_W1_MASK & 0xfff00000U;
+	  PYXIS_DMA_WIN_SIZE += 0x00100000U;
+#if 1
+	  printk("pyxis_init: using Window 1 settings\n");
+	  printk("pyxis_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n",
+		 *(vuip)PYXIS_W1_BASE,
+		 *(vuip)PYXIS_W1_MASK,
+		 *(vuip)PYXIS_T1_BASE);
+#endif
+	}
+	else  /* check window 2 for enabled and mapped to 0 */
+	if (((*(vuip)PYXIS_W2_BASE & 3) == 1) &&
+	    (*(vuip)PYXIS_T2_BASE == 0) &&
+	    ((*(vuip)PYXIS_W2_MASK & 0xfff00000U) > 0x0ff00000U))
+	{
+	  PYXIS_DMA_WIN_BASE = *(vuip)PYXIS_W2_BASE & 0xfff00000U;
+	  PYXIS_DMA_WIN_SIZE = *(vuip)PYXIS_W2_MASK & 0xfff00000U;
+	  PYXIS_DMA_WIN_SIZE += 0x00100000U;
+#if 1
+	  printk("pyxis_init: using Window 2 settings\n");
+	  printk("pyxis_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n",
+		 *(vuip)PYXIS_W2_BASE,
+		 *(vuip)PYXIS_W2_MASK,
+		 *(vuip)PYXIS_T2_BASE);
+#endif
+	}
+	else  /* check window 3 for enabled and mapped to 0 */
+	if (((*(vuip)PYXIS_W3_BASE & 3) == 1) &&
+	    (*(vuip)PYXIS_T3_BASE == 0) &&
+	    ((*(vuip)PYXIS_W3_MASK & 0xfff00000U) > 0x0ff00000U))
+	{
+	  PYXIS_DMA_WIN_BASE = *(vuip)PYXIS_W3_BASE & 0xfff00000U;
+	  PYXIS_DMA_WIN_SIZE = *(vuip)PYXIS_W3_MASK & 0xfff00000U;
+	  PYXIS_DMA_WIN_SIZE += 0x00100000U;
+#if 1
+	  printk("pyxis_init: using Window 3 settings\n");
+	  printk("pyxis_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n",
+		 *(vuip)PYXIS_W3_BASE,
+		 *(vuip)PYXIS_W3_MASK,
+		 *(vuip)PYXIS_T3_BASE);
+#endif
+	}
+	else  /* we must use our defaults which were pre-initialized... */
+#endif /* SRM_SETUP */
+	{
+#if defined(CONFIG_ALPHA_RUFFIAN)
+#if 1
+	printk("pyxis_init: skipping window register rewrites... "
 	       " trust DeskStation firmware!\n");
-#else
+#endif
+#else /* RUFFIAN */
 	/*
 	 * Set up the PCI->physical memory translation windows.
 	 * For now, windows 1,2 and 3 are disabled.  In the future, we may
@@ -395,7 +460,8 @@
 	*(vuip)PYXIS_W2_BASE = 0x0 ;
 	*(vuip)PYXIS_W3_BASE = 0x0 ;
 	mb();
-#endif
+#endif /* RUFFIAN */
+	}
 
 	/*
 	 * check ASN in HWRPB for validity, report if bad
@@ -407,18 +473,21 @@
 	}
 
 	/*
-	 * Finally, clear the PYXIS_CFG register, which gets used
+         * Next, clear the PYXIS_CFG register, which gets used
 	 *  for PCI Config Space accesses. That is the way
 	 *  we want to use it, and we do not want to depend on
 	 *  what ARC or SRM might have left behind...
 	 */
 	{
-		unsigned int pyxis_cfg;
+          unsigned int pyxis_cfg, temp;
 		pyxis_cfg = *(vuip)PYXIS_CFG; mb();
-#if 0
+	  if (pyxis_cfg != 0) {
+#if 1
 		printk("PYXIS_init: CFG was 0x%x\n", pyxis_cfg);
 #endif
 		*(vuip)PYXIS_CFG = 0; mb();
+	    temp = *(vuip)PYXIS_CFG;  /* re-read to force write */
+	  }
 	}
  
 	{
@@ -428,10 +497,48 @@
 		printk("PYXIS_init: HAE_MEM was 0x%x\n", pyxis_hae_mem);
 		printk("PYXIS_init: HAE_IO was 0x%x\n", pyxis_hae_io);
 #endif
-		*(vuip)PYXIS_HAE_MEM = 0; mb();
-		pyxis_hae_mem = *(vuip)PYXIS_HAE_MEM;
+#ifdef CONFIG_ALPHA_SRM_SETUP
+	  /*
+	   * sigh... For the SRM setup, unless we know apriori what the HAE
+	   * contents will be, we need to setup the arbitrary region bases
+	   * so we can test against the range of addresses and tailor the
+	   * region chosen for the SPARSE memory access.
+	   * 
+	   * see include/asm-alpha/pyxis.h for the SPARSE mem read/write
+	  */
+	  pyxis_sm_base_r1 = (pyxis_hae_mem      ) & 0xe0000000UL;/* region 1 */
+	  pyxis_sm_base_r2 = (pyxis_hae_mem << 16) & 0xf8000000UL;/* region 2 */
+	  pyxis_sm_base_r3 = (pyxis_hae_mem << 24) & 0xfc000000UL;/* region 3 */
+
+	  /*
+	    Set the HAE cache, so that setup_arch() code
+	    will use the SRM setting always. Our readb/writeb
+	    code in pyxis.h expects never to have to change
+	    the contents of the HAE.
+	   */
+	  hae.cache = pyxis_hae_mem;
+#else /* SRM_SETUP */
+          *(vuip)PYXIS_HAE_MEM = 0U; mb();
+	  pyxis_hae_mem = *(vuip)PYXIS_HAE_MEM;  /* re-read to force write */
 		*(vuip)PYXIS_HAE_IO = 0; mb();
-		pyxis_hae_io = *(vuip)PYXIS_HAE_IO;
+	  pyxis_hae_io = *(vuip)PYXIS_HAE_IO;  /* re-read to force write */
+#endif /* SRM_SETUP */
+        }
+
+	/*
+	 * Finally, check that the PYXIS_CTRL1 has IOA_BEN set for
+	 * enabling byte/word PCI bus space(s) access.
+	 */
+	{
+	  unsigned int ctrl1;
+	  ctrl1 = *(vuip) PYXIS_CTRL1;
+	  if (!(ctrl1 & 1)) {
+#if 1
+	    printk("PYXIS_init: enabling byte/word PCI space\n");
+#endif
+	    *(vuip) PYXIS_CTRL1 = ctrl1 | 1; mb();
+	    ctrl1 = *(vuip)PYXIS_CTRL1;  /* re-read to force write */
+	  }
 	}
 
 	return mem_start;
@@ -441,9 +548,8 @@
 {
 	PYXIS_jd = *(vuip)PYXIS_ERR;
 	DBG(("PYXIS_pci_clr_err: PYXIS ERR after read 0x%x\n", PYXIS_jd));
-	*(vuip)PYXIS_ERR = 0x0180;
-	mb();
-	PYXIS_jd = *(vuip)PYXIS_ERR;
+	*(vuip)PYXIS_ERR = 0x0180; mb();
+	PYXIS_jd = *(vuip)PYXIS_ERR;  /* re-read to force write */
 	return 0;
 }
 
@@ -486,7 +592,7 @@
 	 */
 	mb();
 	mb();  /* magic */
-	if (PYXIS_mcheck_expected/* && (mchk_sysdata->epic_dcsr && 0x0c00UL)*/) {
+	if (PYXIS_mcheck_expected) {
 		DBG(("PYXIS machine check expected\n"));
 		PYXIS_mcheck_expected = 0;
 		PYXIS_mcheck_taken = 1;
@@ -502,7 +608,8 @@
 		printk("PYXIS machine check NOT expected\n") ;
 		DBG_MCK(("pyxis_machine_check: vector=0x%lx la_ptr=0x%lx\n",
 			 vector, la_ptr));
-		DBG_MCK(("\t\t pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n",
+		DBG_MCK(("\t\t pc=0x%lx size=0x%x procoffset=0x%x"
+			 " sysoffset 0x%x\n",
 			 regs->pc, mchk_header->size, mchk_header->proc_offset,
 			 mchk_header->sys_offset));
 		PYXIS_mcheck_expected = 0;
diff -u --recursive --new-file v2.1.91/linux/arch/alpha/kernel/setup.c linux/arch/alpha/kernel/setup.c
--- v2.1.91/linux/arch/alpha/kernel/setup.c	Tue Mar 10 10:03:30 1998
+++ linux/arch/alpha/kernel/setup.c	Mon Mar 30 00:21:39 1998
@@ -22,6 +22,7 @@
 #include <linux/delay.h>
 #include <linux/config.h>	/* CONFIG_ALPHA_LCA etc */
 #include <linux/ioport.h>
+#include <linux/mc146818rtc.h>
 
 #ifdef CONFIG_RTC
 #include <linux/timex.h>
@@ -34,11 +35,24 @@
 #include <asm/dma.h>
 #include <asm/io.h>
 
+extern void setup_smp(void);
+extern char *smp_info(void);
+
+#if 1
+# define DBG_SRM(args)         printk args
+#else
+# define DBG_SRM(args)
+#endif
+
 struct hae hae = {
 	0,
 	(unsigned long*) HAE_ADDRESS
 };
 
+#ifdef CONFIG_ALPHA_SRM_SETUP
+unsigned long srm_hae;
+#endif
+
 struct hwrpb_struct *hwrpb;
 
 unsigned char aux_device_present = 0xaa;
@@ -106,12 +120,13 @@
     outb(LATCH & 0xff, 0x40); /* LSB */
     outb(LATCH >> 8, 0x40); /* MSB */
     request_region(0x40, 0x20, "timer"); /* reserve pit */
-#else
-#ifndef CONFIG_ALPHA_RUFFIAN
+#else /* RTC */
+#if !defined(CONFIG_ALPHA_RUFFIAN)
+    /* Ruffian depends on the system timer established in MILO!! */
     outb(0x36, 0x43);	/* counter 0: system timer */
     outb(0x00, 0x40);
     outb(0x00, 0x40);
-#endif
+#endif /* RUFFIAN */
     request_region(0x70, 0x10, "timer"); /* reserve rtc */
 #endif /* RTC */
 
@@ -148,9 +163,21 @@
 
 	init_pit();
 
+	if ((CMOS_READ(RTC_FREQ_SELECT) & 0x3f) != 0x26) {
+	  printk("setup_arch: setting RTC_FREQ to 1024/sec\n");
+	  CMOS_WRITE(0x26, RTC_FREQ_SELECT);
+	}
+
 	hwrpb = (struct hwrpb_struct*)(IDENT_ADDR + INIT_HWRPB->phys_addr);
 
+#if !defined(CONFIG_ALPHA_TSUNAMI)
+#ifdef CONFIG_ALPHA_SRM_SETUP
+	srm_hae = *hae.reg; /* save SRM setting for restoration */
+	DBG_SRM(("setup_arch: old HAE base: 0x%016lx\n", srm_hae));
+#endif /* SRM_SETUP */
 	set_hae(hae.cache);	/* sync HAE register w/hae_cache */
+#endif /* !TSUNAMI */
+
 	wrmces(0x7);		/* reset enable correctable error reports */
 
 	ROOT_DEV = to_kdev_t(0x0802);		/* sda2 */
@@ -185,12 +212,54 @@
 	*memory_start_p = pyxis_init(*memory_start_p, *memory_end_p);
 #elif defined(CONFIG_ALPHA_T2)
 	*memory_start_p = t2_init(*memory_start_p, *memory_end_p);
+#elif defined(CONFIG_ALPHA_TSUNAMI)
+	*memory_start_p = tsunami_init(*memory_start_p, *memory_end_p);
+#elif defined(CONFIG_ALPHA_MCPCIA)
+	*memory_start_p = mcpcia_init(*memory_start_p, *memory_end_p);
+#endif
+
+#ifdef __SMP__
+	setup_smp();
 #endif
 }
 
 
 #define N(a) (sizeof(a)/sizeof(a[0]))
 
+/* A change was made to the HWRPB via an ECO and the following code tracks
+ * a part of the ECO.  The HWRPB version must be 5 or higher or the ECO
+ * was not implemented in the console firmware.  If its at rev 5 or greater
+ * we can get the platform ascii string name from the HWRPB.  Thats what this
+ * function does.  It checks the rev level and if the string is in the HWRPB
+ * it returns the addtess of the string ... a pointer to the platform name.
+ *
+ * Returns:
+ *      - Pointer to a ascii string if its in the HWRPB
+ *      - Pointer to a blank string if the data is not in the HWRPB.
+ */
+static char *
+platform_string(void)
+{
+	struct dsr_struct *dsr;
+	static char unk_system_string[] = "N/A";
+
+        /* Go to the console for the string pointer.
+         * If the rpb_vers is not 5 or greater the rpb
+	 * is old and does not have this data in it.
+	 */
+        if (hwrpb->revision < 5)
+		return (unk_system_string);
+	else {
+		/* The Dynamic System Recognition struct
+		 * has the system platform name starting
+		 * after the character count of the string.
+		 */
+		dsr =  ((struct dsr_struct *)
+			  ((char *)hwrpb + hwrpb->dsr_offset));
+		return ((char *)dsr + (dsr->sysname_off +
+						 sizeof(long)));
+        }
+}
 
 static void
 get_sysnames(long type, long variation,
@@ -222,6 +291,10 @@
 	static char * eb66_names[] = {"EB66", "EB66+"};
 	static int eb66_indices[] = {0,0,1};
 
+	static char * rawhide_names[] = {"Dodge", "Wrangler", "Durango",
+					 "Tincup", "DaVinci"};
+	static int rawhide_indices[] = {0,0,0,1,1,2,2,3,3,4,4};
+
 	long member;
 
 	/* Restore real CABRIO and EB66+ family names, ie EB64+ and EB66 */
@@ -249,7 +322,9 @@
 
 	member = (variation >> 10) & 0x3f; /* member ID is a bit-field */
 
-	switch (type) {
+	switch (type) { /* select by family */
+	default: /* default to variation "0" for now */
+		break;
 	case ST_DEC_EB164:
 		if (member < N(eb164_indices))
 			*variation_name = eb164_names[eb164_indices[member]];
@@ -266,7 +341,11 @@
 		if (member < N(eb66_indices))
 			*variation_name = eb66_names[eb66_indices[member]];
 		break;
-	}
+	case ST_DEC_RAWHIDE:
+		if (member < N(rawhide_indices))
+		  *variation_name = rawhide_names[rawhide_indices[member]];
+		break;
+	} /* end family switch */
 }
 
 /*
@@ -315,7 +394,12 @@
 		       "max. addr. space #\t: %ld\n"
 		       "BogoMIPS\t\t: %lu.%02lu\n"
 		       "kernel unaligned acc\t: %ld (pc=%lx,va=%lx)\n"
-		       "user unaligned acc\t: %ld (pc=%lx,va=%lx)\n",
+		       "user unaligned acc\t: %ld (pc=%lx,va=%lx)\n"
+		       "platform string\t: %s\n"
+#ifdef __SMP__
+		       "%s"
+#endif
+		       ,
 
 		       cpu_name, cpu->variation, cpu->revision,
 		       (char*)cpu->serial_no,
@@ -329,5 +413,10 @@
 		       hwrpb->max_asn,
 		       loops_per_sec / 500000, (loops_per_sec / 5000) % 100,
 		       unaligned[0].count, unaligned[0].pc, unaligned[0].va,
-		       unaligned[1].count, unaligned[1].pc, unaligned[1].va);
+		       unaligned[1].count, unaligned[1].pc, unaligned[1].va,
+		       platform_string()
+#ifdef __SMP__
+		       , smp_info()
+#endif
+		       );
 }
diff -u --recursive --new-file v2.1.91/linux/arch/alpha/kernel/smp.c linux/arch/alpha/kernel/smp.c
--- v2.1.91/linux/arch/alpha/kernel/smp.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/alpha/kernel/smp.c	Mon Mar 30 00:21:39 1998
@@ -0,0 +1,1096 @@
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/kernel_stat.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/tasks.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+
+#include <asm/hwrpb.h>
+#include <asm/ptrace.h>
+#include <asm/atomic.h>
+
+#include <asm/delay.h>
+#include <asm/irq.h>
+#include <asm/bitops.h>
+#include <asm/pgtable.h>
+#include <asm/spinlock.h>
+#include <asm/hardirq.h>
+#include <asm/softirq.h>
+
+#define __KERNEL_SYSCALLS__
+#include <asm/unistd.h>
+
+struct ipi_msg_flush_tb_struct ipi_msg_flush_tb;
+
+struct cpuinfo_alpha cpu_data[NR_CPUS];
+
+/* Processor holding kernel spinlock */
+klock_info_t klock_info = { KLOCK_CLEAR, 0 };
+
+spinlock_t ticker_lock = SPIN_LOCK_UNLOCKED;
+
+unsigned int boot_cpu_id = 0;
+static int smp_activated = 0;
+
+int smp_found_config = 0; /* Have we found an SMP box */
+static int max_cpus = -1;
+
+unsigned int cpu_present_map = 0;
+
+int smp_num_cpus = 1;
+int smp_num_probed = 0; /* Internal processor count */
+
+int smp_threads_ready = 0;
+volatile unsigned long cpu_callin_map[NR_CPUS] = {0,};
+volatile unsigned long smp_spinning[NR_CPUS] = { 0, };
+
+unsigned int prof_multiplier[NR_CPUS];
+unsigned int prof_counter[NR_CPUS];
+
+volatile int ipi_bits[NR_CPUS];
+
+unsigned long boot_cpu_palrev;
+
+volatile int smp_commenced = 0;
+volatile int smp_processors_ready = 0;
+
+volatile int cpu_number_map[NR_CPUS];
+volatile int cpu_logical_map[NR_CPUS];
+
+extern int cpu_idle(void *unused);
+extern void calibrate_delay(void);
+extern struct hwrpb_struct *hwrpb;
+extern struct thread_struct * original_pcb_ptr;
+extern void __start_cpu(unsigned long);
+
+static void smp_setup_percpu_timer(void);
+static void secondary_cpu_start(int, struct task_struct *);
+static void send_cpu_msg(char *, int);
+
+/* process bootcommand SMP options, like "nosmp" and "maxcpus=" */
+__initfunc(void smp_setup(char *str, int *ints))
+{
+	if (ints && ints[0] > 0)
+		max_cpus = ints[1];
+        else
+		max_cpus = 0;
+}
+
+void smp_store_cpu_info(int id)
+{
+	/* This is it on Alpha, so far. */
+        cpu_data[id].loops_per_sec = loops_per_sec;
+}
+
+void smp_commence(void)
+{
+	/* Lets the callin's below out of their loop. */
+	mb();
+	smp_commenced = 1;
+}
+
+void smp_callin(void)
+{
+        int cpuid = hard_smp_processor_id();
+
+#if 0
+	printk("CALLIN %d state 0x%lx\n", cpuid, current->state);
+#endif
+#ifdef HUH
+        local_flush_cache_all();
+        local_flush_tlb_all();
+#endif
+#if 0
+        set_irq_udt(mid_xlate[boot_cpu_id]);
+#endif
+
+        /* Get our local ticker going. */
+        smp_setup_percpu_timer();
+
+#if 0
+        calibrate_delay();
+#endif
+        smp_store_cpu_info(cpuid);
+#ifdef HUH
+        local_flush_cache_all();
+        local_flush_tlb_all();
+#endif
+
+        /* Allow master to continue. */
+        set_bit(cpuid, (unsigned long *)&cpu_callin_map[cpuid]);
+#ifdef HUH
+        local_flush_cache_all();
+        local_flush_tlb_all();
+#endif
+
+#ifdef NOT_YET
+        while(!task[cpuid] || current_set[cpuid] != task[cpuid])
+                barrier();
+#endif /* NOT_YET */
+
+#if 0
+        /* Fix idle thread fields. */
+        __asm__ __volatile__("ld [%0], %%g6\n\t"
+                             : : "r" (&current_set[cpuid])
+                             : "memory" /* paranoid */);
+        current->mm->mmap->vm_page_prot = PAGE_SHARED;
+        current->mm->mmap->vm_start = PAGE_OFFSET;
+        current->mm->mmap->vm_end = init_task.mm->mmap->vm_end;
+#endif
+        
+#ifdef HUH
+        local_flush_cache_all();
+        local_flush_tlb_all();
+#endif
+#if 0
+        __sti();
+#endif
+}
+
+asmlinkage int start_secondary(void *unused)
+{
+	extern asmlinkage void entInt(void);
+	extern void paging_init_secondary(void);
+
+	wrmces(7);
+	paging_init_secondary();
+	trap_init();
+	wrent(entInt, 0);
+
+        smp_callin();
+        while (!smp_commenced)
+		barrier();
+#if 1
+printk("start_secondary: commencing CPU %d current %p\n",
+       hard_smp_processor_id(), current);
+#endif
+        return cpu_idle(NULL);
+}
+
+/*
+ *      Cycle through the processors sending START msgs to boot each.
+ */
+void smp_boot_cpus(void)
+{
+        int cpucount = 0;
+        int i, first, prev;
+
+        printk("smp_boot_cpus: Entering SMP Mode...\n");
+
+#if 0
+        __sti();
+#endif
+
+        for(i=0; i < NR_CPUS; i++) {
+		cpu_number_map[i] = -1;
+		cpu_logical_map[i] = -1;
+                prof_counter[i] = 1;
+                prof_multiplier[i] = 1;
+		ipi_bits[i] = 0;
+	}
+
+	cpu_number_map[boot_cpu_id] = 0;
+	cpu_logical_map[0] = boot_cpu_id;
+	current->processor = boot_cpu_id; /* ??? */
+        klock_info.akp = boot_cpu_id;
+
+        smp_store_cpu_info(boot_cpu_id);
+#ifdef NOT_YET
+        printk("CPU%d: ", boot_cpu_id);
+        print_cpu_info(&cpu_data[boot_cpu_id]);
+        set_irq_udt(mid_xlate[boot_cpu_id]);
+#endif /* NOT_YET */
+        smp_setup_percpu_timer();
+#ifdef HUH
+        local_flush_cache_all();
+#endif
+        if (smp_num_probed == 1)
+		return;  /* Not an MP box. */
+
+#if NOT_YET
+        /*
+         * If SMP should be disabled, then really disable it!
+         */
+        if (!max_cpus)
+	{
+		smp_found_config = 0;
+                printk(KERN_INFO "SMP mode deactivated.\n");
+        }
+#endif /* NOT_YET */
+
+        for (i = 0; i < NR_CPUS; i++) {
+
+		if (i == boot_cpu_id)
+			continue;
+
+                if (cpu_present_map & (1 << i)) {
+                        struct task_struct *idle;
+                        int timeout;
+
+                        /* Cook up an idler for this guy. */
+                        kernel_thread(start_secondary, NULL, CLONE_PID);
+                        idle = task[++cpucount];
+			if (!idle)
+				panic("No idle process for CPU %d", i);
+                        idle->processor = i;
+
+#if 0
+printk("smp_boot_cpus: CPU %d state 0x%lx flags 0x%lx\n",
+       i, idle->state, idle->flags);
+#endif
+
+                        /* whirrr, whirrr, whirrrrrrrrr... */
+#ifdef HUH
+                        local_flush_cache_all();
+#endif
+                        secondary_cpu_start(i, idle);
+
+                        /* wheee... it's going... wait for 5 secs...*/
+                        for (timeout = 0; timeout < 50000; timeout++) {
+				if (cpu_callin_map[i])
+					break;
+                                udelay(100);
+                        }
+                        if (cpu_callin_map[i]) {
+				/* Another "Red Snapper". */
+				cpu_number_map[i] = cpucount;
+                                cpu_logical_map[cpucount] = i;
+                        } else {
+				cpucount--;
+                                printk("smp_boot_cpus: Processor %d"
+				       " is stuck 0x%lx.\n", i, idle->flags);
+                        }
+                }
+                if (!(cpu_callin_map[i])) {
+			cpu_present_map &= ~(1 << i);
+                        cpu_number_map[i] = -1;
+                }
+        }
+#ifdef HUH
+        local_flush_cache_all();
+#endif
+        if (cpucount == 0) {
+		printk("smp_boot_cpus: ERROR - only one Processor found.\n");
+                cpu_present_map = (1 << smp_processor_id());
+        } else {
+		unsigned long bogosum = 0;
+                for (i = 0; i < NR_CPUS; i++) {
+			if (cpu_present_map & (1 << i))
+				bogosum += cpu_data[i].loops_per_sec;
+                }
+                printk("smp_boot_cpus: Total of %d Processors activated"
+		       " (%lu.%02lu BogoMIPS).\n",
+                       cpucount + 1,
+                       (bogosum + 2500)/500000,
+                       ((bogosum + 2500)/5000)%100);
+                smp_activated = 1;
+                smp_num_cpus = cpucount + 1;
+        }
+
+        /* Setup CPU list for IRQ distribution scheme. */
+        first = prev = -1;
+        for (i = 0; i < NR_CPUS; i++) {
+		if (cpu_present_map & (1 << i)) {
+			if (first == -1)
+				first = i;
+			if (prev != -1)
+				cpu_data[i].next = i;
+                        prev = i;
+                }
+        }
+        cpu_data[prev].next = first;
+
+        /* Ok, they are spinning and ready to go. */
+        smp_processors_ready = 1;
+}
+
+__initfunc(void ioapic_pirq_setup(char *str, int *ints))
+{
+  /* this is prolly INTEL-specific */
+}
+
+static void smp_setup_percpu_timer(void)
+{
+        int cpu = smp_processor_id();
+
+        prof_counter[cpu] = prof_multiplier[cpu] = 1;
+#ifdef NOT_YET
+        load_profile_irq(mid_xlate[cpu], lvl14_resolution);
+        if (cpu == boot_cpu_id)
+		enable_pil_irq(14);
+#endif
+}
+
+extern void update_one_process(struct task_struct *p, unsigned long ticks,
+                               unsigned long user, unsigned long system,
+			       int cpu);
+
+void smp_percpu_timer_interrupt(struct pt_regs *regs)
+{
+	int cpu = smp_processor_id();
+
+#ifdef NOT_YET
+        clear_profile_irq(mid_xlate[cpu]);
+#ifdef CONFIG_PROFILE
+        if(!user_mode(regs))
+		sparc_do_profile(regs->pc);
+#endif
+#endif
+
+        if (!--prof_counter[cpu]) {
+		int user = user_mode(regs);
+                if (current->pid) {
+			update_one_process(current, 1, user, !user, cpu);
+
+                        if (--current->counter < 0) {
+				current->counter = 0;
+                                need_resched = 1;
+                        }
+
+                        spin_lock(&ticker_lock);
+                        if (user) {
+				if (current->priority < DEF_PRIORITY) {
+					kstat.cpu_nice++;
+					kstat.per_cpu_nice[cpu]++;
+				} else {
+					kstat.cpu_user++;
+					kstat.per_cpu_user[cpu]++;
+				}
+                        } else {
+				kstat.cpu_system++;
+				kstat.per_cpu_system[cpu]++;
+                        }
+                        spin_unlock(&ticker_lock);
+                }
+                prof_counter[cpu] = prof_multiplier[cpu];
+        }
+}
+
+int setup_profiling_timer(unsigned int multiplier)
+{
+#ifdef NOT_YET
+        int i;
+        unsigned long flags;
+
+        /* Prevent level14 ticker IRQ flooding. */
+        if((!multiplier) || (lvl14_resolution / multiplier) < 500)
+                return -EINVAL;
+
+        save_and_cli(flags);
+        for(i = 0; i < NR_CPUS; i++) {
+                if(cpu_present_map & (1 << i)) {
+                        load_profile_irq(mid_xlate[i], lvl14_resolution / multip
+lier);
+                        prof_multiplier[i] = multiplier;
+                }
+        }
+        restore_flags(flags);
+
+        return 0;
+
+#endif
+  return -EINVAL;
+}
+
+/* Only broken Intel needs this, thus it should not even be referenced
+ * globally...
+ */
+__initfunc(void initialize_secondary(void))
+{
+	printk("initialize_secondary: entry\n");
+}
+
+static void
+secondary_cpu_start(int cpuid, struct task_struct *idle)
+{
+	struct percpu_struct *cpu;
+        int timeout;
+	  
+	cpu = (struct percpu_struct *)
+		((char*)hwrpb
+			+ hwrpb->processor_offset
+			+ cpuid * hwrpb->processor_size);
+
+	/* set context to idle thread this CPU will use when running */
+	/* assumption is that the idle thread is all set to go... ??? */
+	memcpy(&cpu->hwpcb[0], &idle->tss, sizeof(struct pcb_struct));
+	cpu->hwpcb[4] = cpu->hwpcb[0]; /* UNIQUE set to KSP ??? */
+#if 0
+printk("KSP 0x%lx PTBR 0x%lx VPTBR 0x%lx\n",
+       cpu->hwpcb[0], cpu->hwpcb[2], hwrpb->vptb);
+printk("Starting secondary cpu %d: state 0x%lx pal_flags 0x%lx\n",
+       cpuid, idle->state, idle->tss.pal_flags);
+#endif
+
+	/* setup HWRPB fields that SRM uses to activate secondary CPU */
+	 hwrpb->CPU_restart = __start_cpu;
+	 hwrpb->CPU_restart_data = (unsigned long) idle;
+
+	 /* recalculate and update the HWRPB checksum */
+	 {
+	   unsigned long sum, *lp1, *lp2;
+	   sum = 0;
+	   lp1 = (unsigned long *)hwrpb;
+	   lp2 = &hwrpb->chksum;
+	   while (lp1 < lp2)
+	     sum += *lp1++;
+	   *lp2 = sum;
+	 }
+
+	/*
+	 * Send a "start" command to the specified processor.
+	 */
+
+	/* SRM III 3.4.1.3 */
+	cpu->flags |= 0x22; /* turn on Context Valid and Restart Capable */
+	cpu->flags &= ~1;/* turn off Bootstrap In Progress */
+	mb();
+
+	send_cpu_msg("START\r\n", cpuid);
+
+	/* now, we wait... */
+	for (timeout = 10000; !(cpu->flags & 1); timeout--) {
+		if (timeout <= 0) {
+			printk("Processor %d failed to start\n", cpuid);
+                                /* needed for pset_info to work */
+#if 0
+			ipc_processor_enable(cpu_to_processor(cpunum));
+#endif
+			return;
+		}
+		udelay(1000);
+	}
+#if 0
+	printk("secondary_cpu_start: SUCCESS for CPU %d!!!\n", cpuid);
+#endif
+}
+
+static void
+send_cpu_msg(char *str, int cpuid)
+{
+	struct percpu_struct *cpu;
+        register char *cp1, *cp2;
+        unsigned long cpumask;
+        int timeout;
+
+	  
+	cpu = (struct percpu_struct *)
+		((char*)hwrpb
+			+ hwrpb->processor_offset
+			+ cpuid * hwrpb->processor_size);
+
+        cpumask = (1L << cpuid);
+        for (timeout = 10000; (hwrpb->txrdy & cpumask); timeout--) {
+                if (timeout <= 0) {
+                        printk("Processor %x not ready\n", cpuid);
+                        return;
+                }
+                udelay(1000);
+        }
+
+        cp1 = (char *) &cpu->ipc_buffer[1];
+        cp2 = str;
+        while (*cp2) *cp1++ = *cp2++;
+        *(unsigned int *)&cpu->ipc_buffer[0] = cp2 - str; /* hack */
+
+        /* atomic test and set */
+        set_bit(cpuid, &hwrpb->rxrdy);
+
+        for (timeout = 10000; (hwrpb->txrdy & cpumask); timeout--) {
+                if (timeout <= 0) {
+                        printk("Processor %x not ready\n", cpuid);
+                        return;
+                }
+                udelay(1000);
+        }
+}
+
+/*
+ * setup_smp()
+ *
+ * called from arch/alpha/kernel/setup.c:setup_arch() when __SMP__ defined
+ */
+__initfunc(void setup_smp(void))
+{
+	struct percpu_struct *cpubase, *cpu;
+	int i;
+	  
+	boot_cpu_id = hard_smp_processor_id();
+	if (boot_cpu_id != 0) {
+		printk("setup_smp: boot_cpu_id != 0 (%d).\n", boot_cpu_id);
+	}
+
+	if (hwrpb->nr_processors > 1) {
+#if 0
+printk("setup_smp: nr_processors 0x%lx\n",
+       hwrpb->nr_processors);
+#endif
+		cpubase = (struct percpu_struct *)
+			((char*)hwrpb + hwrpb->processor_offset);
+		boot_cpu_palrev = cpubase->pal_revision;
+
+		for (i = 0; i < hwrpb->nr_processors; i++ ) {
+			cpu = (struct percpu_struct *)
+				((char *)cpubase + i*hwrpb->processor_size);
+			if ((cpu->flags & 0x1cc) == 0x1cc) {
+				smp_num_probed++;
+				/* assume here that "whami" == index */
+				cpu_present_map |= (1 << i);
+				if (i != boot_cpu_id)
+				  cpu->pal_revision = boot_cpu_palrev;
+			}
+#if 0
+printk("setup_smp: CPU %d: flags 0x%lx type 0x%lx\n",
+       i, cpu->flags, cpu->type);
+ printk("setup_smp: CPU %d: PAL rev 0x%lx\n",
+	i, cpu->pal_revision);
+#endif
+		}
+	} else {
+		smp_num_probed = 1;
+		cpu_present_map = (1 << boot_cpu_id);
+	}
+	printk("setup_smp: %d CPUs probed, cpu_present_map 0x%x,"
+	       " boot_cpu_id %d\n",
+	       smp_num_probed, cpu_present_map, boot_cpu_id);
+}
+
+static void
+secondary_console_message(void)
+{
+        int mycpu, i, cnt;
+	unsigned long txrdy = hwrpb->txrdy;
+	char *cp1, *cp2, buf[80];
+        struct percpu_struct *cpu;
+
+        mycpu = hard_smp_processor_id();
+
+#if 0
+printk("secondary_console_message: TXRDY 0x%lx.\n", txrdy);
+#endif
+	 for (i = 0; i < NR_CPUS; i++) {
+	   if (txrdy & (1L << i)) {
+#if 0
+printk("secondary_console_message: TXRDY contains CPU %d.\n", i);
+#endif
+	     cpu = (struct percpu_struct *)
+	       ((char*)hwrpb
+		+ hwrpb->processor_offset
+		+ i * hwrpb->processor_size);
+#if 1
+	     printk("secondary_console_message: on %d from %d"
+		    " HALT_REASON 0x%lx FLAGS 0x%lx\n",
+		    mycpu, i, cpu->halt_reason, cpu->flags);
+#endif
+	     cnt = cpu->ipc_buffer[0] >> 32;
+	     if (cnt <= 0 || cnt >= 80)
+	       strcpy(buf,"<<< BOGUS MSG >>>");
+	     else {
+	       cp1 = (char *) &cpu->ipc_buffer[11];
+	       cp2 = buf;
+	       while (cnt--) {
+		 if (*cp1 == '\r' || *cp1 == '\n') {
+		   *cp2++ = ' '; cp1++;
+		 } else
+		   *cp2++ = *cp1++;
+	       }
+	       *cp2 = 0;
+	     }
+#if 1
+	     printk("secondary_console_message: on %d message is '%s'\n",
+		    mycpu, buf);
+#endif
+	   }
+		}
+	 hwrpb->txrdy = 0;
+	 return;
+}
+
+static int
+halt_on_panic(unsigned int this_cpu)
+{
+	halt();
+	return 0;
+}
+
+static int
+local_flush_tlb_all(unsigned int this_cpu)
+{
+	tbia();
+	clear_bit(this_cpu, &ipi_msg_flush_tb.flush_tb_mask);
+	mb();
+	return 0;
+}
+
+static int
+local_flush_tlb_mm(unsigned int this_cpu)
+{
+	struct mm_struct * mm = ipi_msg_flush_tb.p.flush_mm;
+	if (mm != current->mm)
+		flush_tlb_other(mm);
+	else
+		flush_tlb_current(mm);
+	clear_bit(this_cpu, &ipi_msg_flush_tb.flush_tb_mask);
+	mb();
+	return 0;
+}
+
+static int
+local_flush_tlb_page(unsigned int this_cpu)
+{
+	struct vm_area_struct * vma = ipi_msg_flush_tb.p.flush_vma;
+	struct mm_struct * mm = vma->vm_mm;
+
+	if (mm != current->mm)
+		flush_tlb_other(mm);
+	else
+		flush_tlb_current_page(mm, vma, ipi_msg_flush_tb.flush_addr);
+	clear_bit(this_cpu, &ipi_msg_flush_tb.flush_tb_mask);
+	mb();
+	return 0;
+}
+
+static int
+wrapper_local_flush_tlb_page(unsigned int this_cpu)
+{
+#if 0
+	int cpu = smp_processor_id();
+
+	if (cpu) {
+	  printk("wrapper: ipi_msg_flush_tb.flush_addr 0x%lx [%d]\n",
+		 ipi_msg_flush_tb.flush_addr, atomic_read(&global_irq_count));
+	}
+#endif
+	local_flush_tlb_page(this_cpu);
+	return 0;
+}
+
+static int
+unknown_ipi(unsigned int this_cpu)
+{
+	printk("unknown_ipi() on cpu %d:  ", this_cpu);
+	return 1;
+}
+
+enum ipi_message_type {
+  CPU_STOP,
+  TLB_ALL,
+  TLB_MM,
+  TLB_PAGE,
+  TLB_RANGE
+};
+
+static int (* ipi_func[32])(unsigned int) = {
+  halt_on_panic,
+  local_flush_tlb_all,
+  local_flush_tlb_mm,
+  wrapper_local_flush_tlb_page,
+  local_flush_tlb_mm,		/* a.k.a. local_flush_tlb_range */
+  unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi,
+  unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi,
+  unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi,
+  unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi,
+  unknown_ipi, unknown_ipi, unknown_ipi
+};
+
+void
+handle_ipi(struct pt_regs *regs)
+{
+	int this_cpu = smp_processor_id();
+	volatile int * pending_ipis = &ipi_bits[this_cpu];
+	int ops;
+
+	mb();
+#if 0
+	printk("handle_ipi: on CPU %d ops 0x%x PC 0x%lx\n",
+	       this_cpu, *pending_ipis, regs->pc);
+#endif
+	while ((ops = *pending_ipis)) {
+		int first;
+		for (first = 0; (ops & 1) == 0; ++first, ops >>= 1)
+			; /* look for the first thing to do */
+		clear_bit(first, pending_ipis);
+		mb();
+		if ((*ipi_func[first])(this_cpu))
+		  printk("%d\n", first);
+		mb();
+	}
+	if (hwrpb->txrdy)
+	  secondary_console_message();
+}
+
+void
+send_ipi_message(long to_whom, enum ipi_message_type operation)
+{
+	int i;
+	unsigned int j;
+
+	for (i = 0, j = 1; i < NR_CPUS; ++i, j += j) {
+		if ((to_whom & j) == 0)
+			continue;
+		set_bit(operation, &ipi_bits[i]);
+		mb();
+		wripir(i);
+	}
+}
+
+static char smp_buf[256];
+
+char *smp_info(void)
+{
+        sprintf(smp_buf, "CPUs probed %d active %d map 0x%x AKP %d\n",
+		smp_num_probed, smp_num_cpus, cpu_present_map,
+		klock_info.akp);
+
+        return smp_buf;
+}
+
+/* wrapper for call from panic() */
+void
+smp_message_pass(int target, int msg, unsigned long data, int wait)
+{
+	int me = smp_processor_id();
+
+	if (msg != MSG_STOP_CPU)
+		goto barf;
+
+	send_ipi_message(CPU_STOP, cpu_present_map ^ (1 << me));
+	return;
+barf:
+	printk("Yeeee, trying to send SMP msg(%d) on cpu %d\n", msg, me);
+	panic("Bogon SMP message pass.");
+}
+
+void
+flush_tlb_all(void)
+{
+	unsigned int to_whom = cpu_present_map ^ (1 << smp_processor_id());
+	int timeout = 10000;
+
+#if 1
+	if (!kernel_lock_held()) {
+	  printk("flush_tlb_all: kernel_flag %d (cpu %d akp %d)!\n",
+		 klock_info.kernel_flag, smp_processor_id(), klock_info.akp);
+	}
+#endif
+	ipi_msg_flush_tb.flush_tb_mask = to_whom;
+	send_ipi_message(to_whom, TLB_ALL);
+	tbia();
+
+	while (ipi_msg_flush_tb.flush_tb_mask) {
+	  if (--timeout < 0) {
+	    printk("flush_tlb_all: STUCK on CPU %d mask 0x%x\n",
+		   smp_processor_id(), ipi_msg_flush_tb.flush_tb_mask);
+	    ipi_msg_flush_tb.flush_tb_mask = 0;
+	    break;
+	  }
+	  udelay(100);
+		; /* Wait for all clear from other CPUs. */
+	}
+}
+
+void
+flush_tlb_mm(struct mm_struct *mm)
+{
+	unsigned int to_whom = cpu_present_map ^ (1 << smp_processor_id());
+	int timeout = 10000;
+
+#if 1
+	if (!kernel_lock_held()) {
+	  printk("flush_tlb_mm: kernel_flag %d (cpu %d akp %d)!\n",
+		 klock_info.kernel_flag, smp_processor_id(), klock_info.akp);
+	}
+#endif
+	ipi_msg_flush_tb.p.flush_mm = mm;
+	ipi_msg_flush_tb.flush_tb_mask = to_whom;
+	send_ipi_message(to_whom, TLB_MM);
+
+	if (mm != current->mm)
+		flush_tlb_other(mm);
+	else
+		flush_tlb_current(mm);
+
+	while (ipi_msg_flush_tb.flush_tb_mask) {
+	  if (--timeout < 0) {
+	    printk("flush_tlb_mm: STUCK on CPU %d mask 0x%x\n",
+		   smp_processor_id(), ipi_msg_flush_tb.flush_tb_mask);
+	    ipi_msg_flush_tb.flush_tb_mask = 0;
+	    break;
+	  }
+	  udelay(100);
+		; /* Wait for all clear from other CPUs. */
+	}
+}
+
+void
+flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)
+{
+	int cpu = smp_processor_id();
+	unsigned int to_whom = cpu_present_map ^ (1 << cpu);
+	struct mm_struct * mm = vma->vm_mm;
+	int timeout = 10000;
+
+#if 1
+	if (!kernel_lock_held()) {
+	  printk("flush_tlb_page: kernel_flag %d (cpu %d akp %d)!\n",
+		 klock_info.kernel_flag, cpu, klock_info.akp);
+	}
+#endif
+	ipi_msg_flush_tb.p.flush_vma = vma;
+	ipi_msg_flush_tb.flush_addr = addr;
+	ipi_msg_flush_tb.flush_tb_mask = to_whom;
+	send_ipi_message(to_whom, TLB_PAGE);
+
+	if (mm != current->mm)
+		flush_tlb_other(mm);
+	else
+		flush_tlb_current_page(mm, vma, addr);
+
+	while (ipi_msg_flush_tb.flush_tb_mask) {
+	  if (--timeout < 0) {
+	    printk("flush_tlb_page: STUCK on CPU %d [0x%x,0x%lx,%d,%d]\n",
+		   cpu, ipi_msg_flush_tb.flush_tb_mask, addr,
+		   klock_info.akp, global_irq_holder);
+	    ipi_msg_flush_tb.flush_tb_mask = 0;
+	    break;
+	  }
+	  udelay(100);
+		; /* Wait for all clear from other CPUs. */
+	}
+}
+
+void
+flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end)
+{
+#if 0
+	flush_tlb_mm(mm);
+#else
+	unsigned int to_whom;
+	int timeout;
+        unsigned long where;
+
+        __asm__("mov $26, %0" : "=r" (where));
+
+	timeout = 10000;
+	to_whom = cpu_present_map ^ (1 << smp_processor_id());
+
+#if 1
+	if (!kernel_lock_held()) {
+	  printk("flush_tlb_range: kernel_flag %d (cpu %d akp %d) @ 0x%lx\n",
+		 klock_info.kernel_flag, smp_processor_id(), klock_info.akp,
+		 where);
+	}
+#endif
+	ipi_msg_flush_tb.p.flush_mm = mm;
+	ipi_msg_flush_tb.flush_tb_mask = to_whom;
+	send_ipi_message(to_whom, TLB_MM);
+
+	if (mm != current->mm)
+		flush_tlb_other(mm);
+	else
+		flush_tlb_current(mm);
+
+	while (ipi_msg_flush_tb.flush_tb_mask) {
+	  if (--timeout < 0) {
+	    printk("flush_tlb_range: STUCK on CPU %d mask 0x%x\n",
+		   smp_processor_id(), ipi_msg_flush_tb.flush_tb_mask);
+	    ipi_msg_flush_tb.flush_tb_mask = 0;
+	    break;
+	  }
+	  udelay(100);
+		; /* Wait for all clear from other CPUs. */
+	}
+#endif
+}
+
+#ifdef DEBUG_KERNEL_LOCK
+void ___lock_kernel(klock_info_t *klip, int cpu, long ipl)
+{
+	long regx;
+	int stuck_lock;
+	unsigned long inline_pc;
+
+        __asm__("mov $26, %0" : "=r" (inline_pc));
+
+ try_again:
+
+	stuck_lock = 1<<26;
+
+	__asm__ __volatile__(
+	"1:	ldl_l	%1,%0;"
+	"	blbs	%1,6f;"
+	"	or	%1,1,%1;"
+	"	stl_c	%1,%0;"
+	"	beq	%1,6f;"
+	"4:	mb\n"
+	".section .text2,\"ax\"\n"
+	"6:	mov	%5,$16;"
+	"	call_pal %4;"
+	"7:	ldl	%1,%0;"
+	"	blt	%2,4b	# debug\n"
+	"	subl	%2,1,%2	# debug\n"
+	"	blbs	%1,7b;"
+	"	bis	$31,7,$16;"
+	"	call_pal %4;"
+	"	br	1b\n"
+	".previous"
+	: "=m,=m" (__dummy_lock(klip)), "=&r,=&r" (regx),
+	  "=&r,=&r" (stuck_lock)
+	: "0,0" (__dummy_lock(klip)), "i,i" (PAL_swpipl),
+	  "i,r" (ipl), "2,2" (stuck_lock)
+	: "$0", "$1", "$16", "$22", "$23", "$24", "$25", "memory");
+
+	if (stuck_lock < 0) {
+		printk("___kernel_lock stuck at %lx(%d) held %lx(%d)\n",
+		       inline_pc, cpu, klip->pc, klip->cpu);
+		goto try_again;
+	} else {
+		klip->pc = inline_pc;
+		klip->cpu = cpu;
+	}
+}
+#endif
+
+#ifdef DEBUG_SPINLOCK
+void spin_lock(spinlock_t * lock)
+{
+	long tmp;
+	long stuck;
+	unsigned long inline_pc;
+
+        __asm__("mov $26, %0" : "=r" (inline_pc));
+
+ try_again:
+
+	stuck = 0x10000000; /* was 4G, now 256M */
+
+	/* Use sub-sections to put the actual loop at the end
+	   of this object file's text section so as to perfect
+	   branch prediction.  */
+	__asm__ __volatile__(
+	"1:	ldq_l	%0,%1\n"
+	"	subq	%2,1,%2\n"
+	"	blbs	%0,2f\n"
+	"	or	%0,1,%0\n"
+	"	stq_c	%0,%1\n"
+	"	beq	%0,3f\n"
+	"4:	mb\n"
+	".section .text2,\"ax\"\n"
+	"2:	ldq	%0,%1\n"
+	"	subq	%2,1,%2\n"
+	"3:	blt	%2,4b\n"
+	"	blbs	%0,2b\n"
+	"	br	1b\n"
+	".previous"
+	: "=r" (tmp),
+	  "=m" (__dummy_lock(lock)),
+	  "=r" (stuck)
+	: "2" (stuck));
+
+	if (stuck < 0) {
+		printk("spinlock stuck at %lx (cur=%lx, own=%lx)\n",
+		       inline_pc,
+#if 0
+		       lock->previous, lock->task
+#else
+		       (unsigned long) current, lock->task
+#endif
+		       );
+		goto try_again;
+	} else {
+		lock->previous = (unsigned long) inline_pc;
+		lock->task = (unsigned long) current;
+	}
+}
+#endif /* DEBUG_SPINLOCK */
+
+#ifdef DEBUG_RWLOCK
+void write_lock(rwlock_t * lock)
+{
+	long regx, regy;
+	int stuck_lock, stuck_reader;
+	unsigned long inline_pc;
+
+        __asm__("mov $26, %0" : "=r" (inline_pc));
+
+ try_again:
+
+	stuck_lock = 1<<26;
+	stuck_reader = 1<<26;
+
+	__asm__ __volatile__(
+	"1:	ldl_l	%1,%0;"
+	"	blbs	%1,6f;"
+	"	or	%1,1,%2;"
+	"	stl_c	%2,%0;"
+	"	beq	%2,6f;"
+	"	blt	%1,8f;"
+	"4:	mb\n"
+	".section .text2,\"ax\"\n"
+	"6:	ldl	%1,%0;"
+	"	blt	%3,4b	# debug\n"
+	"	subl	%3,1,%3	# debug\n"
+	"	blbs	%1,6b;"
+	"	br	1b;"
+	"8:	ldl	%1,%0;"
+	"	blt	%4,4b	# debug\n"
+	"	subl	%4,1,%4	# debug\n"
+	"	blt	%1,8b;"
+	"9:	br	4b\n"
+	".previous"
+	: "=m" (__dummy_lock(lock)), "=&r" (regx), "=&r" (regy)
+	, "=&r" (stuck_lock), "=&r" (stuck_reader)
+	: "0" (__dummy_lock(lock))
+	, "3" (stuck_lock), "4" (stuck_reader)
+	);
+
+	if (stuck_lock < 0) {
+		printk("write_lock stuck at %lx\n", inline_pc);
+		goto try_again;
+	}
+	if (stuck_reader < 0) {
+		printk("write_lock stuck on readers at %lx\n", inline_pc);
+		goto try_again;
+	}
+}
+
+void _read_lock(rwlock_t * lock)
+{
+	long regx;
+	int stuck_lock;
+	unsigned long inline_pc;
+
+        __asm__("mov $26, %0" : "=r" (inline_pc));
+
+ try_again:
+
+	stuck_lock = 1<<26;
+
+	__asm__ __volatile__(
+	"1:	ldl_l	%1,%0;"
+	"	blbs	%1,6f;"
+	"	subl	%1,2,%1;"
+	"	stl_c	%1,%0;"
+	"	beq	%1,6f;"
+	"4:	mb\n"
+	".section .text2,\"ax\"\n"
+	"6:	ldl	%1,%0;"
+	"	blt	%2,4b	# debug\n"
+	"	subl	%2,1,%2	# debug\n"
+	"	blbs	%1,6b;"
+	"	br	1b\n"
+	".previous"
+	: "=m" (__dummy_lock(lock)), "=&r" (regx), "=&r" (stuck_lock)
+	: "0" (__dummy_lock(lock)), "2" (stuck_lock)
+	);
+
+	if (stuck_lock < 0) {
+		printk("_read_lock stuck at %lx\n", inline_pc);
+		goto try_again;
+	}
+}
+#endif /* DEBUG_RWLOCK */
diff -u --recursive --new-file v2.1.91/linux/arch/alpha/kernel/t2.c linux/arch/alpha/kernel/t2.c
--- v2.1.91/linux/arch/alpha/kernel/t2.c	Mon Feb 23 18:12:01 1998
+++ linux/arch/alpha/kernel/t2.c	Mon Mar 30 00:21:39 1998
@@ -8,6 +8,7 @@
  *
  */
 #include <linux/kernel.h>
+#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/bios32.h>
 #include <linux/pci.h>
@@ -19,17 +20,14 @@
 #include <asm/ptrace.h>
 #include <asm/mmu_context.h>
 
-/* NOTE: Herein are back-to-back mb insns.  They are magic.
-   A plausable explanation is that the i/o controler does not properly
-   handle the system transaction.  Another involves timing.  Ho hum.  */
+/*
+ * NOTE: Herein lie back-to-back mb instructions.  They are magic. 
+ * One plausible explanation is that the i/o controller does not properly
+ * handle the system transaction.  Another involves timing.  Ho hum.
+ */
 
 extern struct hwrpb_struct *hwrpb;
 extern asmlinkage void wrmces(unsigned long mces);
-extern asmlinkage unsigned long whami(void);
-extern int alpha_sys_type;
-
-#define CPUID whami()
-
 
 /*
  * Machine check reasons.  Defined according to PALcode sources
@@ -62,10 +60,14 @@
 #define vulp	volatile unsigned long *
 #define vuip	volatile unsigned int  *
 
-static volatile unsigned int T2_mcheck_expected = 0;
-static volatile unsigned int T2_mcheck_taken = 0;
-static unsigned long T2_jd;
+static volatile unsigned int T2_mcheck_expected[NR_CPUS];
+static volatile unsigned int T2_mcheck_taken[NR_CPUS];
 
+#ifdef CONFIG_ALPHA_SRM_SETUP
+unsigned int T2_DMA_WIN_BASE = T2_DMA_WIN_BASE_DEFAULT;
+unsigned int T2_DMA_WIN_SIZE = T2_DMA_WIN_SIZE_DEFAULT;
+unsigned long t2_sm_base;
+#endif /* SRM_SETUP */
 
 /*
  * Given a bus, device, and function number, compute resulting
@@ -145,8 +147,10 @@
 static unsigned int conf_read(unsigned long addr, unsigned char type1)
 {
 	unsigned long flags;
-	unsigned int stat0, value;
-	unsigned int t2_cfg = 0; /* to keep gcc quiet */
+	unsigned int stat0, value, cpu;
+	unsigned long t2_cfg = 0; /* to keep gcc quiet */
+
+	cpu = smp_processor_id();
 
 	save_flags(flags);	/* avoid getting hit by machine check */
 	cli();
@@ -155,43 +159,41 @@
 
 #if 0
 	/* reset status register to avoid losing errors: */
-	stat0 = *(vuip)T2_IOCSR;
-	*(vuip)T2_IOCSR = stat0;
+	stat0 = *(vulp)T2_IOCSR;
+	*(vulp)T2_IOCSR = stat0;
 	mb();
 	DBG(("conf_read: T2 IOCSR was 0x%x\n", stat0));
+#endif
 	/* if Type1 access, must set T2 CFG */
 	if (type1) {
-		t2_cfg = *(vuip)T2_IOC_CFG;
+		t2_cfg = *(vulp)T2_HAE_3 & ~0xc0000000UL;
+		*(vulp)T2_HAE_3 = 0x40000000UL | t2_cfg;
 		mb();
-		*(vuip)T2_IOC_CFG = t2_cfg | 1;
 		DBG(("conf_read: TYPE1 access\n"));
 	}
 	mb();
 	draina();
-#endif
 
-	T2_mcheck_expected = 1;
-	T2_mcheck_taken = 0;
+	T2_mcheck_expected[cpu] = 1;
+	T2_mcheck_taken[cpu] = 0;
 	mb();
 	/* access configuration space: */
 	value = *(vuip)addr;
 	mb();
 	mb();  /* magic */
-	if (T2_mcheck_taken) {
-		T2_mcheck_taken = 0;
+	if (T2_mcheck_taken[cpu]) {
+		T2_mcheck_taken[cpu] = 0;
 		value = 0xffffffffU;
 		mb();
 	}
-	T2_mcheck_expected = 0;
+	T2_mcheck_expected[cpu] = 0;
 	mb();
 
-#if 0
-	/* if Type1 access, must reset IOC CFG so normal IO space ops work */
+	/* if Type1 access, must reset T2 CFG so normal IO space ops work */
 	if (type1) {
-		*(vuip)T2_IOC_CFG = t2_cfg & ~1;
+		*(vulp)T2_HAE_3 = t2_cfg;
 		mb();
 	}
-#endif
 	DBG(("conf_read(): finished\n"));
 
 	restore_flags(flags);
@@ -203,44 +205,45 @@
 		       unsigned char type1)
 {
 	unsigned long flags;
-	unsigned int stat0;
-	unsigned int t2_cfg = 0; /* to keep gcc quiet */
+	unsigned int stat0, cpu;
+	unsigned long t2_cfg = 0; /* to keep gcc quiet */
+
+	cpu = smp_processor_id();
 
 	save_flags(flags);	/* avoid getting hit by machine check */
 	cli();
 
 #if 0
 	/* reset status register to avoid losing errors: */
-	stat0 = *(vuip)T2_IOCSR;
-	*(vuip)T2_IOCSR = stat0;
+	stat0 = *(vulp)T2_IOCSR;
+	*(vulp)T2_IOCSR = stat0;
 	mb();
 	DBG(("conf_write: T2 ERR was 0x%x\n", stat0));
+#endif
 	/* if Type1 access, must set T2 CFG */
 	if (type1) {
-		t2_cfg = *(vuip)T2_IOC_CFG;
+		t2_cfg = *(vulp)T2_HAE_3 & ~0xc0000000UL;
+		*(vulp)T2_HAE_3 = t2_cfg | 0x40000000UL;
 		mb();
-		*(vuip)T2_IOC_CFG = t2_cfg | 1;
 		DBG(("conf_write: TYPE1 access\n"));
 	}
+	mb();
 	draina();
-#endif
 
-	T2_mcheck_expected = 1;
+	T2_mcheck_expected[cpu] = 1;
 	mb();
 	/* access configuration space: */
 	*(vuip)addr = value;
 	mb();
 	mb();  /* magic */
-	T2_mcheck_expected = 0;
+	T2_mcheck_expected[cpu] = 0;
 	mb();
 
-#if 0
-	/* if Type1 access, must reset IOC CFG so normal IO space ops work */
+	/* if Type1 access, must reset T2 CFG so normal IO space ops work */
 	if (type1) {
-		*(vuip)T2_IOC_CFG = t2_cfg & ~1;
+		*(vulp)T2_HAE_3 = t2_cfg;
 		mb();
 	}
-#endif
 	DBG(("conf_write(): finished\n"));
 	restore_flags(flags);
 }
@@ -362,17 +365,21 @@
 
 unsigned long t2_init(unsigned long mem_start, unsigned long mem_end)
 {
-	unsigned int t2_err;
-	struct percpu_struct *cpu;
-	int i;
+	unsigned long t2_err;
+	unsigned int i;
+
+	for (i = 0; i < NR_CPUS; i++) {
+		T2_mcheck_expected[i] = 0;
+		T2_mcheck_taken[i] = 0;
+	}
 
 #if 0
 	/* 
 	 * Set up error reporting.
 	 */
-	t2_err = *(vuip)T2_IOCSR ;
+	t2_err = *(vulp)T2_IOCSR ;
 	t2_err |= (0x1 << 7) ;   /* master abort */
-	*(vuip)T2_IOC_T2_ERR = t2_err ;
+	*(vulp)T2_IOCSR = t2_err ;
 	mb() ;
 #endif
 
@@ -388,6 +395,42 @@
 	       *(vulp)T2_TBASE2);
 #endif
 
+#ifdef CONFIG_ALPHA_SRM_SETUP
+	/* check window 1 for enabled and mapped to 0 */
+	if (((*(vulp)T2_WBASE1 & (3UL<<18)) == (2UL<<18)) &&
+	     (*(vulp)T2_TBASE1 == 0))
+	{
+	  T2_DMA_WIN_BASE = *(vulp)T2_WBASE1 & 0xfff00000UL;
+	  T2_DMA_WIN_SIZE = *(vulp)T2_WMASK1 & 0xfff00000UL;
+	  T2_DMA_WIN_SIZE += 0x00100000UL;
+/* DISABLE window 2!! ?? */
+#if 1
+	  printk("t2_init: using Window 1 settings\n");
+	  printk("t2_init: BASE 0x%lx MASK 0x%lx TRANS 0x%lx\n",
+		 *(vulp)T2_WBASE1,
+		 *(vulp)T2_WMASK1,
+		 *(vulp)T2_TBASE1);
+#endif
+	}
+	else	/* check window 2 for enabled and mapped to 0 */
+	if (((*(vulp)T2_WBASE2 & (3UL<<18)) == (2UL<<18)) &&
+	     (*(vulp)T2_TBASE2 == 0))
+	{
+	  T2_DMA_WIN_BASE = *(vulp)T2_WBASE2 & 0xfff00000UL;
+	  T2_DMA_WIN_SIZE = *(vulp)T2_WMASK2 & 0xfff00000UL;
+	  T2_DMA_WIN_SIZE += 0x00100000UL;
+/* DISABLE window 1!! ?? */
+#if 1
+	  printk("t2_init: using Window 2 settings\n");
+	  printk("t2_init: BASE 0x%lx MASK 0x%lx TRANS 0x%lx\n",
+		 *(vulp)T2_WBASE2,
+		 *(vulp)T2_WMASK2,
+		 *(vulp)T2_TBASE2);
+#endif
+	}
+	else /* we must use our defaults... */
+#endif /* SRM_SETUP */
+	{
 	/*
 	 * Set up the PCI->physical memory translation windows.
 	 * For now, window 2 is  disabled.  In the future, we may
@@ -396,13 +439,13 @@
 	 */
 
 	/* WARNING!! must correspond to the DMA_WIN params!!! */
-	*(vuip)T2_WBASE1 = 0x400807ffU;
-	*(vuip)T2_WMASK1 = 0x3ff00000U;
-	*(vuip)T2_TBASE1 = 0;
+	*(vulp)T2_WBASE1 = 0x400807ffU;
+	*(vulp)T2_WMASK1 = 0x3ff00000U;
+	*(vulp)T2_TBASE1 = 0;
 
-	*(vuip)T2_WBASE2 = 0x0;
-
-	*(vuip)T2_HBASE = 0x0;
+	*(vulp)T2_WBASE2 = 0x0;
+	*(vulp)T2_HBASE = 0x0;
+	}
 
 	/*
 	 * check ASN in HWRPB for validity, report if bad
@@ -420,41 +463,43 @@
 	 *  what ARC or SRM might have left behind...
 	 */
 	{
+	  unsigned long t2_hae_1 = *(vulp)T2_HAE_1;
+	  unsigned long t2_hae_2 = *(vulp)T2_HAE_2;
+	  unsigned long t2_hae_3 = *(vulp)T2_HAE_3;
+	  unsigned long t2_hae_4 = *(vulp)T2_HAE_4;
+#if 1
+          printk("T2_init: HAE1 was 0x%lx\n", t2_hae_1);
+          printk("T2_init: HAE2 was 0x%lx\n", t2_hae_2);
+          printk("T2_init: HAE3 was 0x%lx\n", t2_hae_3);
+          printk("T2_init: HAE4 was 0x%lx\n", t2_hae_4);
+#endif
+#ifdef CONFIG_ALPHA_SRM_SETUP
+	  /*
+	   * sigh... For the SRM setup, unless we know apriori what the HAE
+	   * contents will be, we need to setup the arbitrary region bases
+	   * so we can test against the range of addresses and tailor the
+	   * 	    region chosen for the SPARSE memory access.
+	   *
+	   * see include/asm-alpha/t2.h for the SPARSE mem read/write
+	  */
+	  t2_sm_base = (t2_hae_1 << 27) & 0xf8000000UL;
+	  /*
+	    Set the HAE cache, so that setup_arch() code
+	    will use the SRM setting always. Our readb/writeb
+	    code in .h expects never to have to change
+	    the contents of the HAE.
+	   */
+	  hae.cache = t2_hae_1;
+#else /* SRM_SETUP */
+	  *(vulp)T2_HAE_1 = 0; mb();
+	  *(vulp)T2_HAE_2 = 0; mb();
+	  *(vulp)T2_HAE_3 = 0; mb();
 #if 0
-		printk("T2_init: HAE1 was 0x%lx\n", *(vulp)T2_HAE_1);
-		printk("T2_init: HAE2 was 0x%lx\n", *(vulp)T2_HAE_2);
-		printk("T2_init: HAE3 was 0x%lx\n", *(vulp)T2_HAE_3);
-		printk("T2_init: HAE4 was 0x%lx\n", *(vulp)T2_HAE_4);
-#endif
-#if 0
-		*(vuip)T2_HAE_1 = 0; mb();
-		*(vuip)T2_HAE_2 = 0; mb();
-		*(vuip)T2_HAE_3 = 0; mb();
-		*(vuip)T2_HAE_4 = 0; mb();
+	  *(vulp)T2_HAE_4 = 0; mb(); /* do not touch this */
 #endif
+#endif /* SRM_SETUP */
 	}
  
-#if 1
-	if (hwrpb->nr_processors > 1) {
-		printk("T2_init: nr_processors 0x%lx\n",
-		       hwrpb->nr_processors);
-		printk("T2_init: processor_size 0x%lx\n",
-		       hwrpb->processor_size);
-		printk("T2_init: processor_offset 0x%lx\n",
-		       hwrpb->processor_offset);
-
-		cpu = (struct percpu_struct *)
-			((char*)hwrpb + hwrpb->processor_offset);
-
-		for (i = 0; i < hwrpb->nr_processors; i++ ) {
-			printk("T2_init: CPU 0x%x: flags 0x%lx type 0x%lx\n",
-			       i, cpu->flags, cpu->type);
-			cpu = (struct percpu_struct *)
-				((char *)cpu + hwrpb->processor_size);
-		}
-	}
-#endif
-
 	return mem_start;
 }
 
@@ -469,17 +514,19 @@
 
 int t2_clear_errors(void)
 {
+	unsigned int cpu = smp_processor_id();
+
 	DBGMC(("???????? t2_clear_errors\n"));
 
-	sable_cpu_regs[CPUID]->sic &= ~SIC_SEIC;
+	sable_cpu_regs[cpu]->sic &= ~SIC_SEIC;
 
 	/* 
 	 * clear cpu errors
 	 */
-	sable_cpu_regs[CPUID]->bcce |= sable_cpu_regs[CPUID]->bcce;
-	sable_cpu_regs[CPUID]->cbe  |= sable_cpu_regs[CPUID]->cbe;
-	sable_cpu_regs[CPUID]->bcue |= sable_cpu_regs[CPUID]->bcue;
-	sable_cpu_regs[CPUID]->dter |= sable_cpu_regs[CPUID]->dter;
+	sable_cpu_regs[cpu]->bcce |= sable_cpu_regs[cpu]->bcce;
+	sable_cpu_regs[cpu]->cbe  |= sable_cpu_regs[cpu]->cbe;
+	sable_cpu_regs[cpu]->bcue |= sable_cpu_regs[cpu]->bcue;
+	sable_cpu_regs[cpu]->dter |= sable_cpu_regs[cpu]->dter;
 
 	*(vulp)T2_CERR1 |= *(vulp)T2_CERR1;
 	*(vulp)T2_PERR1 |= *(vulp)T2_PERR1;
@@ -499,6 +546,7 @@
 	const char * reason;
 	char buf[128];
 	long i;
+	unsigned int cpu = smp_processor_id();
 
 	DBGMC(("t2_machine_check: vector=0x%lx la_ptr=0x%lx\n",
 	       vector, la_ptr));
@@ -516,7 +564,7 @@
 	DBGMC(("         pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n",
 	       regs->pc, mchk_header->elfl_size, mchk_header->elfl_procoffset,
 	       mchk_header->elfl_sysoffset));
-	DBGMC(("t2_machine_check: expected %d\n", T2_mcheck_expected));
+	DBGMC(("t2_machine_check: expected %d\n", T2_mcheck_expected[cpu]));
 
 #ifdef DEBUG_DUMP
 	{
@@ -537,11 +585,11 @@
 	 */
 	mb();
 	mb();  /* magic */
-	if (T2_mcheck_expected/* && (mchk_sysdata->epic_dcsr && 0x0c00UL)*/) {
+	if (T2_mcheck_expected[cpu]) {
 		DBGMC(("T2 machine check expected\n"));
-		T2_mcheck_taken = 1;
+		T2_mcheck_taken[cpu] = 1;
 		t2_clear_errors();
-		T2_mcheck_expected = 0;
+		T2_mcheck_expected[cpu] = 0;
 		mb();
 		mb();  /* magic */
 		wrmces(rdmces()|1);/* ??? */
diff -u --recursive --new-file v2.1.91/linux/arch/alpha/kernel/time.c linux/arch/alpha/kernel/time.c
--- v2.1.91/linux/arch/alpha/kernel/time.c	Sun Nov 30 10:59:02 1997
+++ linux/arch/alpha/kernel/time.c	Mon Mar 30 00:21:39 1998
@@ -82,6 +82,16 @@
 	__u32 now;
 	long nticks;
 
+#ifdef __SMP__
+	extern void smp_percpu_timer_interrupt(struct pt_regs *);
+	extern unsigned int boot_cpu_id;
+	/* when SMP, do this for *all* CPUs, 
+	   but only do the rest for the boot CPU */
+	smp_percpu_timer_interrupt(regs);
+	if (smp_processor_id() != boot_cpu_id)
+	  return;
+#endif
+
 	/*
 	 * Estimate how many ticks have passed since the last update.
 	 * Round the result, .5 to even.  When we loose ticks due to
diff -u --recursive --new-file v2.1.91/linux/arch/alpha/kernel/tsunami.c linux/arch/alpha/kernel/tsunami.c
--- v2.1.91/linux/arch/alpha/kernel/tsunami.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/alpha/kernel/tsunami.c	Mon Mar 30 00:21:39 1998
@@ -0,0 +1,504 @@
+/*
+ * Code common to all TSUNAMI chips.
+ *
+ * Based on code written by David A Rusling (david.rusling@reo.mts.dec.com).
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/sched.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/hwrpb.h>
+#include <asm/ptrace.h>
+#include <asm/mmu_context.h>
+
+/*
+ * NOTE: Herein lie back-to-back mb instructions.  They are magic. 
+ * One plausible explanation is that the i/o controller does not properly
+ * handle the system transaction.  Another involves timing.  Ho hum.
+ */
+
+extern struct hwrpb_struct *hwrpb;
+extern asmlinkage void wrmces(unsigned long mces);
+
+/*
+ * BIOS32-style PCI interface:
+ */
+
+#ifdef CONFIG_ALPHA_TSUNAMI
+
+#ifdef DEBUG 
+# define DBG(args)	printk args
+#else
+# define DBG(args)
+#endif
+
+#define DEBUG_MCHECK
+#ifdef DEBUG_MCHECK
+# define DBG_MCK(args)	printk args
+#define DEBUG_MCHECK_DUMP
+#else
+# define DBG_MCK(args)
+#endif
+
+#define vuip	volatile unsigned int  *
+#define vulp	volatile unsigned long  *
+
+static volatile unsigned int TSUNAMI_mcheck_expected[NR_CPUS];
+static volatile unsigned int TSUNAMI_mcheck_taken[NR_CPUS];
+static unsigned int TSUNAMI_jd[NR_CPUS];
+
+#ifdef CONFIG_ALPHA_SRM_SETUP
+unsigned int TSUNAMI_DMA_WIN_BASE = TSUNAMI_DMA_WIN_BASE_DEFAULT;
+unsigned int TSUNAMI_DMA_WIN_SIZE = TSUNAMI_DMA_WIN_SIZE_DEFAULT;
+#endif /* SRM_SETUP */
+
+/*
+ * Given a bus, device, and function number, compute resulting
+ * configuration space address
+ * accordingly.  It is therefore not safe to have concurrent
+ * invocations to configuration space access routines, but there
+ * really shouldn't be any need for this.
+ *
+ * Note that all config space accesses use Type 1 address format.
+ *
+ * Note also that type 1 is determined by non-zero bus number.
+ *
+ * Type 1:
+ *
+ *  3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 
+ *  3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | | | | | | | | | | |B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|1|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ *	31:24	reserved
+ *	23:16	bus number (8 bits = 128 possible buses)
+ *	15:11	Device number (5 bits)
+ *	10:8	function number
+ *	 7:2	register number
+ *  
+ * Notes:
+ *	The function number selects which function of a multi-function device 
+ *	(e.g., scsi and ethernet).
+ * 
+ *	The register selects a DWORD (32 bit) register offset.  Hence it
+ *	doesn't get shifted by 2 bits as we want to "drop" the bottom two
+ *	bits.
+ */
+static int mk_conf_addr(unsigned char bus, unsigned char device_fn,
+			unsigned char where, unsigned long *pci_addr,
+			unsigned char *type1)
+{
+	unsigned long addr;
+
+	DBG(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x, pci_addr=0x%p, type1=0x%p)\n",
+	     bus, device_fn, where, pci_addr, type1));
+
+	if (bus == 0) {
+		*type1 = 0;
+	} else {
+		/* type 1 configuration cycle: */
+		*type1 = 1;
+	}
+	addr = (bus << 16) | (device_fn << 8) | (where);
+	*pci_addr = addr;
+	DBG(("mk_conf_addr: returning pci_addr 0x%lx\n", addr));
+	return 0;
+}
+
+int pcibios_read_config_byte (unsigned char bus, unsigned char device_fn,
+			      unsigned char where, unsigned char *value)
+{
+	unsigned long addr;
+	unsigned char type1;
+	unsigned char result;
+
+	*value = 0xff;
+
+	if (mk_conf_addr(bus, device_fn, where, &addr, &type1) < 0) {
+		return PCIBIOS_SUCCESSFUL;
+	}
+
+	__asm__ __volatile__ (
+		 "ldbu %0,%1"
+		 : "=r" (result)
+		 : "m"  (*(unsigned char *)(addr+TSUNAMI_PCI0_CONF)));
+
+	*value = result;
+	return PCIBIOS_SUCCESSFUL;
+}
+
+
+int pcibios_read_config_word (unsigned char bus, unsigned char device_fn,
+			      unsigned char where, unsigned short *value)
+{
+	unsigned long addr;
+	unsigned char type1;
+	unsigned short result;
+
+	*value = 0xffff;
+
+	if (where & 0x1) {
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+	}
+
+	if (mk_conf_addr(bus, device_fn, where, &addr, &type1)) {
+		return PCIBIOS_SUCCESSFUL;
+	}
+
+	__asm__ __volatile__ (
+		 "ldwu %0,%1"
+		 : "=r" (result)
+		 : "m"  (*(unsigned short *)(addr+TSUNAMI_PCI0_CONF)));
+
+	*value = result;
+	return PCIBIOS_SUCCESSFUL;
+}
+
+
+int pcibios_read_config_dword (unsigned char bus, unsigned char device_fn,
+			       unsigned char where, unsigned int *value)
+{
+	unsigned long addr;
+	unsigned char type1;
+	unsigned int result;
+
+	*value = 0xffffffff;
+	if (where & 0x3) {
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+	}
+
+	if (mk_conf_addr(bus, device_fn, where, &addr, &type1)) {
+		return PCIBIOS_SUCCESSFUL;
+	}
+
+	__asm__ __volatile__ (
+		 "ldl %0,%1"
+		 : "=r" (result)
+		 : "m"  (*(unsigned int *)(addr+TSUNAMI_PCI0_CONF)));
+
+	*value = result;
+	return PCIBIOS_SUCCESSFUL;
+}
+
+
+int pcibios_write_config_byte (unsigned char bus, unsigned char device_fn,
+			       unsigned char where, unsigned char value)
+{
+	unsigned long addr;
+	unsigned char type1;
+
+	if (mk_conf_addr(bus, device_fn, where, &addr, &type1) < 0) {
+		return PCIBIOS_SUCCESSFUL;
+	}
+
+	__asm__ __volatile__ (
+		 "stb %1,%0\n\t"
+		 "mb"
+		 : : "m" (*(unsigned char *)(addr+TSUNAMI_PCI0_CONF)),
+		     "r" (value));
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+
+int pcibios_write_config_word (unsigned char bus, unsigned char device_fn,
+			       unsigned char where, unsigned short value)
+{
+	unsigned long addr;
+	unsigned char type1;
+
+	if (where & 0x1) {
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+	}
+
+	if (mk_conf_addr(bus, device_fn, where, &addr, &type1) < 0) {
+		return PCIBIOS_SUCCESSFUL;
+	}
+
+	__asm__ __volatile__ (
+		 "stw %1,%0\n\t"
+		 "mb"
+		 : : "m" (*(unsigned short *)(addr+TSUNAMI_PCI0_CONF)),
+		     "r" (value));
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+
+int pcibios_write_config_dword (unsigned char bus, unsigned char device_fn,
+				unsigned char where, unsigned int value)
+{
+	unsigned long addr;
+	unsigned char type1;
+
+	if (where & 0x3) {
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+	}
+
+	if (mk_conf_addr(bus, device_fn, where, &addr, &type1) < 0) {
+		return PCIBIOS_SUCCESSFUL;
+	}
+
+	__asm__ __volatile__ (
+		 "stl %1,%0\n\t"
+		 "mb"
+		 : : "m" (*(unsigned int *)(addr+TSUNAMI_PCI0_CONF)),
+		     "r" (value));
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+
+unsigned long tsunami_init(unsigned long mem_start, unsigned long mem_end)
+{
+        unsigned long tsunami_err;
+	unsigned int i;
+
+#if 0
+printk("tsunami_init: CChip registers:\n");
+printk("tsunami_init: CSR_CSC 0x%lx\n", *(vulp)TSUNAMI_CSR_CSC);
+printk("tsunami_init: CSR_MTR 0x%lx\n", *(vulp)TSUNAMI_CSR_MTR);
+printk("tsunami_init: CSR_MISC 0x%lx\n", *(vulp)TSUNAMI_CSR_MISC);
+printk("tsunami_init: CSR_DIM0 0x%lx\n", *(vulp)TSUNAMI_CSR_DIM0);
+printk("tsunami_init: CSR_DIM1 0x%lx\n", *(vulp)TSUNAMI_CSR_DIM1);
+printk("tsunami_init: CSR_DIR0 0x%lx\n", *(vulp)TSUNAMI_CSR_DIR0);
+printk("tsunami_init: CSR_DIR1 0x%lx\n", *(vulp)TSUNAMI_CSR_DIR1);
+printk("tsunami_init: CSR_DRIR 0x%lx\n", *(vulp)TSUNAMI_CSR_DRIR);
+
+printk("tsunami_init: DChip registers:\n");
+printk("tsunami_init: CSR_DSC 0x%lx\n", *(vulp)TSUNAMI_CSR_DSC);
+printk("tsunami_init: CSR_STR 0x%lx\n", *(vulp)TSUNAMI_CSR_STR);
+printk("tsunami_init: CSR_DREV 0x%lx\n", *(vulp)TSUNAMI_CSR_DREV);
+
+printk("tsunami_init: PChip registers:\n");
+printk("tsunami_init: PCHIP0_WSBA0 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_WSBA0);
+printk("tsunami_init: PCHIP0_WSBA1 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_WSBA1);
+printk("tsunami_init: PCHIP0_WSBA2 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_WSBA2);
+printk("tsunami_init: PCHIP0_WSBA3 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_WSBA3);
+printk("tsunami_init: PCHIP0_WSM0 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_WSM0);
+printk("tsunami_init: PCHIP0_WSM1 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_WSM1);
+printk("tsunami_init: PCHIP0_WSM2 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_WSM2);
+printk("tsunami_init: PCHIP0_WSM3 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_WSM3);
+printk("tsunami_init: PCHIP0_TBA0 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_TBA0);
+printk("tsunami_init: PCHIP0_TBA1 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_TBA1);
+printk("tsunami_init: PCHIP0_TBA2 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_TBA2);
+printk("tsunami_init: PCHIP0_TBA3 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_TBA3);
+
+printk("tsunami_init: PCHIP0_PCTL 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_PCTL);
+printk("tsunami_init: PCHIP0_PLAT 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_PLAT);
+printk("tsunami_init: PCHIP0_PERROR 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_PERROR);
+printk("tsunami_init: PCHIP0_PERRMASK 0x%lx\n", *(vulp)TSUNAMI_PCHIP0_PERRMASK);
+
+#endif
+
+	for (i = 0; i < NR_CPUS; i++) {
+		TSUNAMI_mcheck_expected[i] = 0;
+		TSUNAMI_mcheck_taken[i] = 0;
+	}
+#ifdef NOT_YET
+        /* 
+	 * Set up error reporting. Make sure CPU_PE is OFF in the mask.
+	 */
+	tsunami_err = *(vulp)TSUNAMI_PCHIP0_PERRMASK;
+	tsunami_err &= ~20;   
+	*(vulp)TSUNAMI_PCHIP0_PERRMASK = tsunami_err;
+	mb();
+	tsunami_err = *(vulp)TSUNAMI_PCHIP0_PERRMASK;
+
+	tsunami_err = *(vulp)TSUNAMI_PCHIP0_PERROR ;
+	tsunami_err |= 0x40;   /* master/target abort */
+	*(vulp)TSUNAMI_PCHIP0_PERROR = tsunami_err ;
+	mb() ;
+	tsunami_err = *(vulp)TSUNAMI_PCHIP0_PERROR ;
+#endif /* NOT_YET */
+
+#ifdef CONFIG_ALPHA_SRM_SETUP
+	/* check window 0 for enabled and mapped to 0 */
+	if (((*(vulp)TSUNAMI_PCHIP0_WSBA0 & 3) == 1) &&
+	    (*(vulp)TSUNAMI_PCHIP0_TBA0 == 0) &&
+	    ((*(vulp)TSUNAMI_PCHIP0_WSM0 & 0xfff00000U) > 0x0ff00000U))
+	{
+	  TSUNAMI_DMA_WIN_BASE = *(vulp)TSUNAMI_PCHIP0_WSBA0 & 0xfff00000U;
+	  TSUNAMI_DMA_WIN_SIZE = *(vulp)TSUNAMI_PCHIP0_WSM0 & 0xfff00000U;
+	  TSUNAMI_DMA_WIN_SIZE += 0x00100000U;
+#if 1
+	  printk("tsunami_init: using Window 0 settings\n");
+	  printk("tsunami_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n",
+		 *(vulp)TSUNAMI_PCHIP0_WSBA0,
+		 *(vulp)TSUNAMI_PCHIP0_WSM0,
+		 *(vulp)TSUNAMI_PCHIP0_TBA0);
+#endif
+	}
+	else  /* check window 1 for enabled and mapped to 0 */
+	if (((*(vulp)TSUNAMI_PCHIP0_WSBA1 & 3) == 1) &&
+	    (*(vulp)TSUNAMI_PCHIP0_TBA1 == 0) &&
+	    ((*(vulp)TSUNAMI_PCHIP0_WSM1 & 0xfff00000U) > 0x0ff00000U))
+{
+	  TSUNAMI_DMA_WIN_BASE = *(vulp)TSUNAMI_PCHIP0_WSBA1 & 0xfff00000U;
+	  TSUNAMI_DMA_WIN_SIZE = *(vulp)TSUNAMI_PCHIP0_WSM1 & 0xfff00000U;
+	  TSUNAMI_DMA_WIN_SIZE += 0x00100000U;
+#if 1
+	  printk("tsunami_init: using Window 1 settings\n");
+	  printk("tsunami_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n",
+		 *(vulp)TSUNAMI_PCHIP0_WSBA1,
+		 *(vulp)TSUNAMI_PCHIP0_WSM1,
+		 *(vulp)TSUNAMI_PCHIP0_TBA1);
+#endif
+	}
+	else  /* check window 2 for enabled and mapped to 0 */
+	if (((*(vulp)TSUNAMI_PCHIP0_WSBA2 & 3) == 1) &&
+	    (*(vulp)TSUNAMI_PCHIP0_TSB2 == 0) &&
+	    ((*(vulp)TSUNAMI_PCHIP0_WSM2 & 0xfff00000U) > 0x0ff00000U))
+	{
+	  TSUNAMI_DMA_WIN_BASE = *(vulp)TSUNAMI_PCHIP0_WSBA2 & 0xfff00000U;
+	  TSUNAMI_DMA_WIN_SIZE = *(vulp)TSUNAMI_PCHIP0_WSM2 & 0xfff00000U;
+	  TSUNAMI_DMA_WIN_SIZE += 0x00100000U;
+#if 1
+	  printk("tsunami_init: using Window 2 settings\n");
+	  printk("tsunami_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n",
+		 *(vulp)TSUNAMI_PCHIP0_WSBA2,
+		 *(vulp)TSUNAMI_PCHIP0_WSM2,
+		 *(vulp)TSUNAMI_PCHIP0_TSB2);
+#endif
+	}
+	else  /* check window 3 for enabled and mapped to 0 */
+	if (((*(vulp)TSUNAMI_PCHIP0_WSBA3 & 3) == 1) &&
+	    (*(vulp)TSUNAMI_PCHIP0_TBA3 == 0) &&
+	    ((*(vulp)TSUNAMI_PCHIP0_WSM3 & 0xfff00000U) > 0x0ff00000U))
+	{
+	  TSUNAMI_DMA_WIN_BASE = *(vulp)TSUNAMI_PCHIP0_WSBA3 & 0xfff00000U;
+	  TSUNAMI_DMA_WIN_SIZE = *(vulp)TSUNAMI_PCHIP0_WSM3 & 0xfff00000U;
+	  TSUNAMI_DMA_WIN_SIZE += 0x00100000U;
+#if 1
+	  printk("tsunami_init: using Window 3 settings\n");
+	  printk("tsunami_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n",
+		 *(vulp)TSUNAMI_PCHIP0_WSBA3,
+		 *(vulp)TSUNAMI_PCHIP0_WSM3,
+		 *(vulp)TSUNAMI_PCHIP0_TBA3);
+#endif
+	}
+	else  /* we must use our defaults which were pre-initialized... */
+#endif /* SRM_SETUP */
+	{
+	/*
+	 * Set up the PCI->physical memory translation windows.
+	 * For now, windows 1,2 and 3 are disabled.  In the future, we may
+	 * want to use them to do scatter/gather DMA.  Window 0
+	 * goes at 1 GB and is 1 GB large.
+	 */
+
+        *(vulp)TSUNAMI_PCHIP0_WSBA0 = 1L | (TSUNAMI_DMA_WIN_BASE & 0xfff00000U);
+        *(vulp)TSUNAMI_PCHIP0_WSM0 = (TSUNAMI_DMA_WIN_SIZE - 1) & 0xfff00000UL;
+        *(vulp)TSUNAMI_PCHIP0_TBA0 = 0UL;
+
+        *(vulp)TSUNAMI_PCHIP0_WSBA1 = 0UL;
+        *(vulp)TSUNAMI_PCHIP0_WSBA2 = 0UL;
+        *(vulp)TSUNAMI_PCHIP0_WSBA3 = 0UL;
+	mb();
+	}
+
+	/*
+	 * check ASN in HWRPB for validity, report if bad
+	 */
+	if (hwrpb->max_asn != MAX_ASN) {
+		printk("TSUNAMI_init: max ASN from HWRPB is bad (0x%lx)\n",
+			hwrpb->max_asn);
+		hwrpb->max_asn = MAX_ASN;
+	}
+
+	return mem_start;
+}
+
+int tsunami_pci_clr_err(void)
+{
+	unsigned int cpu = smp_processor_id();
+
+	TSUNAMI_jd[cpu] = *((vulp)TSUNAMI_PCHIP0_PERROR);
+	DBG(("TSUNAMI_pci_clr_err: PERROR after read 0x%x\n", TSUNAMI_jd[cpu]));
+	*((vulp)TSUNAMI_PCHIP0_PERROR) = 0x040; mb();
+	TSUNAMI_jd[cpu] = *((vulp)TSUNAMI_PCHIP0_PERROR);
+	return 0;
+}
+
+void tsunami_machine_check(unsigned long vector, unsigned long la_ptr,
+			 struct pt_regs * regs)
+{
+#if 1
+        printk("TSUNAMI machine check ignored\n") ;
+#else
+	struct el_common *mchk_header;
+	struct el_TSUNAMI_sysdata_mcheck *mchk_sysdata;
+	unsigned int cpu = smp_processor_id();
+
+	mchk_header = (struct el_common *)la_ptr;
+
+	mchk_sysdata = 
+	  (struct el_TSUNAMI_sysdata_mcheck *)(la_ptr + mchk_header->sys_offset);
+
+#if 0
+	DBG_MCK(("tsunami_machine_check: vector=0x%lx la_ptr=0x%lx\n",
+	     vector, la_ptr));
+	DBG_MCK(("\t\t pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n",
+	     regs->pc, mchk_header->size, mchk_header->proc_offset,
+	     mchk_header->sys_offset));
+	DBG_MCK(("tsunami_machine_check: expected %d DCSR 0x%lx PEAR 0x%lx\n",
+	     TSUNAMI_mcheck_expected[cpu], mchk_sysdata->epic_dcsr,
+	     mchk_sysdata->epic_pear));
+#endif
+#ifdef DEBUG_MCHECK_DUMP
+	{
+	    unsigned long *ptr;
+	    int i;
+
+	    ptr = (unsigned long *)la_ptr;
+	    for (i = 0; i < mchk_header->size / sizeof(long); i += 2) {
+		printk(" +%lx %lx %lx\n", i*sizeof(long), ptr[i], ptr[i+1]);
+	    }
+	}
+#endif /* DEBUG_MCHECK_DUMP */
+	/*
+	 * Check if machine check is due to a badaddr() and if so,
+	 * ignore the machine check.
+	 */
+	mb();
+	mb();  /* magic */
+	if (TSUNAMI_mcheck_expected[cpu]) {
+		DBG(("TSUNAMI machine check expected\n"));
+		TSUNAMI_mcheck_expected[cpu] = 0;
+		TSUNAMI_mcheck_taken[cpu] = 1;
+		mb();
+		mb();  /* magic */
+		draina();
+		tsunami_pci_clr_err();
+		wrmces(0x7);
+		mb();
+	}
+#if 1
+	else {
+		printk("TSUNAMI machine check NOT expected\n") ;
+	DBG_MCK(("tsunami_machine_check: vector=0x%lx la_ptr=0x%lx\n",
+	     vector, la_ptr));
+	DBG_MCK(("\t\t pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n",
+	     regs->pc, mchk_header->size, mchk_header->proc_offset,
+	     mchk_header->sys_offset));
+		TSUNAMI_mcheck_expected[cpu] = 0;
+		TSUNAMI_mcheck_taken[cpu] = 1;
+		mb();
+		mb();  /* magic */
+		draina();
+		tsunami_pci_clr_err();
+		wrmces(0x7);
+		mb();
+	}
+#endif
+#endif
+}
+
+#endif /* CONFIG_ALPHA_TSUNAMI */
diff -u --recursive --new-file v2.1.91/linux/arch/alpha/mm/fault.c linux/arch/alpha/mm/fault.c
--- v2.1.91/linux/arch/alpha/mm/fault.c	Sun Dec 21 15:29:54 1997
+++ linux/arch/alpha/mm/fault.c	Mon Mar 30 00:21:39 1998
@@ -14,13 +14,52 @@
 #include <linux/ptrace.h>
 #include <linux/mman.h>
 #include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
 
 #include <asm/system.h>
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
 #include <asm/mmu_context.h>
 
+#ifdef __SMP__
+unsigned long last_asn[NR_CPUS] = { /* gag */
+  ASN_FIRST_VERSION +  (0 << WIDTH_HARDWARE_ASN),
+  ASN_FIRST_VERSION +  (1 << WIDTH_HARDWARE_ASN),
+  ASN_FIRST_VERSION +  (2 << WIDTH_HARDWARE_ASN),
+  ASN_FIRST_VERSION +  (3 << WIDTH_HARDWARE_ASN),
+  ASN_FIRST_VERSION +  (4 << WIDTH_HARDWARE_ASN),
+  ASN_FIRST_VERSION +  (5 << WIDTH_HARDWARE_ASN),
+  ASN_FIRST_VERSION +  (6 << WIDTH_HARDWARE_ASN),
+  ASN_FIRST_VERSION +  (7 << WIDTH_HARDWARE_ASN),
+  ASN_FIRST_VERSION +  (8 << WIDTH_HARDWARE_ASN),
+  ASN_FIRST_VERSION +  (9 << WIDTH_HARDWARE_ASN),
+  ASN_FIRST_VERSION + (10 << WIDTH_HARDWARE_ASN),
+  ASN_FIRST_VERSION + (11 << WIDTH_HARDWARE_ASN),
+  ASN_FIRST_VERSION + (12 << WIDTH_HARDWARE_ASN),
+  ASN_FIRST_VERSION + (13 << WIDTH_HARDWARE_ASN),
+  ASN_FIRST_VERSION + (14 << WIDTH_HARDWARE_ASN),
+  ASN_FIRST_VERSION + (15 << WIDTH_HARDWARE_ASN),
+  ASN_FIRST_VERSION + (16 << WIDTH_HARDWARE_ASN),
+  ASN_FIRST_VERSION + (17 << WIDTH_HARDWARE_ASN),
+  ASN_FIRST_VERSION + (18 << WIDTH_HARDWARE_ASN),
+  ASN_FIRST_VERSION + (19 << WIDTH_HARDWARE_ASN),
+  ASN_FIRST_VERSION + (20 << WIDTH_HARDWARE_ASN),
+  ASN_FIRST_VERSION + (21 << WIDTH_HARDWARE_ASN),
+  ASN_FIRST_VERSION + (22 << WIDTH_HARDWARE_ASN),
+  ASN_FIRST_VERSION + (23 << WIDTH_HARDWARE_ASN),
+  ASN_FIRST_VERSION + (24 << WIDTH_HARDWARE_ASN),
+  ASN_FIRST_VERSION + (25 << WIDTH_HARDWARE_ASN),
+  ASN_FIRST_VERSION + (26 << WIDTH_HARDWARE_ASN),
+  ASN_FIRST_VERSION + (27 << WIDTH_HARDWARE_ASN),
+  ASN_FIRST_VERSION + (28 << WIDTH_HARDWARE_ASN),
+  ASN_FIRST_VERSION + (29 << WIDTH_HARDWARE_ASN),
+  ASN_FIRST_VERSION + (30 << WIDTH_HARDWARE_ASN),
+  ASN_FIRST_VERSION + (31 << WIDTH_HARDWARE_ASN)
+};
+#else
 unsigned long asn_cache = ASN_FIRST_VERSION;
+#endif /* __SMP__ */
 
 #ifndef BROKEN_ASN
 /*
@@ -30,7 +69,8 @@
  */
 void get_new_asn_and_reload(struct task_struct *tsk, struct mm_struct *mm)
 {
-	get_new_mmu_context(tsk, mm, asn_cache);
+	mm->context = 0;
+	get_new_mmu_context(tsk, mm);
 	reload_context(tsk);
 }
 #endif
@@ -84,6 +124,7 @@
 		}
 	}
 
+	lock_kernel();
 	down(&mm->mmap_sem);
 	vma = find_vma(mm, address);
 	if (!vma)
@@ -112,7 +153,7 @@
 	}
 	handle_mm_fault(current, vma, address, cause > 0);
 	up(&mm->mmap_sem);
-	return;
+	goto out;
 
 /*
  * Something tried to access memory that isn't in our memory map..
@@ -123,16 +164,17 @@
 
 	if (user_mode(regs)) {
 		force_sig(SIGSEGV, current);
-		return;
+		goto out;
 	}
 
 	/* Are we prepared to handle this fault as an exception?  */
 	if ((fixup = search_exception_table(regs->pc)) != 0) {
 		unsigned long newpc;
 		newpc = fixup_exception(dpf_reg, fixup, regs->pc);
-		printk("%s: Exception at [<%lx>] (%lx)\n", current->comm, regs->pc, newpc);
+		printk("%s: Exception at [<%lx>] (%lx)\n",
+		       current->comm, regs->pc, newpc);
 		regs->pc = newpc;
-		return;
+		goto out;
 	}
 
 /*
@@ -143,4 +185,7 @@
 	       "virtual address %016lx\n", address);
 	die_if_kernel("Oops", regs, cause, (unsigned long*)regs - 16);
 	do_exit(SIGKILL);
+ out:
+	unlock_kernel();
 }
+
diff -u --recursive --new-file v2.1.91/linux/arch/alpha/mm/init.c linux/arch/alpha/mm/init.c
--- v2.1.91/linux/arch/alpha/mm/init.c	Tue May 13 22:41:00 1997
+++ linux/arch/alpha/mm/init.c	Mon Mar 30 00:21:39 1998
@@ -26,6 +26,8 @@
 extern void die_if_kernel(char *,struct pt_regs *,long);
 extern void show_net_buffers(void);
 
+struct thread_struct * original_pcb_ptr;
+
 /*
  * BAD_PAGE is the page that is used for page faults when linux
  * is out-of-memory. Older versions of linux just did a
@@ -81,15 +83,22 @@
 
 extern unsigned long free_area_init(unsigned long, unsigned long);
 
-static void load_PCB(struct thread_struct * pcb)
+static struct thread_struct * load_PCB(struct thread_struct * pcb)
 {
+	struct thread_struct *old_pcb;
+
 	__asm__ __volatile__(
-		"stq $30,0(%0)\n\t"
-		"bis %0,%0,$16\n\t"
-		"call_pal %1"
-		: /* no outputs */
+		"stq $30,0(%1)\n\t"
+		"bis %1,%1,$16\n\t"
+#ifdef CONFIG_ALPHA_DP264
+		"zap $16,0xe0,$16\n\t"
+#endif /* DP264 */
+		"call_pal %2\n\t"
+		"bis $0,$0,%0"
+		: "=r" (old_pcb)
 		: "r" (pcb), "i" (PAL_swpctx)
 		: "$0", "$1", "$16", "$22", "$23", "$24", "$25");
+	return old_pcb;
 }
 
 /*
@@ -107,7 +116,8 @@
 	start_mem = free_area_init(start_mem, end_mem);
 
 	/* find free clusters, update mem_map[] accordingly */
-	memdesc = (struct memdesc_struct *) (INIT_HWRPB->mddt_offset + (unsigned long) INIT_HWRPB);
+	memdesc = (struct memdesc_struct *)
+		(INIT_HWRPB->mddt_offset + (unsigned long) INIT_HWRPB);
 	cluster = memdesc->cluster;
 	for (i = memdesc->numclusters ; i > 0; i--, cluster++) {
 		unsigned long pfn, nr;
@@ -129,15 +139,46 @@
 	memset((void *) ZERO_PAGE, 0, PAGE_SIZE);
 	memset(swapper_pg_dir, 0, PAGE_SIZE);
 	newptbr = ((unsigned long) swapper_pg_dir - PAGE_OFFSET) >> PAGE_SHIFT;
-	pgd_val(swapper_pg_dir[1023]) = (newptbr << 32) | pgprot_val(PAGE_KERNEL);
+	pgd_val(swapper_pg_dir[1023]) =
+		(newptbr << 32) | pgprot_val(PAGE_KERNEL);
 	init_task.tss.ptbr = newptbr;
 	init_task.tss.pal_flags = 1;	/* set FEN, clear everything else */
 	init_task.tss.flags = 0;
-	load_PCB(&init_task.tss);
+	original_pcb_ptr =
+	  phys_to_virt((unsigned long)load_PCB(&init_task.tss));
+#if 0
+printk("OKSP 0x%lx OPTBR 0x%lx\n",
+       original_pcb_ptr->ksp, original_pcb_ptr->ptbr);
+#endif
 
-	flush_tlb_all();
+	tbia();
 	return start_mem;
 }
+
+#ifdef __SMP__
+/*
+ * paging_init_secondary(), called ONLY by secondary CPUs,
+ * sets up current->tss contents appropriately and does a load_PCB.
+ * note that current should be pointing at the idle thread task struct
+ * for this CPU.
+ */
+void paging_init_secondary(void)
+{
+	current->tss.ptbr = init_task.tss.ptbr;
+	current->tss.pal_flags = 1;
+	current->tss.flags = 0;
+
+#if 0
+printk("paging_init_secondary: KSP 0x%lx PTBR 0x%lx\n",
+       current->tss.ksp, current->tss.ptbr);
+#endif
+
+	load_PCB(&current->tss);
+	tbia();
+
+	return;
+}
+#endif /* __SMP__ */
 
 void mem_init(unsigned long start_mem, unsigned long end_mem)
 {
diff -u --recursive --new-file v2.1.91/linux/arch/arm/config.in linux/arch/arm/config.in
--- v2.1.91/linux/arch/arm/config.in	Tue Mar 17 22:18:13 1998
+++ linux/arch/arm/config.in	Sun Mar 29 12:16:12 1998
@@ -105,17 +105,6 @@
 # fi
 # endmenu
 
-# Conditionally compile in the Uniform CD-ROM driver
-if [ "$CONFIG_BLK_DEV_IDECD" = "y" -o "$CONFIG_BLK_DEV_SR" = "y" ]; then
-  define_bool CONFIG_CDROM y
-else
-  if [ "$CONFIG_BLK_DEV_IDECD" = "m" -o "$CONFIG_BLK_DEV_SR" = "m" ]; then
-    define_bool CONFIG_CDROM m
-  else
-    define_bool CONFIG_CDROM n
-  fi
-fi
-
 source fs/Config.in
 
 source fs/nls/Config.in
diff -u --recursive --new-file v2.1.91/linux/arch/arm/defconfig linux/arch/arm/defconfig
--- v2.1.91/linux/arch/arm/defconfig	Tue Mar 17 22:18:13 1998
+++ linux/arch/arm/defconfig	Sun Mar 29 12:16:12 1998
@@ -169,7 +169,6 @@
 CONFIG_ETHER1=m
 CONFIG_ETHER3=m
 CONFIG_ETHERH=m
-CONFIG_CDROM=y
 
 #
 # Filesystems
diff -u --recursive --new-file v2.1.91/linux/arch/i386/Makefile linux/arch/i386/Makefile
--- v2.1.91/linux/arch/i386/Makefile	Tue Mar 17 22:18:13 1998
+++ linux/arch/i386/Makefile	Fri Mar 27 09:32:02 1998
@@ -93,6 +93,7 @@
 	@$(MAKEBOOT) BOOTIMAGE=bzImage install
 
 archclean:
+	rm -f .kernel_offset.lds
 	@$(MAKEBOOT) clean
 
 archdep:
diff -u --recursive --new-file v2.1.91/linux/arch/i386/boot/compressed/head.S linux/arch/i386/boot/compressed/head.S
--- v2.1.91/linux/arch/i386/boot/compressed/head.S	Mon Feb 23 18:12:02 1998
+++ linux/arch/i386/boot/compressed/head.S	Sun Mar 29 11:31:16 1998
@@ -37,10 +37,10 @@
 	cld
 	cli
 	movl $(__KERNEL_DS),%eax
-	mov %ax,%ds
-	mov %ax,%es
-	mov %ax,%fs
-	mov %ax,%gs
+	movl %ax,%ds
+	movl %ax,%es
+	movl %ax,%fs
+	movl %ax,%gs
 #ifdef __SMP__
 	orw %bx,%bx			# What state are we in BX=1 for SMP
 					# 0 for boot
diff -u --recursive --new-file v2.1.91/linux/arch/i386/config.in linux/arch/i386/config.in
--- v2.1.91/linux/arch/i386/config.in	Tue Mar 17 22:18:13 1998
+++ linux/arch/i386/config.in	Sun Mar 29 12:16:12 1998
@@ -114,17 +114,6 @@
 fi
 endmenu
 
-# Conditionally compile in the Uniform CD-ROM driver
-if [ "$CONFIG_BLK_DEV_IDECD" = "y" -o "$CONFIG_BLK_DEV_SR" = "y" -o "$CONFIG_SBPCD" = "y" -o "$CONFIG_MCD" = "y" -o "$CONFIG_MCDX" = "y" -o "$CONFIG_CM206" = "y" -o "$CONFIG_CDU31A" = "y" ]; then
-  define_bool CONFIG_CDROM y
-else
-  if [ "$CONFIG_BLK_DEV_IDECD" = "m" -o "$CONFIG_BLK_DEV_SR" = "m" -o "$CONFIG_SBPCD" = "m" -o "$CONFIG_MCD" = "m" -o "$CONFIG_MCDX" = "m" -o "$CONFIG_CM206" = "m" -o "$CONFIG_CDU31A" = "m" ]; then
-    define_bool CONFIG_CDROM m
-  else
-    define_bool CONFIG_CDROM n
-  fi
-fi
-
 source fs/Config.in
 
 source fs/nls/Config.in
diff -u --recursive --new-file v2.1.91/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v2.1.91/linux/arch/i386/defconfig	Tue Mar 17 22:18:13 1998
+++ linux/arch/i386/defconfig	Sun Mar 29 12:16:12 1998
@@ -214,7 +214,6 @@
 # CD-ROM drivers (not for SCSI or IDE/ATAPI drives)
 #
 # CONFIG_CD_NO_IDESCSI is not set
-CONFIG_CDROM=y
 
 #
 # Filesystems
diff -u --recursive --new-file v2.1.91/linux/arch/i386/kernel/entry.S linux/arch/i386/kernel/entry.S
--- v2.1.91/linux/arch/i386/kernel/entry.S	Thu Feb 12 20:56:04 1998
+++ linux/arch/i386/kernel/entry.S	Tue Mar 31 10:37:53 1998
@@ -81,8 +81,8 @@
 
 #define SAVE_ALL \
 	cld; \
-	push %es; \
-	push %ds; \
+	pushl %es; \
+	pushl %ds; \
 	pushl %eax; \
 	pushl %ebp; \
 	pushl %edi; \
@@ -91,8 +91,8 @@
 	pushl %ecx; \
 	pushl %ebx; \
 	movl $(__KERNEL_DS),%edx; \
-	mov %dx,%ds; \
-	mov %dx,%es;
+	movl %dx,%ds; \
+	movl %dx,%es;
 
 #define RESTORE_ALL \
 	popl %ebx; \
@@ -102,8 +102,8 @@
 	popl %edi; \
 	popl %ebp; \
 	popl %eax; \
-	pop %ds; \
-	pop %es; \
+	popl %ds; \
+	popl %es; \
 	addl $4,%esp; \
 	iret
 
@@ -231,7 +231,7 @@
 	pushl $ SYMBOL_NAME(do_divide_error)
 	ALIGN
 error_code:
-	push %ds
+	pushl %ds
 	pushl %eax
 	xorl %eax,%eax
 	pushl %ebp
@@ -241,17 +241,27 @@
 	decl %eax			# eax = -1
 	pushl %ecx
 	pushl %ebx
+#if 1
 	xorl %ecx,%ecx			# zero ecx
 	cld
 	mov %es,%cx			# get the lower order bits of es
+#else
+	cld
+# Some older processors leave the top 16 bits of the 32 bit destination
+# register undefined, rather than zeroed in the following instruction.
+# This won't matter when restoring or loading a segment register from the
+# stack.  It may be a problem if any code reads the full 32 bit value.
+# dosemu? kernel?  Would somebody like to verify that this way is really OK?
+	movl %es,%cx
+#endif
 	xchgl %eax, ORIG_EAX(%esp)	# orig_eax (get the error code. )
 	movl %esp,%edx
 	xchgl %ecx, ES(%esp)		# get the address and save es.
 	pushl %eax			# push the error code
 	pushl %edx
 	movl $(__KERNEL_DS),%edx
-	mov %dx,%ds
-	mov %dx,%es
+	movl %dx,%ds
+	movl %dx,%es
 	GET_CURRENT(%ebx)
 	call *%ecx
 	addl $8,%esp
@@ -533,6 +543,7 @@
 	.long SYMBOL_NAME(sys_pread)		/* 180 */
 	.long SYMBOL_NAME(sys_pwrite)
 	.long SYMBOL_NAME(sys_chown)
+	.long SYMBOL_NAME(sys_getcwd)
 	
 	.rept NR_syscalls-182
 		.long SYMBOL_NAME(sys_ni_syscall)
diff -u --recursive --new-file v2.1.91/linux/arch/i386/kernel/head.S linux/arch/i386/kernel/head.S
--- v2.1.91/linux/arch/i386/kernel/head.S	Tue Mar 17 22:18:13 1998
+++ linux/arch/i386/kernel/head.S	Sun Mar 29 11:31:16 1998
@@ -45,10 +45,10 @@
  */
 	cld
 	movl $(__KERNEL_DS),%eax
-	mov %ax,%ds
-	mov %ax,%es
-	mov %ax,%fs
-	mov %ax,%gs
+	movl %ax,%ds
+	movl %ax,%es
+	movl %ax,%fs
+	movl %ax,%gs
 #ifdef __SMP__
 	orw %bx,%bx
 	jz 1f
@@ -321,10 +321,10 @@
 	lidt idt_descr
 	ljmp $(__KERNEL_CS),$1f
 1:	movl $(__KERNEL_DS),%eax	# reload all the segment registers
-	mov %ax,%ds		# after changing gdt.
-	mov %ax,%es
-	mov %ax,%fs
-	mov %ax,%gs
+	movl %ax,%ds		# after changing gdt.
+	movl %ax,%es
+	movl %ax,%fs
+	movl %ax,%gs
 #ifdef __SMP__
 	movl $(__KERNEL_DS), %eax
 	mov  %ax,%ss		# Reload the stack pointer (segment only)
@@ -404,16 +404,16 @@
 	pushl %eax
 	pushl %ecx
 	pushl %edx
-	push %es
-	push %ds
+	pushl %es
+	pushl %ds
 	movl $(__KERNEL_DS),%eax
-	mov %ax,%ds
-	mov %ax,%es
+	movl %ax,%ds
+	movl %ax,%es
 	pushl $int_msg
 	call SYMBOL_NAME(printk)
 	popl %eax
-	pop %ds
-	pop %es
+	popl %ds
+	popl %es
 	popl %edx
 	popl %ecx
 	popl %eax
diff -u --recursive --new-file v2.1.91/linux/arch/i386/kernel/ioport.c linux/arch/i386/kernel/ioport.c
--- v2.1.91/linux/arch/i386/kernel/ioport.c	Tue Mar 17 22:18:13 1998
+++ linux/arch/i386/kernel/ioport.c	Wed Apr  1 14:51:41 1998
@@ -76,8 +76,6 @@
 	return 0;
 }
 
-unsigned int *stack;
-
 /*
  * sys_iopl has to be used when you want to access the IO ports
  * beyond the 0x3ff range: to get the full 65536 ports bitmapped
diff -u --recursive --new-file v2.1.91/linux/arch/i386/kernel/irq.h linux/arch/i386/kernel/irq.h
--- v2.1.91/linux/arch/i386/kernel/irq.h	Tue Mar 17 22:18:13 1998
+++ linux/arch/i386/kernel/irq.h	Sun Mar 29 11:31:16 1998
@@ -83,8 +83,8 @@
 
 #define SAVE_ALL \
 	"cld\n\t" \
-	"push %es\n\t" \
-	"push %ds\n\t" \
+	"pushl %es\n\t" \
+	"pushl %ds\n\t" \
 	"pushl %eax\n\t" \
 	"pushl %ebp\n\t" \
 	"pushl %edi\n\t" \
@@ -93,8 +93,8 @@
 	"pushl %ecx\n\t" \
 	"pushl %ebx\n\t" \
 	"movl $" STR(__KERNEL_DS) ",%edx\n\t" \
-	"mov %dx,%ds\n\t" \
-	"mov %dx,%es\n\t"
+	"movl %dx,%ds\n\t" \
+	"movl %dx,%es\n\t"
 
 #define IRQ_NAME2(nr) nr##_interrupt(void)
 #define IRQ_NAME(nr) IRQ_NAME2(IRQ##nr)
diff -u --recursive --new-file v2.1.91/linux/arch/i386/kernel/process.c linux/arch/i386/kernel/process.c
--- v2.1.91/linux/arch/i386/kernel/process.c	Thu Mar 26 15:57:02 1998
+++ linux/arch/i386/kernel/process.c	Sun Mar 29 11:31:16 1998
@@ -375,12 +375,12 @@
 	   registers don't have to be reloaded after switching to real mode:
 	   the values are consistent for real mode operation already. */
 
-	__asm__ __volatile__ ("movw $0x0010,%%ax\n"
-				"\tmovw %%ax,%%ds\n"
-				"\tmovw %%ax,%%es\n"
-				"\tmovw %%ax,%%fs\n"
-				"\tmovw %%ax,%%gs\n"
-				"\tmovw %%ax,%%ss" : : : "eax");
+	__asm__ __volatile__ ("movl $0x0010,%%eax\n"
+				"\tmovl %%ax,%%ds\n"
+				"\tmovl %%ax,%%es\n"
+				"\tmovl %%ax,%%fs\n"
+				"\tmovl %%ax,%%gs\n"
+				"\tmovl %%ax,%%ss" : : : "eax");
 
 	/* Jump to the 16-bit code that we copied earlier.  It disables paging
 	   and the cache, switches to real mode, and jumps to the BIOS reset
@@ -428,7 +428,7 @@
 	if (last_task_used_math == current)
 		last_task_used_math = NULL;
 	/* forget local segments */
-	__asm__ __volatile__("mov %w0,%%fs ; mov %w0,%%gs ; lldt %w0"
+	__asm__ __volatile__("movl %w0,%%fs ; movl %w0,%%gs ; lldt %w0"
 		: /* no outputs */
 		: "r" (0));
 	current->tss.ldt = 0;
@@ -583,8 +583,8 @@
 	dump->regs.eax = regs->eax;
 	dump->regs.ds = regs->xds;
 	dump->regs.es = regs->xes;
-	__asm__("mov %%fs,%0":"=r" (dump->regs.fs));
-	__asm__("mov %%gs,%0":"=r" (dump->regs.gs));
+	__asm__("movl %%fs,%0":"=r" (dump->regs.fs));
+	__asm__("movl %%gs,%0":"=r" (dump->regs.gs));
 	dump->regs.orig_eax = regs->orig_eax;
 	dump->regs.eip = regs->eip;
 	dump->regs.cs = regs->xcs;
diff -u --recursive --new-file v2.1.91/linux/arch/i386/kernel/signal.c linux/arch/i386/kernel/signal.c
--- v2.1.91/linux/arch/i386/kernel/signal.c	Thu Feb 12 20:56:04 1998
+++ linux/arch/i386/kernel/signal.c	Sun Mar 29 11:31:16 1998
@@ -199,7 +199,7 @@
 	      && (tmp & 0x4) != 0x4	/* not a LDT selector */	\
 	      && (tmp & 3) != 3)	/* not a RPL3 GDT selector */	\
 		  goto badframe;					\
-	  __asm__ __volatile__("mov %w0,%%" #seg : : "r"(tmp)); }
+	  __asm__ __volatile__("movl %w0,%%" #seg : : "r"(tmp)); }
 
 	GET_SEG(gs);
 	GET_SEG(fs);
@@ -337,9 +337,9 @@
 	unsigned int tmp;
 
 	tmp = 0;
-	__asm__("mov %%gs,%w0" : "=r"(tmp): "0"(tmp));
+	__asm__("movl %%gs,%w0" : "=r"(tmp): "0"(tmp));
 	__put_user(tmp, (unsigned int *)&sc->gs);
-	__asm__("mov %%fs,%w0" : "=r"(tmp): "0"(tmp));
+	__asm__("movl %%fs,%w0" : "=r"(tmp): "0"(tmp));
 	__put_user(tmp, (unsigned int *)&sc->fs);
 
 	__put_user(regs->xes, (unsigned int *)&sc->es);
@@ -427,7 +427,7 @@
 	regs->eip = (unsigned long) ka->sa.sa_handler;
 	{
 		unsigned long seg = __USER_DS;
-		__asm__("mov %w0,%%fs ; mov %w0,%%gs": "=r"(seg) : "0"(seg));
+		__asm__("movl %w0,%%fs ; movl %w0,%%gs": "=r"(seg) : "0"(seg));
 		set_fs(USER_DS);
 		regs->xds = seg;
 		regs->xes = seg;
@@ -492,7 +492,7 @@
 	regs->eip = (unsigned long) ka->sa.sa_handler;
 	{
 		unsigned long seg = __USER_DS;
-		__asm__("mov %w0,%%fs ; mov %w0,%%gs": "=r"(seg) : "0"(seg));
+		__asm__("movl %w0,%%fs ; movl %w0,%%gs": "=r"(seg) : "0"(seg));
 		set_fs(USER_DS);
 		regs->xds = seg;
 		regs->xes = seg;
diff -u --recursive --new-file v2.1.91/linux/arch/i386/kernel/traps.c linux/arch/i386/kernel/traps.c
--- v2.1.91/linux/arch/i386/kernel/traps.c	Mon Feb 23 18:12:02 1998
+++ linux/arch/i386/kernel/traps.c	Sun Mar 29 11:31:16 1998
@@ -68,19 +68,19 @@
 
 #define get_seg_byte(seg,addr) ({ \
 register unsigned char __res; \
-__asm__("push %%fs;mov %%ax,%%fs;movb %%fs:%2,%%al;pop %%fs" \
+__asm__("pushl %%fs;movl %%ax,%%fs;movb %%fs:%2,%%al;popl %%fs" \
 	:"=a" (__res):"0" (seg),"m" (*(addr))); \
 __res;})
 
 #define get_seg_long(seg,addr) ({ \
 register unsigned long __res; \
-__asm__("push %%fs;mov %%ax,%%fs;movl %%fs:%2,%%eax;pop %%fs" \
+__asm__("pushl %%fs;movl %%ax,%%fs;movl %%fs:%2,%%eax;popl %%fs" \
 	:"=a" (__res):"0" (seg),"m" (*(addr))); \
 __res;})
 
 #define _fs() ({ \
 register unsigned short __res; \
-__asm__("mov %%fs,%%ax":"=a" (__res):); \
+__asm__("movl %%fs,%%ax":"=a" (__res):); \
 __res;})
 
 void page_exception(void);
diff -u --recursive --new-file v2.1.91/linux/arch/i386/kernel/vm86.c linux/arch/i386/kernel/vm86.c
--- v2.1.91/linux/arch/i386/kernel/vm86.c	Sun Jan  4 10:55:08 1998
+++ linux/arch/i386/kernel/vm86.c	Sun Mar 29 11:31:16 1998
@@ -255,7 +255,7 @@
 		mark_screen_rdonly(tsk);
 	unlock_kernel();
 	__asm__ __volatile__(
-		"xorl %%eax,%%eax; mov %%ax,%%fs; mov %%ax,%%gs\n\t"
+		"xorl %%eax,%%eax; movl %%ax,%%fs; movl %%ax,%%gs\n\t"
 		"movl %0,%%esp\n\t"
 		"jmp ret_from_sys_call"
 		: /* no outputs */
diff -u --recursive --new-file v2.1.91/linux/arch/m68k/config.in linux/arch/m68k/config.in
--- v2.1.91/linux/arch/m68k/config.in	Thu Mar 26 15:57:02 1998
+++ linux/arch/m68k/config.in	Sun Mar 29 12:16:12 1998
@@ -269,17 +269,6 @@
 
 fi
 
-# Conditionally compile in the Uniform CD-ROM driver
-if [ "$CONFIG_BLK_DEV_IDECD" = "y" -o "$CONFIG_BLK_DEV_SR" = "y" ]; then
-  define_bool CONFIG_CDROM y
-else
-  if [ "$CONFIG_BLK_DEV_IDECD" = "m" -o "$CONFIG_BLK_DEV_SR" = "m" ]; then
-    define_bool CONFIG_CDROM m
-  else
-    define_bool CONFIG_CDROM n
-  fi
-fi
-
 source fs/Config.in
 
 if [ "$CONFIG_VME" = "n" ]; then
diff -u --recursive --new-file v2.1.91/linux/arch/ppc/config.in linux/arch/ppc/config.in
--- v2.1.91/linux/arch/ppc/config.in	Tue Mar 17 22:18:14 1998
+++ linux/arch/ppc/config.in	Sun Mar 29 12:16:12 1998
@@ -149,17 +149,6 @@
 fi
 endmenu
 
-# Conditionally compile in the Uniform CD-ROM driver
-if [ "$CONFIG_BLK_DEV_SR" = "y" -o "$CONFIG_SBPCD" = "y" -o "$CONFIG_MCD" = "y" -o "$CONFIG_CM206" = "y" -o "$CONFIG_CDU31A" = "y" ]; then
-  define_bool CONFIG_CDROM y
-else
-  if [ "$CONFIG_BLK_DEV_SR" = "m" -o "$CONFIG_SBPCD" = "m" -o "$CONFIG_MCD" = "m" -o "$CONFIG_CM206" = "m" -o "$CONFIG_CDU31A" = "m" ]; then
-    define_bool CONFIG_CDROM m
-  else
-    define_bool CONFIG_CDROM n
-  fi
-fi
-
 source fs/Config.in
 
 source fs/nls/Config.in
diff -u --recursive --new-file v2.1.91/linux/arch/sparc/config.in linux/arch/sparc/config.in
--- v2.1.91/linux/arch/sparc/config.in	Tue Mar 17 22:18:14 1998
+++ linux/arch/sparc/config.in	Sun Mar 29 12:16:12 1998
@@ -159,17 +159,6 @@
 	endmenu
 fi
 
-# Conditionally compile in the Uniform CD-ROM driver
-if [ "$CONFIG_BLK_DEV_SR" = "y" ]; then
-  define_bool CONFIG_CDROM y
-else
-  if [ "$CONFIG_BLK_DEV_SR" = "m" ]; then
-    define_bool CONFIG_CDROM m
-  else
-    define_bool CONFIG_CDROM n
-  fi
-fi
-
 source fs/Config.in
 
 source fs/nls/Config.in
diff -u --recursive --new-file v2.1.91/linux/arch/sparc64/config.in linux/arch/sparc64/config.in
--- v2.1.91/linux/arch/sparc64/config.in	Tue Mar 17 22:18:14 1998
+++ linux/arch/sparc64/config.in	Sun Mar 29 12:16:12 1998
@@ -218,17 +218,6 @@
 	endmenu
 fi
 
-# Conditionally compile in the Uniform CD-ROM driver
-if [ "$CONFIG_BLK_DEV_IDECD" = "y" -o "$CONFIG_BLK_DEV_SR" = "y" ]; then
-  define_bool CONFIG_CDROM y
-else
-  if [ "$CONFIG_BLK_DEV_IDECD" = "m" -o "$CONFIG_BLK_DEV_SR" = "m" ]; then
-    define_bool CONFIG_CDROM m
-  else
-    define_bool CONFIG_CDROM n
-  fi
-fi
-
 source fs/Config.in
 
 source fs/nls/Config.in
diff -u --recursive --new-file v2.1.91/linux/arch/sparc64/defconfig linux/arch/sparc64/defconfig
--- v2.1.91/linux/arch/sparc64/defconfig	Tue Mar 17 22:18:14 1998
+++ linux/arch/sparc64/defconfig	Sun Mar 29 12:16:12 1998
@@ -205,7 +205,6 @@
 CONFIG_SUNQE=m
 CONFIG_MYRI_SBUS=m
 CONFIG_DE4X5=y
-CONFIG_CDROM=y
 
 #
 # Filesystems
diff -u --recursive --new-file v2.1.91/linux/drivers/block/ide-cd.c linux/drivers/block/ide-cd.c
--- v2.1.91/linux/drivers/block/ide-cd.c	Tue Mar 17 22:18:14 1998
+++ linux/drivers/block/ide-cd.c	Sun Mar 29 12:16:12 1998
@@ -197,10 +197,14 @@
  *                         inform me of where "Illegal mode for this track"
  *                         was never returned due to a comparison on data
  *                         types of limited range.
+ * 4.12  Mar 29, 1998  -- Fixed bug in CDROM_SELECT_SPEED so write speed is 
+ *                         now set ionly for CD-R and CD-RW drives.  I had 
+ *                         removed this support because it produced errors.
+ *                         It produced errors _only_ for non-writers. duh.
  *
  *************************************************************************/
 
-#define IDECD_VERSION "4.11"
+#define IDECD_VERSION "4.12"
 
 #include <linux/module.h>
 #include <linux/types.h>
@@ -276,32 +280,34 @@
 		char buf[80];
 
 		printk ("ATAPI device %s:\n", drive->name);
-
-		printk ("  Error code: 0x%02x\n", reqbuf->error_code);
+		if (reqbuf->error_code==0x70)
+			printk("  Error: ");
+		else if (reqbuf->error_code==0x71)
+			printk("  Deferred Error: ");
+		else
+			printk("  Unknown Error Type: ");
 
 		if ( reqbuf->sense_key < ARY_LEN (sense_key_texts))
 			s = sense_key_texts[reqbuf->sense_key];
 		else
-			s = "(bad sense key)";
+			s = "bad sense key!";
 
-		printk ("  Sense key: 0x%02x - %s\n", reqbuf->sense_key, s);
+		printk ("%s -- (Sense key=0x%02x)\n", s, reqbuf->sense_key);
 
 		if (reqbuf->asc == 0x40) {
 			sprintf (buf, "Diagnostic failure on component 0x%02x",
 				 reqbuf->ascq);
 			s = buf;
 		} else {
-			int lo, hi;
+			int lo=0, mid, hi=ARY_LEN (sense_data_texts);
 			unsigned short key = (reqbuf->asc << 8);
 			if ( ! (reqbuf->ascq >= 0x80 && reqbuf->ascq <= 0xdd) )
 				key |= reqbuf->ascq;
 
-			lo = 0;
-			hi = ARY_LEN (sense_data_texts);
 			s = NULL;
 
 			while (hi > lo) {
-				int mid = (lo + hi) / 2;
+				mid = (lo + hi) / 2;
 				if (sense_data_texts[mid].asc_ascq == key) {
 					s = sense_data_texts[mid].text;
 					break;
@@ -320,14 +326,30 @@
 				s = "(reserved error code)";
 		}
 
-		printk ("  Additional sense data: 0x%02x, 0x%02x  - %s\n",
-			reqbuf->asc, reqbuf->ascq, s);
+		printk ("  %s -- (asc=0x%02x, ascq=0x%02x)\n",
+			s, reqbuf->asc, reqbuf->ascq);
 
 		if (failed_command != NULL) {
-			printk ("  Failed packet command: ");
+
+			int lo=0, mid, hi= ARY_LEN (packet_command_texts);
+			s = NULL;
+
+			while (hi > lo) {
+				mid = (lo + hi) / 2;
+				if (packet_command_texts[mid].packet_command == failed_command->c[0]) {
+					s = packet_command_texts[mid].text;
+					break;
+				}
+				else if (packet_command_texts[mid].packet_command > failed_command->c[0])
+					hi = mid;
+				else
+					lo = mid+1;
+			}
+
+			printk ("  The failed \"%s\" packet command was: \n\t\"", s);
 			for (i=0; i<sizeof (failed_command->c); i++)
 				printk ("%02x ", failed_command->c[i]);
-			printk ("\n");
+			printk ("\"\n");
 		}
 
 		if (reqbuf->sense_key == ILLEGAL_REQUEST &&
@@ -358,7 +380,7 @@
 						reqbuf->asc == 0x3a)))
 		return;
 
-	printk ("%s: code: 0x%02x  key: 0x%02x  asc: 0x%02x  ascq: 0x%02x\n",
+	printk ("%s: error code: 0x%02x  sense_key: 0x%02x  asc: 0x%02x  ascq: 0x%02x\n",
 		drive->name,
 		reqbuf->error_code, reqbuf->sense_key,
 		reqbuf->asc, reqbuf->ascq);
@@ -1486,6 +1508,9 @@
 {
 	struct packet_command pc;
 
+	if (CDROM_CONFIG_FLAGS (drive)->no_eject==1 && ejectflag==0)
+		return -EDRIVE_CANT_DO_THIS;
+
 	memset (&pc, 0, sizeof (pc));
 	pc.sense_data = reqbuf;
 
@@ -1747,16 +1772,7 @@
 }
 
 
-/* Note that this takes speed in kbytes/second, so don't try requesting
-   silly speeds like 2 here. Common speeds include:
-     176 kbytes/second  --  1x
-     353 kbytes/second  --  2x
-     387 kbytes/second  --  2.2x
-     528 kbytes/second  --  3x
-     706 kbytes/second  --  4x
-     1400 kbytes/second --  8x
-     2800 kbytes/second --  16x
-   ATAPI drives are free to select the speed you request or any slower
+/* ATAPI cdrom drives are free to select the speed you request or any slower
    rate :-( Requesting too fast a speed will _not_ produce an error. */
 static int
 cdrom_select_speed (ide_drive_t *drive, int speed,
@@ -1766,7 +1782,7 @@
 	memset (&pc, 0, sizeof (pc));
 	pc.sense_data = reqbuf;
 
-	if (speed < 1)
+	if (speed == 0)
 	    speed = 0xffff; /* set to max */
 	else
 	    speed *= 177;   /* Nx to kbytes/s */
@@ -1776,10 +1792,13 @@
 	pc.c[2] = (speed >> 8) & 0xff;	
 	/* Read Drive speed in kbytes/second LSB */
 	pc.c[3] = speed & 0xff;
-	/* Write Drive speed in kbytes/second MSB */
-	//pc.c[4] = (speed >> 8) & 0xff;	
-	/* Write Drive speed in kbytes/second LSB */
-	//pc.c[5] = speed & 0xff;
+	if ( CDROM_CONFIG_FLAGS(drive)->cd_r ||
+                   CDROM_CONFIG_FLAGS(drive)->cd_rw ) {
+		/* Write Drive speed in kbytes/second MSB */
+		pc.c[4] = (speed >> 8) & 0xff;
+		/* Write Drive speed in kbytes/second LSB */
+		pc.c[5] = speed & 0xff;
+       }
 
 	return cdrom_queue_packet_command (drive, &pc);
 }
@@ -2485,7 +2504,7 @@
 	if (stat<0)
 		return stat;
 
-	/* Now that that is done, update the speed fields */
+	/* Now with that done, update the speed fields */
         do {    /* we seem to get stat=0x01,err=0x00 the first time (??) */
                 if (attempts-- <= 0)
                         return 0;
@@ -2802,6 +2821,8 @@
 
 	if (buf.cap.lock == 0)
 		CDROM_CONFIG_FLAGS (drive)->no_doorlock = 1;
+	if (buf.cap.eject)
+		CDROM_CONFIG_FLAGS (drive)->no_eject = 0;
 	if (buf.cap.cd_r_write)
 		CDROM_CONFIG_FLAGS (drive)->cd_r = 1;
 	if (buf.cap.cd_rw_write)
@@ -2903,6 +2924,7 @@
 	CDROM_CONFIG_FLAGS (drive)->is_changer = 0;
 	CDROM_CONFIG_FLAGS (drive)->cd_r = 0;
 	CDROM_CONFIG_FLAGS (drive)->cd_rw = 0;
+	CDROM_CONFIG_FLAGS (drive)->no_eject = 1;
 	CDROM_CONFIG_FLAGS (drive)->supp_disc_present = 0;
 
 #if ! STANDARD_ATAPI
diff -u --recursive --new-file v2.1.91/linux/drivers/block/ide-cd.h linux/drivers/block/ide-cd.h
--- v2.1.91/linux/drivers/block/ide-cd.h	Tue Mar 17 22:18:14 1998
+++ linux/drivers/block/ide-cd.h	Sun Mar 29 12:16:12 1998
@@ -63,17 +63,24 @@
    (Some other cdrom-specific codes are in cdrom.h.) */
 #define TEST_UNIT_READY         0x00
 #define REQUEST_SENSE           0x03
+#define INQUIRY                 0x12
 #define START_STOP              0x1b
 #define ALLOW_MEDIUM_REMOVAL    0x1e
-#define READ_CAPACITY		0x25
+#define READ_CAPACITY           0x25
 #define READ_10                 0x28
 #define SEEK			0x2b
-#define MODE_SENSE_10           0x5a
+#define READ_HEADER             0x44
+#define STOP_PLAY_SCAN		0x4e
 #define MODE_SELECT_10          0x55
-#define READ_CD                 0xbe
-#define SET_CD_SPEED            0xbb
+#define MODE_SENSE_10           0x5a
 #define LOAD_UNLOAD             0xa6
+#define READ_12                 0xa8
+#define READ_CD_MSF             0xb9
+#define SCAN			0xba
+#define SET_CD_SPEED            0xbb
+#define PLAY_CD                 0xbc
 #define MECHANISM_STATUS        0xbd
+#define READ_CD                 0xbe
 
 
 /* Page codes for mode sense/set */
@@ -85,7 +92,7 @@
 #define PAGE_ALL                0x3f
 
 
-/* ATAPI sense keys (mostly copied from scsi.h). */
+/* ATAPI sense keys (from table 140 of ATAPI 2.6) */
 
 #define NO_SENSE                0x00
 #define RECOVERED_ERROR         0x01
@@ -109,6 +116,7 @@
 	__u8 drq_interrupt    : 1; /* Device sends an interrupt when ready
 				      for a packet command. */
 	__u8 no_doorlock      : 1; /* Drive cannot lock the door. */
+	__u8 no_eject         : 1; /* Drive cannot eject the disc. */
 	__u8 nec260           : 1; /* Drive is a pre-1.2 NEC 260 drive. */
 	__u8 playmsf_as_bcd   : 1; /* PLAYMSF command takes BCD args. */
 	__u8 tocaddr_as_bcd   : 1; /* TOC addresses are in BCD. */
@@ -424,6 +432,39 @@
 	"(reserved)",
 	"Miscompare",
 	"(reserved)",
+};
+
+
+/* From Table 37 of the ATAPI 2.6 draft standard. */
+struct {
+	unsigned short packet_command;
+	char *text;
+} packet_command_texts[] = {
+	{ TEST_UNIT_READY, "Test Unit Ready" },
+	{ REQUEST_SENSE, "Request Sense" },
+	{ INQUIRY, "Inquiry" },
+	{ START_STOP, "Start Stop Unit" },
+	{ ALLOW_MEDIUM_REMOVAL, "Prevent/Allow Medium Removal" },
+	{ READ_CAPACITY, "Read CD-ROM Capacity" },
+	{ READ_10, "Read(10)" },
+	{ SEEK, "Seek" },
+	{ SCMD_READ_TOC, "Read TOC" },
+	{ SCMD_READ_SUBCHANNEL, "Read Sub-Channel" },
+	{ READ_HEADER, "Read Header" },
+	{ STOP_PLAY_SCAN, "Stop Play/Scan" },
+	{ SCMD_PLAYAUDIO10, "Play Audio" },
+	{ SCMD_PLAYAUDIO_MSF, "Play Audio MSF" },
+	{ SCMD_PAUSE_RESUME, "Pause/Resume" },
+	{ MODE_SELECT_10, "Mode Select" },
+	{ MODE_SENSE_10, "Mode Sense" },
+	{ LOAD_UNLOAD, "Load/Unload CD" },
+	{ READ_12, "Read(12)" },
+	{ READ_CD_MSF, "Read CD MSF" },
+	{ SCAN, "Scan" },
+	{ SET_CD_SPEED, "Set CD Speed" },
+	{ PLAY_CD, "Play CD" },
+	{ MECHANISM_STATUS, "Mechanism Status" },
+	{ READ_CD, "Read CD" },
 };
 
 
diff -u --recursive --new-file v2.1.91/linux/drivers/block/ide-disk.c linux/drivers/block/ide-disk.c
--- v2.1.91/linux/drivers/block/ide-disk.c	Tue Mar 17 22:18:14 1998
+++ linux/drivers/block/ide-disk.c	Mon Mar 30 14:25:11 1998
@@ -496,25 +496,13 @@
 		drive->special.b.set_multmode = 1;
 }
 
-static int proc_idedisk_read_cache
-	(char *page, char **start, off_t off, int count, int *eof, void *data)
-{
-	ide_drive_t	*drive = (ide_drive_t *) data;
-	char		*out = page;
-	int		len;
-
-	if (drive->id)
-		len = sprintf(out,"%i\n", drive->id->buf_size / 2);
-	else
-		len = sprintf(out,"(none)\n");
-	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
-}
-
 static int smart_enable(ide_drive_t *drive)
 {
 	return ide_wait_cmd(drive, WIN_SMART, 0, SMART_ENABLE, 0, NULL);
 }
 
+#ifdef CONFIG_PROC_FS
+
 static int get_smart_values(ide_drive_t *drive, byte *buf)
 {
 	(void) smart_enable(drive);
@@ -527,6 +515,20 @@
 	return ide_wait_cmd(drive, WIN_SMART, 0, SMART_READ_THRESHOLDS, 1, buf);
 }
 
+static int proc_idedisk_read_cache
+	(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+	ide_drive_t	*drive = (ide_drive_t *) data;
+	char		*out = page;
+	int		len;
+
+	if (drive->id)
+		len = sprintf(out,"%i\n", drive->id->buf_size / 2);
+	else
+		len = sprintf(out,"(none)\n");
+	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
 static int proc_idedisk_read_smart_thresholds
 	(char *page, char **start, off_t off, int count, int *eof, void *data)
 {
@@ -566,12 +568,18 @@
 }
 
 static ide_proc_entry_t idedisk_proc[] = {
-	{ "cache", proc_idedisk_read_cache, NULL },
-	{ "geometry", proc_ide_read_geometry, NULL },
-	{ "smart_values", proc_idedisk_read_smart_values, NULL },
-	{ "smart_thresholds", proc_idedisk_read_smart_thresholds, NULL },
-	{ NULL, NULL, NULL }
+	{ "cache",		S_IFREG|S_IRUGO,	proc_idedisk_read_cache,		NULL },
+	{ "geometry",		S_IFREG|S_IRUGO,	proc_ide_read_geometry,			NULL },
+	{ "smart_values",	S_IFREG|S_IRUSR,	proc_idedisk_read_smart_values,		NULL },
+	{ "smart_thresholds",	S_IFREG|S_IRUSR,	proc_idedisk_read_smart_thresholds,	NULL },
+	{ NULL, 0, NULL, NULL }
 };
+
+#else
+
+#define	idedisk_proc	NULL
+
+#endif	/* CONFIG_PROC_FS */
 
 static int set_multcount(ide_drive_t *drive, int arg)
 {
diff -u --recursive --new-file v2.1.91/linux/drivers/block/ide-floppy.c linux/drivers/block/ide-floppy.c
--- v2.1.91/linux/drivers/block/ide-floppy.c	Sat Jan 10 10:42:55 1998
+++ linux/drivers/block/ide-floppy.c	Mon Mar 30 14:25:11 1998
@@ -1379,10 +1379,18 @@
 	return 0;
 }
 
+#ifdef CONFIG_PROC_FS
+
 static ide_proc_entry_t idefloppy_proc[] = {
-	{ "geometry", proc_ide_read_geometry, NULL },
-	{ NULL, NULL, NULL }
+	{ "geometry",	S_IFREG|S_IRUGO,	proc_ide_read_geometry,	NULL },
+	{ NULL, 0, NULL, NULL }
 };
+
+#else
+
+#define	idefloppy_proc	NULL
+
+#endif	/* CONFIG_PROC_FS */
 
 /*
  *	IDE subdriver functions, registered with ide.c
diff -u --recursive --new-file v2.1.91/linux/drivers/block/ide-pci.c linux/drivers/block/ide-pci.c
--- v2.1.91/linux/drivers/block/ide-pci.c	Tue Mar 17 22:18:14 1998
+++ linux/drivers/block/ide-pci.c	Sun Mar 29 12:24:05 1998
@@ -174,9 +174,13 @@
 	 * just in case there's another interface yet-to-be-scanned
 	 * which uses ports 1f0/170 (the ide0/ide1 defaults).
 	 */
-	for (h = 0; h < MAX_HWIFS; ++h) {
-		int hwifs[] = {2,3,1,0}; /* assign 3rd/4th before 1st/2nd */
-		hwif = &ide_hwifs[hwifs[h]];
+	for (h = 2; h < MAX_HWIFS; ++h) {
+		hwif = ide_hwifs + h;
+		if (hwif->chipset == ide_unknown)
+			return hwif;	/* pick an unused entry */
+	}
+	for (h = 0; h < 2; ++h) {
+		hwif = ide_hwifs + h;
 		if (hwif->chipset == ide_unknown)
 			return hwif;	/* pick an unused entry */
 	}
@@ -366,8 +370,10 @@
 		 * workaround Intel Advanced/ZP with bios <= 1.04;
 		 * these appear in some Dell Dimension XPS's 
 		 */
-		if (!hedt && IDE_PCI_DEVID_EQ(devid, DEVID_PIIXa))
+		if (!hedt && IDE_PCI_DEVID_EQ(devid, DEVID_PIIXa)) {
+			printk("ide: implementing workaround for PIIX detection\n");
 		        hedt = 0x80;
+		}
 
 		for (d = ide_pci_chipsets; d->devid.vid && !IDE_PCI_DEVID_EQ(d->devid, devid); ++d);
 		if (d->init_hwif == IDE_IGNORE)
@@ -382,7 +388,7 @@
 				printk("%s: IDE controller on PCI bus %d function %d\n", d->name, bus, fn);
 			ide_setup_pci_device(bus, fn, ccode, d);
 		}
-	} while (hedt == 0x80 && (++fn & 7));
+	} while ((hedt & 0x80) && (++fn & 7));
 }
 
 /*
diff -u --recursive --new-file v2.1.91/linux/drivers/block/ide-probe.c linux/drivers/block/ide-probe.c
--- v2.1.91/linux/drivers/block/ide-probe.c	Sat Jan 10 10:42:55 1998
+++ linux/drivers/block/ide-probe.c	Sun Mar 29 12:24:05 1998
@@ -662,6 +662,12 @@
 #if MAX_HWIFS > 3
 	case IDE3_MAJOR: rfn = &do_ide3_request; break;
 #endif
+#if MAX_HWIFS > 4
+	case IDE4_MAJOR: rfn = &do_ide4_request; break;
+#endif
+#if MAX_HWIFS > 5
+	case IDE5_MAJOR: rfn = &do_ide5_request; break;
+#endif
 	default:
 		printk("%s: request_fn NOT DEFINED\n", hwif->name);
 		return (hwif->present = 0);
diff -u --recursive --new-file v2.1.91/linux/drivers/block/ide-proc.c linux/drivers/block/ide-proc.c
--- v2.1.91/linux/drivers/block/ide-proc.c	Tue Mar 10 10:03:31 1998
+++ linux/drivers/block/ide-proc.c	Mon Mar 30 14:25:11 1998
@@ -73,8 +73,6 @@
 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
 #endif
 
-#ifdef CONFIG_PCI
-
 static int ide_getxdigit(char c)
 {
 	int digit;
@@ -125,7 +123,7 @@
 	}
 	/*
 	 * Do one full pass to verify all parameters,
-	 * then do another to actually write the pci regs.
+	 * then do another to actually write the regs.
 	 */
 	save_flags(flags);
 	do {
@@ -136,11 +134,11 @@
 			ide_hwgroup_t *mategroup = NULL;
 			if (hwif->mate && hwif->mate->hwgroup)
 				mategroup = (ide_hwgroup_t *)(hwif->mate->hwgroup);
-			cli();	/* ensure all PCI writes are done together */
+			cli();	/* ensure all writes are done together */
 			while (mygroup->active || (mategroup && mategroup->active)) {
 				restore_flags(flags);
 				if (0 < (signed long)(jiffies - timeout)) {
-					printk("/proc/ide/%s/pci: channel(s) busy, cannot write\n", hwif->name);
+					printk("/proc/ide/%s/config: channel(s) busy, cannot write\n", hwif->name);
 					return -EBUSY;
 				}
 				cli();
@@ -157,7 +155,12 @@
 				case 'R':	is_pci = 0;
 						break;
 				case 'P':	is_pci = 1;
-						break;
+#ifdef CONFIG_BLK_DEV_IDEPCI
+						if (!IDE_PCI_DEVID_EQ(hwif->pci_devid, IDE_PCI_DEVID_NULL))
+							break;
+#endif	/* CONFIG_BLK_DEV_IDEPCI */
+						msg = "not a PCI device";
+						goto parse_error;
 				default:	msg = "expected 'R' or 'P'";
 						goto parse_error;
 			}
@@ -195,15 +198,18 @@
 				--n;
 				++p;
 			}
+#ifdef CONFIG_BLK_DEV_IDEPCI
 			if (is_pci && (reg & ((digits >> 1) - 1))) {
 				msg = "misaligned access";
 				goto parse_error;
 			}
+#endif	/* CONFIG_BLK_DEV_IDEPCI */
 			if (for_real) {
 #if 0
 				printk("proc_ide_write_config: type=%c, reg=0x%x, val=0x%x, digits=%d\n", is_pci ? 'PCI' : 'non-PCI', reg, val, digits);
 #endif
 				if (is_pci) {
+#ifdef CONFIG_BLK_DEV_IDEPCI
 					int rc = 0;
 					switch (digits) {
 						case 2:	msg = "byte";
@@ -223,6 +229,7 @@
 						printk("proc_ide_write_config: %s\n", pcibios_strerror(rc));
 						return -EIO;
 					}
+#endif	/* CONFIG_BLK_DEV_IDEPCI */
 				} else {	/* not pci */
 					switch (digits) {
 						case 2:	outb(val, reg);
@@ -251,7 +258,8 @@
 	char		*out = page;
 	int		len, reg = 0;
 
-	out += sprintf(out, "pci bus %d function %d vendor %04x device %04x channel %d\n",
+#ifdef CONFIG_BLK_DEV_IDEPCI
+	out += sprintf(out, "pci bus %d device %d vid %04x did %04x channel %d\n",
 		hwif->pci_bus, hwif->pci_fn, hwif->pci_devid.vid, hwif->pci_devid.did, hwif->channel);
 	do {
 		byte val;
@@ -265,20 +273,13 @@
 		} else
 			out += sprintf(out, "%02x%c", val, (++reg & 0xf) ? ' ' : '\n');
 	} while (reg < 0x100);
+#else	/* CONFIG_BLK_DEV_IDEPCI */
+	out += sprintf(out, "(none)\n");
+#endif	/* CONFIG_BLK_DEV_IDEPCI */
 	len = out - page;
 	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
 }
 
-static int proc_ide_read_imodel
-	(char *page, char **start, off_t off, int count, int *eof, void *data)
-{
-	ide_hwif_t	*hwif = (ide_hwif_t *) data;
-	int		len;
-
-	len = sprintf(page,"%04x: %04x\n", hwif->pci_devid.vid, hwif->pci_devid.did);
-	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
-}
-#endif	/* CONFIG_PCI */
 
 static int ide_getdigit(char c)
 {
@@ -308,7 +309,7 @@
 	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
 }
 
-static int proc_ide_read_type
+static int proc_ide_read_imodel
 	(char *page, char **start, off_t off, int count, int *eof, void *data)
 {
 	ide_hwif_t	*hwif = (ide_hwif_t *) data;
@@ -341,7 +342,10 @@
 	ide_hwif_t	*hwif = (ide_hwif_t *) data;
 	int		len;
 
-	len = sprintf(page, "%s\n", hwif->mate->name);
+	if (hwif && hwif->mate && hwif->mate->present)
+		len = sprintf(page, "%s\n", hwif->mate->name);
+	else
+		len = sprintf(page, "(none)\n");
 	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
 }
 
@@ -426,7 +430,6 @@
 
 	if (!suser())
 		return -EACCES;
-
 	/*
 	 * Skip over leading whitespace
 	 */
@@ -447,11 +450,11 @@
 			ide_hwgroup_t *mategroup = NULL;
 			if (hwif->mate && hwif->mate->hwgroup)
 				mategroup = (ide_hwgroup_t *)(hwif->mate->hwgroup);
-			cli();	/* ensure all PCI writes are done together */
+			cli();	/* ensure all writes are done together */
 			while (mygroup->active || (mategroup && mategroup->active)) {
 				restore_flags(flags);
 				if (0 < (signed long)(jiffies - timeout)) {
-					printk("/proc/ide/%s/pci: channel(s) busy, cannot write\n", hwif->name);
+					printk("/proc/ide/%s/settings: channel(s) busy, cannot write\n", drive->name);
 					return -EBUSY;
 				}
 				cli();
@@ -517,9 +520,9 @@
 	int		len;
 
 	if (!driver)
-	    len = sprintf(page, "(none)\n");
+		len = sprintf(page, "(none)\n");
         else
-	    len = sprintf(page,"%li\n", ((ide_driver_t *)drive->driver)->capacity(drive));
+		len = sprintf(page,"%li\n", ((ide_driver_t *)drive->driver)->capacity(drive));
 	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
 }
 
@@ -598,40 +601,37 @@
 }
 
 static ide_proc_entry_t generic_drive_entries[] = {
-	{ "driver", proc_ide_read_driver, proc_ide_write_driver },
-	{ "identify", proc_ide_read_identify, NULL },
-	{ "media", proc_ide_read_media, NULL },
-	{ "model", proc_ide_read_dmodel, NULL },
-	{ "settings", proc_ide_read_settings, proc_ide_write_settings },
-	{ NULL, NULL, NULL }
+	{ "driver",	S_IFREG|S_IRUGO,	proc_ide_read_driver,	proc_ide_write_driver },
+	{ "identify",	S_IFREG|S_IRUSR,	proc_ide_read_identify,	NULL },
+	{ "media",	S_IFREG|S_IRUGO,	proc_ide_read_media,	NULL },
+	{ "model",	S_IFREG|S_IRUGO,	proc_ide_read_dmodel,	NULL },
+	{ "settings",	S_IFREG|S_IRUSR|S_IWUSR,proc_ide_read_settings,	proc_ide_write_settings },
+	{ NULL,	0, NULL, NULL }
 };
 
-void ide_add_proc_entries(ide_drive_t *drive, ide_proc_entry_t *p)
+void ide_add_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p, void *data)
 {
 	struct proc_dir_entry *ent;
 
-	if (!drive->proc || !p)
+	if (!dir || !p)
 		return;
 	while (p->name != NULL) {
-		mode_t mode = S_IFREG|S_IRUSR;
-		if (!strcmp(p->name,"settings"))
-			mode |= S_IWUSR;
-		ent = create_proc_entry(p->name, mode, drive->proc);
+		ent = create_proc_entry(p->name, p->mode, dir);
 		if (!ent) return;
 		ent->nlink = 1;
-		ent->data = drive;
+		ent->data = data;
 		ent->read_proc = p->read_proc;
 		ent->write_proc = p->write_proc;
 		p++;
 	}
 }
 
-void ide_remove_proc_entries(ide_drive_t *drive, ide_proc_entry_t *p)
+void ide_remove_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p)
 {
-	if (!drive->proc || !p)
+	if (!dir || !p)
 		return;
 	while (p->name != NULL) {
-		remove_proc_entry(p->name, drive->proc);
+		remove_proc_entry(p->name, dir);
 		p++;
 	}
 }
@@ -654,7 +654,7 @@
 			continue;
 		drive->proc = create_proc_entry(drive->name, S_IFDIR, parent);
 		if (drive->proc)
-			ide_add_proc_entries(drive, generic_drive_entries);
+			ide_add_proc_entries(drive->proc, generic_drive_entries, drive);
 
 		ent = create_proc_entry(drive->name, S_IFLNK | S_IRUGO | S_IWUGO | S_IXUGO, root);
 		if (!ent) return;
@@ -664,10 +664,18 @@
 	}
 }
 
+static ide_proc_entry_t hwif_entries[] = {
+	{ "channel",	S_IFREG|S_IRUGO,	proc_ide_read_channel,	NULL },
+	{ "config",	S_IFREG|S_IRUGO|S_IWUSR,proc_ide_read_config,	proc_ide_write_config },
+	{ "mate",	S_IFREG|S_IRUGO,	proc_ide_read_mate,	NULL },
+	{ "model",	S_IFREG|S_IRUGO,	proc_ide_read_imodel,	NULL },
+	{ NULL,	0, NULL, NULL }
+};
+
 static void create_proc_ide_interfaces (struct proc_dir_entry *parent)
 {
 	int	h;
-	struct proc_dir_entry *hwif_ent, *ent;
+	struct proc_dir_entry *hwif_ent;
 
 	for (h = 0; h < MAX_HWIFS; h++) {
 		ide_hwif_t *hwif = &ide_hwifs[h];
@@ -676,43 +684,12 @@
 			continue;
 		hwif_ent = create_proc_entry(hwif->name, S_IFDIR, parent);
 		if (!hwif_ent) return;
-#ifdef CONFIG_PCI
-		if (!IDE_PCI_DEVID_EQ(hwif->pci_devid, IDE_PCI_DEVID_NULL)) {
-			ent = create_proc_entry("config", S_IFREG|S_IRUSR|S_IWUSR, hwif_ent);
-			if (!ent) return;
-			ent->nlink = 1;
-			ent->data = hwif;
-			ent->read_proc  = proc_ide_read_config;
-			ent->write_proc = proc_ide_write_config;;
-
-			ent = create_proc_entry("model", 0, hwif_ent);
-			if (!ent) return;
-			ent->data = hwif;
-			ent->read_proc  = proc_ide_read_imodel;
-		}
-#endif	/* CONFIG_PCI */
-		ent = create_proc_entry("channel", 0, hwif_ent);
-		if (!ent) return;
-		ent->data = hwif;
-		ent->read_proc  = proc_ide_read_channel;
-
-		if (hwif->mate && hwif->mate->present) {
-			ent = create_proc_entry("mate", 0, hwif_ent);
-			if (!ent) return;
-			ent->data = hwif;
-			ent->read_proc  = proc_ide_read_mate;
-		}
-
-		ent = create_proc_entry("type", 0, hwif_ent);
-		if (!ent) return;
-		ent->data = hwif;
-		ent->read_proc  = proc_ide_read_type;
-
+		ide_add_proc_entries(hwif_ent, hwif_entries, hwif);
 		create_proc_ide_drives(hwif, hwif_ent, parent);
 	}
 }
 
-void proc_ide_init(void)
+void proc_ide_create(void)
 {
 	struct proc_dir_entry *root, *ent;
 	root = create_proc_entry("ide", S_IFDIR, 0);
@@ -722,4 +699,14 @@
 	ent = create_proc_entry("drivers", 0, root);
 	if (!ent) return;
 	ent->read_proc  = proc_ide_read_drivers;
+}
+
+void proc_ide_destroy(void)
+{
+	/*
+	 * Mmmm.. does this free up all resources,
+	 * or do we need to do a more proper cleanup here ??
+	 */
+	remove_proc_entry("ide/drivers", 0);
+	remove_proc_entry("ide", 0);
 }
diff -u --recursive --new-file v2.1.91/linux/drivers/block/ide-tape.c linux/drivers/block/ide-tape.c
--- v2.1.91/linux/drivers/block/ide-tape.c	Sat Jan 10 10:42:55 1998
+++ linux/drivers/block/ide-tape.c	Mon Mar 30 14:25:11 1998
@@ -3640,6 +3640,8 @@
 	return 0;
 }
 
+#ifdef CONFIG_PROC_FS
+
 static int proc_idetape_read_name
 	(char *page, char **start, off_t off, int count, int *eof, void *data)
 {
@@ -3653,9 +3655,15 @@
 }
 
 static ide_proc_entry_t idetape_proc[] = {
-	{ "name", proc_idetape_read_name, NULL },
-	{ NULL, NULL, NULL }
+	{ "name",	S_IFREG|S_IRUGO,	proc_idetape_read_name,	NULL },
+	{ NULL, 0, NULL, NULL }
 };
+
+#else
+
+#define	idetape_proc	NULL
+
+#endif
 
 /*
  *	IDE subdriver functions, registered with ide.c
diff -u --recursive --new-file v2.1.91/linux/drivers/block/ide.c linux/drivers/block/ide.c
--- v2.1.91/linux/drivers/block/ide.c	Tue Mar 17 22:18:14 1998
+++ linux/drivers/block/ide.c	Mon Mar 30 14:25:11 1998
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/ide.c	Version 6.12  January  2, 1998
+ *  linux/drivers/block/ide.c	Version 6.13  March 29, 1998
  *
  *  Copyright (C) 1994-1998  Linus Torvalds & authors (see below)
  */
@@ -10,29 +10,14 @@
  *            and Gadi Oxman <gadio@netvision.net.il>
  *
  * This is the multiple IDE interface driver, as evolved from hd.c.
- * It supports up to four IDE interfaces, on one or more IRQs (usually 14 & 15).
+ * It supports up to MAX_HWIFS IDE interfaces, on one or more IRQs (usually 14 & 15).
  * There can be up to two drives per interface, as per the ATA-2 spec.
  *
  * Primary:    ide0, port 0x1f0; major=3;  hda is minor=0; hdb is minor=64
  * Secondary:  ide1, port 0x170; major=22; hdc is minor=0; hdd is minor=64
  * Tertiary:   ide2, port 0x???; major=33; hde is minor=0; hdf is minor=64
  * Quaternary: ide3, port 0x???; major=34; hdg is minor=0; hdh is minor=64
- *
- * It is easy to extend ide.c to handle more than four interfaces:
- *
- *	Change the MAX_HWIFS constant in ide.h.
- *
- *	Define some new major numbers (in major.h), and insert them into
- *	the ide_hwif_to_major table in ide.c.
- *
- *	Fill in the extra values for the new interfaces into the two tables
- *	inside ide.c:  default_io_base[]  and  default_irqs[].
- *
- *	Create the new request handlers by cloning "do_ide3_request()"
- *	for each new interface, and add them to the switch statement
- *	in the ide_init() function in ide.c.
- *
- *	Recompile, create the new /dev/ entries, and it will probably work.
+ * ...
  *
  *  From hd.c:
  *  |
@@ -100,6 +85,7 @@
  *			mask all hwgroup interrupts on each irq entry
  * Version 6.12		integrate ioctl and proc interfaces
  *			fix parsing of "idex=" command line parameter
+ * Version 6.13		add support for ide4/ide5 courtesy rjones@orchestream.com
  *
  *  Some additional driver compile-time options are in ide.h
  *
@@ -138,7 +124,7 @@
 #include <linux/kmod.h>
 #endif /* CONFIG_KMOD */
 
-static const byte	ide_hwif_to_major[] = {IDE0_MAJOR, IDE1_MAJOR, IDE2_MAJOR, IDE3_MAJOR};
+static const byte	ide_hwif_to_major[] = {IDE0_MAJOR, IDE1_MAJOR, IDE2_MAJOR, IDE3_MAJOR, IDE4_MAJOR, IDE5_MAJOR };
 
 static int	idebus_parameter; /* holds the "idebus=" parameter */
 static int	system_bus_speed; /* holds what we think is VESA/PCI bus speed */
@@ -1183,6 +1169,20 @@
 }
 #endif /* MAX_HWIFS > 3 */
 
+#if MAX_HWIFS > 4
+void do_ide4_request (void)	/* invoked with cli() */
+{
+	do_hwgroup_request (ide_hwifs[4].hwgroup);
+}
+#endif /* MAX_HWIFS > 4 */
+
+#if MAX_HWIFS > 5
+void do_ide5_request (void)	/* invoked with cli() */
+{
+	do_hwgroup_request (ide_hwifs[5].hwgroup);
+}
+#endif /* MAX_HWIFS > 5 */
+
 void ide_timer_expiry (unsigned long data)
 {
 	ide_hwgroup_t *hwgroup = (ide_hwgroup_t *) data;
@@ -2575,7 +2575,7 @@
 #endif /* CONFIG_BLK_DEV_IDE */
 
 #ifdef CONFIG_PROC_FS
-	proc_ide_init();
+	proc_ide_create();
 #endif
 
 	/*
@@ -2691,10 +2691,12 @@
 	return NULL;
 }
 
+#ifdef CONFIG_PROC_FS
 static ide_proc_entry_t generic_subdriver_entries[] = {
-	{ "capacity", proc_ide_read_capacity, NULL },
-	{ NULL, NULL, NULL }
+	{ "capacity",	S_IFREG|S_IRUGO,	proc_ide_read_capacity,	NULL },
+	{ NULL, 0, NULL, NULL }
 };
+#endif
 
 int ide_register_subdriver (ide_drive_t *drive, ide_driver_t *driver, int version)
 {
@@ -2717,8 +2719,10 @@
 		drive->nice1 = 1;
 	}
 	drive->revalidate = 1;
-	ide_add_proc_entries(drive, generic_subdriver_entries);
-	ide_add_proc_entries(drive, driver->proc);
+#ifdef CONFIG_PROC_FS
+	ide_add_proc_entries(drive->proc, generic_subdriver_entries, drive);
+	ide_add_proc_entries(drive->proc, driver->proc, drive);
+#endif
 	return 0;
 }
 
@@ -2732,8 +2736,10 @@
 		__restore_flags(flags);
 		return 1;
 	}
-	ide_remove_proc_entries(drive, DRIVER(drive)->proc);
-	ide_remove_proc_entries(drive, generic_subdriver_entries);
+#ifdef CONFIG_PROC_FS
+	ide_remove_proc_entries(drive->proc, DRIVER(drive)->proc);
+	ide_remove_proc_entries(drive->proc, generic_subdriver_entries);
+#endif
 	auto_remove_settings(drive);
 	drive->driver = NULL;
 	__restore_flags(flags);
@@ -2803,6 +2809,12 @@
 #if MAX_HWIFS > 3
 EXPORT_SYMBOL(do_ide3_request);
 #endif /* MAX_HWIFS > 3 */
+#if MAX_HWIFS > 4
+EXPORT_SYMBOL(do_ide4_request);
+#endif /* MAX_HWIFS > 4 */
+#if MAX_HWIFS > 5
+EXPORT_SYMBOL(do_ide5_request);
+#endif /* MAX_HWIFS > 5 */
 
 /*
  * Driver module
@@ -2828,11 +2840,13 @@
 EXPORT_SYMBOL(ide_cmd);
 EXPORT_SYMBOL(ide_wait_cmd);
 EXPORT_SYMBOL(ide_stall_queue);
+#ifdef CONFIG_PROC_FS
 EXPORT_SYMBOL(ide_add_proc_entries);
 EXPORT_SYMBOL(ide_remove_proc_entries);
+EXPORT_SYMBOL(proc_ide_read_geometry);
+#endif
 EXPORT_SYMBOL(ide_add_setting);
 EXPORT_SYMBOL(ide_remove_setting);
-EXPORT_SYMBOL(proc_ide_read_geometry);
 
 EXPORT_SYMBOL(ide_register);
 EXPORT_SYMBOL(ide_unregister);
@@ -2881,5 +2895,8 @@
 
 	for (index = 0; index < MAX_HWIFS; ++index)
 		ide_unregister(index);
+#ifdef CONFIG_PROC_FS
+	proc_ide_destroy();
+#endif
 }
 #endif /* MODULE */
diff -u --recursive --new-file v2.1.91/linux/drivers/block/ide.h linux/drivers/block/ide.h
--- v2.1.91/linux/drivers/block/ide.h	Fri Feb  6 15:33:06 1998
+++ linux/drivers/block/ide.h	Wed Apr  1 17:30:46 1998
@@ -405,14 +405,17 @@
  * /proc/ide interface
  */
 typedef struct {
-	const char *name;
-	read_proc_t *read_proc;
-	write_proc_t *write_proc;
+	const char	*name;
+	mode_t		mode;
+	read_proc_t	*read_proc;
+	write_proc_t	*write_proc;
 } ide_proc_entry_t;
 
-void proc_ide_init(void);
-void ide_add_proc_entries(ide_drive_t *drive, ide_proc_entry_t *p);
-void ide_remove_proc_entries(ide_drive_t *drive, ide_proc_entry_t *p);
+#ifdef CONFIG_PROC_FS
+void proc_ide_create(void);
+void proc_ide_destroy(void);
+void ide_add_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p, void *data);
+void ide_remove_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p);
 read_proc_t proc_ide_read_capacity;
 read_proc_t proc_ide_read_geometry;
 
@@ -431,6 +434,7 @@
 	*start = page + off;		\
 	return len;			\
 }
+#endif
 
 /*
  * Subdrivers support.
@@ -672,6 +676,12 @@
 #endif
 #if MAX_HWIFS > 3
 void do_ide3_request (void);
+#endif
+#if MAX_HWIFS > 4
+void do_ide4_request (void);
+#endif
+#if MAX_HWIFS > 5
+void do_ide5_request (void);
 #endif
 void ide_init_subdrivers (void);
 
diff -u --recursive --new-file v2.1.91/linux/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
--- v2.1.91/linux/drivers/block/ll_rw_blk.c	Tue Mar 17 22:18:14 1998
+++ linux/drivers/block/ll_rw_blk.c	Sun Mar 29 12:24:05 1998
@@ -428,6 +428,8 @@
 	     case FLOPPY_MAJOR:
 	     case IDE2_MAJOR:
 	     case IDE3_MAJOR:
+	     case IDE4_MAJOR:
+	     case IDE5_MAJOR:
 	     case ACSI_MAJOR:
 		/*
 		 * The scsi disk and cdrom drivers completely remove the request
@@ -716,9 +718,6 @@
 #ifdef CONFIG_BLK_DEV_LOOP
 	loop_init();
 #endif
-#ifdef CONFIG_CDROM		/* this must precede all CD-ROM drivers */
-	cdrom_init();
-#endif CONFIG_CDROM
 #ifdef CONFIG_ISP16_CDI
 	isp16_init();
 #endif CONFIG_ISP16_CDI
diff -u --recursive --new-file v2.1.91/linux/drivers/block/md.c linux/drivers/block/md.c
--- v2.1.91/linux/drivers/block/md.c	Tue Mar 17 22:18:14 1998
+++ linux/drivers/block/md.c	Sun Mar 29 12:24:05 1998
@@ -851,11 +851,13 @@
 EXPORT_SYMBOL(md_wakeup_thread);
 EXPORT_SYMBOL(md_do_sync);
 
+#ifdef CONFIG_PROC_FS
 static struct proc_dir_entry proc_md = {
 	PROC_MD, 6, "mdstat",
 	S_IFREG | S_IRUGO, 1, 0, 0,
 	0, &proc_array_inode_operations,
 };
+#endif
 
 static void md_geninit (struct gendisk *gdisk)
 {
@@ -873,7 +875,9 @@
   blksize_size[MD_MAJOR] = md_blocksizes;
   max_readahead[MD_MAJOR] = md_maxreadahead;
 
+#ifdef CONFIG_PROC_FS
   proc_register(&proc_root, &proc_md);
+#endif
 }
 
 int md_error (kdev_t mddev, kdev_t rdev)
diff -u --recursive --new-file v2.1.91/linux/drivers/block/nbd.c linux/drivers/block/nbd.c
--- v2.1.91/linux/drivers/block/nbd.c	Thu Mar 26 15:57:02 1998
+++ linux/drivers/block/nbd.c	Fri Mar 27 09:37:42 1998
@@ -32,6 +32,7 @@
 #include <linux/fs.h>
 #include <linux/stat.h>
 #include <linux/errno.h>
+#include <linux/file.h>
 
 #include <asm/segment.h>
 #include <asm/uaccess.h>
@@ -432,7 +433,7 @@
 #endif
 	blksize_size[MAJOR_NR] = nbd_blksizes;
 	blk_size[MAJOR_NR] = nbd_sizes;
-	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+	blk_dev[MAJOR_NR].request_fn = do_nbd_request;
 	for (i = 0; i < MAX_NBD; i++) {
 		nbd_dev[i].refcnt = 0;
 		nbd_dev[i].file = NULL;
diff -u --recursive --new-file v2.1.91/linux/drivers/cdrom/Makefile linux/drivers/cdrom/Makefile
--- v2.1.91/linux/drivers/cdrom/Makefile	Sun Dec 28 12:05:45 1997
+++ linux/drivers/cdrom/Makefile	Sun Mar 29 12:16:13 1998
@@ -1,131 +1,66 @@
-#
 # Makefile for the kernel cdrom device drivers.
 #
-# Note! Dependencies are done automagically by 'make dep', which also
-# removes any old dependencies. DON'T put your own dependencies here
-# unless it's something special (ie not a .c file).
-#
-# Note 2! The CFLAGS definition is now inherited from the
-# parent makefile.
-#
+# 30 Jan 1998, Michael Elizabeth Chastain, <mailto:mec@shout.net>
+# Rewritten to use lists instead of if-statements.
+
+
+
+# All of the (potential) objects that export symbols.
+# This list comes from 'grep -l EXPORT_SYMBOL *.[hc]'.
+
+export-objs	:= cdrom.o
+
+
+
+# Object file lists.
+
+obj-y		:=
+obj-m		:=
+obj-n		:=
+obj-		:=
+
+
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_BLK_DEV_IDECD)	+=              cdrom.o
+obj-$(CONFIG_BLK_DEV_SR)	+=              cdrom.o
+
+obj-$(CONFIG_AZTCD)		+= aztcd.o
+obj-$(CONFIG_CDU31A)		+= cdu31a.o     cdrom.o
+obj-$(CONFIG_CM206)		+= cm206.o      cdrom.o
+obj-$(CONFIG_GSCD)		+= gscd.o
+obj-$(CONFIG_ISP16_CDI)		+= isp16.o
+obj-$(CONFIG_MCD)		+= mcd.o        cdrom.o
+obj-$(CONFIG_MCDX)		+= mcdx.o       cdrom.o
+obj-$(CONFIG_OPTCD)		+= optcd.o
+obj-$(CONFIG_SBPCD)		+= sbpcd.o      cdrom.o
+obj-$(CONFIG_SBPCD2)		+= sbpcd2.o     cdrom.o
+obj-$(CONFIG_SBPCD3)		+= sbpcd3.o     cdrom.o
+obj-$(CONFIG_SBPCD4)		+= sbpcd4.o     cdrom.o
+obj-$(CONFIG_SJCD)		+= sjcd.o
+obj-$(CONFIG_CDU535)		+= sonycd535.o
+
+
+
+# Files that are both resident and modular: remove from modular.
+
+obj-m		:= $(filter-out $(obj-y), $(obj-m))
+
+
+
+# Translate to Rules.make lists.
+
+L_TARGET	:= cdrom.a
+MOD_LIST_NAME	:= CDROM_MODULES
+
+L_OBJS		:= $(sort $(filter-out $(export-objs), $(obj-y)))
+LX_OBJS		:= $(sort $(filter     $(export-objs), $(obj-y)))
+M_OBJS		:= $(sort $(filter-out $(export-objs), $(obj-m)))
+MX_OBJS		:= $(sort $(filter     $(export-objs), $(obj-m)))
 
-#
-# Note : at this point, these files are compiled on all systems. 
-# In the future, some of these should be built conditionally.
-#
 
 
-L_TARGET := cdrom.a
-L_OBJS   :=
-M_OBJS   :=
-MOD_LIST_NAME := CDROM_MODULES
-
-ifeq ($(CONFIG_AZTCD),y)
-L_OBJS += aztcd.o
-else
-  ifeq ($(CONFIG_AZTCD),m)
-  M_OBJS += aztcd.o
-  endif
-endif #CONFIG_AZTCD
-
-ifeq ($(CONFIG_CDU31A),y)
-L_OBJS += cdu31a.o
-else
-  ifeq ($(CONFIG_CDU31A),m)
-  M_OBJS += cdu31a.o
-  endif
-endif #CONFIG_CDU31A
-
-ifeq ($(CONFIG_MCD),y)
-L_OBJS += mcd.o
-else
-  ifeq ($(CONFIG_MCD),m)
-  M_OBJS += mcd.o
-  endif
-endif #CONFIG_MCD
-
-ifeq ($(CONFIG_MCDX),y)
-L_OBJS += mcdx.o
-else
-  ifeq ($(CONFIG_MCDX),m)
-  M_OBJS += mcdx.o
-  endif
-endif #CONFIG_MCDX
-
-ifeq ($(CONFIG_SBPCD),y)
-L_OBJS += sbpcd.o
-else
-  ifeq ($(CONFIG_SBPCD),m)
-  M_OBJS += sbpcd.o
-  endif
-endif #CONFIG_SBPCD
-
-ifeq ($(CONFIG_SBPCD2),y)
-L_OBJS += sbpcd2.o
-endif #CONFIG_SBPCD2
-
-ifeq ($(CONFIG_SBPCD3),y)
-L_OBJS += sbpcd3.o
-endif #CONFIG_SBPCD3
-
-ifeq ($(CONFIG_SBPCD4),y)
-L_OBJS += sbpcd4.o
-endif #CONFIG_SBPCD4
-
-ifeq ($(CONFIG_CDU535),y)
-L_OBJS += sonycd535.o
-else
-  ifeq ($(CONFIG_CDU535),m)
-  M_OBJS += sonycd535.o
-  endif
-endif #CONFIG_CDU535
-
-ifeq ($(CONFIG_GSCD),y)
-L_OBJS += gscd.o
-else
-  ifeq ($(CONFIG_GSCD),m)
-  M_OBJS += gscd.o
-  endif
-endif #CONFIG_GSCD
-
-ifeq ($(CONFIG_CM206),y)
-L_OBJS += cm206.o
-else
-  ifeq ($(CONFIG_CM206),m)
-  M_OBJS += cm206.o
-  endif
-endif #CONFIG_CM206
-
-ifeq ($(CONFIG_OPTCD),y)
-L_OBJS += optcd.o
-else
-  ifeq ($(CONFIG_OPTCD),m)
-  M_OBJS += optcd.o
-  endif
-endif #CONFIG_OPTCD
-
-ifeq ($(CONFIG_SJCD),y)
-L_OBJS += sjcd.o
-else
-  ifeq ($(CONFIG_SJCD),m)
-  M_OBJS += sjcd.o
-  endif
-endif #CONFIG_SJCD
-
-ifeq ($(CONFIG_ISP16_CDI),y)
-L_OBJS += isp16.o
-else
-  ifeq ($(CONFIG_ISP16_CDI),m)
-  M_OBJS += isp16.o
-  endif
-endif #CONFIG_ISP16_CDI
-
-ifeq ($(CONFIG_CDROM),y)
-LX_OBJS += cdrom.o
-else
-  ifeq ($(CONFIG_CDROM),m)
-  MX_OBJS += cdrom.o
-  endif
-endif #CONFIG_CDROM for the Uniform CD-ROM driver
+# Hand off to Rules.make.
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.91/linux/drivers/cdrom/cdrom.c linux/drivers/cdrom/cdrom.c
--- v2.1.91/linux/drivers/cdrom/cdrom.c	Fri Jan 30 11:28:06 1998
+++ linux/drivers/cdrom/cdrom.c	Sun Mar 29 12:16:13 1998
@@ -76,6 +76,22 @@
 #define REVISION "Revision: 2.12"
 #define VERSION "Id: cdrom.c 2.12 1998/01/24 22:15:45 erik Exp"
 
+/* I use an error-log mask to give fine grain control over the type of
+   messages dumped to the system logs.  The available masks include: */
+#define CD_NOTHING      0x0
+#define CD_WARNING	0x1
+#define CD_REG_UNREG	0x2
+#define CD_DO_IOCTL	0x4
+#define CD_OPEN		0x8
+#define CD_CLOSE	0x10
+#define CD_COUNT_TRACKS 0x20
+
+/* Define this to remove _all_ the debugging messages */
+/* #define ERRLOGMASK CD_NOTHING */
+#define ERRLOGMASK (CD_WARNING)
+/* #define ERRLOGMASK (CD_WARNING|CD_OPEN|CD_COUNT_TRACKS|CD_CLOSE) */
+/* #define ERRLOGMASK (CD_WARNING|CD_REG_UNREG|CD_DO_IOCTL|CD_OPEN|CD_CLOSE|CD_COUNT_TRACKS) */
+
 
 #include <linux/config.h>
 #include <linux/module.h>
@@ -93,24 +109,7 @@
 #include <asm/uaccess.h>
 
 
-/* I use an error-log mask to give fine grain control over the type of
-   error messages dumped to the system logs.  The available masks include: */
-#define CD_WARNING	0x1
-#define CD_REG_UNREG	0x2
-#define CD_DO_IOCTL	0x4
-#define CD_OPEN		0x8
-#define CD_CLOSE	0x10
-#define CD_COUNT_TRACKS 0x20
-
-/* When VERBOSE_STATUS_INFO is not defined, the debugging printks don't 
-   get compiled in at all */
-#define VERBOSE_STATUS_INFO
-
-#define ERRLOGMASK (CD_WARNING) 
-/* #define ERRLOGMASK (CD_WARNING|CD_OPEN|CD_COUNT_TRACKS|CD_CLOSE) */
-/* #define ERRLOGMASK (CD_WARNING|CD_REG_UNREG|CD_DO_IOCTL|CD_OPEN|CD_CLOSE) */
-
-#ifdef VERBOSE_STATUS_INFO
+#if (ERRLOGMASK!=CD_NOTHING)
 #define cdinfo(type, fmt, args...) \
 	if (ERRLOGMASK & type) printk(KERN_INFO "cdrom: " fmt, ## args)
 #else
@@ -168,11 +167,6 @@
 	NULL                            /* revalidate */
 };
 
-void cdrom_init(void) {
-	if (!topCdromPtr)
-	    printk(KERN_INFO "Uniform CD-ROM driver " REVISION "\n");
-}
-
 /* This macro makes sure we don't have to check on cdrom_device_ops
  * existence in the run-time routines below. Change_capability is a
  * hack to have the capability flags defined const, while we can still
@@ -205,6 +199,15 @@
 					/* default compatibility mode */
 	cdi->mc_flags = 0;
 	cdo->n_minors = 0;
+
+	{
+	    static char banner_printed = 0;
+	    if ( !banner_printed ) {
+		printk(KERN_INFO "Uniform CD-ROM driver " REVISION "\n");
+		banner_printed = 1;
+	    }
+	}
+
 	cdinfo(CD_REG_UNREG, "drive \"/dev/%s\" registered\n", cdi->name);
 	cdi->next = topCdromPtr; 	
 	topCdromPtr = cdi;
@@ -293,13 +296,14 @@
 		ret = cdo->drive_status(cdi, CDSL_CURRENT);
 		cdinfo(CD_OPEN, "drive_status=%d\n", ret); 
 		if (ret == CDS_TRAY_OPEN) {
-			cdinfo(CD_WARNING, "tray is open...\n"); 
+			cdinfo(CD_OPEN, "the tray is open...\n"); 
 			/* can/may i close it? */
 			if (cdo->capability & ~cdi->mask & CDC_CLOSE_TRAY &&
 			    cdi->options & CDO_AUTO_CLOSE) {
+				cdinfo(CD_OPEN, "trying to close the tray.\n"); 
 				ret=cdo->tray_move(cdi,0);
 				if (ret) {
-					cdinfo(CD_WARNING, "bummer. tried to close tray but failed.\n"); 
+					cdinfo(CD_OPEN, "bummer. tried to close the tray but failed.\n"); 
 					/* Ignore the error from the low
 					level driver.  We don't care why it
 					couldn't close the tray.  We only care 
@@ -309,36 +313,37 @@
 					goto clean_up_and_return;
 				}
 			} else {
-				cdinfo(CD_WARNING, "this driver can't close the tray.\n"); 
+				cdinfo(CD_OPEN, "bummer. this driver can't close the tray.\n"); 
 				ret=-ENOMEDIUM;
 				goto clean_up_and_return;
 			}
 			/* Ok, the door should be closed now.. Check again */
 			ret = cdo->drive_status(cdi, CDSL_CURRENT);
-			cdinfo(CD_OPEN, "tried again. drive_status=%d\n", ret); 
 			if ((ret == CDS_NO_DISC) || (ret==CDS_TRAY_OPEN)) {
+				cdinfo(CD_OPEN, "bummer. the tray is still not closed.\n"); 
 				ret=-ENOMEDIUM;
 				goto clean_up_and_return;
 			}
+			cdinfo(CD_OPEN, "the tray is now closed.\n"); 
 		}
 		if (ret!=CDS_DISC_OK)
 			goto clean_up_and_return;
 	}
 	cdrom_count_tracks(cdi, &tracks);
 	if (tracks.error == CDS_NO_DISC) {
-		cdinfo(CD_OPEN, "bummer. no disc...\n");
+		cdinfo(CD_OPEN, "bummer. no disc.\n");
 		ret=-ENOMEDIUM;
 		goto clean_up_and_return;
 	}
 	/* CD-Players which don't use O_NONBLOCK, workman
 	 * for example, need bit CDO_CHECK_TYPE cleared! */
 	if (cdi->options & CDO_CHECK_TYPE && tracks.data==0) {
-		cdinfo(CD_OPEN, "bummer. wrong media type...\n"); 
+		cdinfo(CD_OPEN, "bummer. wrong media type.\n"); 
 		ret=-EMEDIUMTYPE;
 		goto clean_up_and_return;
 	}
 
-	cdinfo(CD_OPEN, "all seems well, opening the device...\n"); 
+	cdinfo(CD_OPEN, "all seems well, opening the device.\n"); 
 
 	/* all seems well, we can open the device */
 	ret = cdo->open(cdi, 0); /* open for data */
@@ -347,7 +352,7 @@
 	   opening the device, but we don't want the device locked if 
 	   this somehow fails... */
 	if (ret) {
-		cdinfo(CD_OPEN, "open device failed...\n"); 
+		cdinfo(CD_OPEN, "open device failed.\n"); 
 		goto clean_up_and_return;
 	}
 	if (cdo->capability & ~cdi->mask & CDC_LOCK && 
@@ -364,11 +369,11 @@
 	This ensures that the drive gets unlocked after a mount fails.  This 
 	is a goto to avoid adding bloating the driver with redundant code. */ 
 clean_up_and_return:
-	cdinfo(CD_WARNING, "failed to open the device.\n"); 
+	cdinfo(CD_WARNING, "open failed.\n"); 
 	if (cdo->capability & ~cdi->mask & CDC_LOCK && 
 		cdi->options & CDO_LOCK) {
 			cdo->lock_door(cdi, 0);
-			cdinfo(CD_WARNING, "door unlocked.\n");
+			cdinfo(CD_OPEN, "door unlocked.\n");
 	}
 	return ret;
 }
@@ -389,13 +394,14 @@
 		ret = cdo->drive_status(cdi, CDSL_CURRENT);
 		cdinfo(CD_OPEN, "drive_status=%d\n", ret); 
 		if (ret == CDS_TRAY_OPEN) {
-			cdinfo(CD_WARNING, "tray is open...\n"); 
+			cdinfo(CD_OPEN, "the tray is open...\n"); 
 			/* can/may i close it? */
 			if (cdo->capability & ~cdi->mask & CDC_CLOSE_TRAY &&
 			    cdi->options & CDO_AUTO_CLOSE) {
+				cdinfo(CD_OPEN, "trying to close the tray.\n"); 
 				ret=cdo->tray_move(cdi,0);
 				if (ret) {
-					cdinfo(CD_WARNING, "bummer. tried to close tray but failed.\n"); 
+					cdinfo(CD_OPEN, "bummer. tried to close tray but failed.\n"); 
 					/* Ignore the error from the low
 					level driver.  We don't care why it
 					couldn't close the tray.  We only care 
@@ -404,16 +410,20 @@
 					return -ENOMEDIUM;
 				}
 			} else {
-				cdinfo(CD_WARNING, "this driver can't close the tray.\n"); 
+				cdinfo(CD_OPEN, "bummer. this driver can't close the tray.\n"); 
 				return -ENOMEDIUM;
 			}
 			/* Ok, the door should be closed now.. Check again */
 			ret = cdo->drive_status(cdi, CDSL_CURRENT);
-			cdinfo(CD_OPEN, "tried again. drive_status=%d\n", ret); 
-			if ((ret == CDS_NO_DISC) || (ret==CDS_TRAY_OPEN))
+			if ((ret == CDS_NO_DISC) || (ret==CDS_TRAY_OPEN)) {
+				cdinfo(CD_OPEN, "bummer. the tray is still not closed.\n"); 
 				return -ENOMEDIUM;
-			if (ret!=CDS_DISC_OK)
+			}	
+			if (ret!=CDS_DISC_OK) {
+				cdinfo(CD_OPEN, "bummer. disc isn't ready.\n"); 
 				return -EIO;
+			}	
+			cdinfo(CD_OPEN, "the tray is now closed.\n"); 
 		}	
 	}
 	cdrom_count_tracks(cdi, &tracks);
@@ -1031,7 +1041,6 @@
 
 int init_module(void)
 {
-	cdrom_init();
 #ifdef CONFIG_SYSCTL
 	cdrom_sysctl_register();
 #endif /* CONFIG_SYSCTL */ 
diff -u --recursive --new-file v2.1.91/linux/drivers/char/apm_bios.c linux/drivers/char/apm_bios.c
--- v2.1.91/linux/drivers/char/apm_bios.c	Tue Mar 17 22:18:14 1998
+++ linux/drivers/char/apm_bios.c	Wed Apr  1 14:41:32 1998
@@ -231,10 +231,10 @@
 		"pushl %%fs\n\t" \
 		"pushl %%gs\n\t" \
 		"xorl %%edx, %%edx\n\t" \
-		"mov %%dx, %%ds\n\t" \
-		"mov %%dx, %%es\n\t" \
-		"mov %%dx, %%fs\n\t" \
-		"mov %%dx, %%gs\n\t"
+		"movl %%dx, %%ds\n\t" \
+		"movl %%dx, %%es\n\t" \
+		"movl %%dx, %%fs\n\t" \
+		"movl %%dx, %%gs\n\t"
 #	define APM_DO_RESTORE_SEGS	\
 		"popl %%gs\n\t" \
 		"popl %%fs\n\t" \
@@ -1159,8 +1159,10 @@
 	static struct proc_dir_entry *ent;
 
 #ifdef __SMP__
-	printk(KERN_NOTICE "APM disabled: APM is not SMP safe.\n");
-	return;
+	if (smp_num_cpus > 1) {
+		printk(KERN_NOTICE "APM disabled: APM is not SMP safe.\n");
+		return;
+	}
 #endif
 	if (apm_bios_info.version == 0) {
 		printk(KERN_INFO "APM BIOS not found.\n");
diff -u --recursive --new-file v2.1.91/linux/drivers/char/console.c linux/drivers/char/console.c
--- v2.1.91/linux/drivers/char/console.c	Thu Feb 12 20:56:06 1998
+++ linux/drivers/char/console.c	Mon Mar 30 00:21:39 1998
@@ -204,6 +204,121 @@
 int want_console = -1;
 int kmsg_redirect = 0;
 
+#define CONFIG_SERIAL_ECHO
+#ifdef CONFIG_SERIAL_ECHO
+
+#include <linux/serial_reg.h>
+
+extern int serial_echo_init (int base);
+extern int serial_echo_print (const char *s);
+
+/*
+ * this defines the address for the port to which printk echoing is done
+ *  when CONFIG_SERIAL_ECHO is defined
+ */
+#define SERIAL_ECHO_PORT	0x3f8	/* COM1 */
+
+static int serial_echo_port = 0;
+
+#define serial_echo_outb(v,a) outb((v),(a)+serial_echo_port)
+#define serial_echo_inb(a)    inb((a)+serial_echo_port)
+
+#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
+
+/* Wait for transmitter & holding register to empty */
+#define WAIT_FOR_XMITR \
+ do { \
+       lsr = serial_echo_inb(UART_LSR); \
+ } while ((lsr & BOTH_EMPTY) != BOTH_EMPTY)
+
+/* These two functions abstract the actual communications with the
+ * debug port.	This is so we can change the underlying communications
+ * mechanism without modifying the rest of the code.
+ */
+int
+serial_echo_print(const char *s)
+{
+	int     lsr, ier;
+	int     i;
+
+	if (!serial_echo_port) return (0);
+
+	/*
+	 * First save the IER then disable the interrupts
+	 */
+	ier = serial_echo_inb(UART_IER);
+	serial_echo_outb(0x00, UART_IER);
+
+	/*
+	 * Now, do each character
+	 */
+	for (i = 0; *s; i++, s++) {
+		WAIT_FOR_XMITR;
+
+		/* Send the character out. */
+		serial_echo_outb(*s, UART_TX);
+
+		/* if a LF, also do CR... */
+		if (*s == 10) {
+			WAIT_FOR_XMITR;
+			serial_echo_outb(13, UART_TX);
+		}
+	}
+
+	/*
+	 * Finally, Wait for transmitter & holding register to empty
+	 *  and restore the IER
+	 */
+	do {
+		lsr = serial_echo_inb(UART_LSR);
+	} while ((lsr & BOTH_EMPTY) != BOTH_EMPTY);
+	serial_echo_outb(ier, UART_IER);
+
+	return (0);
+}
+
+
+int
+serial_echo_init(int base)
+{
+	int comstat, hi, lo;
+	
+	if (base != 0x2f8 && base != 0x3f8) {
+		serial_echo_port = 0;
+		return (0);
+	} else
+	  serial_echo_port = base;
+
+	/*
+	 * read the Divisor Latch
+	 */
+	comstat = serial_echo_inb(UART_LCR);
+	serial_echo_outb(comstat | UART_LCR_DLAB, UART_LCR);
+	hi = serial_echo_inb(UART_DLM);
+	lo = serial_echo_inb(UART_DLL);
+	serial_echo_outb(comstat, UART_LCR);
+
+	/*
+	 * now do hardwired init
+	 */
+	serial_echo_outb(0x03, UART_LCR); /* No parity, 8 data bits, 1 stop */
+	serial_echo_outb(0x83, UART_LCR); /* Access divisor latch */
+	serial_echo_outb(0x00, UART_DLM); /* 9600 baud */
+	serial_echo_outb(0x0c, UART_DLL);
+	serial_echo_outb(0x03, UART_LCR); /* Done with divisor */
+
+	/* Prior to disabling interrupts, read the LSR and RBR
+	 * registers
+	 */
+	comstat = serial_echo_inb(UART_LSR); /* COM? LSR */
+	comstat = serial_echo_inb(UART_RX);	/* COM? RBR */
+	serial_echo_outb(0x00, UART_IER); /* Disable all interrupts */
+
+	return(0);
+}
+
+#endif /* CONFIG_SERIAL_ECHO */
+
 int vc_cons_allocated(unsigned int i)
 {
 	return (i < MAX_NR_CONSOLES && vc_cons[i].d);
@@ -579,6 +694,37 @@
 	has_scrolled = 1;
 }
 
+/*
+ * Routine to reset the visible "screen" to the top of video memory.
+ * This is necessary when exiting from the kernel back to a console
+ * which expects only the top of video memory to be used for the visible
+ * screen (with scrolling down by moving the memory contents).
+ * The normal action of the LINUX console is to scroll using all of the
+ * video memory and diddling the hardware top-of-video register as needed.
+ */
+void
+scrreset(void)
+{
+	int currcons = fg_console;
+	unsigned short * d = (unsigned short *) video_mem_start;
+	unsigned short * s = (unsigned short *) origin;
+	unsigned int count;
+
+	count = (video_num_lines-1)*video_num_columns;
+	memcpyw(d, s, 2*count);
+	memsetw(d + count, video_erase_char,
+		2*video_num_columns);
+	scr_end -= origin-video_mem_start;
+	pos -= origin-video_mem_start;
+	origin = video_mem_start;
+
+	has_scrolled = 1;
+	has_wrapped = 1;
+
+	set_origin(currcons);
+	set_cursor(currcons);
+}
+
 static void lf(int currcons)
 {
     	/* don't scroll if above bottom of scrolling region, or
@@ -1866,6 +2012,10 @@
 		return;
 	}
 
+#ifdef CONFIG_SERIAL_ECHO
+        serial_echo_print(b);
+#endif /* CONFIG_SERIAL_ECHO */
+
 	while (count-- > 0) {
 		c = *(b++);
 		if (c == 10 || c == 13 || need_wrap) {
@@ -2116,6 +2266,10 @@
 		default_font_height = video_font_height = ORIG_VIDEO_POINTS;
 		/* This may be suboptimal but is a safe bet - go with it */
 		video_scan_lines = video_font_height * video_num_lines;
+
+#ifdef CONFIG_SERIAL_ECHO
+		serial_echo_init(SERIAL_ECHO_PORT);
+#endif /* CONFIG_SERIAL_ECHO */
 
 		printk("Console: %ld point font, %ld scans\n",
 		       video_font_height, video_scan_lines);
diff -u --recursive --new-file v2.1.91/linux/drivers/char/lp.c linux/drivers/char/lp.c
--- v2.1.91/linux/drivers/char/lp.c	Tue Mar 10 10:03:31 1998
+++ linux/drivers/char/lp.c	Wed Apr  1 16:17:29 1998
@@ -163,14 +163,22 @@
 	unsigned long count = 0;
 	struct lp_stats *stats;
 
-	do {
-		status = r_str (minor);
-		count++;
+	for (;;) {
 		lp_yield(minor);
-	} while (!LP_READY(minor, status) && count < LP_CHAR(minor));
-
-	if (count == LP_CHAR(minor))
-		return 0;
+		status = r_str (minor);
+		if (++count == LP_CHAR(minor))
+			return 0;
+		if (LP_POLLING(minor))
+		{
+			if (LP_READY(minor, status))
+				break;
+		} else {
+			if (!LP_READY(minor, status))
+				return 0;
+			else
+				break;
+		}
+	}
 
 	w_dtr(minor, lpchar);
 	stats = &LP_STAT(minor);
diff -u --recursive --new-file v2.1.91/linux/drivers/char/mem.c linux/drivers/char/mem.c
--- v2.1.91/linux/drivers/char/mem.c	Thu Mar 26 15:57:03 1998
+++ linux/drivers/char/mem.c	Wed Apr  1 14:52:15 1998
@@ -385,9 +385,6 @@
 		default:
 			return -EINVAL;
 	}
-	if (file->f_pos < 0)
-		return 0;
-	return file->f_pos;
 }
 
 #define mmap_kmem	mmap_mem
diff -u --recursive --new-file v2.1.91/linux/drivers/char/pc_keyb.c linux/drivers/char/pc_keyb.c
--- v2.1.91/linux/drivers/char/pc_keyb.c	Wed Dec 17 15:23:47 1997
+++ linux/drivers/char/pc_keyb.c	Mon Mar 30 00:21:39 1998
@@ -22,6 +22,7 @@
 #include <asm/keyboard.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
+#include <asm/irq.h>
 #include <asm/system.h>
 
 /* Some configuration switches are present in the include file... */
@@ -55,18 +56,17 @@
 
 __initfunc(static int kbd_wait_for_input(void))
 {
-	int     n;
 	int     status, data;
 	unsigned long start = jiffies;
 
 	do {
                 status = inb(KBD_STATUS_REG);
+
                 /*
                  * Wait for input data to become available.  This bit will
                  * then be cleared by the following read of the DATA
                  * register.
                  */
-
                 if (!(status & KBD_STAT_OBF))
 			continue;
 
@@ -98,6 +98,8 @@
 
 __initfunc(static char *initialize_kbd2(void))
 {
+	int status;
+
 	/* Flush any pending input. */
 
 	while (kbd_wait_for_input() != -1)
@@ -132,22 +134,37 @@
 	 * then the assumption is that no keyboard is
 	 * plugged into the machine.
 	 * This defaults the keyboard to scan-code set 2.
+	 *
+	 * Set up to try again if the keyboard asks for RESEND.
 	 */
 
+        do {
 	kbd_write(KBD_DATA_REG, KBD_CMD_RESET);
-	if (kbd_wait_for_input() != KBD_REPLY_ACK)
+                status = kbd_wait_for_input();
+                if (status == KBD_REPLY_ACK)
+			break;
+                else if (status != KBD_REPLY_RESEND)
 		return "Keyboard reset failed, no ACK";
+        } while (1);
+
 	if (kbd_wait_for_input() != KBD_REPLY_POR)
 		return "Keyboard reset failed, no POR";
 
 	/*
 	 * Set keyboard controller mode. During this, the keyboard should be
 	 * in the disabled state.
+	 *
+	 * Set up to try again if the keyboard asks for RESEND.
 	 */
 
+	do {
 	kbd_write(KBD_DATA_REG, KBD_CMD_DISABLE);
-	if (kbd_wait_for_input() != KBD_REPLY_ACK)
+		status = kbd_wait_for_input();
+		if (status == KBD_REPLY_ACK)
+			break;
+		else if (status != KBD_REPLY_RESEND)
 		return "Disable keyboard: no ACK";
+	} while (1);
 
 	kbd_write(KBD_CNTL_REG, KBD_CCMD_WRITE_MODE);
 	kbd_write(KBD_DATA_REG, KBD_MODE_KBD_INT
diff -u --recursive --new-file v2.1.91/linux/drivers/char/serial.c linux/drivers/char/serial.c
--- v2.1.91/linux/drivers/char/serial.c	Tue Mar 17 22:18:14 1998
+++ linux/drivers/char/serial.c	Wed Apr  1 14:58:54 1998
@@ -3212,7 +3212,7 @@
 	 *	The interrupt of the serial console port
 	 *	can't be shared.
 	 */
-	if (sercons.flags & CON_FIRST) {
+	if (sercons.flags & CON_CONSDEV) {
 		for(i = 0; i < NR_PORTS; i++)
 			if (i != sercons.index &&
 			    rs_table[i].irq == rs_table[sercons.index].irq)
diff -u --recursive --new-file v2.1.91/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- v2.1.91/linux/drivers/char/tty_io.c	Thu Mar 26 15:57:03 1998
+++ linux/drivers/char/tty_io.c	Fri Mar 27 09:34:00 1998
@@ -1285,7 +1285,7 @@
 	}
 	if ((tty->driver.type == TTY_DRIVER_TYPE_SERIAL) &&
 	    (tty->driver.subtype == SERIAL_TYPE_CALLOUT)) {
-		printk("Warning, %s opened, is a deprecated tty "
+		printk(KERN_INFO "Warning, %s opened, is a deprecated tty "
 		       "callout device\n", tty_name(tty, buf));
 	}
 	return 0;
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/Config.in linux/drivers/isdn/Config.in
--- v2.1.91/linux/drivers/isdn/Config.in	Thu May 29 21:53:05 1997
+++ linux/drivers/isdn/Config.in	Wed Apr  1 16:20:57 1998
@@ -9,24 +9,44 @@
   fi
 fi
 bool 'Support audio via ISDN' CONFIG_ISDN_AUDIO
+if [ "$CONFIG_X25" != "n" ]; then
+  bool 'X.25 PLP on top of ISDN (EXPERIMENTAL)' CONFIG_ISDN_X25
+fi
 dep_tristate 'ICN 2B and 4B support' CONFIG_ISDN_DRV_ICN $CONFIG_ISDN
+dep_tristate 'isdnloop support' CONFIG_ISDN_DRV_LOOP $CONFIG_ISDN
 dep_tristate 'PCBIT-D support' CONFIG_ISDN_DRV_PCBIT $CONFIG_ISDN
 dep_tristate 'HiSax SiemensChipSet driver support' CONFIG_ISDN_DRV_HISAX $CONFIG_ISDN
 if [ "$CONFIG_ISDN_DRV_HISAX" != "n" ]; then
+    bool 'HiSax Support for EURO/DSS1' CONFIG_HISAX_EURO
+    if [ "$CONFIG_HISAX_EURO" != "n" ]; then
+	    bool 'Support for german tarifinfo' CONFIG_DE_AOC
+	    bool 'Support for australian Microlink service (not for std. EURO)' CONFIG_HISAX_ML
+    fi
+    bool 'HiSax Support for german 1TR6' CONFIG_HISAX_1TR6
     bool 'HiSax Support for Teles 16.0/8.0' CONFIG_HISAX_16_0
     bool 'HiSax Support for Teles 16.3 or PNP or PCMCIA' CONFIG_HISAX_16_3
+    bool 'HiSax Support for Teles 16.3c' CONFIG_HISAX_TELES3C 
     bool 'HiSax Support for AVM A1 (Fritz)' CONFIG_HISAX_AVM_A1
-    bool 'HiSax Support for Elsa ISA cards' CONFIG_HISAX_ELSA_PCC
-    bool 'HiSax Support for Elsa PCMCIA card' CONFIG_HISAX_ELSA_PCMCIA
+    bool 'HiSax Support for Elsa cards' CONFIG_HISAX_ELSA
     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 US/NI-1' CONFIG_HISAX_NI1
-    bool 'HiSax Support for german 1TR6' CONFIG_HISAX_1TR6
+    bool 'HiSax Support for Eicon.Diehl Diva cards' CONFIG_HISAX_DIEHLDIVA
+    bool 'HiSax Support for ASUSCOM cards' CONFIG_HISAX_ASUSCOM
+    bool 'HiSax Support for TELEINT cards' CONFIG_HISAX_TELEINT
+    bool 'HiSax Support for Sedlbauer speed card/win/star' CONFIG_HISAX_SEDLBAUER
+    bool 'HiSax Support for USR Sportster internal TA' CONFIG_HISAX_SPORTSTER
+    bool 'HiSax Support for MIC card' CONFIG_HISAX_MIC
+    bool 'HiSax Support for NETjet card' CONFIG_HISAX_NETJET
+    bool 'HiSax Support for Niccy PnP/PCI card' CONFIG_HISAX_NICCY
+    if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then
+        bool 'HiSax Support for Am7930' CONFIG_HISAX_AMD7930
+    fi
 fi
 if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then
 	dep_tristate 'Spellcaster support (EXPERIMENTAL)' CONFIG_ISDN_DRV_SC $CONFIG_ISDN
+	dep_tristate 'IBM Active 2000 support (EXPERIMENTAL)' CONFIG_ISDN_DRV_ACT2000 $CONFIG_ISDN
 fi
 dep_tristate 'AVM-B1 with CAPI2.0 support' CONFIG_ISDN_DRV_AVMB1 $CONFIG_ISDN
 if [ "$CONFIG_ISDN_DRV_AVMB1" != "n" ]; then
     bool 'Verbose reason code reporting (kernel size +=7K)' CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON
 fi
+
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/Makefile linux/drivers/isdn/Makefile
--- v2.1.91/linux/drivers/isdn/Makefile	Fri Jan 16 11:18:09 1998
+++ linux/drivers/isdn/Makefile	Wed Apr  1 16:20:57 1998
@@ -1,6 +1,6 @@
 SUB_DIRS     :=
 MOD_SUB_DIRS :=
-ALL_SUB_DIRS := icn pcbit hisax avmb1
+ALL_SUB_DIRS := icn pcbit hisax avmb1 act2000
 
 L_OBJS :=
 LX_OBJS :=
@@ -13,11 +13,15 @@
 
 ifeq ($(CONFIG_ISDN),y)
   L_TARGET := isdn.a
-  L_OBJS += isdn_common.o isdn_net.o isdn_tty.o isdn_cards.o
-  LX_OBJS += isdn_syms.o
+  L_OBJS += isdn_net.o isdn_tty.o isdn_cards.o isdn_v110.o
+  LX_OBJS += isdn_common.o 
   ifdef CONFIG_ISDN_PPP
     L_OBJS += isdn_ppp.o
   endif
+  ifdef CONFIG_ISDN_X25
+    L_OBJS += isdn_x25iface.o
+    L_OBJS += isdn_concap.o
+  endif
   ifdef CONFIG_ISDN_AUDIO
     L_OBJS += isdn_audio.o
   endif
@@ -25,11 +29,15 @@
   ifeq ($(CONFIG_ISDN),m)
     M_OBJS += isdn.o
     O_TARGET += isdn.o
-    O_OBJS += isdn_common.o isdn_net.o isdn_tty.o
-    OX_OBJS += isdn_syms.o
+    O_OBJS += isdn_net.o isdn_tty.o isdn_v110.o
+    OX_OBJS += isdn_common.o
     ifdef CONFIG_ISDN_PPP
       O_OBJS += isdn_ppp.o
     endif
+    ifdef CONFIG_ISDN_X25
+      O_OBJS += isdn_x25iface.o
+      O_OBJS += isdn_concap.o
+    endif  
     ifdef CONFIG_ISDN_AUDIO
       O_OBJS += isdn_audio.o
     endif
@@ -93,6 +101,16 @@
 else
   ifeq ($(CONFIG_ISDN_DRV_LOOP),m)
     MOD_SUB_DIRS += isdnloop
+  endif
+endif
+
+ifeq ($(CONFIG_ISDN_DRV_ACT2000),y)
+  L_OBJS += act2000/act2000.o
+  SUB_DIRS += act2000
+  MOD_SUB_DIRS += act2000
+else
+  ifeq ($(CONFIG_ISDN_DRV_ACT2000),m)
+    MOD_SUB_DIRS += act2000
   endif
 endif
 
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/act2000/Makefile linux/drivers/isdn/act2000/Makefile
--- v2.1.91/linux/drivers/isdn/act2000/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/act2000/Makefile	Wed Apr  1 16:20:57 1998
@@ -0,0 +1,15 @@
+L_OBJS :=
+M_OBJS :=
+O_OBJS := module.o capi.o act2000_isa.o
+
+O_TARGET :=
+ifeq ($(CONFIG_ISDN_DRV_ACT2000),y)
+  O_TARGET += act2000.o
+else
+  ifeq ($(CONFIG_ISDN_DRV_ACT2000),m)
+  	O_TARGET += act2000.o
+	M_OBJS = act2000.o
+  endif
+endif
+
+include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/act2000/act2000.h linux/drivers/isdn/act2000/act2000.h
--- v2.1.91/linux/drivers/isdn/act2000/act2000.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/act2000/act2000.h	Wed Apr  1 16:20:57 1998
@@ -0,0 +1,239 @@
+/* $Id: act2000.h,v 1.5 1997/10/09 22:22:59 fritz Exp $
+ *
+ * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000.
+ *
+ * Copyright 1997 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Thanks to Friedemann Baitinger and IBM Germany
+ *
+ * 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: act2000.h,v $
+ * Revision 1.5  1997/10/09 22:22:59  fritz
+ * New HL<->LL interface:
+ *   New BSENT callback with nr. of bytes included.
+ *   Sending without ACK.
+ *
+ * Revision 1.4  1997/09/25 17:25:37  fritz
+ * Support for adding cards at runtime.
+ * Support for new Firmware.
+ *
+ * Revision 1.3  1997/09/24 23:11:43  fritz
+ * Optimized IRQ load and polling-mode.
+ *
+ * Revision 1.2  1997/09/24 19:44:12  fritz
+ * Added MSN mapping support, some cleanup.
+ *
+ * Revision 1.1  1997/09/23 18:00:05  fritz
+ * New driver for IBM Active 2000.
+ *
+ */
+
+#ifndef act2000_h
+#define act2000_h
+
+#ifdef __KERNEL__
+/* Kernel includes */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
+#define ACT2000_IOCTL_SETPORT    1
+#define ACT2000_IOCTL_GETPORT    2
+#define ACT2000_IOCTL_SETIRQ     3
+#define ACT2000_IOCTL_GETIRQ     4
+#define ACT2000_IOCTL_SETBUS     5
+#define ACT2000_IOCTL_GETBUS     6
+#define ACT2000_IOCTL_SETPROTO   7
+#define ACT2000_IOCTL_GETPROTO   8
+#define ACT2000_IOCTL_SETMSN     9
+#define ACT2000_IOCTL_GETMSN    10
+#define ACT2000_IOCTL_LOADBOOT  11
+#define ACT2000_IOCTL_ADDCARD   12
+
+#define ACT2000_IOCTL_TEST      98
+#define ACT2000_IOCTL_DEBUGVAR  99
+
+#define ACT2000_BUS_ISA          1
+#define ACT2000_BUS_MCA          2
+#define ACT2000_BUS_PCMCIA       3
+
+/* Struct for adding new cards */
+typedef struct act2000_cdef {
+	int bus;
+        int port;
+        int irq;
+        char id[10];
+} act2000_cdef;
+
+/* Struct for downloading firmware */
+typedef struct act2000_ddef {
+        int length;             /* Length of code */
+        char *buffer;           /* Ptr. to code   */
+} act2000_ddef;
+
+typedef struct act2000_fwid {
+        char isdn[4];
+        char revlen[2];
+        char revision[504];
+} act2000_fwid;
+
+#if defined(__KERNEL__) || defined(__DEBUGVAR__)
+
+#ifdef __KERNEL__
+/* Kernel includes */
+
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/tqueue.h>
+#include <linux/interrupt.h>
+#include <linux/skbuff.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/major.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/ioport.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+#include <linux/ctype.h>
+#include <linux/isdnif.h>
+
+#endif                           /* __KERNEL__ */
+
+#define ACT2000_PORTLEN        8
+
+#define ACT2000_FLAGS_RUNNING  1 /* Cards driver activated */
+#define ACT2000_FLAGS_PVALID   2 /* Cards port is valid    */
+#define ACT2000_FLAGS_IVALID   4 /* Cards irq is valid     */
+#define ACT2000_FLAGS_LOADED   8 /* Firmware loaded        */
+
+#define ACT2000_BCH            2 /* # of channels per card */
+
+/* D-Channel states */
+#define ACT2000_STATE_NULL     0
+#define ACT2000_STATE_ICALL    1
+#define ACT2000_STATE_OCALL    2
+#define ACT2000_STATE_IWAIT    3
+#define ACT2000_STATE_OWAIT    4
+#define ACT2000_STATE_IBWAIT   5
+#define ACT2000_STATE_OBWAIT   6
+#define ACT2000_STATE_BWAIT    7
+#define ACT2000_STATE_BHWAIT   8
+#define ACT2000_STATE_BHWAIT2  9
+#define ACT2000_STATE_DHWAIT  10
+#define ACT2000_STATE_DHWAIT2 11
+#define ACT2000_STATE_BSETUP  12
+#define ACT2000_STATE_ACTIVE  13
+
+#define ACT2000_MAX_QUEUED  8000 /* 2 * maxbuff */
+
+#define ACT2000_LOCK_TX 0
+#define ACT2000_LOCK_RX 1
+
+typedef struct act2000_chan {
+	unsigned short callref;          /* Call Reference              */
+	unsigned short fsm_state;        /* Current D-Channel state     */
+	unsigned short eazmask;          /* EAZ-Mask for this Channel   */
+	short queued;                    /* User-Data Bytes in TX queue */
+	unsigned short plci;
+	unsigned short ncci;
+	unsigned char  l2prot;           /* Layer 2 protocol            */
+	unsigned char  l3prot;           /* Layer 3 protocol            */
+} act2000_chan;
+
+typedef struct msn_entry {
+	char eaz;
+        char msn[16];
+        struct msn_entry * next;
+} msn_entry;
+
+typedef struct irq_data_isa {
+	__u8           *rcvptr;
+	__u16           rcvidx;
+	__u16           rcvlen;
+	struct sk_buff *rcvskb;
+	__u8            rcvignore;
+	__u8            rcvhdr[8];
+} irq_data_isa;
+
+typedef union irq_data {
+	irq_data_isa isa;
+} irq_data;
+
+/*
+ * Per card driver data
+ */
+typedef struct act2000_card {
+        unsigned short port;             /* Base-port-address                */
+        unsigned short irq;              /* Interrupt                        */
+        u_char ptype;                    /* Protocol type (1TR6 or Euro)     */
+        u_char bus;                      /* Cardtype (ISA, MCA, PCMCIA)      */
+        struct act2000_card *next;	 /* Pointer to next device struct    */
+        int myid;                        /* Driver-Nr. assigned by linklevel */
+        unsigned long flags;             /* Statusflags                      */
+        unsigned long ilock;             /* Semaphores for IRQ-Routines      */
+	struct sk_buff_head rcvq;        /* Receive-Message queue            */
+	struct sk_buff_head sndq;        /* Send-Message queue               */
+	struct sk_buff_head ackq;        /* Data-Ack-Message queue           */
+	u_char *ack_msg;                 /* Ptr to User Data in User skb     */
+	__u16 need_b3ack;                /* Flag: Need ACK for current skb   */
+	struct sk_buff *sbuf;            /* skb which is currently sent      */
+	struct timer_list ptimer;        /* Poll timer                       */
+	struct tq_struct snd_tq;         /* Task struct for xmit bh          */
+	struct tq_struct rcv_tq;         /* Task struct for rcv bh           */
+	struct tq_struct poll_tq;        /* Task struct for polled rcv bh    */
+	msn_entry *msn_list;
+	unsigned short msgnum;           /* Message number fur sending       */
+	act2000_chan bch[ACT2000_BCH];   /* B-Channel status/control         */
+	char   status_buf[256];          /* Buffer for status messages       */
+	char   *status_buf_read;
+	char   *status_buf_write;
+	char   *status_buf_end;
+	irq_data idat;                   /* Data used for IRQ handler        */
+        isdn_if interface;               /* Interface to upper layer         */
+        char regname[35];                /* Name used for request_region     */
+} act2000_card;
+
+extern act2000_card *cards;
+
+extern __inline__ void act2000_schedule_tx(act2000_card *card)
+{
+        queue_task(&card->snd_tq, &tq_immediate);
+        mark_bh(IMMEDIATE_BH);
+}
+
+extern __inline__ void act2000_schedule_rx(act2000_card *card)
+{
+        queue_task(&card->rcv_tq, &tq_immediate);
+        mark_bh(IMMEDIATE_BH);
+}
+
+extern __inline__ void act2000_schedule_poll(act2000_card *card)
+{
+        queue_task(&card->poll_tq, &tq_immediate);
+        mark_bh(IMMEDIATE_BH);
+}
+
+extern char *act2000_find_eaz(act2000_card *, char);
+
+#endif                          /* defined(__KERNEL__) || defined(__DEBUGVAR__) */
+#endif                          /* act2000_h */
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/act2000/act2000_isa.c linux/drivers/isdn/act2000/act2000_isa.c
--- v2.1.91/linux/drivers/isdn/act2000/act2000_isa.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/act2000/act2000_isa.c	Wed Apr  1 16:20:57 1998
@@ -0,0 +1,525 @@
+/* $Id: act2000_isa.c,v 1.5 1998/02/12 23:06:47 keil Exp $
+ *
+ * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000 (ISA-Version).
+ *
+ * Copyright 1997 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Thanks to Friedemann Baitinger and IBM Germany
+ *
+ * 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: act2000_isa.c,v $
+ * Revision 1.5  1998/02/12 23:06:47  keil
+ * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
+ *
+ * Revision 1.4  1997/10/09 22:23:00  fritz
+ * New HL<->LL interface:
+ *   New BSENT callback with nr. of bytes included.
+ *   Sending without ACK.
+ *
+ * Revision 1.3  1997/09/25 17:25:38  fritz
+ * Support for adding cards at runtime.
+ * Support for new Firmware.
+ *
+ * Revision 1.2  1997/09/24 23:11:44  fritz
+ * Optimized IRQ load and polling-mode.
+ *
+ * Revision 1.1  1997/09/23 18:00:05  fritz
+ * New driver for IBM Active 2000.
+ *
+ */
+
+#define __NO_VERSION__
+#include "act2000.h"
+#include "act2000_isa.h"
+#include "capi.h"
+
+static act2000_card *irq2card_map[16] =
+{
+	0, 0, 0, 0, 0, 0, 0, 0,	0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static int isa_irqs[] =
+{
+        3, 5, 7, 10, 11, 12, 15
+};
+#define ISA_NRIRQS (sizeof(isa_irqs)/sizeof(int))
+
+static void
+isa_delay(long t)
+{
+        sti();
+        current->state = TASK_INTERRUPTIBLE;
+        current->timeout = jiffies + t;
+        schedule();
+        sti();
+}
+
+/*
+ * Reset Controller, then try to read the Card's signature.
+ + Return:
+ *   1 = Signature found.
+ *   0 = Signature not found.
+ */
+static int
+isa_reset(unsigned short portbase)
+{
+        unsigned char reg;
+        int i;
+        int found;
+        int serial = 0;
+
+        found = 0;
+        if ((reg = inb(portbase + ISA_COR)) != 0xff) {
+                outb(reg | ISA_COR_RESET, portbase + ISA_COR);
+                udelay(10000);
+                outb(reg, portbase + ISA_COR);
+                udelay(10000);
+
+                for (i = 0; i < 16; i++) {
+                        if (inb(portbase + ISA_ISR) & ISA_ISR_SERIAL)
+                                serial |= 0x10000;
+                        serial >>= 1;
+                }
+                if (serial == ISA_SER_ID)
+                        found++;
+        }
+        return found;
+}
+
+int
+isa_detect(unsigned short portbase)
+{
+        int ret = 0;
+        unsigned long flags;
+
+        save_flags(flags);
+        cli();
+        if (!check_region(portbase, ISA_REGION))
+                ret = isa_reset(portbase);
+        restore_flags(flags);
+        return ret;
+}
+
+static void
+isa_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+        act2000_card *card = irq2card_map[irq];
+        u_char istatus;
+
+        if (!card) {
+                printk(KERN_WARNING
+                       "act2000: Spurious interrupt!\n");
+                return;
+        }
+        istatus = (inb(ISA_PORT_ISR) & 0x07);
+        if (istatus & ISA_ISR_OUT) {
+                /* RX fifo has data */
+		istatus &= ISA_ISR_OUT_MASK;
+		outb(0, ISA_PORT_SIS);
+		isa_receive(card);
+		outb(ISA_SIS_INT, ISA_PORT_SIS);
+        }
+        if (istatus & ISA_ISR_ERR) {
+                /* Error Interrupt */
+		istatus &= ISA_ISR_ERR_MASK;
+                printk(KERN_WARNING "act2000: errIRQ\n");
+        }
+	if (istatus)
+		printk(KERN_DEBUG "act2000: ?IRQ %d %02x\n", irq, istatus);
+}
+
+static void
+isa_select_irq(act2000_card * card)
+{
+	unsigned char reg;
+
+	reg = (inb(ISA_PORT_COR) & ~ISA_COR_IRQOFF) | ISA_COR_PERR;
+	switch (card->irq) {
+		case 3:
+			reg = ISA_COR_IRQ03;
+			break;
+		case 5:
+			reg = ISA_COR_IRQ05;
+			break;
+		case 7:
+			reg = ISA_COR_IRQ07;
+			break;
+		case 10:
+			reg = ISA_COR_IRQ10;
+			break;
+		case 11:
+			reg = ISA_COR_IRQ11;
+			break;
+		case 12:
+			reg = ISA_COR_IRQ12;
+			break;
+		case 15:
+			reg = ISA_COR_IRQ15;
+			break;
+	}
+	outb(reg, ISA_PORT_COR);
+}
+
+static void
+isa_enable_irq(act2000_card * card)
+{
+	isa_select_irq(card);
+	/* Enable READ irq */
+	outb(ISA_SIS_INT, ISA_PORT_SIS);
+}
+
+/*
+ * Install interrupt handler, enable irq on card.
+ * If irq is -1, choose next free irq, else irq is given explicitely.
+ */
+int
+isa_config_irq(act2000_card * card, short irq)
+{
+        int i;
+        unsigned long flags;
+
+        if (card->flags & ACT2000_FLAGS_IVALID) {
+                free_irq(card->irq, NULL);
+                irq2card_map[card->irq] = NULL;
+        }
+        card->flags &= ~ACT2000_FLAGS_IVALID;
+        outb(ISA_COR_IRQOFF, ISA_PORT_COR);
+        if (!irq)
+                return 0;
+        save_flags(flags);
+        cli();
+        if (irq == -1) {
+                /* Auto select */
+                for (i = 0; i < ISA_NRIRQS; i++) {
+                        if (!request_irq(isa_irqs[i], &isa_interrupt, 0, card->regname, NULL)) {
+                                card->irq = isa_irqs[i];
+                                irq2card_map[card->irq] = card;
+                                card->flags |= ACT2000_FLAGS_IVALID;
+                                break;
+                        }
+                }
+        } else {
+                /* Fixed irq */
+                if (!request_irq(irq, &isa_interrupt, 0, card->regname, NULL)) {
+                        card->irq = irq;
+                        irq2card_map[card->irq] = card;
+			card->flags |= ACT2000_FLAGS_IVALID;
+                }
+        }
+        restore_flags(flags);
+        if (!card->flags & ACT2000_FLAGS_IVALID) {
+                printk(KERN_WARNING
+                       "act2000: Could not request irq\n");
+                return -EBUSY;
+        } else {
+		isa_select_irq(card);
+                /* Disable READ and WRITE irq */
+                outb(0, ISA_PORT_SIS);
+                outb(0, ISA_PORT_SOS);
+        }
+        return 0;
+}
+
+int
+isa_config_port(act2000_card * card, unsigned short portbase)
+{
+        if (card->flags & ACT2000_FLAGS_PVALID) {
+                release_region(card->port, ISA_REGION);
+                card->flags &= ~ACT2000_FLAGS_PVALID;
+        }
+        if (!check_region(portbase, ISA_REGION)) {
+                request_region(portbase, ACT2000_PORTLEN, card->regname);
+                card->port = portbase;
+                card->flags |= ACT2000_FLAGS_PVALID;
+                return 0;
+        }
+        return -EBUSY;
+}
+
+/*
+ * Release ressources, used by an adaptor.
+ */
+void
+isa_release(act2000_card * card)
+{
+        unsigned long flags;
+
+        save_flags(flags);
+        cli();
+        if (card->flags & ACT2000_FLAGS_IVALID) {
+                free_irq(card->irq, NULL);
+                irq2card_map[card->irq] = NULL;
+        }
+        card->flags &= ~ACT2000_FLAGS_IVALID;
+        if (card->flags & ACT2000_FLAGS_PVALID)
+                release_region(card->port, ISA_REGION);
+        card->flags &= ~ACT2000_FLAGS_PVALID;
+        restore_flags(flags);
+}
+
+static int
+isa_writeb(act2000_card * card, u_char data)
+{
+        u_char timeout = 40;
+
+        while (timeout) {
+                if (inb(ISA_PORT_SOS) & ISA_SOS_READY) {
+                        outb(data, ISA_PORT_SDO);
+                        return 0;
+                } else {
+                        timeout--;
+                        udelay(10);
+                }
+        }
+        return 1;
+}
+
+static int
+isa_readb(act2000_card * card, u_char * data)
+{
+        u_char timeout = 40;
+
+        while (timeout) {
+                if (inb(ISA_PORT_SIS) & ISA_SIS_READY) {
+                        *data = inb(ISA_PORT_SDI);
+                        return 0;
+                } else {
+                        timeout--;
+                        udelay(10);
+                }
+        }
+        return 1;
+}
+
+void
+isa_receive(act2000_card *card)
+{
+	u_char c;
+
+        if (test_and_set_bit(ACT2000_LOCK_RX, (void *) &card->ilock) != 0)
+		return;
+	while (!isa_readb(card, &c)) {
+		if (card->idat.isa.rcvidx < 8) {
+                        card->idat.isa.rcvhdr[card->idat.isa.rcvidx++] = c;
+			if (card->idat.isa.rcvidx == 8) {
+				int valid = actcapi_chkhdr(card, (actcapi_msghdr *)&card->idat.isa.rcvhdr);
+
+				if (valid) {
+					card->idat.isa.rcvlen = ((actcapi_msghdr *)&card->idat.isa.rcvhdr)->len;
+					card->idat.isa.rcvskb = dev_alloc_skb(card->idat.isa.rcvlen);
+					if (card->idat.isa.rcvskb == NULL) {
+						card->idat.isa.rcvignore = 1;
+						printk(KERN_WARNING
+						       "isa_receive: no memory\n");
+						test_and_clear_bit(ACT2000_LOCK_RX, (void *) &card->ilock);
+						return;
+					}
+					memcpy(skb_put(card->idat.isa.rcvskb, 8), card->idat.isa.rcvhdr, 8);
+					card->idat.isa.rcvptr = skb_put(card->idat.isa.rcvskb, card->idat.isa.rcvlen - 8);
+				} else {
+					card->idat.isa.rcvidx = 0;
+					printk(KERN_WARNING
+					       "isa_receive: Invalid CAPI msg\n");
+					{
+						int i; __u8 *p; __u8 *c; __u8 tmp[30];
+						for (i = 0, p = (__u8 *)&card->idat.isa.rcvhdr, c = tmp; i < 8; i++)
+							c += sprintf(c, "%02x ", *(p++));
+						printk(KERN_WARNING "isa_receive: %s\n", tmp);
+					}
+				}
+			}
+		} else {
+			if (!card->idat.isa.rcvignore)
+				*card->idat.isa.rcvptr++ = c;
+			if (++card->idat.isa.rcvidx >= card->idat.isa.rcvlen) {
+				if (!card->idat.isa.rcvignore) {
+					skb_queue_tail(&card->rcvq, card->idat.isa.rcvskb);
+					act2000_schedule_rx(card);
+				}
+				card->idat.isa.rcvidx = 0;
+				card->idat.isa.rcvlen = 8;
+				card->idat.isa.rcvignore = 0;
+				card->idat.isa.rcvskb = NULL;
+				card->idat.isa.rcvptr = card->idat.isa.rcvhdr;
+			}
+		}
+	}
+	if (!(card->flags & ACT2000_FLAGS_IVALID)) {
+		/* In polling mode, schedule myself */
+		if ((card->idat.isa.rcvidx) &&
+		    (card->idat.isa.rcvignore ||
+		     (card->idat.isa.rcvidx < card->idat.isa.rcvlen)))
+			act2000_schedule_poll(card);
+	}
+	test_and_clear_bit(ACT2000_LOCK_RX, (void *) &card->ilock);
+}
+
+void
+isa_send(act2000_card * card)
+{
+	unsigned long flags;
+	struct sk_buff *skb;
+	actcapi_msg *msg;
+	int l;
+
+        if (test_and_set_bit(ACT2000_LOCK_TX, (void *) &card->ilock) != 0)
+		return;
+	while (1) {
+		save_flags(flags);
+		cli();
+		if (!(card->sbuf)) {
+			if ((card->sbuf = skb_dequeue(&card->sndq))) {
+				card->ack_msg = card->sbuf->data;
+				msg = (actcapi_msg *)card->sbuf->data;
+				if ((msg->hdr.cmd.cmd == 0x86) &&
+				    (msg->hdr.cmd.subcmd == 0)   ) {
+					/* Save flags in message */
+					card->need_b3ack = msg->msg.data_b3_req.flags;
+					msg->msg.data_b3_req.flags = 0;
+				}
+			}
+		}
+		restore_flags(flags);
+		if (!(card->sbuf)) {
+			/* No more data to send */
+			test_and_clear_bit(ACT2000_LOCK_TX, (void *) &card->ilock);
+			return;
+		}
+		skb = card->sbuf;
+		l = 0;
+		while (skb->len) {
+			if (isa_writeb(card, *(skb->data))) {
+				/* Fifo is full, but more data to send */
+#if 0
+				printk(KERN_DEBUG "isa_send: %d bytes\n", l);
+#endif
+				test_and_clear_bit(ACT2000_LOCK_TX, (void *) &card->ilock);
+				/* Schedule myself */
+				act2000_schedule_tx(card);
+				return;
+			}
+			skb_pull(skb, 1);
+			l++;
+		}
+		msg = (actcapi_msg *)card->ack_msg;
+		if ((msg->hdr.cmd.cmd == 0x86) &&
+		    (msg->hdr.cmd.subcmd == 0)   ) {
+			/*
+			 * If it's user data, reset data-ptr
+			 * and put skb into ackq.
+			 */
+			skb->data = card->ack_msg;
+			/* Restore flags in message */
+			msg->msg.data_b3_req.flags = card->need_b3ack;
+			skb_queue_tail(&card->ackq, skb);
+		} else
+			dev_kfree_skb(skb);
+		card->sbuf = NULL;
+#if 0
+		printk(KERN_DEBUG "isa_send: %d bytes\n", l);
+#endif
+	}
+}
+
+/*
+ * Get firmware ID, check for 'ISDN' signature.
+ */
+static int
+isa_getid(act2000_card * card)
+{
+
+        act2000_fwid fid;
+        u_char *p = (u_char *) & fid;
+        int count = 0;
+
+        while (1) {
+                if (count > 510)
+                        return -EPROTO;
+                if (isa_readb(card, p++))
+                        break;
+                count++;
+        }
+        if (count <= 20) {
+                printk(KERN_WARNING "act2000: No Firmware-ID!\n");
+                return -ETIME;
+        }
+        *p = '\0';
+        fid.revlen[0] = '\0';
+        if (strcmp(fid.isdn, "ISDN")) {
+                printk(KERN_WARNING "act2000: Wrong Firmware-ID!\n");
+                return -EPROTO;
+        }
+	if ((p = strchr(fid.revision, '\n')))
+		*p = '\0';
+        printk(KERN_INFO "act2000: Firmware-ID: %s\n", fid.revision);
+	if (card->flags & ACT2000_FLAGS_IVALID) {
+		printk(KERN_DEBUG "Enabling Interrupts ...\n");
+		isa_enable_irq(card);
+	}
+        return 0;
+}
+
+/*
+ * Download microcode into card, check Firmware signature.
+ */
+int
+isa_download(act2000_card * card, act2000_ddef * cb)
+{
+        int length;
+        int ret;
+        int l;
+        int c;
+        long timeout;
+        u_char *b;
+        u_char *p;
+        u_char *buf;
+        act2000_ddef cblock;
+
+        if (!isa_reset(card->port))
+                return -ENXIO;
+        isa_delay(HZ / 2);
+        if ((ret = verify_area(VERIFY_READ, (void *) cb, sizeof(cblock))))
+                return ret;
+        copy_from_user(&cblock, (char *) cb, sizeof(cblock));
+        length = cblock.length;
+        p = cblock.buffer;
+        if ((ret = verify_area(VERIFY_READ, (void *) p, length)))
+                return ret;
+        buf = (u_char *) kmalloc(1024, GFP_KERNEL);
+        if (!buf)
+                return -ENOMEM;
+        timeout = 0;
+        while (length) {
+                l = (length > 1024) ? 1024 : length;
+                c = 0;
+                b = buf;
+                copy_from_user(buf, p, l);
+                while (c < l) {
+                        if (isa_writeb(card, *b++)) {
+                                printk(KERN_WARNING
+                                       "act2000: loader timed out"
+                                       " len=%d c=%d\n", length, c);
+                                kfree(buf);
+                                return -ETIME;
+                        }
+                        c++;
+                }
+                length -= l;
+                p += l;
+        }
+        kfree(buf);
+        isa_delay(HZ / 2);
+        return (isa_getid(card));
+}
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/act2000/act2000_isa.h linux/drivers/isdn/act2000/act2000_isa.h
--- v2.1.91/linux/drivers/isdn/act2000/act2000_isa.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/act2000/act2000_isa.h	Wed Apr  1 16:20:57 1998
@@ -0,0 +1,149 @@
+/* $Id: act2000_isa.h,v 1.1 1997/09/23 18:00:07 fritz Exp $
+ *
+ * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000 (ISA-Version).
+ *
+ * Copyright 1997 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Thanks to Friedemann Baitinger and IBM Germany
+ *
+ * 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: act2000_isa.h,v $
+ * Revision 1.1  1997/09/23 18:00:07  fritz
+ * New driver for IBM Active 2000.
+ *
+ */
+
+#ifndef act2000_isa_h
+#define act2000_isa_h
+
+#define ISA_POLL_LOOP 40        /* Try to read-write before give up */
+
+typedef enum {
+        INT_NO_CHANGE = 0,      /* Do not change the Mask */
+        INT_ON = 1,             /* Set to Enable */
+        INT_OFF = 2,            /* Set to Disable */
+} ISA_INT_T;
+
+/**************************************************************************/
+/*      Configuration Register COR (RW)                                   */
+/**************************************************************************/
+/*    7    |   6    |    5   |   4    |    3   |    2   |    1   |    0   */
+/* Soft Res|  IRQM  |        IRQ Select        |   N/A  |  WAIT  |Proc err */
+/**************************************************************************/
+#define        ISA_COR             0	/* Offset for ISA config register */
+#define        ISA_COR_PERR     0x01	/* Processor Error Enabled        */
+#define        ISA_COR_WS       0x02	/* Insert Wait State if 1         */
+#define        ISA_COR_IRQOFF   0x38	/* No Interrupt                   */
+#define        ISA_COR_IRQ07    0x30	/* IRQ 7 Enable                   */
+#define        ISA_COR_IRQ05    0x28	/* IRQ 5 Enable                   */
+#define        ISA_COR_IRQ03    0x20	/* IRQ 3 Enable                   */
+#define        ISA_COR_IRQ10    0x18	/* IRQ 10 Enable                  */
+#define        ISA_COR_IRQ11    0x10	/* IRQ 11 Enable                  */
+#define        ISA_COR_IRQ12    0x08	/* IRQ 12 Enable                  */
+#define        ISA_COR_IRQ15    0x00	/* IRQ 15 Enable                  */
+#define        ISA_COR_IRQPULSE 0x40	/* 0 = Level 1 = Pulse Interrupt  */
+#define        ISA_COR_RESET    0x80	/* Soft Reset for Transputer      */
+
+/**************************************************************************/
+/*      Interrupt Source Register ISR (RO)                                */
+/**************************************************************************/
+/*    7    |   6    |    5   |   4    |    3   |    2   |    1   |    0   */
+/*   N/A   |  N/A   |   N/A  |Err sig |Ser ID  |IN Intr |Out Intr| Error  */
+/**************************************************************************/
+#define        ISA_ISR             1	/* Offset for Interrupt Register  */
+#define        ISA_ISR_ERR      0x01	/* Error Interrupt                */
+#define        ISA_ISR_OUT      0x02	/* Output Interrupt               */
+#define        ISA_ISR_INP      0x04	/* Input Interrupt                */
+#define        ISA_ISR_SERIAL   0x08	/* Read out Serial ID after Reset */
+#define        ISA_ISR_ERRSIG   0x10	/* Error Signal Input             */
+#define        ISA_ISR_ERR_MASK 0xfe    /* Mask Error Interrupt           */
+#define        ISA_ISR_OUT_MASK 0xfd    /* Mask Output Interrupt          */
+#define        ISA_ISR_INP_MASK 0xfb    /* Mask Input Interrupt           */
+
+/* Signature delivered after Reset at ISA_ISR_SERIAL (LSB first)          */
+#define        ISA_SER_ID     0x0201	/* ID for ISA Card                */
+
+/**************************************************************************/
+/*      EEPROM Register EPR (RW)                                          */
+/**************************************************************************/
+/*    7    |   6    |    5   |   4    |    3   |    2   |    1   |    0   */
+/*   N/A   |  N/A   |   N/A  |ROM Hold| ROM CS |ROM CLK | ROM IN |ROM Out */
+/**************************************************************************/
+#define        ISA_EPR             2	/* Offset for this Register       */
+#define        ISA_EPR_OUT      0x01	/* Rome Register Out (RO)         */
+#define        ISA_EPR_IN       0x02	/* Rom Register In (WR)           */
+#define        ISA_EPR_CLK      0x04	/* Rom Clock (WR)                 */
+#define        ISA_EPR_CS       0x08	/* Rom Cip Select (WR)            */
+#define        ISA_EPR_HOLD     0x10	/* Rom Hold Signal (WR)           */
+
+/**************************************************************************/
+/*      EEPROM enable Register EER (unused)                               */
+/**************************************************************************/
+#define        ISA_EER             3	/* Offset for this Register       */
+
+/**************************************************************************/
+/*      SLC Data Input SDI (RO)                                           */
+/**************************************************************************/
+#define        ISA_SDI             4	/* Offset for this Register       */
+
+/**************************************************************************/
+/*      SLC Data Output SDO (WO)                                          */
+/**************************************************************************/
+#define        ISA_SDO             5	/* Offset for this Register       */
+
+/**************************************************************************/
+/*      IMS C011 Mode 2 Input Status Register for INMOS CPU SIS (RW)      */
+/**************************************************************************/
+/*    7    |   6    |    5   |   4    |    3   |    2   |    1   |    0   */
+/*   N/A   |  N/A   |   N/A  |  N/A   |   N/A  |   N/A  |Int Ena |Data Pre */
+/**************************************************************************/
+#define        ISA_SIS             6	/* Offset for this Register       */
+#define        ISA_SIS_READY    0x01	/* If 1 : data is available       */
+#define        ISA_SIS_INT      0x02	/* Enable Interrupt for READ      */
+
+/**************************************************************************/
+/*      IMS C011 Mode 2 Output Status Register from INMOS CPU SOS (RW)    */
+/**************************************************************************/
+/*    7    |   6    |    5   |   4    |    3   |    2   |    1   |    0   */
+/*   N/A   |  N/A   |   N/A  |  N/A   |   N/A  |   N/A  |Int Ena |Out Rdy */
+/**************************************************************************/
+#define        ISA_SOS             7	/* Offset for this Register       */
+#define        ISA_SOS_READY    0x01	/* If 1 : we can write Data       */
+#define        ISA_SOS_INT      0x02	/* Enable Interrupt for WRITE     */
+
+#define        ISA_REGION          8	/* Number of Registers            */
+
+
+/* Macros for accessing ports */
+#define ISA_PORT_COR (card->port+ISA_COR)
+#define ISA_PORT_ISR (card->port+ISA_ISR)
+#define ISA_PORT_EPR (card->port+ISA_EPR)
+#define ISA_PORT_EER (card->port+ISA_EER)
+#define ISA_PORT_SDI (card->port+ISA_SDI)
+#define ISA_PORT_SDO (card->port+ISA_SDO)
+#define ISA_PORT_SIS (card->port+ISA_SIS)
+#define ISA_PORT_SOS (card->port+ISA_SOS)
+
+/* Prototypes */
+
+extern int isa_detect(unsigned short portbase);
+extern int isa_config_irq(act2000_card * card, short irq);
+extern int isa_config_port(act2000_card * card, unsigned short portbase);
+extern int isa_download(act2000_card * card, act2000_ddef * cb);
+extern void isa_release(act2000_card * card);
+extern void isa_receive(act2000_card *card);
+extern void isa_send(act2000_card *card);
+
+#endif                          /* act2000_isa_h */
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/act2000/capi.c linux/drivers/isdn/act2000/capi.c
--- v2.1.91/linux/drivers/isdn/act2000/capi.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/act2000/capi.c	Wed Apr  1 16:20:57 1998
@@ -0,0 +1,1218 @@
+/* $Id: capi.c,v 1.7 1998/02/23 23:35:41 fritz Exp $
+ *
+ * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000.
+ *        CAPI encoder/decoder
+ *
+ * Copyright 1997 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Thanks to Friedemann Baitinger and IBM Germany
+ *
+ * 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: capi.c,v $
+ * Revision 1.7  1998/02/23 23:35:41  fritz
+ * Eliminated some compiler warnings.
+ *
+ * Revision 1.6  1998/02/12 23:06:50  keil
+ * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
+ *
+ * Revision 1.5  1997/10/09 22:23:02  fritz
+ * New HL<->LL interface:
+ *   New BSENT callback with nr. of bytes included.
+ *   Sending without ACK.
+ *
+ * Revision 1.4  1997/09/25 17:25:39  fritz
+ * Support for adding cards at runtime.
+ * Support for new Firmware.
+ *
+ * Revision 1.3  1997/09/24 19:44:14  fritz
+ * Added MSN mapping support, some cleanup.
+ *
+ * Revision 1.2  1997/09/23 19:41:24  fritz
+ * Disabled Logging of DATA_B3_IND/RESP/REQ/CONF Messages.
+ *
+ * Revision 1.1  1997/09/23 18:00:08  fritz
+ * New driver for IBM Active 2000.
+ *
+ */
+
+#define __NO_VERSION__
+#include "act2000.h"
+#include "capi.h"
+
+static actcapi_msgdsc valid_msg[] = {
+	{{ 0x86, 0x02}, "DATA_B3_IND"},       /* DATA_B3_IND/CONF must be first because of speed!!! */
+	{{ 0x86, 0x01}, "DATA_B3_CONF"},
+	{{ 0x02, 0x01}, "CONNECT_CONF"},
+	{{ 0x02, 0x02}, "CONNECT_IND"},
+	{{ 0x09, 0x01}, "CONNECT_INFO_CONF"},
+	{{ 0x03, 0x02}, "CONNECT_ACTIVE_IND"},
+	{{ 0x04, 0x01}, "DISCONNECT_CONF"},
+	{{ 0x04, 0x02}, "DISCONNECT_IND"},
+	{{ 0x05, 0x01}, "LISTEN_CONF"},
+	{{ 0x06, 0x01}, "GET_PARAMS_CONF"},
+	{{ 0x07, 0x01}, "INFO_CONF"},
+	{{ 0x07, 0x02}, "INFO_IND"},
+	{{ 0x08, 0x01}, "DATA_CONF"},
+	{{ 0x08, 0x02}, "DATA_IND"},
+	{{ 0x40, 0x01}, "SELECT_B2_PROTOCOL_CONF"},
+	{{ 0x80, 0x01}, "SELECT_B3_PROTOCOL_CONF"},
+	{{ 0x81, 0x01}, "LISTEN_B3_CONF"},
+	{{ 0x82, 0x01}, "CONNECT_B3_CONF"},
+	{{ 0x82, 0x02}, "CONNECT_B3_IND"},
+	{{ 0x83, 0x02}, "CONNECT_B3_ACTIVE_IND"},
+	{{ 0x84, 0x01}, "DISCONNECT_B3_CONF"},
+	{{ 0x84, 0x02}, "DISCONNECT_B3_IND"},
+	{{ 0x85, 0x01}, "GET_B3_PARAMS_CONF"},
+	{{ 0x01, 0x01}, "RESET_B3_CONF"},
+	{{ 0x01, 0x02}, "RESET_B3_IND"},
+	/* {{ 0x87, 0x02, "HANDSET_IND"}, not implemented */
+	{{ 0xff, 0x01}, "MANUFACTURER_CONF"},
+	{{ 0xff, 0x02}, "MANUFACTURER_IND"},
+#ifdef DEBUG_MSG
+	/* Requests */
+	{{ 0x01, 0x00}, "RESET_B3_REQ"},
+	{{ 0x02, 0x00}, "CONNECT_REQ"},
+	{{ 0x04, 0x00}, "DISCONNECT_REQ"},
+	{{ 0x05, 0x00}, "LISTEN_REQ"},
+	{{ 0x06, 0x00}, "GET_PARAMS_REQ"},
+	{{ 0x07, 0x00}, "INFO_REQ"},
+	{{ 0x08, 0x00}, "DATA_REQ"},
+	{{ 0x09, 0x00}, "CONNECT_INFO_REQ"},
+	{{ 0x40, 0x00}, "SELECT_B2_PROTOCOL_REQ"},
+	{{ 0x80, 0x00}, "SELECT_B3_PROTOCOL_REQ"},
+	{{ 0x81, 0x00}, "LISTEN_B3_REQ"},
+	{{ 0x82, 0x00}, "CONNECT_B3_REQ"},
+	{{ 0x84, 0x00}, "DISCONNECT_B3_REQ"},
+	{{ 0x85, 0x00}, "GET_B3_PARAMS_REQ"},
+	{{ 0x86, 0x00}, "DATA_B3_REQ"},
+	{{ 0xff, 0x00}, "MANUFACTURER_REQ"},
+	/* Responses */
+	{{ 0x01, 0x03}, "RESET_B3_RESP"},	
+	{{ 0x02, 0x03}, "CONNECT_RESP"},	
+	{{ 0x03, 0x03}, "CONNECT_ACTIVE_RESP"},	
+	{{ 0x04, 0x03}, "DISCONNECT_RESP"},	
+	{{ 0x07, 0x03}, "INFO_RESP"},	
+	{{ 0x08, 0x03}, "DATA_RESP"},	
+	{{ 0x82, 0x03}, "CONNECT_B3_RESP"},	
+	{{ 0x83, 0x03}, "CONNECT_B3_ACTIVE_RESP"},	
+	{{ 0x84, 0x03}, "DISCONNECT_B3_RESP"},
+	{{ 0x86, 0x03}, "DATA_B3_RESP"},
+	{{ 0xff, 0x03}, "MANUFACTURER_RESP"},
+#if 0
+/* CAPI 2.0 */
+	{{ 0x05, 0x80}, "LISTEN_REQ (CAPI 2.0)"},
+#endif
+#endif
+	{{ 0x00, 0x00}, NULL},
+};
+#define num_valid_msg (sizeof(valid_msg)/sizeof(actcapi_msgdsc))
+#define num_valid_imsg 27 /* MANUFACTURER_IND */
+
+/*
+ * Check for a valid incoming CAPI message.
+ * Return:
+ *   0 = Invalid message
+ *   1 = Valid message, no B-Channel-data
+ *   2 = Valid message, B-Channel-data
+ */
+int
+actcapi_chkhdr(act2000_card * card, actcapi_msghdr *hdr)
+{
+	int i;
+
+	if (hdr->applicationID != 1)
+		return 0;
+	if (hdr->len < 9)
+		return 0;
+	for (i = 0; i < num_valid_imsg; i++)
+		if ((hdr->cmd.cmd == valid_msg[i].cmd.cmd) &&
+		    (hdr->cmd.subcmd == valid_msg[i].cmd.subcmd)) {
+			return (i?1:2);
+		}
+	return 0;
+}
+
+#define ACTCAPI_MKHDR(l, c, s) { \
+	skb = alloc_skb(l + 8, GFP_ATOMIC); \
+	if (skb) { \
+	        m = (actcapi_msg *)skb_put(skb, l + 8); \
+		m->hdr.len = l + 8; \
+		m->hdr.applicationID = 1; \
+	        m->hdr.cmd.cmd = c; \
+	        m->hdr.cmd.subcmd = s; \
+	        m->hdr.msgnum = actcapi_nextsmsg(card); \
+	} \
+}
+
+#define ACTCAPI_CHKSKB if (!skb) { \
+	printk(KERN_WARNING "actcapi: alloc_skb failed\n"); \
+	return; \
+}
+
+#define ACTCAPI_QUEUE_TX { \
+	actcapi_debug_msg(skb, 1); \
+	skb_queue_tail(&card->sndq, skb); \
+	act2000_schedule_tx(card); \
+}
+
+int
+actcapi_listen_req(act2000_card *card)
+{
+	__u16 eazmask = 0;
+	int i;
+	actcapi_msg *m;
+	struct sk_buff *skb;
+
+	for (i = 0; i < ACT2000_BCH; i++)
+		eazmask |= card->bch[i].eazmask;
+	ACTCAPI_MKHDR(9, 0x05, 0x00);
+        if (!skb) {
+                printk(KERN_WARNING "actcapi: alloc_skb failed\n");
+                return -ENOMEM;
+        }
+	m->msg.listen_req.controller = 0;
+	m->msg.listen_req.infomask = 0x3f; /* All information */
+	m->msg.listen_req.eazmask = eazmask;
+	m->msg.listen_req.simask = (eazmask)?0x86:0; /* All SI's  */
+	ACTCAPI_QUEUE_TX;
+        return 0;
+}
+
+int
+actcapi_connect_req(act2000_card *card, act2000_chan *chan, char *phone,
+		    char eaz, int si1, int si2)
+{
+	actcapi_msg *m;
+	struct sk_buff *skb;
+
+	ACTCAPI_MKHDR((11 + strlen(phone)), 0x02, 0x00);
+	if (!skb) {
+                printk(KERN_WARNING "actcapi: alloc_skb failed\n");
+		chan->fsm_state = ACT2000_STATE_NULL;
+		return -ENOMEM;
+	}
+	m->msg.connect_req.controller = 0;
+	m->msg.connect_req.bchan = 0x83;
+	m->msg.connect_req.infomask = 0x3f;
+	m->msg.connect_req.si1 = si1;
+	m->msg.connect_req.si2 = si2;
+	m->msg.connect_req.eaz = eaz?eaz:'0';
+	m->msg.connect_req.addr.len = strlen(phone) + 1;
+	m->msg.connect_req.addr.tnp = 0x81;
+	memcpy(m->msg.connect_req.addr.num, phone, strlen(phone));
+	chan->callref = m->hdr.msgnum;
+	ACTCAPI_QUEUE_TX;
+	return 0;
+}
+
+static void
+actcapi_connect_b3_req(act2000_card *card, act2000_chan *chan)
+{
+	actcapi_msg *m;
+	struct sk_buff *skb;
+
+	ACTCAPI_MKHDR(17, 0x82, 0x00);
+	ACTCAPI_CHKSKB;
+	m->msg.connect_b3_req.plci = chan->plci;
+	memset(&m->msg.connect_b3_req.ncpi, 0,
+	       sizeof(m->msg.connect_b3_req.ncpi));
+	m->msg.connect_b3_req.ncpi.len = 13;
+	m->msg.connect_b3_req.ncpi.modulo = 8;
+	ACTCAPI_QUEUE_TX;
+}
+
+/*
+ * Set net type (1TR6) or (EDSS1)
+ */
+int
+actcapi_manufacturer_req_net(act2000_card *card)
+{
+	actcapi_msg *m;
+	struct sk_buff *skb;
+
+	ACTCAPI_MKHDR(5, 0xff, 0x00);
+        if (!skb) {
+                printk(KERN_WARNING "actcapi: alloc_skb failed\n");
+                return -ENOMEM;
+        }
+	m->msg.manufacturer_req_net.manuf_msg = 0x11;
+	m->msg.manufacturer_req_net.controller = 1;
+	m->msg.manufacturer_req_net.nettype = (card->ptype == ISDN_PTYPE_EURO)?1:0;
+	ACTCAPI_QUEUE_TX;
+	printk(KERN_INFO "act2000 %s: D-channel protocol now %s\n",
+	       card->interface.id, (card->ptype == ISDN_PTYPE_EURO)?"euro":"1tr6");
+	card->interface.features &=
+		~(ISDN_FEATURE_P_UNKNOWN | ISDN_FEATURE_P_EURO | ISDN_FEATURE_P_1TR6);
+	card->interface.features |=
+		((card->ptype == ISDN_PTYPE_EURO)?ISDN_FEATURE_P_EURO:ISDN_FEATURE_P_1TR6);
+        return 0;
+}
+
+/*
+ * Switch V.42 on or off
+ */
+int
+actcapi_manufacturer_req_v42(act2000_card *card, ulong arg)
+{
+	actcapi_msg *m;
+	struct sk_buff *skb;
+
+	ACTCAPI_MKHDR(8, 0xff, 0x00);
+        if (!skb) {
+
+                printk(KERN_WARNING "actcapi: alloc_skb failed\n");
+                return -ENOMEM;
+        }
+	m->msg.manufacturer_req_v42.manuf_msg = 0x10;
+	m->msg.manufacturer_req_v42.controller = 0;
+	m->msg.manufacturer_req_v42.v42control = (arg?1:0);
+	ACTCAPI_QUEUE_TX;
+        return 0;
+}
+
+/*
+ * Set error-handler
+ */
+int
+actcapi_manufacturer_req_errh(act2000_card *card)
+{
+	actcapi_msg *m;
+	struct sk_buff *skb;
+
+	ACTCAPI_MKHDR(4, 0xff, 0x00);
+        if (!skb) {
+
+                printk(KERN_WARNING "actcapi: alloc_skb failed\n");
+                return -ENOMEM;
+        }
+	m->msg.manufacturer_req_err.manuf_msg = 0x03;
+	m->msg.manufacturer_req_err.controller = 0;
+	ACTCAPI_QUEUE_TX;
+        return 0;
+}
+
+/*
+ * Set MSN-Mapping.
+ */
+int
+actcapi_manufacturer_req_msn(act2000_card *card)
+{
+	msn_entry *p = card->msn_list;
+	actcapi_msg *m;
+	struct sk_buff *skb;
+	int len;
+
+	while (p) {
+		int i;
+
+		len = strlen(p->msn);
+		for (i = 0; i < 2; i++) {
+			ACTCAPI_MKHDR(6 + len, 0xff, 0x00);
+			if (!skb) {
+				printk(KERN_WARNING "actcapi: alloc_skb failed\n");
+				return -ENOMEM;
+			}
+			m->msg.manufacturer_req_msn.manuf_msg = 0x13 + i;
+			m->msg.manufacturer_req_msn.controller = 0;
+			m->msg.manufacturer_req_msn.msnmap.eaz = p->eaz;
+			m->msg.manufacturer_req_msn.msnmap.len = len;
+			memcpy(m->msg.manufacturer_req_msn.msnmap.msn, p->msn, len);
+			ACTCAPI_QUEUE_TX;
+		}
+		p = p->next;
+	}
+        return 0;
+}
+
+void
+actcapi_select_b2_protocol_req(act2000_card *card, act2000_chan *chan)
+{
+	actcapi_msg *m;
+	struct sk_buff *skb;
+
+	ACTCAPI_MKHDR(10, 0x40, 0x00);
+	ACTCAPI_CHKSKB;
+	m->msg.select_b2_protocol_req.plci = chan->plci;
+	memset(&m->msg.select_b2_protocol_req.dlpd, 0,
+	       sizeof(m->msg.select_b2_protocol_req.dlpd));
+	m->msg.select_b2_protocol_req.dlpd.len = 6;
+	switch (chan->l2prot) {
+		case ISDN_PROTO_L2_TRANS:
+			m->msg.select_b2_protocol_req.protocol = 0x03;
+			m->msg.select_b2_protocol_req.dlpd.dlen = 4000;
+			break;
+		case ISDN_PROTO_L2_HDLC:
+			m->msg.select_b2_protocol_req.protocol = 0x02;
+			m->msg.select_b2_protocol_req.dlpd.dlen = 4000;
+			break;
+		case ISDN_PROTO_L2_X75I:
+		case ISDN_PROTO_L2_X75UI:
+		case ISDN_PROTO_L2_X75BUI:
+			m->msg.select_b2_protocol_req.protocol = 0x01;
+			m->msg.select_b2_protocol_req.dlpd.dlen = 4000;
+			m->msg.select_b2_protocol_req.dlpd.laa = 3;
+			m->msg.select_b2_protocol_req.dlpd.lab = 1;
+			m->msg.select_b2_protocol_req.dlpd.win = 7;
+			m->msg.select_b2_protocol_req.dlpd.modulo = 8;
+			break;
+	}
+	ACTCAPI_QUEUE_TX;
+}
+
+static void
+actcapi_select_b3_protocol_req(act2000_card *card, act2000_chan *chan)
+{
+	actcapi_msg *m;
+	struct sk_buff *skb;
+
+	ACTCAPI_MKHDR(17, 0x80, 0x00);
+	ACTCAPI_CHKSKB;
+	m->msg.select_b3_protocol_req.plci = chan->plci;
+	memset(&m->msg.select_b3_protocol_req.ncpd, 0,
+	       sizeof(m->msg.select_b3_protocol_req.ncpd));
+	switch (chan->l3prot) {
+		case ISDN_PROTO_L3_TRANS:
+			m->msg.select_b3_protocol_req.protocol = 0x04;
+			m->msg.select_b3_protocol_req.ncpd.len = 13;
+			m->msg.select_b3_protocol_req.ncpd.modulo = 8;
+			break;
+	}
+	ACTCAPI_QUEUE_TX;
+}
+
+static void
+actcapi_listen_b3_req(act2000_card *card, act2000_chan *chan)
+{
+	actcapi_msg *m;
+	struct sk_buff *skb;
+
+	ACTCAPI_MKHDR(2, 0x81, 0x00);
+	ACTCAPI_CHKSKB;
+	m->msg.listen_b3_req.plci = chan->plci;
+	ACTCAPI_QUEUE_TX;
+}
+
+static void
+actcapi_disconnect_req(act2000_card *card, act2000_chan *chan)
+{
+	actcapi_msg *m;
+	struct sk_buff *skb;
+
+	ACTCAPI_MKHDR(3, 0x04, 0x00);
+	ACTCAPI_CHKSKB;
+	m->msg.disconnect_req.plci = chan->plci;
+	m->msg.disconnect_req.cause = 0;
+	ACTCAPI_QUEUE_TX;
+}
+
+void
+actcapi_disconnect_b3_req(act2000_card *card, act2000_chan *chan)
+{
+	actcapi_msg *m;
+	struct sk_buff *skb;
+
+	ACTCAPI_MKHDR(17, 0x84, 0x00);
+	ACTCAPI_CHKSKB;
+	m->msg.disconnect_b3_req.ncci = chan->ncci;
+	memset(&m->msg.disconnect_b3_req.ncpi, 0,
+	       sizeof(m->msg.disconnect_b3_req.ncpi));
+	m->msg.disconnect_b3_req.ncpi.len = 13;
+	m->msg.disconnect_b3_req.ncpi.modulo = 8;
+	chan->fsm_state = ACT2000_STATE_BHWAIT;
+	ACTCAPI_QUEUE_TX;
+}
+
+void
+actcapi_connect_resp(act2000_card *card, act2000_chan *chan, __u8 cause)
+{
+	actcapi_msg *m;
+	struct sk_buff *skb;
+
+	ACTCAPI_MKHDR(3, 0x02, 0x03);
+	ACTCAPI_CHKSKB;
+	m->msg.connect_resp.plci = chan->plci;
+	m->msg.connect_resp.rejectcause = cause;
+	if (cause) {
+		chan->fsm_state = ACT2000_STATE_NULL;
+		chan->plci = 0x8000;
+	} else
+		chan->fsm_state = ACT2000_STATE_IWAIT;
+	ACTCAPI_QUEUE_TX;
+}
+
+static void
+actcapi_connect_active_resp(act2000_card *card, act2000_chan *chan)
+{
+	actcapi_msg *m;
+	struct sk_buff *skb;
+
+	ACTCAPI_MKHDR(2, 0x03, 0x03);
+	ACTCAPI_CHKSKB;
+	m->msg.connect_resp.plci = chan->plci;
+	if (chan->fsm_state == ACT2000_STATE_IWAIT)
+		chan->fsm_state = ACT2000_STATE_IBWAIT;
+	ACTCAPI_QUEUE_TX;
+}
+
+static void
+actcapi_connect_b3_resp(act2000_card *card, act2000_chan *chan, __u8 rejectcause)
+{
+	actcapi_msg *m;
+	struct sk_buff *skb;
+
+	ACTCAPI_MKHDR((rejectcause?3:17), 0x82, 0x03);
+	ACTCAPI_CHKSKB;
+	m->msg.connect_b3_resp.ncci = chan->ncci;
+	m->msg.connect_b3_resp.rejectcause = rejectcause;
+	if (!rejectcause) {
+		memset(&m->msg.connect_b3_resp.ncpi, 0,
+		       sizeof(m->msg.connect_b3_resp.ncpi));
+		m->msg.connect_b3_resp.ncpi.len = 13;
+		m->msg.connect_b3_resp.ncpi.modulo = 8;
+		chan->fsm_state = ACT2000_STATE_BWAIT;
+	}
+	ACTCAPI_QUEUE_TX;
+}
+
+static void
+actcapi_connect_b3_active_resp(act2000_card *card, act2000_chan *chan)
+{
+	actcapi_msg *m;
+	struct sk_buff *skb;
+
+	ACTCAPI_MKHDR(2, 0x83, 0x03);
+	ACTCAPI_CHKSKB;
+	m->msg.connect_b3_active_resp.ncci = chan->ncci;
+	chan->fsm_state = ACT2000_STATE_ACTIVE;
+	ACTCAPI_QUEUE_TX;
+}
+
+static void
+actcapi_info_resp(act2000_card *card, act2000_chan *chan)
+{
+	actcapi_msg *m;
+	struct sk_buff *skb;
+
+	ACTCAPI_MKHDR(2, 0x07, 0x03);
+	ACTCAPI_CHKSKB;
+	m->msg.info_resp.plci = chan->plci;
+	ACTCAPI_QUEUE_TX;
+}
+
+static void
+actcapi_disconnect_b3_resp(act2000_card *card, act2000_chan *chan)
+{
+	actcapi_msg *m;
+	struct sk_buff *skb;
+
+	ACTCAPI_MKHDR(2, 0x84, 0x03);
+	ACTCAPI_CHKSKB;
+	m->msg.disconnect_b3_resp.ncci = chan->ncci;
+	chan->ncci = 0x8000;
+	chan->queued = 0;
+	ACTCAPI_QUEUE_TX;
+}
+
+static void
+actcapi_disconnect_resp(act2000_card *card, act2000_chan *chan)
+{
+	actcapi_msg *m;
+	struct sk_buff *skb;
+
+	ACTCAPI_MKHDR(2, 0x04, 0x03);
+	ACTCAPI_CHKSKB;
+	m->msg.disconnect_resp.plci = chan->plci;
+	chan->plci = 0x8000;
+	ACTCAPI_QUEUE_TX;
+}
+
+static int
+new_plci(act2000_card *card, __u16 plci)
+{
+	int i;
+	for (i = 0; i < ACT2000_BCH; i++)
+		if (card->bch[i].plci == 0x8000) {
+			card->bch[i].plci = plci;
+			return i;
+		}
+	return -1;
+}
+
+static int
+find_plci(act2000_card *card, __u16 plci)
+{
+	int i;
+	for (i = 0; i < ACT2000_BCH; i++)
+		if (card->bch[i].plci == plci)
+			return i;
+	return -1;
+}
+
+static int
+find_ncci(act2000_card *card, __u16 ncci)
+{
+	int i;
+	for (i = 0; i < ACT2000_BCH; i++)
+		if (card->bch[i].ncci == ncci)
+			return i;
+	return -1;
+}
+
+static int
+find_dialing(act2000_card *card, __u16 callref)
+{
+	int i;
+	for (i = 0; i < ACT2000_BCH; i++)
+		if ((card->bch[i].callref == callref) &&
+		    (card->bch[i].fsm_state == ACT2000_STATE_OCALL))
+			return i;
+	return -1;
+}
+
+static int
+actcapi_data_b3_ind(act2000_card *card, struct sk_buff *skb) {
+	__u16 plci;
+	__u16 ncci;
+	__u16 controller;
+	__u8  blocknr;
+	int chan;
+	actcapi_msg *msg = (actcapi_msg *)skb->data;
+
+	EVAL_NCCI(msg->msg.data_b3_ind.fakencci, plci, controller, ncci);
+	chan = find_ncci(card, ncci);
+	if (chan < 0)
+		return 0;
+	if (card->bch[chan].fsm_state != ACT2000_STATE_ACTIVE)
+		return 0;
+	if (card->bch[chan].plci != plci)
+		return 0;
+	blocknr = msg->msg.data_b3_ind.blocknr;
+	skb_pull(skb, 19);
+	card->interface.rcvcallb_skb(card->myid, chan, skb);
+        if (!(skb = alloc_skb(11, GFP_ATOMIC))) {
+                printk(KERN_WARNING "actcapi: alloc_skb failed\n");
+                return 1;
+        }
+	msg = (actcapi_msg *)skb_put(skb, 11);
+	msg->hdr.len = 11;
+	msg->hdr.applicationID = 1;
+	msg->hdr.cmd.cmd = 0x86;
+	msg->hdr.cmd.subcmd = 0x03;
+	msg->hdr.msgnum = actcapi_nextsmsg(card);
+	msg->msg.data_b3_resp.ncci = ncci;
+	msg->msg.data_b3_resp.blocknr = blocknr;
+	ACTCAPI_QUEUE_TX;
+	return 1;
+}
+
+/*
+ * Walk over ackq, unlink DATA_B3_REQ from it, if
+ * ncci and blocknr are matching.
+ * Decrement queued-bytes counter.
+ */
+static int
+handle_ack(act2000_card *card, act2000_chan *chan, __u8 blocknr) {
+	unsigned long flags;
+	struct sk_buff *skb;
+	struct sk_buff *tmp;
+	struct actcapi_msg *m;
+	int ret = 0;
+
+	save_flags(flags);
+	cli();
+	skb = skb_peek(&card->ackq);
+	restore_flags(flags);
+        if (!skb) {
+		printk(KERN_WARNING "act2000: handle_ack nothing found!\n");
+		return 0;
+	}
+        tmp = skb;
+        while (1) {
+                m = (actcapi_msg *)tmp->data;
+                if ((((m->msg.data_b3_req.fakencci >> 8) & 0xff) == chan->ncci) &&
+		    (m->msg.data_b3_req.blocknr == blocknr)) {
+			/* found corresponding DATA_B3_REQ */
+                        skb_unlink(tmp);
+			chan->queued -= m->msg.data_b3_req.datalen;
+			if (m->msg.data_b3_req.flags)
+				ret = m->msg.data_b3_req.datalen;
+			dev_kfree_skb(tmp);
+			if (chan->queued < 0)
+				chan->queued = 0;
+                        return ret;
+                }
+		save_flags(flags);
+		cli();
+                tmp = skb_peek((struct sk_buff_head *)tmp);
+		restore_flags(flags);
+                if ((tmp == skb) || (tmp == NULL)) {
+			/* reached end of queue */
+			printk(KERN_WARNING "act2000: handle_ack nothing found!\n");
+                        return 0;
+		}
+        }
+}
+
+void
+actcapi_dispatch(act2000_card *card)
+{
+	struct sk_buff *skb;
+	actcapi_msg *msg;
+	__u16 ccmd;
+	int chan;
+	int len;
+	act2000_chan *ctmp;
+	isdn_ctrl cmd;
+	char tmp[170];
+
+	while ((skb = skb_dequeue(&card->rcvq))) {
+		actcapi_debug_msg(skb, 0);
+		msg = (actcapi_msg *)skb->data;
+		ccmd = ((msg->hdr.cmd.cmd << 8) | msg->hdr.cmd.subcmd);
+		switch (ccmd) {
+			case 0x8602:
+				/* DATA_B3_IND */
+				if (actcapi_data_b3_ind(card, skb))
+					return;
+				break;
+			case 0x8601:
+				/* DATA_B3_CONF */
+				chan = find_ncci(card, msg->msg.data_b3_conf.ncci);
+				if ((chan >= 0) && (card->bch[chan].fsm_state == ACT2000_STATE_ACTIVE)) {
+					if (msg->msg.data_b3_conf.info != 0)
+						printk(KERN_WARNING "act2000: DATA_B3_CONF: %04x\n",
+						       msg->msg.data_b3_conf.info);
+					len = handle_ack(card, &card->bch[chan],
+							 msg->msg.data_b3_conf.blocknr);
+					if (len) {
+						cmd.driver = card->myid;
+						cmd.command = ISDN_STAT_BSENT;
+						cmd.arg = chan;
+						cmd.parm.length = len;
+						card->interface.statcallb(&cmd);
+					}
+				}
+				break;
+			case 0x0201:
+				/* CONNECT_CONF */
+				chan = find_dialing(card, msg->hdr.msgnum);
+				if (chan >= 0) {
+					if (msg->msg.connect_conf.info) {
+						card->bch[chan].fsm_state = ACT2000_STATE_NULL;
+						cmd.driver = card->myid;
+						cmd.command = ISDN_STAT_DHUP;
+						cmd.arg = chan;
+						card->interface.statcallb(&cmd);
+					} else {
+						card->bch[chan].fsm_state = ACT2000_STATE_OWAIT;
+						card->bch[chan].plci = msg->msg.connect_conf.plci;
+					}
+				}
+				break;
+			case 0x0202:
+				/* CONNECT_IND */
+				chan = new_plci(card, msg->msg.connect_ind.plci);
+				if (chan < 0) {
+					ctmp = (act2000_chan *)tmp;
+					ctmp->plci = msg->msg.connect_ind.plci;
+					actcapi_connect_resp(card, ctmp, 0x11); /* All Card-Cannels busy */
+				} else {
+					card->bch[chan].fsm_state = ACT2000_STATE_ICALL;
+					cmd.driver = card->myid;
+					cmd.command = ISDN_STAT_ICALL;
+					cmd.arg = chan;
+					cmd.parm.setup.si1 = msg->msg.connect_ind.si1;
+					cmd.parm.setup.si2 = msg->msg.connect_ind.si2;
+					if (card->ptype == ISDN_PTYPE_EURO)
+						strcpy(cmd.parm.setup.eazmsn,
+						       act2000_find_eaz(card, msg->msg.connect_ind.eaz));
+					else {
+						cmd.parm.setup.eazmsn[0] = msg->msg.connect_ind.eaz;
+						cmd.parm.setup.eazmsn[1] = 0;
+					}
+					memset(cmd.parm.setup.phone, 0, sizeof(cmd.parm.setup.phone));
+					memcpy(cmd.parm.setup.phone, msg->msg.connect_ind.addr.num,
+					       msg->msg.connect_ind.addr.len - 1);
+					cmd.parm.setup.plan = msg->msg.connect_ind.addr.tnp;
+					cmd.parm.setup.screen = 0;
+					if (card->interface.statcallb(&cmd) == 2)
+						actcapi_connect_resp(card, &card->bch[chan], 0x15); /* Reject Call */
+				}
+				break;
+			case 0x0302:
+				/* CONNECT_ACTIVE_IND */
+				chan = find_plci(card, msg->msg.connect_active_ind.plci);
+				if (chan >= 0)
+					switch (card->bch[chan].fsm_state) {
+						case ACT2000_STATE_IWAIT:
+							actcapi_connect_active_resp(card, &card->bch[chan]);
+							break;
+						case ACT2000_STATE_OWAIT:
+							actcapi_connect_active_resp(card, &card->bch[chan]);
+							actcapi_select_b2_protocol_req(card, &card->bch[chan]);
+							break;
+					}
+				break;
+			case 0x8202:
+				/* CONNECT_B3_IND */
+				chan = find_plci(card, msg->msg.connect_b3_ind.plci);
+				if ((chan >= 0) && (card->bch[chan].fsm_state == ACT2000_STATE_IBWAIT)) {
+					card->bch[chan].ncci = msg->msg.connect_b3_ind.ncci;
+					actcapi_connect_b3_resp(card, &card->bch[chan], 0);
+				} else {
+					ctmp = (act2000_chan *)tmp;
+					ctmp->ncci = msg->msg.connect_b3_ind.ncci;
+					actcapi_connect_b3_resp(card, ctmp, 0x11); /* All Card-Cannels busy */
+				}
+				break;
+			case 0x8302:
+				/* CONNECT_B3_ACTIVE_IND */
+				chan = find_ncci(card, msg->msg.connect_b3_active_ind.ncci);
+				if ((chan >= 0) && (card->bch[chan].fsm_state == ACT2000_STATE_BWAIT)) {
+					actcapi_connect_b3_active_resp(card, &card->bch[chan]);
+					cmd.driver = card->myid;
+					cmd.command = ISDN_STAT_BCONN;
+					cmd.arg = chan;
+					card->interface.statcallb(&cmd);
+				}
+				break;
+			case 0x8402:
+				/* DISCONNECT_B3_IND */
+				chan = find_ncci(card, msg->msg.disconnect_b3_ind.ncci);
+				if (chan >= 0) {
+					ctmp = &card->bch[chan];
+					actcapi_disconnect_b3_resp(card, ctmp);
+					switch (ctmp->fsm_state) {
+						case ACT2000_STATE_ACTIVE:
+							ctmp->fsm_state = ACT2000_STATE_DHWAIT2;
+							cmd.driver = card->myid;
+							cmd.command = ISDN_STAT_BHUP;
+							cmd.arg = chan;
+							card->interface.statcallb(&cmd);
+							break;
+						case ACT2000_STATE_BHWAIT2:
+							actcapi_disconnect_req(card, ctmp);
+							ctmp->fsm_state = ACT2000_STATE_DHWAIT;
+							cmd.driver = card->myid;
+							cmd.command = ISDN_STAT_BHUP;
+							cmd.arg = chan;
+							card->interface.statcallb(&cmd);
+							break;
+					}
+				}
+				break;
+			case 0x0402:
+				/* DISCONNECT_IND */
+				chan = find_plci(card, msg->msg.disconnect_ind.plci);
+				if (chan >= 0) {
+					ctmp = &card->bch[chan];
+					actcapi_disconnect_resp(card, ctmp);
+					ctmp->fsm_state = ACT2000_STATE_NULL;
+					cmd.driver = card->myid;
+					cmd.command = ISDN_STAT_DHUP;
+					cmd.arg = chan;
+					card->interface.statcallb(&cmd);
+				} else {
+					ctmp = (act2000_chan *)tmp;
+					ctmp->plci = msg->msg.disconnect_ind.plci;
+					actcapi_disconnect_resp(card, ctmp);
+				}
+				break;
+			case 0x4001:
+				/* SELECT_B2_PROTOCOL_CONF */
+				chan = find_plci(card, msg->msg.select_b2_protocol_conf.plci);
+				if (chan >= 0)
+					switch (card->bch[chan].fsm_state) {
+						case ACT2000_STATE_ICALL:
+						case ACT2000_STATE_OWAIT:
+							ctmp = &card->bch[chan];
+							if (msg->msg.select_b2_protocol_conf.info == 0)
+								actcapi_select_b3_protocol_req(card, ctmp);
+							else {
+								ctmp->fsm_state = ACT2000_STATE_NULL;
+								cmd.driver = card->myid;
+								cmd.command = ISDN_STAT_DHUP;
+								cmd.arg = chan;
+								card->interface.statcallb(&cmd);
+							}
+							break;
+					}
+				break;
+			case 0x8001:
+				/* SELECT_B3_PROTOCOL_CONF */
+				chan = find_plci(card, msg->msg.select_b3_protocol_conf.plci);
+				if (chan >= 0)
+					switch (card->bch[chan].fsm_state) {
+						case ACT2000_STATE_ICALL:
+						case ACT2000_STATE_OWAIT:
+							ctmp = &card->bch[chan];
+							if (msg->msg.select_b3_protocol_conf.info == 0)
+								actcapi_listen_b3_req(card, ctmp);
+							else {
+								ctmp->fsm_state = ACT2000_STATE_NULL;
+								cmd.driver = card->myid;
+								cmd.command = ISDN_STAT_DHUP;
+								cmd.arg = chan;
+								card->interface.statcallb(&cmd);
+							}
+					}
+				break;
+			case 0x8101:
+				/* LISTEN_B3_CONF */
+				chan = find_plci(card, msg->msg.listen_b3_conf.plci);
+				if (chan >= 0)
+					switch (card->bch[chan].fsm_state) {
+						case ACT2000_STATE_ICALL:
+							ctmp = &card->bch[chan];
+							if (msg->msg.listen_b3_conf.info == 0)
+								actcapi_connect_resp(card, ctmp, 0);
+							else {
+								ctmp->fsm_state = ACT2000_STATE_NULL;
+								cmd.driver = card->myid;
+								cmd.command = ISDN_STAT_DHUP;
+								cmd.arg = chan;
+								card->interface.statcallb(&cmd);
+							}
+							break;
+						case ACT2000_STATE_OWAIT:
+							ctmp = &card->bch[chan];
+							if (msg->msg.listen_b3_conf.info == 0) {
+								actcapi_connect_b3_req(card, ctmp);
+								ctmp->fsm_state = ACT2000_STATE_OBWAIT;
+								cmd.driver = card->myid;
+								cmd.command = ISDN_STAT_DCONN;
+								cmd.arg = chan;
+								card->interface.statcallb(&cmd);
+							} else {
+								ctmp->fsm_state = ACT2000_STATE_NULL;
+								cmd.driver = card->myid;
+								cmd.command = ISDN_STAT_DHUP;
+								cmd.arg = chan;
+								card->interface.statcallb(&cmd);
+							}
+							break;
+					}
+				break;
+			case 0x8201:
+				/* CONNECT_B3_CONF */
+				chan = find_plci(card, msg->msg.connect_b3_conf.plci);
+				if ((chan >= 0) && (card->bch[chan].fsm_state == ACT2000_STATE_OBWAIT)) {
+					ctmp = &card->bch[chan];
+					if (msg->msg.connect_b3_conf.info) {
+						ctmp->fsm_state = ACT2000_STATE_NULL;
+						cmd.driver = card->myid;
+						cmd.command = ISDN_STAT_DHUP;
+						cmd.arg = chan;
+						card->interface.statcallb(&cmd);
+					} else {
+						ctmp->ncci = msg->msg.connect_b3_conf.ncci;
+						ctmp->fsm_state = ACT2000_STATE_BWAIT;
+					}
+				}
+				break;
+			case 0x8401:
+				/* DISCONNECT_B3_CONF */
+				chan = find_ncci(card, msg->msg.disconnect_b3_conf.ncci);
+				if ((chan >= 0) && (card->bch[chan].fsm_state == ACT2000_STATE_BHWAIT))
+					card->bch[chan].fsm_state = ACT2000_STATE_BHWAIT2;
+				break;
+			case 0x0702:
+				/* INFO_IND */
+				chan = find_plci(card, msg->msg.info_ind.plci);
+				if (chan >= 0)
+					/* TODO: Eval Charging info / cause */
+					actcapi_info_resp(card, &card->bch[chan]);
+				break;
+			case 0x0401:
+				/* LISTEN_CONF */
+			case 0x0501:
+				/* LISTEN_CONF */
+			case 0xff01:
+				/* MANUFACTURER_CONF */
+				break;
+			case 0xff02:
+				/* MANUFACTURER_IND */
+				if (msg->msg.manuf_msg == 3) {
+					memset(tmp, 0, sizeof(tmp));
+					strncpy(tmp,
+						&msg->msg.manufacturer_ind_err.errstring,
+						msg->hdr.len - 16);
+					if (msg->msg.manufacturer_ind_err.errcode)
+						printk(KERN_WARNING "act2000: %s\n", tmp);
+					else {
+						printk(KERN_DEBUG "act2000: %s\n", tmp);
+						if ((!strncmp(tmp, "INFO: Trace buffer con", 22)) ||
+						    (!strncmp(tmp, "INFO: Compile Date/Tim", 22))) {
+							card->flags |= ACT2000_FLAGS_RUNNING;
+							cmd.command = ISDN_STAT_RUN;
+							cmd.driver = card->myid;
+							cmd.arg = 0;
+							actcapi_manufacturer_req_net(card);
+							actcapi_manufacturer_req_msn(card);
+							actcapi_listen_req(card);
+							card->interface.statcallb(&cmd);
+						}
+					}
+				}
+				break;
+			default:
+				printk(KERN_WARNING "act2000: UNHANDLED Message %04x\n", ccmd);
+				break;
+		}
+		dev_kfree_skb(skb);
+	}
+}
+
+#ifdef DEBUG_MSG
+static void
+actcapi_debug_caddr(actcapi_addr *addr)
+{
+	char tmp[30];
+
+	printk(KERN_DEBUG " Alen  = %d\n", addr->len);
+	if (addr->len > 0)
+		printk(KERN_DEBUG " Atnp  = 0x%02x\n", addr->tnp);
+	if (addr->len > 1) {
+		memset(tmp, 0, 30);
+		memcpy(tmp, addr->num, addr->len - 1);
+		printk(KERN_DEBUG " Anum  = '%s'\n", tmp);
+	}
+}
+
+static void
+actcapi_debug_ncpi(actcapi_ncpi *ncpi)
+{
+	printk(KERN_DEBUG " ncpi.len = %d\n", ncpi->len);
+	if (ncpi->len >= 2)
+		printk(KERN_DEBUG " ncpi.lic = 0x%04x\n", ncpi->lic);
+	if (ncpi->len >= 4)
+		printk(KERN_DEBUG " ncpi.hic = 0x%04x\n", ncpi->hic);
+	if (ncpi->len >= 6)
+		printk(KERN_DEBUG " ncpi.ltc = 0x%04x\n", ncpi->ltc);
+	if (ncpi->len >= 8)
+		printk(KERN_DEBUG " ncpi.htc = 0x%04x\n", ncpi->htc);
+	if (ncpi->len >= 10)
+		printk(KERN_DEBUG " ncpi.loc = 0x%04x\n", ncpi->loc);
+	if (ncpi->len >= 12)
+		printk(KERN_DEBUG " ncpi.hoc = 0x%04x\n", ncpi->hoc);
+	if (ncpi->len >= 13)
+		printk(KERN_DEBUG " ncpi.mod = %d\n", ncpi->modulo);
+}
+
+static void
+actcapi_debug_dlpd(actcapi_dlpd *dlpd)
+{
+	printk(KERN_DEBUG " dlpd.len = %d\n", dlpd->len);
+	if (dlpd->len >= 2)
+		printk(KERN_DEBUG " dlpd.dlen   = 0x%04x\n", dlpd->dlen);
+	if (dlpd->len >= 3)
+		printk(KERN_DEBUG " dlpd.laa    = 0x%02x\n", dlpd->laa);
+	if (dlpd->len >= 4)
+		printk(KERN_DEBUG " dlpd.lab    = 0x%02x\n", dlpd->lab);
+	if (dlpd->len >= 5)
+		printk(KERN_DEBUG " dlpd.modulo = %d\n", dlpd->modulo);
+	if (dlpd->len >= 6)
+		printk(KERN_DEBUG " dlpd.win    = %d\n", dlpd->win);
+}
+
+#ifdef DEBUG_DUMP_SKB
+static void dump_skb(struct sk_buff *skb) {
+	char tmp[80];
+	char *p = skb->data;
+	char *t = tmp;
+	int i;
+
+	for (i = 0; i < skb->len; i++) {
+		t += sprintf(t, "%02x ", *p++ & 0xff);
+		if ((i & 0x0f) == 8) {
+			printk(KERN_DEBUG "dump: %s\n", tmp);
+			t = tmp;
+		}
+	}
+	if (i & 0x07)
+		printk(KERN_DEBUG "dump: %s\n", tmp);
+}
+#endif
+
+void
+actcapi_debug_msg(struct sk_buff *skb, int direction)
+{
+	actcapi_msg *msg = (actcapi_msg *)skb->data;
+	char *descr;
+	int i;
+	char tmp[170];
+	
+#ifndef DEBUG_DATA_MSG
+	if (msg->hdr.cmd.cmd == 0x86)
+		return;
+#endif
+	descr = "INVALID";
+#ifdef DEBUG_DUMP_SKB
+	dump_skb(skb);
+#endif
+	for (i = 0; i < num_valid_msg; i++)
+		if ((msg->hdr.cmd.cmd == valid_msg[i].cmd.cmd) &&
+		    (msg->hdr.cmd.subcmd == valid_msg[i].cmd.subcmd)) {
+			descr = valid_msg[i].description;
+			break;
+		}
+	printk(KERN_DEBUG "%s %s msg\n", direction?"Outgoing":"Incoming", descr);
+	printk(KERN_DEBUG " ApplID = %d\n", msg->hdr.applicationID);
+	printk(KERN_DEBUG " Len    = %d\n", msg->hdr.len);
+	printk(KERN_DEBUG " MsgNum = 0x%04x\n", msg->hdr.msgnum);
+	printk(KERN_DEBUG " Cmd    = 0x%02x\n", msg->hdr.cmd.cmd);
+	printk(KERN_DEBUG " SubCmd = 0x%02x\n", msg->hdr.cmd.subcmd);
+	switch (i) {
+		case 0:
+			/* DATA B3 IND */
+			printk(KERN_DEBUG " BLOCK = 0x%02x\n",
+			       msg->msg.data_b3_ind.blocknr);
+			break;
+		case 2:
+			/* CONNECT CONF */
+			printk(KERN_DEBUG " PLCI = 0x%04x\n",
+			       msg->msg.connect_conf.plci);
+			printk(KERN_DEBUG " Info = 0x%04x\n",
+			       msg->msg.connect_conf.info);
+			break;
+		case 3:
+			/* CONNECT IND */
+			printk(KERN_DEBUG " PLCI = 0x%04x\n",
+			       msg->msg.connect_ind.plci);
+			printk(KERN_DEBUG " Contr = %d\n",
+			       msg->msg.connect_ind.controller);
+			printk(KERN_DEBUG " SI1   = %d\n",
+			       msg->msg.connect_ind.si1);
+			printk(KERN_DEBUG " SI2   = %d\n",
+			       msg->msg.connect_ind.si2);
+			printk(KERN_DEBUG " EAZ   = '%c'\n",
+			       msg->msg.connect_ind.eaz);
+			actcapi_debug_caddr(&msg->msg.connect_ind.addr);
+			break;
+		case 5:
+			/* CONNECT ACTIVE IND */
+			printk(KERN_DEBUG " PLCI = 0x%04x\n",
+			       msg->msg.connect_active_ind.plci);
+			actcapi_debug_caddr(&msg->msg.connect_active_ind.addr);
+			break;
+		case 8:
+			/* LISTEN CONF */
+			printk(KERN_DEBUG " Contr = %d\n",
+			       msg->msg.listen_conf.controller);
+			printk(KERN_DEBUG " Info = 0x%04x\n",
+			       msg->msg.listen_conf.info);
+			break;
+		case 11:
+			/* INFO IND */
+			printk(KERN_DEBUG " PLCI = 0x%04x\n",
+			       msg->msg.info_ind.plci);
+			printk(KERN_DEBUG " Imsk = 0x%04x\n",
+			       msg->msg.info_ind.nr.mask);
+			if (msg->hdr.len > 12) {
+				int l = msg->hdr.len - 12;
+				int j;
+				char *p = tmp;
+				for (j = 0; j < l ; j++)
+					p += sprintf(p, "%02x ", msg->msg.info_ind.el.display[j]);
+				printk(KERN_DEBUG " D = '%s'\n", tmp);
+			}
+			break;
+		case 14:
+			/* SELECT B2 PROTOCOL CONF */
+			printk(KERN_DEBUG " PLCI = 0x%04x\n",
+			       msg->msg.select_b2_protocol_conf.plci);
+			printk(KERN_DEBUG " Info = 0x%04x\n",
+			       msg->msg.select_b2_protocol_conf.info);
+			break;
+		case 15:
+			/* SELECT B3 PROTOCOL CONF */
+			printk(KERN_DEBUG " PLCI = 0x%04x\n",
+			       msg->msg.select_b3_protocol_conf.plci);
+			printk(KERN_DEBUG " Info = 0x%04x\n",
+			       msg->msg.select_b3_protocol_conf.info);
+			break;
+		case 16:
+			/* LISTEN B3 CONF */
+			printk(KERN_DEBUG " PLCI = 0x%04x\n",
+			       msg->msg.listen_b3_conf.plci);
+			printk(KERN_DEBUG " Info = 0x%04x\n",
+			       msg->msg.listen_b3_conf.info);
+			break;
+		case 18:
+			/* CONNECT B3 IND */
+			printk(KERN_DEBUG " NCCI = 0x%04x\n",
+			       msg->msg.connect_b3_ind.ncci);
+			printk(KERN_DEBUG " PLCI = 0x%04x\n",
+			       msg->msg.connect_b3_ind.plci);
+			actcapi_debug_ncpi(&msg->msg.connect_b3_ind.ncpi);
+			break;
+		case 19:
+			/* CONNECT B3 ACTIVE IND */
+			printk(KERN_DEBUG " NCCI = 0x%04x\n",
+			       msg->msg.connect_b3_active_ind.ncci);
+			actcapi_debug_ncpi(&msg->msg.connect_b3_active_ind.ncpi);
+			break;
+		case 26:
+			/* MANUFACTURER IND */
+			printk(KERN_DEBUG " Mmsg = 0x%02x\n",
+			       msg->msg.manufacturer_ind_err.manuf_msg);
+			switch (msg->msg.manufacturer_ind_err.manuf_msg) {
+				case 3:
+					printk(KERN_DEBUG " Contr = %d\n",
+					       msg->msg.manufacturer_ind_err.controller);
+					printk(KERN_DEBUG " Code = 0x%08x\n",
+					       msg->msg.manufacturer_ind_err.errcode);
+					memset(tmp, 0, sizeof(tmp));
+					strncpy(tmp, &msg->msg.manufacturer_ind_err.errstring,
+						msg->hdr.len - 16);
+					printk(KERN_DEBUG " Emsg = '%s'\n", tmp);
+					break;
+			}
+			break;
+		case 30:
+			/* LISTEN REQ */
+			printk(KERN_DEBUG " Imsk = 0x%08x\n",
+			       msg->msg.listen_req.infomask);
+			printk(KERN_DEBUG " Emsk = 0x%04x\n",
+			       msg->msg.listen_req.eazmask);
+			printk(KERN_DEBUG " Smsk = 0x%04x\n",
+			       msg->msg.listen_req.simask);
+			break;
+		case 35:
+			/* SELECT_B2_PROTOCOL_REQ */
+			printk(KERN_DEBUG " PLCI  = 0x%04x\n",
+			       msg->msg.select_b2_protocol_req.plci);
+			printk(KERN_DEBUG " prot  = 0x%02x\n",
+			       msg->msg.select_b2_protocol_req.protocol);
+			if (msg->hdr.len >= 11)
+				printk(KERN_DEBUG "No dlpd\n");
+			else
+				actcapi_debug_dlpd(&msg->msg.select_b2_protocol_req.dlpd);
+			break;
+		case 44:
+			/* CONNECT RESP */
+			printk(KERN_DEBUG " PLCI  = 0x%04x\n",
+			       msg->msg.connect_resp.plci);
+			printk(KERN_DEBUG " CAUSE = 0x%02x\n",
+			       msg->msg.connect_resp.rejectcause);
+			break;
+		case 45:
+			/* CONNECT ACTIVE RESP */
+			printk(KERN_DEBUG " PLCI  = 0x%04x\n",
+			       msg->msg.connect_active_resp.plci);
+			break;
+	}
+}
+#endif
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/act2000/capi.h linux/drivers/isdn/act2000/capi.h
--- v2.1.91/linux/drivers/isdn/act2000/capi.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/act2000/capi.h	Wed Apr  1 16:20:57 1998
@@ -0,0 +1,406 @@
+/* $Id: capi.h,v 1.4 1997/10/01 09:21:04 fritz Exp $
+ *
+ * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000.
+ *
+ * Copyright 1997 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Thanks to Friedemann Baitinger and IBM Germany
+ *
+ * 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: capi.h,v $
+ * Revision 1.4  1997/10/01 09:21:04  fritz
+ * Removed old compatibility stuff for 2.0.X kernels.
+ * From now on, this code is for 2.1.X ONLY!
+ * Old stuff is still in the separate branch.
+ *
+ * Revision 1.3  1997/09/25 17:25:41  fritz
+ * Support for adding cards at runtime.
+ * Support for new Firmware.
+ *
+ * Revision 1.2  1997/09/24 19:44:15  fritz
+ * Added MSN mapping support, some cleanup.
+ *
+ * Revision 1.1  1997/09/23 18:00:10  fritz
+ * New driver for IBM Active 2000.
+ *
+ */
+
+#ifndef CAPI_H
+#define CAPI_H
+
+/* Command-part of a CAPI message */
+typedef struct actcapi_msgcmd {
+	__u8 cmd;
+	__u8 subcmd;
+} actcapi_msgcmd;
+
+/* CAPI message header */
+typedef struct actcapi_msghdr {
+	__u16 len;
+	__u16 applicationID;
+	actcapi_msgcmd cmd;
+	__u16 msgnum;
+} actcapi_msghdr;
+
+/* CAPI message description (for debugging) */
+typedef struct actcapi_msgdsc {
+	actcapi_msgcmd cmd;
+	char *description;
+} actcapi_msgdsc;
+
+/* CAPI Adress */
+typedef struct actcapi_addr {
+	__u8 len;                            /* Length of element            */
+	__u8 tnp;                            /* Type/Numbering Plan          */
+	__u8 num[20];                        /* Caller ID                    */
+} actcapi_addr;
+
+/* CAPI INFO element mask */
+typedef  union actcapi_infonr {              /* info number                  */
+	__u16 mask;                          /* info-mask field              */
+	struct bmask {                       /* bit definitions              */
+		unsigned  codes : 3;         /* code set                     */
+		unsigned  rsvd  : 5;         /* reserved                     */
+		unsigned  svind : 1;         /* single, variable length ind. */
+		unsigned  wtype : 7;         /* W-element type               */
+	} bmask;
+} actcapi_infonr;
+
+/* CAPI INFO element */
+typedef union  actcapi_infoel {              /* info element                 */
+	__u8 len;                            /* length of info element       */
+	__u8 display[40];                    /* display contents             */
+	__u8 uuinfo[40];                     /* User-user info field         */
+	struct cause {                       /* Cause information            */
+		unsigned ext2  : 1;          /* extension                    */
+		unsigned cod   : 2;          /* coding standard              */
+		unsigned spare : 1;          /* spare                        */
+		unsigned loc   : 4;          /* location                     */
+		unsigned ext1  : 1;          /* extension                    */
+		unsigned cval  : 7;          /* Cause value                  */
+	} cause;                     
+	struct charge {                      /* Charging information         */
+		__u8 toc;                    /* type of charging info        */
+		__u8 unit[10];               /* charging units               */
+	} charge;
+	__u8 date[20];                       /* date fields                  */
+	__u8 stat;                           /* state of remote party        */
+} actcapi_infoel;
+
+/* Message for EAZ<->MSN Mapping */
+typedef struct actcapi_msn {
+	__u8 eaz;
+	__u8 len;                            /* Length of MSN                */
+	__u8 msn[15] __attribute__ ((packed));
+} actcapi_msn;
+
+typedef struct actcapi_dlpd {
+	__u8 len;                            /* Length of structure          */
+	__u16 dlen __attribute__ ((packed)); /* Data Length                  */
+	__u8 laa __attribute__ ((packed));   /* Link Address A               */
+	__u8 lab;                            /* Link Address B               */
+	__u8 modulo;                         /* Modulo Mode                  */
+	__u8 win;                            /* Window size                  */
+	__u8 xid[100];                       /* XID Information              */
+} actcapi_dlpd;
+
+typedef struct actcapi_ncpd {
+	__u8   len;                          /* Length of structure          */
+	__u16  lic __attribute__ ((packed));
+	__u16  hic __attribute__ ((packed));
+	__u16  ltc __attribute__ ((packed));
+	__u16  htc __attribute__ ((packed));
+	__u16  loc __attribute__ ((packed));
+	__u16  hoc __attribute__ ((packed));
+	__u8   modulo __attribute__ ((packed));
+} actcapi_ncpd;
+#define actcapi_ncpi actcapi_ncpd
+
+/*
+ * Layout of NCCI field in a B3 DATA CAPI message is different from
+ * standard at act2000:
+ *
+ * Bit 0-4  = PLCI
+ * Bit 5-7  = Controller
+ * Bit 8-15 = NCCI
+ */
+#define MAKE_NCCI(plci,contr,ncci) \
+        ((plci & 0x1f) | ((contr & 0x7) << 5) | ((ncci & 0xff) << 8))
+
+#define EVAL_NCCI(fakencci,plci,contr,ncci) { \
+	plci  = fakencci & 0x1f; \
+	contr = (fakencci >> 5) & 0x7; \
+	ncci  = (fakencci >> 8) & 0xff; \
+}
+
+/*
+ * Layout of PLCI field in a B3 DATA CAPI message is different from
+ * standard at act2000:
+ *
+ * Bit 0-4  = PLCI
+ * Bit 5-7  = Controller
+ * Bit 8-15 = reserved (must be 0)
+ */
+#define MAKE_PLCI(plci,contr) \
+        ((plci & 0x1f) | ((contr & 0x7) << 5))
+
+#define EVAL_PLCI(fakeplci,plci,contr) { \
+	plci  = fakeplci & 0x1f; \
+	contr = (fakeplci >> 5) & 0x7; \
+}
+
+typedef struct actcapi_msg {
+	actcapi_msghdr hdr;
+	union msg {
+		__u16 manuf_msg;
+		struct manufacturer_req_net {
+			__u16 manuf_msg;
+			__u16 controller;
+			__u8  nettype;
+		} manufacturer_req_net;
+		struct manufacturer_req_v42 {
+			__u16 manuf_msg;
+			__u16 controller;
+			__u32 v42control;
+		} manufacturer_req_v42;
+		struct manufacturer_conf_v42 {
+			__u16 manuf_msg;
+			__u16 controller;
+		} manufacturer_conf_v42;
+		struct manufacturer_req_err {
+			__u16 manuf_msg;
+			__u16 controller;
+		} manufacturer_req_err;
+		struct manufacturer_ind_err {
+			__u16 manuf_msg;
+			__u16 controller;
+			__u32 errcode;
+			__u8  errstring; /* actually up to 160 */
+		} manufacturer_ind_err;
+		struct manufacturer_req_msn {
+			__u16 manuf_msg;
+			__u16 controller;
+			actcapi_msn msnmap;
+		} manufacturer_req_msn;
+		/* TODO: TraceInit-req/conf/ind/resp and
+		 *       TraceDump-req/conf/ind/resp
+		 */
+		struct connect_req {
+			__u8  controller;
+			__u8  bchan;
+			__u32 infomask __attribute__ ((packed));
+			__u8  si1;
+			__u8  si2;
+			__u8  eaz;
+			actcapi_addr addr;
+		} connect_req;
+		struct connect_conf {
+			__u16 plci;
+			__u16 info;
+		} connect_conf;
+		struct connect_ind {
+			__u16 plci;
+			__u8  controller;
+			__u8  si1;
+			__u8  si2;
+			__u8  eaz;
+			actcapi_addr addr;
+		} connect_ind;
+		struct connect_resp {
+			__u16 plci;
+			__u8  rejectcause;
+		} connect_resp;
+		struct connect_active_ind {
+			__u16 plci;
+			actcapi_addr addr;
+		} connect_active_ind;
+		struct connect_active_resp {
+			__u16 plci;
+		} connect_active_resp;
+		struct connect_b3_req {
+			__u16 plci;
+			actcapi_ncpi ncpi;
+		} connect_b3_req;
+		struct connect_b3_conf {
+			__u16 plci;
+			__u16 ncci;
+			__u16 info;
+		} connect_b3_conf;
+		struct connect_b3_ind {
+			__u16 ncci;
+			__u16 plci;
+			actcapi_ncpi ncpi;
+		} connect_b3_ind;
+		struct connect_b3_resp {
+			__u16 ncci;
+			__u8  rejectcause;
+			actcapi_ncpi ncpi __attribute__ ((packed));
+		} connect_b3_resp;
+		struct disconnect_req {
+			__u16 plci;
+			__u8  cause;
+		} disconnect_req;
+		struct disconnect_conf {
+			__u16 plci;
+			__u16 info;
+		} disconnect_conf;
+		struct disconnect_ind {
+			__u16 plci;
+			__u16 info;
+		} disconnect_ind;
+		struct disconnect_resp {
+			__u16 plci;
+		} disconnect_resp;
+		struct connect_b3_active_ind {
+			__u16 ncci;
+			actcapi_ncpi ncpi;
+		} connect_b3_active_ind;
+		struct connect_b3_active_resp {
+			__u16 ncci;
+		} connect_b3_active_resp;
+		struct disconnect_b3_req {
+			__u16 ncci;
+			actcapi_ncpi ncpi;
+		} disconnect_b3_req;
+		struct disconnect_b3_conf {
+			__u16 ncci;
+			__u16 info;
+		} disconnect_b3_conf;
+		struct disconnect_b3_ind {
+			__u16 ncci;
+			__u16 info;
+			actcapi_ncpi ncpi;
+		} disconnect_b3_ind;
+		struct disconnect_b3_resp {
+			__u16 ncci;
+		} disconnect_b3_resp;
+		struct info_ind {
+			__u16 plci;
+			actcapi_infonr nr;
+			actcapi_infoel el;
+		} info_ind;
+		struct info_resp {
+			__u16 plci;
+		} info_resp;
+		struct listen_b3_req {
+			__u16 plci;
+		} listen_b3_req;
+		struct listen_b3_conf {
+			__u16 plci;
+			__u16 info;
+		} listen_b3_conf;
+		struct select_b2_protocol_req {
+			__u16 plci;
+			__u8  protocol;
+			actcapi_dlpd dlpd __attribute__ ((packed));
+		} select_b2_protocol_req;
+		struct select_b2_protocol_conf {
+			__u16 plci;
+			__u16 info;
+		} select_b2_protocol_conf;
+		struct select_b3_protocol_req {
+			__u16 plci;
+			__u8  protocol;
+			actcapi_ncpd ncpd __attribute__ ((packed));
+		} select_b3_protocol_req;
+		struct select_b3_protocol_conf {
+			__u16 plci;
+			__u16 info;
+		} select_b3_protocol_conf;
+#if 0
+		struct listen_req {
+			__u32 controller;
+			__u32 infomask;  
+			__u32 cipmask;
+			__u32 cipmask2;
+			__u16 dummy; /* 2 Length-bytes of 2 Structs MUST always be 0!!! */
+		} listen_req;
+		struct listen_conf {
+			__u32  controller;
+			__u16 info;
+		} listen_conf;
+#else
+		struct listen_req {
+			__u8  controller;
+			__u32 infomask __attribute__ ((packed));  
+			__u16 eazmask __attribute__ ((packed));
+			__u16 simask __attribute__ ((packed));
+		} listen_req;
+		struct listen_conf {
+			__u8  controller;
+			__u16 info __attribute__ ((packed));
+		} listen_conf;
+#endif
+		struct data_b3_req {
+			__u16 fakencci;
+			__u16 datalen;
+			__u32 unused;
+			__u8  blocknr;
+			__u16 flags __attribute__ ((packed));
+		} data_b3_req;
+		struct data_b3_ind {
+			__u16 fakencci;
+			__u16 datalen;
+			__u32 unused;
+			__u8  blocknr;
+			__u16 flags __attribute__ ((packed));
+		} data_b3_ind;
+		struct data_b3_resp {
+			__u16 ncci;
+			__u8  blocknr;
+		} data_b3_resp;
+		struct data_b3_conf {
+			__u16 ncci;
+			__u8  blocknr;
+			__u16 info __attribute__ ((packed));
+		} data_b3_conf;
+	} msg;
+} actcapi_msg;
+
+extern __inline__ unsigned short
+actcapi_nextsmsg(act2000_card *card)
+{
+	unsigned long flags;
+	unsigned short n;
+
+	save_flags(flags);
+	cli();
+	n = card->msgnum;
+	card->msgnum++;
+	card->msgnum &= 0x7fff;
+	restore_flags(flags);
+	return n;
+}
+#define DEBUG_MSG
+#undef DEBUG_DATA_MSG
+#undef DEBUG_DUMP_SKB
+
+extern int actcapi_chkhdr(act2000_card *, actcapi_msghdr *);
+extern int actcapi_listen_req(act2000_card *);
+extern int actcapi_manufacturer_req_net(act2000_card *);
+extern int actcapi_manufacturer_req_v42(act2000_card *, ulong);
+extern int actcapi_manufacturer_req_errh(act2000_card *);
+extern int actcapi_manufacturer_req_msn(act2000_card *);
+extern int actcapi_connect_req(act2000_card *, act2000_chan *, char *, char, int, int);
+extern void actcapi_select_b2_protocol_req(act2000_card *, act2000_chan *);
+extern void actcapi_disconnect_b3_req(act2000_card *, act2000_chan *);
+extern void actcapi_connect_resp(act2000_card *, act2000_chan *, __u8);
+extern void actcapi_dispatch(act2000_card *);
+#ifdef DEBUG_MSG
+extern void actcapi_debug_msg(struct sk_buff *skb, int);
+#else
+#define actcapi_debug_msg(skb, len)
+#endif
+#endif
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/act2000/module.c linux/drivers/isdn/act2000/module.c
--- v2.1.91/linux/drivers/isdn/act2000/module.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/act2000/module.c	Wed Apr  1 16:20:57 1998
@@ -0,0 +1,953 @@
+/* $Id: module.c,v 1.7 1998/02/12 23:06:52 keil Exp $
+ *
+ * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000.
+ *
+ * Copyright 1997 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Thanks to Friedemann Baitinger and IBM Germany
+ *
+ * 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: module.c,v $
+ * Revision 1.7  1998/02/12 23:06:52  keil
+ * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
+ *
+ * Revision 1.6  1998/01/31 22:10:42  keil
+ * changes for 2.1.82
+ *
+ * Revision 1.5  1997/10/09 22:23:04  fritz
+ * New HL<->LL interface:
+ *   New BSENT callback with nr. of bytes included.
+ *   Sending without ACK.
+ *
+ * Revision 1.4  1997/09/25 17:25:43  fritz
+ * Support for adding cards at runtime.
+ * Support for new Firmware.
+ *
+ * Revision 1.3  1997/09/24 23:11:45  fritz
+ * Optimized IRQ load and polling-mode.
+ *
+ * Revision 1.2  1997/09/24 19:44:17  fritz
+ * Added MSN mapping support, some cleanup.
+ *
+ * Revision 1.1  1997/09/23 18:00:13  fritz
+ * New driver for IBM Active 2000.
+ *
+ */
+
+#include "act2000.h"
+#include "act2000_isa.h"
+#include "capi.h"
+
+static unsigned short isa_ports[] =
+{
+        0x0200, 0x0240, 0x0280, 0x02c0, 0x0300, 0x0340, 0x0380,
+        0xcfe0, 0xcfa0, 0xcf60, 0xcf20, 0xcee0, 0xcea0, 0xce60,
+};
+#define ISA_NRPORTS (sizeof(isa_ports)/sizeof(unsigned short))
+
+act2000_card *cards = (act2000_card *) NULL;
+
+/* Parameters to be set by insmod */
+static int   act_bus  =  0;
+static int   act_port = -1;  /* -1 = Autoprobe  */
+static int   act_irq  = -1;  /* -1 = Autoselect */
+static char *act_id   = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
+
+MODULE_DESCRIPTION(       "Driver for IBM Active 2000 ISDN card");
+MODULE_AUTHOR(            "Fritz Elfert");
+MODULE_SUPPORTED_DEVICE(  "ISDN subsystem");
+MODULE_PARM_DESC(act_bus, "BusType of first card, 1=ISA, 2=MCA, 3=PCMCIA, currently only ISA");
+MODULE_PARM_DESC(membase, "Base port address of first card");
+MODULE_PARM_DESC(act_irq, "IRQ of first card (-1 = grab next free IRQ)");
+MODULE_PARM_DESC(act_id,  "ID-String of first card");
+MODULE_PARM(act_bus,  "i");
+MODULE_PARM(act_port, "i");
+MODULE_PARM(act_irq,  "i");
+MODULE_PARM(act_id,   "s");
+
+static int act2000_addcard(int, int, int, char *);
+
+static act2000_chan *
+find_channel(act2000_card *card, int channel)
+{
+	if ((channel >= 0) && (channel < ACT2000_BCH))
+        	return &(card->bch[channel]);
+	printk(KERN_WARNING "act2000: Invalid channel %d\n", channel);
+	return NULL;
+}
+
+/*
+ * Free MSN list
+ */
+static void
+act2000_clear_msn(act2000_card *card)
+{
+        struct msn_entry *p = card->msn_list;
+        struct msn_entry *q;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+        card->msn_list = NULL;
+	restore_flags(flags);
+        while (p) {
+                q  = p->next;
+                kfree(p);
+                p = q;
+        }
+}
+
+/*
+ * Find an MSN entry in the list.
+ * If ia5 != 0, return IA5-encoded EAZ, else
+ * return a bitmask with corresponding bit set.
+ */
+static __u16
+act2000_find_msn(act2000_card *card, char *msn, int ia5)
+{
+        struct msn_entry *p = card->msn_list;
+	__u8 eaz = '0';
+
+	while (p) {
+		if (!strcmp(p->msn, msn)) {
+			eaz = p->eaz;
+			break;
+		}
+		p = p->next;
+	}
+	if (!ia5)
+		return (1 << (eaz - '0'));
+	else
+		return eaz;
+}
+
+/*
+ * Find an EAZ entry in the list.
+ * return a string with corresponding msn.
+ */
+char *
+act2000_find_eaz(act2000_card *card, char eaz)
+{
+        struct msn_entry *p = card->msn_list;
+
+	while (p) {
+		if (p->eaz == eaz)
+			return(p->msn);
+		p = p->next;
+	}
+	return("\0");
+}
+
+/*
+ * Add or delete an MSN to the MSN list
+ *
+ * First character of msneaz is EAZ, rest is MSN.
+ * If length of eazmsn is 1, delete that entry.
+ */
+static int
+act2000_set_msn(act2000_card *card, char *eazmsn)
+{
+        struct msn_entry *p = card->msn_list;
+        struct msn_entry *q = NULL;
+	unsigned long flags;
+	int i;
+	
+	if (!strlen(eazmsn))
+		return 0;
+	if (strlen(eazmsn) > 16)
+		return -EINVAL;
+	for (i = 0; i < strlen(eazmsn); i++)
+		if (!isdigit(eazmsn[i]))
+			return -EINVAL;
+        if (strlen(eazmsn) == 1) {
+		/* Delete a single MSN */
+		while (p) {
+			if (p->eaz == eazmsn[0]) {
+				save_flags(flags);
+				cli();
+				if (q)
+					q->next = p->next;
+				else
+					card->msn_list = p->next;
+				restore_flags(flags);
+				kfree(p);
+				printk(KERN_DEBUG
+				       "Mapping for EAZ %c deleted\n",
+				       eazmsn[0]);
+				return 0;
+			}
+			q = p;
+			p = p->next;
+		}
+		return 0;
+        }
+	/* Add a single MSN */
+	while (p) {
+		/* Found in list, replace MSN */
+		if (p->eaz == eazmsn[0]) {
+			save_flags(flags);
+			cli();
+			strcpy(p->msn, &eazmsn[1]);
+			restore_flags(flags);
+			printk(KERN_DEBUG
+			       "Mapping for EAZ %c changed to %s\n",
+			       eazmsn[0],
+			       &eazmsn[1]);
+			return 0;
+		}
+		p = p->next;
+	}
+	/* Not found in list, add new entry */
+	p = kmalloc(sizeof(msn_entry), GFP_KERNEL);
+	if (!p)
+		return -ENOMEM;
+	p->eaz = eazmsn[0];
+	strcpy(p->msn, &eazmsn[1]);
+	p->next = card->msn_list;
+	save_flags(flags);
+	cli();
+	card->msn_list = p;
+	restore_flags(flags);
+	printk(KERN_DEBUG
+	       "Mapping %c -> %s added\n",
+	       eazmsn[0],
+	       &eazmsn[1]);
+	return 0;
+}
+
+static void
+act2000_transmit(struct act2000_card *card)
+{
+	switch (card->bus) {
+		case ACT2000_BUS_ISA:
+			isa_send(card);
+			break;
+		case ACT2000_BUS_PCMCIA:
+		case ACT2000_BUS_MCA:
+		default:
+			printk(KERN_WARNING
+			       "act2000_transmit: Illegal bustype %d\n", card->bus);
+	}
+}
+
+static void
+act2000_receive(struct act2000_card *card)
+{
+	switch (card->bus) {
+		case ACT2000_BUS_ISA:
+			isa_receive(card);
+			break;
+		case ACT2000_BUS_PCMCIA:
+		case ACT2000_BUS_MCA:
+		default:
+			printk(KERN_WARNING
+			       "act2000_receive: Illegal bustype %d\n", card->bus);
+	}
+}
+
+static void
+act2000_poll(unsigned long data)
+{
+	act2000_card * card = (act2000_card *)data;
+	unsigned long flags;
+
+	act2000_receive(card);
+        save_flags(flags);
+        cli();
+        del_timer(&card->ptimer);
+        card->ptimer.expires = jiffies + 3;
+        add_timer(&card->ptimer);
+        restore_flags(flags);
+}
+
+static int
+act2000_command(act2000_card * card, isdn_ctrl * c)
+{
+        ulong a;
+        act2000_chan *chan;
+	act2000_cdef cdef;
+	isdn_ctrl cmd;
+	char tmp[17];
+	int ret;
+	unsigned long flags;
+ 
+        switch (c->command) {
+		case ISDN_CMD_IOCTL:
+			memcpy(&a, c->parm.num, sizeof(ulong));
+			switch (c->arg) {
+				case ACT2000_IOCTL_LOADBOOT:
+					switch (card->bus) {
+						case ACT2000_BUS_ISA:
+							ret = isa_download(card,
+									   (act2000_ddef *)a);
+							if (!ret) {
+								card->flags |= ACT2000_FLAGS_LOADED;
+								if (!(card->flags & ACT2000_FLAGS_IVALID)) {
+									card->ptimer.expires = jiffies + 3;
+									card->ptimer.function = act2000_poll;
+									card->ptimer.data = (unsigned long)card;
+									add_timer(&card->ptimer);
+								}
+								actcapi_manufacturer_req_errh(card);
+							}
+							break;
+						default:
+							printk(KERN_WARNING
+							       "act2000: Illegal BUS type %d\n",
+							       card->bus);
+							ret = -EIO;
+					}
+					return ret;
+				case ACT2000_IOCTL_SETPROTO:
+					card->ptype = a?ISDN_PTYPE_EURO:ISDN_PTYPE_1TR6;
+					if (!(card->flags & ACT2000_FLAGS_RUNNING))
+						return 0;
+					actcapi_manufacturer_req_net(card);
+					return 0;
+				case ACT2000_IOCTL_SETMSN:
+					if ((ret = copy_from_user(tmp, (char *)a, sizeof(tmp))))
+						return ret;
+					if ((ret = act2000_set_msn(card, tmp)))
+						return ret;
+					if (card->flags & ACT2000_FLAGS_RUNNING)
+						return(actcapi_manufacturer_req_msn(card));
+					return 0;
+				case ACT2000_IOCTL_ADDCARD:
+					if ((ret = copy_from_user(&cdef, (char *)a, sizeof(cdef))))
+						return ret;
+					if (act2000_addcard(cdef.bus, cdef.port, cdef.irq, cdef.id))
+						return -EIO;
+					return 0;
+				case ACT2000_IOCTL_TEST:
+					if (!(card->flags & ACT2000_FLAGS_RUNNING))
+						return -ENODEV;
+					return 0;
+				default:
+					return -EINVAL;
+			}
+			break;
+		case ISDN_CMD_DIAL:
+			if (!card->flags & ACT2000_FLAGS_RUNNING)
+				return -ENODEV;
+			if (!(chan = find_channel(card, c->arg & 0x0f)))
+				break;
+			save_flags(flags);
+			cli();
+			if (chan->fsm_state != ACT2000_STATE_NULL) {
+				restore_flags(flags);
+				printk(KERN_WARNING "Dial on channel with state %d\n",
+					chan->fsm_state);
+				return -EBUSY;
+			}
+			if (card->ptype == ISDN_PTYPE_EURO)
+				tmp[0] = act2000_find_msn(card, c->parm.setup.eazmsn, 1);
+			else
+				tmp[0] = c->parm.setup.eazmsn[0];
+			chan->fsm_state = ACT2000_STATE_OCALL;
+			chan->callref = 0xffff;
+			restore_flags(flags);
+			ret = actcapi_connect_req(card, chan, c->parm.setup.phone,
+						  tmp[0], c->parm.setup.si1,
+						  c->parm.setup.si2);
+			if (ret) {
+				cmd.driver = card->myid;
+				cmd.command = ISDN_STAT_DHUP;
+				cmd.arg &= 0x0f;
+				card->interface.statcallb(&cmd);
+			}
+			return ret;
+		case ISDN_CMD_ACCEPTD:
+			if (!card->flags & ACT2000_FLAGS_RUNNING)
+				return -ENODEV;
+			if (!(chan = find_channel(card, c->arg & 0x0f)))
+				break;
+			if (chan->fsm_state == ACT2000_STATE_ICALL)
+				actcapi_select_b2_protocol_req(card, chan);
+			return 0;
+		case ISDN_CMD_ACCEPTB:
+			if (!card->flags & ACT2000_FLAGS_RUNNING)
+				return -ENODEV;
+			return 0;
+		case ISDN_CMD_HANGUP:
+			if (!card->flags & ACT2000_FLAGS_RUNNING)
+				return -ENODEV;
+			if (!(chan = find_channel(card, c->arg & 0x0f)))
+				break;
+			switch (chan->fsm_state) {
+				case ACT2000_STATE_ICALL:
+				case ACT2000_STATE_BSETUP:
+					actcapi_connect_resp(card, chan, 0x15);
+					break;
+				case ACT2000_STATE_ACTIVE:
+					actcapi_disconnect_b3_req(card, chan);
+					break;
+			}
+			return 0;
+		case ISDN_CMD_SETEAZ:
+			if (!card->flags & ACT2000_FLAGS_RUNNING)
+				return -ENODEV;
+			if (!(chan = find_channel(card, c->arg & 0x0f)))
+				break;
+			if (strlen(c->parm.num)) {
+				if (card->ptype == ISDN_PTYPE_EURO) {
+					chan->eazmask = act2000_find_msn(card, c->parm.num, 0);
+				}
+				if (card->ptype == ISDN_PTYPE_1TR6) {
+					int i;
+					chan->eazmask = 0;
+					for (i = 0; i < strlen(c->parm.num); i++)
+						if (isdigit(c->parm.num[i]))
+							chan->eazmask |= (1 << (c->parm.num[i] - '0'));
+				}
+			} else
+				chan->eazmask = 0x3ff;
+			actcapi_listen_req(card);
+			return 0;
+		case ISDN_CMD_CLREAZ:
+			if (!card->flags & ACT2000_FLAGS_RUNNING)
+				return -ENODEV;
+			if (!(chan = find_channel(card, c->arg & 0x0f)))
+				break;
+			chan->eazmask = 0;
+			actcapi_listen_req(card);
+			return 0;
+		case ISDN_CMD_SETL2:
+			if (!card->flags & ACT2000_FLAGS_RUNNING)
+				return -ENODEV;
+			if (!(chan = find_channel(card, c->arg & 0x0f)))
+				break;
+			chan->l2prot = (c->arg >> 8);
+			return 0;
+		case ISDN_CMD_GETL2:
+			if (!card->flags & ACT2000_FLAGS_RUNNING)
+				return -ENODEV;
+			if (!(chan = find_channel(card, c->arg & 0x0f)))
+				break;
+			return chan->l2prot;
+		case ISDN_CMD_SETL3:
+			if (!card->flags & ACT2000_FLAGS_RUNNING)
+				return -ENODEV;
+			if ((c->arg >> 8) != ISDN_PROTO_L3_TRANS) {
+				printk(KERN_WARNING "L3 protocol unknown\n");
+				return -1;
+			}
+			if (!(chan = find_channel(card, c->arg & 0x0f)))
+				break;
+			chan->l3prot = (c->arg >> 8);
+			return 0;
+		case ISDN_CMD_GETL3:
+			if (!card->flags & ACT2000_FLAGS_RUNNING)
+				return -ENODEV;
+			if (!(chan = find_channel(card, c->arg & 0x0f)))
+				break;
+			return chan->l3prot;
+		case ISDN_CMD_GETEAZ:
+			if (!card->flags & ACT2000_FLAGS_RUNNING)
+				return -ENODEV;
+			printk(KERN_DEBUG "act2000 CMD_GETEAZ not implemented\n");
+			return 0;
+		case ISDN_CMD_SETSIL:
+			if (!card->flags & ACT2000_FLAGS_RUNNING)
+				return -ENODEV;
+			printk(KERN_DEBUG "act2000 CMD_SETSIL not implemented\n");
+			return 0;
+		case ISDN_CMD_GETSIL:
+			if (!card->flags & ACT2000_FLAGS_RUNNING)
+				return -ENODEV;
+			printk(KERN_DEBUG "act2000 CMD_GETSIL not implemented\n");
+			return 0;
+		case ISDN_CMD_LOCK:
+			MOD_INC_USE_COUNT;
+			return 0;
+		case ISDN_CMD_UNLOCK:
+			MOD_DEC_USE_COUNT;
+			return 0;
+        }
+	
+        return -EINVAL;
+}
+
+static int
+act2000_sendbuf(act2000_card *card, int channel, int ack, struct sk_buff *skb)
+{
+        struct sk_buff *xmit_skb;
+        int len;
+        act2000_chan *chan;
+	actcapi_msg *msg;
+
+        if (!(chan = find_channel(card, channel)))
+		return -1;
+        if (chan->fsm_state != ACT2000_STATE_ACTIVE)
+                return -1;
+        len = skb->len;
+        if ((chan->queued + len) >= ACT2000_MAX_QUEUED)
+                return 0;
+	if (!len)
+		return 0;
+	if (skb_headroom(skb) < 19) {
+		printk(KERN_WARNING "act2000_sendbuf: Headroom only %d\n",
+		       skb_headroom(skb));
+		xmit_skb = alloc_skb(len + 19, GFP_ATOMIC);
+		if (!xmit_skb) {
+			printk(KERN_WARNING "act2000_sendbuf: Out of memory\n");
+			return 0;
+		}
+		skb_reserve(xmit_skb, 19);
+		memcpy(skb_put(xmit_skb, len), skb->data, len);
+	} else {
+		xmit_skb = skb_clone(skb, GFP_ATOMIC);
+		if (!xmit_skb) {
+			printk(KERN_WARNING "act2000_sendbuf: Out of memory\n");
+			return 0;
+		}
+	}
+	dev_kfree_skb(skb);
+	msg = (actcapi_msg *)skb_push(xmit_skb, 19);
+	msg->hdr.len = 19 + len;
+	msg->hdr.applicationID = 1;
+	msg->hdr.cmd.cmd = 0x86;
+	msg->hdr.cmd.subcmd = 0x00;
+	msg->hdr.msgnum = actcapi_nextsmsg(card);
+	msg->msg.data_b3_req.datalen = len;
+	msg->msg.data_b3_req.blocknr = (msg->hdr.msgnum & 0xff);
+	msg->msg.data_b3_req.fakencci = MAKE_NCCI(chan->plci, 0, chan->ncci);
+	msg->msg.data_b3_req.flags = ack; /* Will be set to 0 on actual sending */
+	actcapi_debug_msg(xmit_skb, 1);
+        chan->queued += len;
+	skb_queue_tail(&card->sndq, xmit_skb);
+	act2000_schedule_tx(card);
+        return len;
+}
+
+
+/* Read the Status-replies from the Interface */
+static int
+act2000_readstatus(u_char * buf, int len, int user, act2000_card * card)
+{
+        int count;
+        u_char *p;
+
+        for (p = buf, count = 0; count < len; p++, count++) {
+                if (card->status_buf_read == card->status_buf_write)
+                        return count;
+                if (user)
+                        put_user(*card->status_buf_read++, p);
+                else
+                        *p = *card->status_buf_read++;
+                if (card->status_buf_read > card->status_buf_end)
+                        card->status_buf_read = card->status_buf;
+        }
+        return count;
+}
+
+static void
+act2000_putmsg(act2000_card *card, char c)
+{
+        ulong flags;
+
+        save_flags(flags);
+        cli();
+        *card->status_buf_write++ = c;
+        if (card->status_buf_write == card->status_buf_read) {
+                if (++card->status_buf_read > card->status_buf_end)
+                card->status_buf_read = card->status_buf;
+        }
+        if (card->status_buf_write > card->status_buf_end)
+                card->status_buf_write = card->status_buf;
+        restore_flags(flags);
+}
+
+static void
+act2000_logstat(struct act2000_card *card, char *str)
+{
+        char *p = str;
+        isdn_ctrl c;
+
+	while (*p)
+		act2000_putmsg(card, *p++);
+        c.command = ISDN_STAT_STAVAIL;
+        c.driver = card->myid;
+        c.arg = strlen(str);
+        card->interface.statcallb(&c);
+}
+
+/*
+ * Find card with given driverId
+ */
+static inline act2000_card *
+act2000_findcard(int driverid)
+{
+        act2000_card *p = cards;
+
+        while (p) {
+                if (p->myid == driverid)
+                        return p;
+                p = p->next;
+        }
+        return (act2000_card *) 0;
+}
+
+/*
+ * Wrapper functions for interface to linklevel
+ */
+static int
+if_command(isdn_ctrl * c)
+{
+        act2000_card *card = act2000_findcard(c->driver);
+
+        if (card)
+                return (act2000_command(card, c));
+        printk(KERN_ERR
+             "act2000: 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)
+{
+        act2000_card *card = act2000_findcard(id);
+
+        if (card) {
+                if (!card->flags & ACT2000_FLAGS_RUNNING)
+                        return -ENODEV;
+                return (len);
+        }
+        printk(KERN_ERR
+               "act2000: if_writecmd called with invalid driverId!\n");
+        return -ENODEV;
+}
+
+static int
+if_readstatus(u_char * buf, int len, int user, int id, int channel)
+{
+        act2000_card *card = act2000_findcard(id);
+	
+        if (card) {
+                if (!card->flags & ACT2000_FLAGS_RUNNING)
+                        return -ENODEV;
+                return (act2000_readstatus(buf, len, user, card));
+        }
+        printk(KERN_ERR
+               "act2000: if_readstatus called with invalid driverId!\n");
+        return -ENODEV;
+}
+
+static int
+if_sendbuf(int id, int channel, int ack, struct sk_buff *skb)
+{
+        act2000_card *card = act2000_findcard(id);
+	
+        if (card) {
+                if (!card->flags & ACT2000_FLAGS_RUNNING)
+                        return -ENODEV;
+		return (act2000_sendbuf(card, channel, ack, skb));
+        }
+        printk(KERN_ERR
+               "act2000: if_sendbuf called with invalid driverId!\n");
+        return -ENODEV;
+}
+
+
+/*
+ * Allocate a new card-struct, initialize it
+ * link it into cards-list.
+ */
+static void
+act2000_alloccard(int bus, int port, int irq, char *id)
+{
+	int i;
+        act2000_card *card;
+        if (!(card = (act2000_card *) kmalloc(sizeof(act2000_card), GFP_KERNEL))) {
+                printk(KERN_WARNING
+		       "act2000: (%s) Could not allocate card-struct.\n", id);
+                return;
+        }
+        memset((char *) card, 0, sizeof(act2000_card));
+	skb_queue_head_init(&card->sndq);
+	skb_queue_head_init(&card->rcvq);
+	skb_queue_head_init(&card->ackq);
+	card->snd_tq.routine = (void *) (void *) act2000_transmit;
+	card->snd_tq.data = card;
+	card->rcv_tq.routine = (void *) (void *) actcapi_dispatch;
+	card->rcv_tq.data = card;
+	card->poll_tq.routine = (void *) (void *) act2000_receive;
+	card->poll_tq.data = card;
+	init_timer(&card->ptimer);
+        card->interface.channels = ACT2000_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 |
+#if 0
+/* Not yet! New Firmware is on the way ... */
+		ISDN_FEATURE_L2_TRANS |
+#endif
+		ISDN_FEATURE_L3_TRANS |
+		ISDN_FEATURE_P_UNKNOWN;
+        card->interface.hl_hdrlen = 20;
+        card->ptype = ISDN_PTYPE_EURO;
+        strncpy(card->interface.id, id, sizeof(card->interface.id) - 1);
+        for (i=0; i<ACT2000_BCH; i++) {
+                card->bch[i].plci = 0x8000;
+                card->bch[i].ncci = 0x8000;
+                card->bch[i].l2prot = ISDN_PROTO_L2_X75I;
+                card->bch[i].l3prot = ISDN_PROTO_L3_TRANS;
+        }
+        card->myid = -1;
+        card->bus = bus;
+        card->port = port;
+        card->irq = irq;
+        card->next = cards;
+        cards = card;
+}
+
+/*
+ * register card at linklevel
+ */
+static int
+act2000_registercard(act2000_card * card)
+{
+        switch (card->bus) {
+		case ACT2000_BUS_ISA:
+			break;
+		case ACT2000_BUS_MCA:
+		case ACT2000_BUS_PCMCIA:
+		default:
+			printk(KERN_WARNING
+			       "act2000: Illegal BUS type %d\n",
+			       card->bus);
+			return -1;
+        }
+        if (!register_isdn(&card->interface)) {
+                printk(KERN_WARNING
+                       "act2000: Unable to register %s\n",
+                       card->interface.id);
+                return -1;
+        }
+        card->myid = card->interface.channels;
+        sprintf(card->regname, "act2000-isdn (%s)", card->interface.id);
+        return 0;
+}
+
+static void
+unregister_card(act2000_card * card)
+{
+        isdn_ctrl cmd;
+
+        cmd.command = ISDN_STAT_UNLOAD;
+        cmd.driver = card->myid;
+        card->interface.statcallb(&cmd);
+        switch (card->bus) {
+		case ACT2000_BUS_ISA:
+			isa_release(card);
+			break;
+		case ACT2000_BUS_MCA:
+		case ACT2000_BUS_PCMCIA:
+		default:
+			printk(KERN_WARNING
+			       "act2000: Invalid BUS type %d\n",
+			       card->bus);
+			break;
+        }
+}
+
+static int
+act2000_addcard(int bus, int port, int irq, char *id)
+{
+	act2000_card *p;
+	act2000_card *q = NULL;
+	int initialized;
+	int added = 0;
+	int failed = 0;
+	int i;
+
+	if (!bus)
+		bus = ACT2000_BUS_ISA;
+	if (port != -1) {
+		/* Port defined, do fixed setup */
+		act2000_alloccard(bus, port, irq, id);
+	} else {
+		/* No port defined, perform autoprobing.
+		 * This may result in more than one card detected.
+		 */
+		switch (bus) {
+			case ACT2000_BUS_ISA:
+				for (i = 0; i < ISA_NRPORTS; i++)
+					if (isa_detect(isa_ports[i])) {
+						printk(KERN_INFO
+						       "act2000: Detected ISA card at port 0x%x\n",
+						       isa_ports[i]);
+						act2000_alloccard(bus, isa_ports[i], irq, id);
+					}
+				break;
+			case ACT2000_BUS_MCA:
+			case ACT2000_BUS_PCMCIA:
+			default:
+				printk(KERN_WARNING
+				       "act2000: addcard: Invalid BUS type %d\n",
+				       bus);
+		}
+	}
+	if (!cards)
+		return 1;
+        p = cards;
+        while (p) {
+		initialized = 0;
+		if (!p->interface.statcallb) {
+			/* Not yet registered.
+			 * Try to register and activate it.
+			 */
+			added++;
+			switch (p->bus) {
+				case ACT2000_BUS_ISA:
+					if (isa_detect(p->port)) {
+						if (act2000_registercard(p))
+							break;
+						if (isa_config_port(p, p->port)) {
+							printk(KERN_WARNING
+							       "act2000: Could not request port 0x%04x\n",
+							       p->port);
+							unregister_card(p);
+							p->interface.statcallb = NULL;
+							break;
+						}
+						if (isa_config_irq(p, p->irq)) {
+							printk(KERN_INFO
+							       "act2000: No IRQ available, fallback to polling\n");
+							/* Fall back to polled operation */
+							p->irq = 0;
+						}
+						printk(KERN_INFO
+						       "act2000: ISA"
+						       "-type card at port "
+						       "0x%04x ",
+						       p->port);
+						if (p->irq)
+							printk("irq %d\n", p->irq);
+						else
+							printk("polled\n");
+						initialized = 1;
+					}
+					break;
+				case ACT2000_BUS_MCA:
+				case ACT2000_BUS_PCMCIA:
+				default:
+					printk(KERN_WARNING
+					       "act2000: addcard: Invalid BUS type %d\n",
+					       p->bus);
+			}
+		} else
+			/* Card already initialized */
+			initialized = 1;
+                if (initialized) {
+			/* Init OK, next card ... */
+                        q = p;
+                        p = p->next;
+                } else {
+                        /* Init failed, remove card from list, free memory */
+                        printk(KERN_WARNING
+                               "act2000: Initialization of %s failed\n",
+                               p->interface.id);
+                        if (q) {
+                                q->next = p->next;
+                                kfree(p);
+                                p = q->next;
+                        } else {
+                                cards = p->next;
+                                kfree(p);
+                                p = cards;
+                        }
+			failed++;
+                }
+	}
+        return (added - failed);
+}
+
+#define DRIVERNAME "IBM Active 2000 ISDN driver"
+
+#ifdef MODULE
+#define act2000_init init_module
+#endif
+
+int
+act2000_init(void)
+{
+        printk(KERN_INFO "%s\n", DRIVERNAME);
+        if (!cards)
+		act2000_addcard(act_bus, act_port, act_irq, act_id);
+        if (!cards)
+                printk(KERN_INFO "act2000: No cards defined yet\n");
+        /* No symbols to export, hide all symbols */
+        EXPORT_NO_SYMBOLS;
+        return 0;
+}
+
+#ifdef MODULE
+void
+cleanup_module(void)
+{
+        act2000_card *card = cards;
+        act2000_card *last;
+        while (card) {
+                unregister_card(card);
+		del_timer(&card->ptimer);
+                card = card->next;
+        }
+        card = cards;
+        while (card) {
+                last = card;
+                card = card->next;
+		act2000_clear_msn(last);
+                kfree(last);
+        }
+        printk(KERN_INFO "%s unloaded\n", DRIVERNAME);
+}
+
+#else
+void
+act2000_setup(char *str, int *ints)
+{
+        int i, j, argc, port, irq, bus;
+	
+        argc = ints[0];
+        i = 1;
+        if (argc)
+                while (argc) {
+                        port = irq = -1;
+			bus = 0;
+                        if (argc) {
+                                bus = ints[i];
+                                i++;
+                                argc--;
+                        }
+                        if (argc) {
+                                port = ints[i];
+                                i++;
+                                argc--;
+                        }
+                        if (argc) {
+                                irq = ints[i];
+                                i++;
+                                argc--;
+                        }
+			act2000_addcard(bus, port, irq, act_id);
+		}
+}
+#endif
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/avmb1/b1capi.c linux/drivers/isdn/avmb1/b1capi.c
--- v2.1.91/linux/drivers/isdn/avmb1/b1capi.c	Thu Feb 12 20:56:06 1998
+++ linux/drivers/isdn/avmb1/b1capi.c	Wed Apr  1 16:20:57 1998
@@ -1,11 +1,39 @@
 /*
- * $Id: b1capi.c,v 1.4 1997/05/27 15:17:45 fritz Exp $
+ * $Id: b1capi.c,v 1.10 1998/02/13 07:09:10 calle Exp $
  * 
  * CAPI 2.0 Module for AVM B1-card.
  * 
  * (c) Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
  * $Log: b1capi.c,v $
+ * Revision 1.10  1998/02/13 07:09:10  calle
+ * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
+ *
+ * Revision 1.9  1998/01/31 11:14:39  calle
+ * merged changes to 2.0 tree, prepare 2.1.82 to work.
+ *
+ * Revision 1.8  1997/12/10 20:00:46  calle
+ * get changes from 2.0 version
+ *
+ * Revision 1.4.2.5  1997/12/07 19:59:54  calle
+ * more changes for M1/T1/B1 + config
+ *
+ * Revision 1.4.2.4  1997/11/26 16:57:20  calle
+ * more changes for B1/M1/T1.
+ *
+ * Revision 1.7  1997/10/19 14:45:40  calle
+ * fixed capi_get_version.
+ *
+ * Revision 1.6  1997/10/01 09:21:09  fritz
+ * Removed old compatibility stuff for 2.0.X kernels.
+ * From now on, this code is for 2.1.X ONLY!
+ * Old stuff is still in the separate branch.
+ *
+ * Revision 1.5  1997/07/12 08:22:26  calle
+ * Correct bug in CARD_NR macro, so now more than one card will work.
+ * Allow card reset, even if card is in running state.
+ *
+ *
  * Revision 1.4  1997/05/27 15:17:45  fritz
  * Added changes for recent 2.1.x kernels:
  *   changed return type of isdn_close
@@ -48,24 +76,16 @@
 #include "capicmd.h"
 #include "capiutil.h"
 
-static char *revision = "$Revision: 1.4 $";
+static char *revision = "$Revision: 1.10 $";
 
 /* ------------------------------------------------------------- */
 
 int showcapimsgs = 0;		/* used in lli.c */
 int loaddebug = 0;
-static int portbase = 0x150;
-#ifdef MODULE
-static int irq = 15;
 
-#ifdef HAS_NEW_SYMTAB
 MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>");
-MODULE_PARM(portbase, "i");
-MODULE_PARM(irq, "2-15i");
 MODULE_PARM(showcapimsgs, "0-3i");
 MODULE_PARM(loaddebug, "0-1i");
-#endif
-#endif
 
 /* ------------------------------------------------------------- */
 
@@ -97,7 +117,7 @@
 
 /* ------------------------------------------------------------- */
 
-static struct capi_version driver_version = {2, 0, 0, 9};
+static struct capi_version driver_version = {2, 0, 1, 1<<4};
 static char driver_serial[CAPI_SERIAL_LEN] = "4711";
 static char capi_manufakturer[64] = "AVM Berlin";
 
@@ -109,9 +129,9 @@
 
 #define NCCI2CTRL(ncci)    (((ncci) >> 24) & 0x7f)
 
-#define VALID_CARD(c)	   ((c) > 0 && (c) <= ncards)
+#define VALID_CARD(c)	   ((c) > 0 && (c) <= CAPI_MAXCONTR)
 #define CARD(c)		   (&cards[(c)-1])
-#define CARDNR(cp)	   ((cards-(cp))+1)
+#define CARDNR(cp)	   (((cp)-cards)+1)
 
 static avmb1_appl applications[CAPI_MAXAPPL];
 static avmb1_card cards[CAPI_MAXCONTR];
@@ -126,6 +146,17 @@
 
 /* -------- util functions ------------------------------------ */
 
+static char *cardtype2str(int cardtype)
+{
+	switch (cardtype) {
+		default:
+		case AVM_CARDTYPE_B1: return "B1";
+		case AVM_CARDTYPE_M1: return "M1";
+		case AVM_CARDTYPE_M2: return "M2";
+		case AVM_CARDTYPE_T1: return "T1";
+	}
+}
+
 static inline int capi_cmd_valid(__u8 cmd)
 {
 	switch (cmd) {
@@ -287,6 +318,8 @@
 	return 0;
 }
 
+
+
 /* -------- Receiver ------------------------------------------ */
 
 
@@ -367,6 +400,7 @@
 {
 	struct capi_interface_user *p;
 
+        printk(KERN_NOTICE "b1capi: notify up contr %d\n", contr);
 	for (p = capi_users; p; p = p->next) {
 		if (p->callback)
 			(*p->callback) (KCI_CONTRUP, contr,
@@ -378,6 +412,7 @@
 static void notify_down(__u16 contr)
 {
 	struct capi_interface_user *p;
+        printk(KERN_NOTICE "b1capi: notify down contr %d\n", contr);
 	for (p = capi_users; p; p = p->next) {
 		if (p->callback)
 			(*p->callback) (KCI_CONTRDOWN, contr, 0);
@@ -400,18 +435,22 @@
 
 void avmb1_card_ready(avmb1_card * card)
 {
+        struct capi_profile *profp =
+			(struct capi_profile *)card->version[VER_PROFILE];
+	char *dversion = card->version[VER_DRIVER];
 	__u16 appl;
+	char *cardname, cname[20];
+	__u32 flag;
 
 	card->cversion.majorversion = 2;
 	card->cversion.minorversion = 0;
-	card->cversion.majormanuversion = (card->version[VER_DRIVER][0] - '0') << 4;
-	card->cversion.majormanuversion |= (card->version[VER_DRIVER][2] - '0');
-	card->cversion.minormanuversion = (card->version[VER_DRIVER][3] - '0') << 4;
-	card->cversion.minormanuversion |= (card->version[VER_DRIVER][5] - '0') * 10;
-	card->cversion.minormanuversion |= (card->version[VER_DRIVER][6] - '0');
+	card->cversion.majormanuversion = (((dversion[0] - '0') & 0xf) << 4);
+	card->cversion.majormanuversion |= ((dversion[2] - '0') & 0xf);
+	card->cversion.minormanuversion = (dversion[3] - '0') << 4;
+	card->cversion.minormanuversion |=
+		(dversion[5] - '0') * 10 + ((dversion[6] - '0') & 0xf);
 	card->cardstate = CARD_RUNNING;
 
-
 	for (appl = 1; appl <= CAPI_MAXAPPL; appl++) {
 		if (VALID_APPLID(appl) && !APPL(appl)->releasing) {
 			B1_send_register(card->port, appl,
@@ -424,56 +463,190 @@
 
         set_bit(CARDNR(card), &notify_up_set);
         queue_task(&tq_state_notify, &tq_scheduler);
+
+        flag = ((__u8 *)(profp->manu))[1];
+        switch (flag) {
+	case 0: cardname = cardtype2str(card->cardtype); break;
+	case 3: cardname = "PCMCIA B"; break;
+	case 4: cardname = "PCMCIA M1"; break;
+	case 5: cardname = "PCMCIA M2"; break;
+	case 6: cardname = "B1 V3.0"; break;
+	case 7: cardname = "B1 PCI"; break;
+	default: cardname = cname; break;
+                 sprintf(cname, "AVM?%u", (unsigned int)flag);
+                 break;
+        }
+        printk(KERN_NOTICE "b1capi: card %d \"%s\" ready.\n",
+		CARDNR(card), cardname);
+        flag = ((__u8 *)(profp->manu))[3];
+        if (flag)
+		printk(KERN_NOTICE "b1capi: card %d Protocol:%s%s%s%s%s%s%s\n",
+			CARDNR(card),
+			(flag & 0x01) ? " DSS1" : "",
+			(flag & 0x02) ? " CT1" : "",
+			(flag & 0x04) ? " VN3" : "",
+			(flag & 0x08) ? " NI1" : "",
+			(flag & 0x10) ? " AUSTEL" : "",
+			(flag & 0x20) ? " ESS" : "",
+			(flag & 0x40) ? " 1TR6" : ""
+			);
+        flag = ((__u8 *)(profp->manu))[5];
+	if (flag)
+		printk(KERN_NOTICE "b1capi: card %d Linetype:%s%s%s%s\n",
+			CARDNR(card),
+			(flag & 0x01) ? " point to point" : "",
+			(flag & 0x02) ? " point to multipoint" : "",
+			(flag & 0x08) ? " leased line without D-channel" : "",
+			(flag & 0x04) ? " leased line with D-channel" : ""
+			);
+}
+
+static void avmb1_card_down(avmb1_card * card, int notify)
+{
+	__u16 appl;
+
+        card->cardstate = CARD_DETECTED;
+
+	for (appl = 1; appl <= CAPI_MAXAPPL; appl++) {
+		avmb1_ncci **pp, **nextpp;
+		for (pp = &APPL(appl)->nccilist; *pp; pp = nextpp) {
+			if (NCCI2CTRL((*pp)->ncci) == card->cnr) {
+				avmb1_ncci *np = *pp;
+				*pp = np->next;
+				printk(KERN_INFO "b1capi: appl %d ncci 0x%x forced down!\n", appl, np->ncci);
+				kfree(np);
+				nextpp = pp;
+			} else {
+				nextpp = &(*pp)->next;
+			}
+		}
+	}
+	set_bit(CARDNR(card), &notify_down_set);
+	queue_task(&tq_state_notify, &tq_scheduler);
+	printk(KERN_NOTICE "b1capi: card %d down.\n", CARDNR(card));
 }
 
 /* ------------------------------------------------------------- */
 
-int avmb1_addcard(int port, int irq)
+
+int avmb1_registercard(int port, int irq, int cardtype, int allocio)
 {
 	struct avmb1_card *card;
-	int irqval;
+	int irqval,i;
 
 
-	card = &cards[ncards];
+	for (i=0; i < CAPI_MAXCONTR && cards[i].cardstate != CARD_FREE; i++) ;
+   
+	if (i == CAPI_MAXCONTR) {
+		printk(KERN_ERR "b1capi: out of controller slots\n");
+	   	return -ENFILE;
+	}
+
+	card = &cards[i];
 	memset(card, 0, sizeof(avmb1_card));
-	sprintf(card->name, "avmb1-%d", ncards + 1);
+	sprintf(card->name, "avmb1-%d", CARDNR(card));
 
-	request_region(port, AVMB1_PORTLEN, card->name);
+        if (allocio)
+		request_region(port, AVMB1_PORTLEN, card->name);
 
-	if ((irqval = request_irq(irq, avmb1_interrupt, SA_SHIRQ, card->name, card)) != 0) {
+	if ((irqval = request_irq(irq, avmb1_interrupt,
+				 SA_SHIRQ, card->name, card)) != 0) {
 		printk(KERN_ERR "b1capi: unable to get IRQ %d (irqval=%d).\n",
 		       irq, irqval);
 		release_region((unsigned short) port, AVMB1_PORTLEN);
 		return -EIO;
 	}
+
+	card->cardstate = CARD_DETECTED;
 	ncards++;
-	card->cnr = ncards;
+	card->cnr = CARDNR(card);
 	card->port = port;
 	card->irq = irq;
-	card->cardstate = CARD_DETECTED;
-	return 0;
+	card->cardtype = cardtype;
+	return card->cnr;
 }
 
-int avmb1_probecard(int port, int irq)
+int avmb1_addcard(int port, int irq, int cardtype)
+{
+	return avmb1_registercard(port, irq, cardtype, 1);
+}
+
+int avmb1_detectcard(int port, int irq, int cardtype)
 {
 	int rc;
 
-	if (check_region((unsigned short) port, AVMB1_PORTLEN)) {
-		printk(KERN_WARNING
-		       "b1capi: ports 0x%03x-0x%03x in use.\n",
-		       portbase, portbase + AVMB1_PORTLEN);
+	if (!B1_valid_irq(irq, cardtype)) {
+		printk(KERN_WARNING "b1capi: irq %d not valid for %s-card.\n",
+				irq, cardtype2str(cardtype));
 		return -EIO;
 	}
-	if (!B1_valid_irq(irq)) {
-		printk(KERN_WARNING "b1capi: irq %d not valid.\n", irq);
+	if ((rc = B1_detect(port, cardtype)) != 0) {
+		printk(KERN_NOTICE "b1capi: NO %s-card at 0x%x (%d)\n",
+					  cardtype2str(cardtype), port, rc);
 		return -EIO;
 	}
-	if ((rc = B1_detect(port)) != 0) {
-		printk(KERN_NOTICE "b1capi: NO card at 0x%x (%d)\n", port, rc);
+	B1_reset(port);
+	switch (cardtype) {
+		default:
+	   	case AVM_CARDTYPE_M1:
+	   	case AVM_CARDTYPE_M2:
+	   	case AVM_CARDTYPE_B1:
+	    		printk(KERN_NOTICE "b1capi: AVM-%s-Controller detected at 0x%x\n", cardtype2str(cardtype), port);
+			break;
+	   	case AVM_CARDTYPE_T1:
+	    		printk(KERN_NOTICE "b1capi: AVM-%s-Controller may be at 0x%x\n", cardtype2str(cardtype), port);
+			break;
+	}
+
+	return 0;
+}
+
+int avmb1_probecard(int port, int irq, int cardtype)
+{
+	if (check_region((unsigned short) port, AVMB1_PORTLEN)) {
+		printk(KERN_WARNING
+		       "b1capi: ports 0x%03x-0x%03x in use.\n",
+		       port, port + AVMB1_PORTLEN);
 		return -EIO;
 	}
-	B1_reset(port);
-	printk(KERN_NOTICE "b1capi: AVM-B1-Controller detected at 0x%x\n", port);
+        return avmb1_detectcard(port, irq, cardtype);
+}
+
+int avmb1_unregistercard(int cnr, int freeio)
+{
+	avmb1_card * card;
+   	if (!VALID_CARD(cnr)) 
+		return -ESRCH;
+	card = CARD(cnr);
+	if (card->cardstate == CARD_FREE)
+		return -ESRCH;
+	if (card->cardstate == CARD_RUNNING)
+		avmb1_card_down(card, freeio);
+
+	free_irq(card->irq, card);
+	if (freeio)
+		release_region(card->port, AVMB1_PORTLEN);
+	card->cardstate = CARD_FREE;
+	return 0;
+}
+
+int avmb1_resetcard(int cnr)
+{
+	avmb1_card * card;
+
+	if (!VALID_CARD(cnr))
+		return -ESRCH;
+	card = CARD(cnr);
+	if (card->cardstate == CARD_FREE)
+		return -ESRCH;
+
+	if (card->cardstate == CARD_RUNNING)
+		avmb1_card_down(card, 0);
+
+	B1_reset(card->port);
+	B1_reset(card->port);
+
+	card->cardstate = CARD_DETECTED;
 
 	return 0;
 }
@@ -485,7 +658,7 @@
 static int capi_installed(void)
 {
 	int i;
-	for (i = 0; i < ncards; i++) {
+	for (i = 0; i < CAPI_MAXCONTR; i++) {
 		if (cards[i].cardstate == CARD_RUNNING)
 			return 1;
 	}
@@ -512,7 +685,7 @@
 
 	memcpy(&APPL(appl)->rparam, rparam, sizeof(capi_register_params));
 
-	for (i = 0; i < ncards; i++) {
+	for (i = 0; i < CAPI_MAXCONTR; i++) {
 		if (cards[i].cardstate != CARD_RUNNING)
 			continue;
 		B1_send_register(cards[i].port, appl,
@@ -538,9 +711,10 @@
 		return CAPI_ILLAPPNR;
 	while ((skb = skb_dequeue(&APPL(applid)->recv_queue)) != 0)
 		kfree_skb(skb);
-	for (i = 0; i < ncards; i++) {
-		if (cards[i].cardstate != CARD_RUNNING)
+	for (i = 0; i < CAPI_MAXCONTR; i++) {
+		if (cards[i].cardstate != CARD_RUNNING) {
 			continue;
+		}
 		APPL(applid)->releasing++;
 		B1_send_release(cards[i].port, applid);
 	}
@@ -628,7 +802,7 @@
 	if (!VALID_CARD(contr) || CARD(contr)->cardstate != CARD_RUNNING) 
 		return 0x2002;
 
-	memcpy((void *) verp, CARD(contr)->version[VER_SERIAL],
+	memcpy((void *) verp, &CARD(contr)->cversion,
 	       sizeof(capi_version));
 	return CAPI_NOERROR;
 }
@@ -665,33 +839,52 @@
 static int capi_manufacturer(unsigned int cmd, void *data)
 {
 	unsigned long flags;
-	avmb1_loaddef ldef;
-	avmb1_carddef cdef;
+	avmb1_loadandconfigdef ldef;
+	avmb1_extcarddef cdef;
 	avmb1_resetdef rdef;
+	avmb1_getdef gdef;
 	avmb1_card *card;
 	int rc;
 
 	switch (cmd) {
 	case AVMB1_ADDCARD:
-		if ((rc = copy_from_user((void *) &cdef, data,
-					 sizeof(avmb1_carddef))))
-			return rc;
-		if (!B1_valid_irq(cdef.irq))
-			return -EINVAL;
+	case AVMB1_ADDCARD_WITH_TYPE:
+		if (cmd == AVMB1_ADDCARD) {
+		   if ((rc = copy_from_user((void *) &cdef, data,
+					    sizeof(avmb1_carddef))))
+			   return rc;
+		   cdef.cardtype = AVM_CARDTYPE_B1;
+		} else {
+		   if ((rc = copy_from_user((void *) &cdef, data,
+					    sizeof(avmb1_extcarddef))))
+			   return rc;
+		}
 
-		if ((rc = avmb1_probecard(cdef.port, cdef.irq)) != 0)
+		if ((rc = avmb1_probecard(cdef.port, cdef.irq, cdef.cardtype)) != 0)
 			return rc;
 
-		return avmb1_addcard(cdef.port, cdef.irq);
+		return avmb1_addcard(cdef.port, cdef.irq, cdef.cardtype);
 
 	case AVMB1_LOAD:
+	case AVMB1_LOAD_AND_CONFIG:
 
-		if ((rc = copy_from_user((void *) &ldef, data,
-					 sizeof(avmb1_loaddef))))
-			return rc;
-		if (!VALID_CARD(ldef.contr) || ldef.t4file.len <= 0) {
+		if (cmd == AVMB1_LOAD) {
+			if ((rc = copy_from_user((void *) &ldef, data,
+						sizeof(avmb1_loaddef))))
+				return rc;
+			ldef.t4config.len = 0;
+			ldef.t4config.data = 0;
+		} else {
+			if ((rc = copy_from_user((void *) &ldef, data,
+					    	sizeof(avmb1_loadandconfigdef))))
+				return rc;
+		}
+		if (!VALID_CARD(ldef.contr))
+			return -ESRCH;
+
+		if (ldef.t4file.len <= 0) {
 			if (loaddebug)
-				printk(KERN_DEBUG "b1capi: load: invalid parameter contr=%d len=%d\n", ldef.contr, ldef.t4file.len);
+				printk(KERN_DEBUG "b1capi: load: invalid parameter length of t4file is %d ?\n", ldef.t4file.len);
 			return -EINVAL;
 		}
 
@@ -720,6 +913,20 @@
 			return rc;
 		}
 		B1_disable_irq(card->port);
+
+		if (ldef.t4config.len > 0) { /* load config */
+		        if (loaddebug) {
+				printk(KERN_DEBUG "b1capi: loading config to contr %d\n",
+				   			ldef.contr);
+		   	}
+			if ((rc = B1_load_config(card->port, &ldef.t4config))) {
+				B1_reset(card->port);
+				printk(KERN_ERR "b1capi: failed to load config!!\n");
+				card->cardstate = CARD_DETECTED;
+				return rc;
+			}
+		}
+
 		if (loaddebug) {
 			printk(KERN_DEBUG "b1capi: load: ready contr %d: checking\n",
 				ldef.contr);
@@ -737,7 +944,7 @@
 		card->cardstate = CARD_INITSTATE;
 		save_flags(flags);
 		cli();
-		B1_assign_irq(card->port, card->irq);
+		B1_assign_irq(card->port, card->irq, card->cardtype);
 		B1_enable_irq(card->port);
 		restore_flags(flags);
 
@@ -766,23 +973,31 @@
 				return -EINTR;
 		}
 		return 0;
+
 	case AVMB1_RESETCARD:
 		if ((rc = copy_from_user((void *) &rdef, data,
 					 sizeof(avmb1_resetdef))))
 			return rc;
 
-		if (!VALID_CARD(rdef.contr))
-			return -EINVAL;
+		return avmb1_resetcard(rdef.contr);
 
-		card = CARD(rdef.contr);
+	case AVMB1_GET_CARDINFO:
+		if ((rc = copy_from_user((void *) &gdef, data,
+					 sizeof(avmb1_getdef))))
+			return rc;
 
-		if (card->cardstate == CARD_RUNNING)
-			return -EBUSY;
+		if (!VALID_CARD(gdef.contr))
+			return -ESRCH;
 
-		B1_reset(card->port);
-		B1_reset(card->port);
+		card = CARD(gdef.contr);
+
+		gdef.cardstate = card->cardstate;
+		gdef.cardtype = card->cardtype;
+
+		if ((rc = copy_to_user(data, (void *) &gdef,
+					 sizeof(avmb1_getdef))))
+			return rc;
 
-		card->cardstate = CARD_DETECTED;
 		return 0;
 	}
 	return -EINVAL;
@@ -845,22 +1060,14 @@
 /* -------- Init & Cleanup ------------------------------------- */
 /* ------------------------------------------------------------- */
 
-#ifdef HAS_NEW_SYMTAB
 EXPORT_SYMBOL(attach_capi_interface);
 EXPORT_SYMBOL(detach_capi_interface);
 EXPORT_SYMBOL(avmb1_addcard);
 EXPORT_SYMBOL(avmb1_probecard);
-#else
-static struct symbol_table capidev_syms =
-{
-#include <linux/symtab_begin.h>
-	X(attach_capi_interface),
-	X(detach_capi_interface),
-	X(avmb1_addcard),
-	X(avmb1_probecard),
-#include <linux/symtab_end.h>
-};
-#endif
+EXPORT_SYMBOL(avmb1_registercard);
+EXPORT_SYMBOL(avmb1_unregistercard);
+EXPORT_SYMBOL(avmb1_resetcard);
+EXPORT_SYMBOL(avmb1_detectcard);
 
 
 /*
@@ -876,11 +1083,6 @@
 	char *p;
 	char rev[10];
 
-
-#ifndef HAS_NEW_SYMTAB
-	/* No symbols to export, hide all symbols */
-	register_symtab(&capidev_syms);
-#endif
 	skb_queue_head_init(&recv_queue);
 	/* init_bh(CAPI_BH, do_capi_bh); */
 
@@ -899,15 +1101,7 @@
 		strcpy(rev, " ??? ");
 
 #ifdef MODULE
-	if (portbase) {
-	        int rc;
-		if ((rc = avmb1_probecard(portbase, irq)) != 0)
-			return rc;
-		if ((rc = avmb1_addcard(portbase, irq)) != 0)
-			return rc;
-	} else {
-		printk(KERN_NOTICE "AVM-B1-CAPI-driver Rev%s: loaded\n", rev);
-	}
+        printk(KERN_NOTICE "AVM-B1-CAPI-driver Rev%s: loaded\n", rev);
 #else
 	printk(KERN_NOTICE "AVM-B1-CAPI-driver Rev%s: started\n", rev);
 #endif
@@ -929,20 +1123,20 @@
 		strcpy(rev, " ??? ");
 	}
 
-	for (i = 0; i < ncards; i++) {
-		/*
-		 * disable card
-		 */
-		B1_disable_irq(cards[i].port);
-		B1_reset(cards[i].port);
-		B1_reset(cards[i].port);
-		/*
-		 * free kernel resources
-		 */
-		free_irq(cards[i].irq, &cards[i]);
-		release_region(cards[i].port, AVMB1_PORTLEN);
-
+	for (i = 0; i < CAPI_MAXCONTR; i++) {
+		if (cards[i].cardstate != CARD_FREE) {
+			/*
+			 * disable card
+			 */
+			B1_disable_irq(cards[i].port);
+			avmb1_resetcard(i+1);
+			/*
+			 * free kernel resources
+			 */
+			avmb1_unregistercard(i+1, 1);
+		}
 	}
+	schedule(); /* execute queued tasks .... */
 	printk(KERN_NOTICE "AVM-B1-CAPI-driver Rev%s: unloaded\n", rev);
 }
 #endif
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/avmb1/b1lli.c linux/drivers/isdn/avmb1/b1lli.c
--- v2.1.91/linux/drivers/isdn/avmb1/b1lli.c	Thu Feb 12 20:56:06 1998
+++ linux/drivers/isdn/avmb1/b1lli.c	Wed Apr  1 16:20:57 1998
@@ -1,11 +1,35 @@
 /*
- * $Id: b1lli.c,v 1.1 1997/03/04 21:50:28 calle Exp $
+ * $Id: b1lli.c,v 1.6 1998/02/13 07:09:11 calle Exp $
  * 
  * ISDN lowlevel-module for AVM B1-card.
  * 
  * (c) Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
  * $Log: b1lli.c,v $
+ * Revision 1.6  1998/02/13 07:09:11  calle
+ * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
+ *
+ * Revision 1.5  1998/01/31 11:14:41  calle
+ * merged changes to 2.0 tree, prepare 2.1.82 to work.
+ *
+ * Revision 1.4  1997/12/10 20:00:48  calle
+ * get changes from 2.0 version
+ *
+ * Revision 1.1.2.2  1997/11/26 10:46:55  calle
+ * prepared for M1 (Mobile) and T1 (PMX) cards.
+ * prepared to set configuration after load to support other D-channel
+ * protocols, point-to-point and leased lines.
+ *
+ * Revision 1.3  1997/10/01 09:21:13  fritz
+ * Removed old compatibility stuff for 2.0.X kernels.
+ * From now on, this code is for 2.1.X ONLY!
+ * Old stuff is still in the separate branch.
+ *
+ * Revision 1.2  1997/07/13 12:22:42  calle
+ * bug fix for more than one controller in connect_req.
+ * debugoutput now with contrnr.
+ *
+ *
  * Revision 1.1  1997/03/04 21:50:28  calle
  * Frirst version in isdn4linux
  *
@@ -66,6 +90,9 @@
 					   * B3Length data .... 
 					 */
 
+#define SEND_CONFIG		0x21    /*
+                                         */
+
 /*
  * LLI Messages from the ISDN-ControllerISDN Controller 
  */
@@ -110,6 +137,9 @@
 					   * int32 AppllID int32 0xffffffff 
 					 */
 
+#define WRITE_REGISTER		0x00
+#define READ_REGISTER		0x01
+
 /*
  * port offsets
  */
@@ -120,7 +150,11 @@
 #define B1_OUTSTAT		0x03
 #define B1_RESET		0x10
 #define B1_ANALYSE		0x04
+#define B1_IDENT		0x17  /* Hema card T1 */
+#define B1_IRQ_MASTER		0x12  /* Hema card T1 */
 
+#define B1_STAT0(cardtype)  ((cardtype) == AVM_CARDTYPE_M1 ? 0x81200000l : 0x80A00000l)
+#define B1_STAT1(cardtype)  (0x80E00000l)
 
 
 static inline unsigned char b1outp(unsigned short base,
@@ -131,6 +165,100 @@
 	return inb(base + B1_ANALYSE);
 }
 
+static inline int B1_rx_full(unsigned short base)
+{
+	return inb(base + B1_INSTAT) & 0x1;
+}
+
+static inline unsigned char B1_get_byte(unsigned short base)
+{
+	unsigned long i = jiffies + 5 * HZ;	/* maximum wait time 5 sec */
+	while (!B1_rx_full(base) && i > jiffies);
+	if (B1_rx_full(base))
+		return inb(base + B1_READ);
+	printk(KERN_CRIT "b1lli: rx not full after 5 second\n");
+	return 0;
+}
+
+static inline unsigned int B1_get_word(unsigned short base)
+{
+	unsigned int val = 0;
+	val |= B1_get_byte(base);
+	val |= (B1_get_byte(base) << 8);
+	val |= (B1_get_byte(base) << 16);
+	val |= (B1_get_byte(base) << 24);
+	return val;
+}
+
+static inline int B1_tx_empty(unsigned short base)
+{
+	return inb(base + B1_OUTSTAT) & 0x1;
+}
+
+static inline void B1_put_byte(unsigned short base, unsigned char val)
+{
+	while (!B1_tx_empty(base));
+	b1outp(base, B1_WRITE, val);
+}
+
+static inline void B1_put_word(unsigned short base, unsigned int val)
+{
+	B1_put_byte(base, val & 0xff);
+	B1_put_byte(base, (val >> 8) & 0xff);
+	B1_put_byte(base, (val >> 16) & 0xff);
+	B1_put_byte(base, (val >> 24) & 0xff);
+}
+
+static inline unsigned int B1_get_slice(unsigned short base,
+					unsigned char *dp)
+{
+	unsigned int len, i;
+
+	len = i = B1_get_word(base);
+	while (i-- > 0)
+		*dp++ = B1_get_byte(base);
+	return len;
+}
+
+static inline void B1_put_slice(unsigned short base,
+				unsigned char *dp, unsigned int len)
+{
+	B1_put_word(base, len);
+	while (len-- > 0)
+		B1_put_byte(base, *dp++);
+}
+
+static void b1_wr_reg(unsigned short base,
+                      unsigned int reg,
+		      unsigned int value)
+{
+	B1_put_byte(base, WRITE_REGISTER);
+        B1_put_word(base, reg);
+        B1_put_word(base, value);
+}
+
+static inline unsigned int b1_rd_reg(unsigned short base,
+                                     unsigned int reg)
+{
+	B1_put_byte(base, READ_REGISTER);
+        B1_put_word(base, reg);
+        return B1_get_word(base);
+	
+}
+
+static inline void b1_set_test_bit(unsigned short base,
+				   int cardtype,
+				   int onoff)
+{
+    b1_wr_reg(base, B1_STAT0(cardtype), onoff ? 0x21 : 0x20);
+}
+
+static inline int b1_get_test_bit(unsigned short base,
+                                  int cardtype)
+{
+    return (b1_rd_reg(base, B1_STAT0(cardtype)) & 0x01) != 0;
+}
+
 static int irq_table[16] =
 {0,
  0,
@@ -150,14 +278,30 @@
  112,				/* irq 15 */
 };
 
-int B1_valid_irq(unsigned irq)
+int B1_valid_irq(unsigned irq, int cardtype)
 {
-	return irq_table[irq] != 0;
+	switch (cardtype) {
+	   default:
+	   case AVM_CARDTYPE_M1:
+	   case AVM_CARDTYPE_M2:
+	   case AVM_CARDTYPE_B1:
+	   	return irq_table[irq] != 0;
+	   case AVM_CARDTYPE_T1:
+	   	return irq == 5;
+	}
 }
 
-unsigned char B1_assign_irq(unsigned short base, unsigned irq)
+unsigned char B1_assign_irq(unsigned short base, unsigned irq, int cardtype)
 {
-	return b1outp(base, B1_RESET, irq_table[irq]);
+	switch (cardtype) {
+	   case AVM_CARDTYPE_T1:
+	      return b1outp(base, B1_IRQ_MASTER, 0x08);
+	   default:
+	   case AVM_CARDTYPE_M1:
+	   case AVM_CARDTYPE_M2:
+	   case AVM_CARDTYPE_B1:
+	      return b1outp(base, B1_RESET, irq_table[irq]);
+	 }
 }
 
 unsigned char B1_enable_irq(unsigned short base)
@@ -182,24 +326,27 @@
 	udelay(55 * 2 * 1000);	/* 2 TIC's */
 }
 
-int B1_detect(unsigned short base)
+int B1_detect(unsigned short base, int cardtype)
 {
+	int onoff, i;
+
+	if (cardtype == AVM_CARDTYPE_T1)
+	   return 0;
+
 	/*
 	 * Statusregister 0000 00xx 
 	 */
 	if ((inb(base + B1_INSTAT) & 0xfc)
 	    || (inb(base + B1_OUTSTAT) & 0xfc))
 		return 1;
-
 	/*
 	 * Statusregister 0000 001x 
 	 */
 	b1outp(base, B1_INSTAT, 0x2);	/* enable irq */
-	b1outp(base, B1_OUTSTAT, 0x2);
+	/* b1outp(base, B1_OUTSTAT, 0x2); */
 	if ((inb(base + B1_INSTAT) & 0xfe) != 0x2
-	    || (inb(base + B1_OUTSTAT) & 0xfe) != 0x2)
+	    /* || (inb(base + B1_OUTSTAT) & 0xfe) != 0x2 */)
 		return 2;
-
 	/*
 	 * Statusregister 0000 000x 
 	 */
@@ -208,72 +355,23 @@
 	if ((inb(base + B1_INSTAT) & 0xfe)
 	    || (inb(base + B1_OUTSTAT) & 0xfe))
 		return 3;
+        
+	for (onoff = !0, i= 0; i < 10 ; i++) {
+		b1_set_test_bit(base, cardtype, onoff);
+		if (b1_get_test_bit(base, cardtype) != onoff)
+		   return 4;
+		onoff = !onoff;
+	}
 
-	return 0;
-}
+	if (cardtype == AVM_CARDTYPE_M1)
+	   return 0;
 
-static inline int B1_rx_full(unsigned short base)
-{
-	return inb(base + B1_INSTAT) & 0x1;
-}
+        if ((b1_rd_reg(base, B1_STAT1(cardtype)) & 0x0f) != 0x01)
+	   return 5;
 
-static inline unsigned char B1_get_byte(unsigned short base)
-{
-	unsigned long i = jiffies + 5 * HZ;	/* maximum wait time 5 sec */
-	while (!B1_rx_full(base) && i > jiffies);
-	if (B1_rx_full(base))
-		return inb(base + B1_READ);
-	printk(KERN_CRIT "b1lli: rx not full after 5 second\n");
 	return 0;
 }
 
-static inline unsigned int B1_get_word(unsigned short base)
-{
-	unsigned int val = 0;
-	val |= B1_get_byte(base);
-	val |= (B1_get_byte(base) << 8);
-	val |= (B1_get_byte(base) << 16);
-	val |= (B1_get_byte(base) << 24);
-	return val;
-}
-
-static inline int B1_tx_empty(unsigned short base)
-{
-	return inb(base + B1_OUTSTAT) & 0x1;
-}
-
-static inline void B1_put_byte(unsigned short base, unsigned char val)
-{
-	while (!B1_tx_empty(base));
-	b1outp(base, B1_WRITE, val);
-}
-
-static inline void B1_put_word(unsigned short base, unsigned int val)
-{
-	B1_put_byte(base, val & 0xff);
-	B1_put_byte(base, (val >> 8) & 0xff);
-	B1_put_byte(base, (val >> 16) & 0xff);
-	B1_put_byte(base, (val >> 24) & 0xff);
-}
-
-static inline unsigned int B1_get_slice(unsigned short base,
-					unsigned char *dp)
-{
-	unsigned int len, i;
-
-	len = i = B1_get_word(base);
-	while (i-- > 0)
-		*dp++ = B1_get_byte(base);
-	return len;
-}
-
-static inline void B1_put_slice(unsigned short base,
-				unsigned char *dp, unsigned int len)
-{
-	B1_put_word(base, len);
-	while (len-- > 0)
-		B1_put_byte(base, *dp++);
-}
 
 extern int loaddebug;
 
@@ -316,6 +414,62 @@
 	return 0;
 }
 
+int B1_load_config(unsigned short base, avmb1_t4file * config)
+{
+	/*
+	 * Data is in user space !!!
+	 */
+	unsigned char buf[256];
+	unsigned char *dp;
+	int i, j, left, retval;
+
+
+	dp = config->data;
+	left = config->len;
+	if (left) {
+		B1_put_byte(base, SEND_CONFIG);
+        	B1_put_word(base, 1);
+		B1_put_byte(base, SEND_CONFIG);
+        	B1_put_word(base, left);
+	}
+	while (left > sizeof(buf)) {
+		retval = copy_from_user(buf, dp, sizeof(buf));
+		if (retval)
+			return -EFAULT;
+		if (loaddebug)
+			printk(KERN_DEBUG "b1capi: conf load: %d bytes ..", sizeof(buf));
+		for (i = 0; i < sizeof(buf); ) {
+			B1_put_byte(base, SEND_CONFIG);
+			for (j=0; j < 4; j++) {
+				B1_put_byte(base, buf[i++]);
+			}
+		}
+		if (loaddebug)
+		   printk("ok\n");
+		left -= sizeof(buf);
+		dp += sizeof(buf);
+	}
+	if (left) {
+		retval = copy_from_user(buf, dp, left);
+		if (retval)
+			return -EFAULT;
+		if (loaddebug)
+			printk(KERN_DEBUG "b1capi: conf load: %d bytes ..", left);
+		for (i = 0; i < left; ) {
+			B1_put_byte(base, SEND_CONFIG);
+			for (j=0; j < 4; j++) {
+				if (i < left)
+					B1_put_byte(base, buf[i++]);
+				else
+					B1_put_byte(base, 0);
+			}
+		}
+		if (loaddebug)
+		   printk("ok\n");
+	}
+	return 0;
+}
+
 int B1_loaded(unsigned short base)
 {
 	int i;
@@ -428,7 +582,9 @@
 				       CAPIMSG_APPID(skb->data),
 				       capi_cmd2str(cmd, subcmd), len);
 			} else {
-				printk(KERN_DEBUG "b1lli: Put %s\n", capi_message2str(skb->data));
+				printk(KERN_DEBUG "b1lli: Put [0x%lx] %s\n",
+						(unsigned long) contr,
+						capi_message2str(skb->data));
 			}
 
 		}
@@ -447,7 +603,7 @@
 				       CAPIMSG_APPID(skb->data),
 				       capi_cmd2str(cmd, subcmd), len);
 			} else {
-				printk(KERN_DEBUG "b1lli: Put %s\n", capi_message2str(skb->data));
+				printk(KERN_DEBUG "b1lli: Put [0x%lx] %s\n", (unsigned long)contr, capi_message2str(skb->data));
 			}
 		}
 		save_flags(flags);
@@ -499,13 +655,12 @@
 				       capi_cmd2str(cmd, subcmd),
 				       MsgLen, DataB3Len);
 			} else {
-				printk(KERN_DEBUG "b1lli: Got %s\n", capi_message2str(card->msgbuf));
+				printk(KERN_DEBUG "b1lli: Got [0x%lx] %s\n", (unsigned long)contr, capi_message2str(card->msgbuf));
 			}
 		}
 		if (!(skb = dev_alloc_skb(DataB3Len + MsgLen))) {
 			printk(KERN_ERR "b1lli: incoming packet dropped\n");
 		} else {
-			SET_SKB_FREE(skb);
 			memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen);
 			memcpy(skb_put(skb, DataB3Len), card->databuf, DataB3Len);
 			CAPIMSG_SETDATA(skb->data, skb->data + MsgLen);
@@ -528,14 +683,15 @@
 				       capi_cmd2str(cmd, subcmd),
 				       MsgLen);
 			} else {
-				printk(KERN_DEBUG "b1lli: Got %s\n", capi_message2str(card->msgbuf));
+				printk(KERN_DEBUG "b1lli: Got [0x%lx] %s\n",
+						(unsigned long) contr,
+						capi_message2str(card->msgbuf));
 			}
 
 		}
 		if (!(skb = dev_alloc_skb(MsgLen))) {
 			printk(KERN_ERR "b1lli: incoming packet dropped\n");
 		} else {
-			SET_SKB_FREE(skb);
 			memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen);
 			avmb1_handle_capimsg(card, ApplId, skb);
 		}
@@ -581,10 +737,9 @@
 		card->versionlen = B1_get_slice(card->port, card->versionbuf);
 		card->cardstate = CARD_ACTIVE;
 		parse_version(card);
-		printk(KERN_INFO "b1lli: %s-card (%s) with %s now active\n",
+		printk(KERN_INFO "b1lli: %s-card (%s) now active\n",
 		       card->version[VER_CARDTYPE],
-		       card->version[VER_DRIVER],
-		       card->version[VER_PROTO]);
+		       card->version[VER_DRIVER]);
 		avmb1_card_ready(card);
 		break;
 	default:
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/avmb1/b1pci.c linux/drivers/isdn/avmb1/b1pci.c
--- v2.1.91/linux/drivers/isdn/avmb1/b1pci.c	Thu May 29 21:53:05 1997
+++ linux/drivers/isdn/avmb1/b1pci.c	Wed Apr  1 16:20:57 1998
@@ -1,11 +1,22 @@
 /*
- * $Id: b1pci.c,v 1.2 1997/05/18 09:24:13 calle Exp $
+ * $Id: b1pci.c,v 1.5 1998/01/31 11:14:43 calle Exp $
  * 
  * Module for AVM B1 PCI-card.
  * 
  * (c) Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
  * $Log: b1pci.c,v $
+ * Revision 1.5  1998/01/31 11:14:43  calle
+ * merged changes to 2.0 tree, prepare 2.1.82 to work.
+ *
+ * Revision 1.4  1997/12/10 20:00:50  calle
+ * get changes from 2.0 version
+ *
+ * Revision 1.3  1997/10/01 09:21:14  fritz
+ * Removed old compatibility stuff for 2.0.X kernels.
+ * From now on, this code is for 2.1.X ONLY!
+ * Old stuff is still in the separate branch.
+ *
  * Revision 1.2  1997/05/18 09:24:13  calle
  * added verbose disconnect reason reporting to avmb1.
  * some fixes in capi20 interface.
@@ -19,7 +30,6 @@
 #include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
-#include <linux/bios32.h>
 #include <linux/pci.h>
 #include <linux/skbuff.h>
 #include "compat.h"
@@ -34,13 +44,11 @@
 #define PCI_DEVICE_ID_AVM_B1	0x700
 #endif
 
-static char *revision = "$Revision: 1.2 $";
+static char *revision = "$Revision: 1.5 $";
 
 /* ------------------------------------------------------------- */
 
-#ifdef HAS_NEW_SYMTAB
 MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>");
-#endif
 
 /* ------------------------------------------------------------- */
 
@@ -61,7 +69,7 @@
 	char *p;
 	char rev[10];
 	int rc;
-	int pci_index;
+	struct pci_dev *dev = NULL;
 
 	if ((p = strchr(revision, ':'))) {
 		strcpy(rev, p + 1);
@@ -72,39 +80,26 @@
 
 
 #ifdef CONFIG_PCI
-	if (!pcibios_present()) {
-		printk(KERN_ERR "b1pci: no PCI-BIOS present\n");
+	if (!pci_present()) {
+		printk(KERN_ERR "b1pci: no PCI bus present\n");
 		return -EIO;
 	}
 
 	printk(KERN_INFO "b1pci: revision %s\n", rev);
 
-	for (pci_index = 0; pci_index < 8; pci_index++) {
-		unsigned char pci_bus, pci_device_fn;
-		unsigned int ioaddr;
-		unsigned char irq;
-
-		if (pcibios_find_device (PCI_VENDOR_ID_AVM,
-					PCI_DEVICE_ID_AVM_B1, pci_index,
-					&pci_bus, &pci_device_fn) != 0) {
-			continue;
-		}
-		pcibios_read_config_byte(pci_bus, pci_device_fn,
-				PCI_INTERRUPT_LINE, &irq);
-		pcibios_read_config_dword(pci_bus, pci_device_fn,
-				PCI_BASE_ADDRESS_1, &ioaddr);
-		/* Strip the I/O address out of the returned value */
-		ioaddr &= PCI_BASE_ADDRESS_IO_MASK;
+	while (dev = pci_find_device(PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_B1, dev)) {
+		unsigned int ioaddr = dev->base_address[1] & PCI_BASE_ADDRESS_IO_MASK;
+		unsigned int irq = dev->irq;
 		printk(KERN_INFO
 			"b1pci: PCI BIOS reports AVM-B1 at i/o %#x, irq %d\n",
 			ioaddr, irq);
-		if ((rc = avmb1_probecard(ioaddr, irq)) != 0) {
+		if ((rc = avmb1_probecard(ioaddr, irq, AVM_CARDTYPE_B1)) != 0) {
 		        printk(KERN_ERR
 			"b1pci: no AVM-B1 at i/o %#x, irq %d detected\n",
 			ioaddr, irq);
 			return rc;
 		}
-		if ((rc = avmb1_addcard(ioaddr, irq)) != 0)
+		if ((rc = avmb1_addcard(ioaddr, irq, AVM_CARDTYPE_B1)) < 0)
 			return rc;
 	}
 	return 0;
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/avmb1/capi.c linux/drivers/isdn/avmb1/capi.c
--- v2.1.91/linux/drivers/isdn/avmb1/capi.c	Tue Mar 10 10:03:31 1998
+++ linux/drivers/isdn/avmb1/capi.c	Wed Apr  1 16:20:57 1998
@@ -1,11 +1,34 @@
 /*
- * $Id: capi.c,v 1.4 1997/05/27 15:17:50 fritz Exp $
+ * $Id: capi.c,v 1.10 1998/02/13 07:09:13 calle Exp $
  *
  * CAPI 2.0 Interface for Linux
  *
  * Copyright 1996 by Carsten Paeth (calle@calle.in-berlin.de)
  *
  * $Log: capi.c,v $
+ * Revision 1.10  1998/02/13 07:09:13  calle
+ * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
+ *
+ * Revision 1.9  1998/01/31 11:14:44  calle
+ * merged changes to 2.0 tree, prepare 2.1.82 to work.
+ *
+ * Revision 1.8  1997/11/04 06:12:08  calle
+ * capi.c: new read/write in file_ops since 2.1.60
+ * capidrv.c: prepared isdnlog interface for d2-trace in newer firmware.
+ * capiutil.c: needs config.h (CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON)
+ * compat.h: added #define LinuxVersionCode
+ *
+ * Revision 1.7  1997/10/11 10:29:34  calle
+ * llseek() parameters changed in 2.1.56.
+ *
+ * Revision 1.6  1997/10/01 09:21:15  fritz
+ * Removed old compatibility stuff for 2.0.X kernels.
+ * From now on, this code is for 2.1.X ONLY!
+ * Old stuff is still in the separate branch.
+ *
+ * Revision 1.5  1997/08/21 23:11:55  fritz
+ * Added changes for kernels >= 2.1.45
+ *
  * Revision 1.4  1997/05/27 15:17:50  fritz
  * Added changes for recent 2.1.x kernels:
  *   changed return type of isdn_close
@@ -45,26 +68,22 @@
 #include <linux/timer.h>
 #include <linux/wait.h>
 #include <linux/skbuff.h>
+#include <linux/poll.h>
 #include <linux/capi.h>
 #include <linux/kernelcapi.h>
-#include <linux/poll.h>
 
 #include "compat.h"
 #include "capiutil.h"
 #include "capicmd.h"
 #include "capidev.h"
 
-#ifdef HAS_NEW_SYMTAB
 MODULE_AUTHOR("Carsten Paeth (calle@calle.in-berlin.de)");
-#endif
 
 /* -------- driver information -------------------------------------- */
 
 int capi_major = 68;		/* allocated */
 
-#ifdef HAS_NEW_SYMTAB
 MODULE_PARM(capi_major, "i");
-#endif
 
 /* -------- global variables ---------------------------------------- */
 
@@ -94,21 +113,25 @@
 
 /* -------- file_operations ----------------------------------------- */
 
-static loff_t capi_llseek(struct file *file, loff_t offset, int origin)
+static long long capi_llseek(struct file *file,
+			     long long offset, int origin)
 {
 	return -ESPIPE;
 }
 
-static ssize_t capi_read(struct file *file,
-		      char *buf, size_t count,
-		      loff_t *off)
+static ssize_t capi_read(struct file *file, char *buf,
+		      size_t count, loff_t *ppos)
 {
-	unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
+        struct inode *inode = file->f_dentry->d_inode;
+	unsigned int minor = MINOR(inode->i_rdev);
 	struct capidev *cdev;
 	struct sk_buff *skb;
 	int retval;
 	size_t copied;
 
+       if (ppos != &file->f_pos)
+		return -ESPIPE;
+
 	if (!minor || minor > CAPI_MAXMINOR || !capidevs[minor].is_registered)
 		return -ENODEV;
 
@@ -149,11 +172,11 @@
 	return copied;
 }
 
-static ssize_t capi_write(struct file *file,
-		       const char *buf, size_t count,
-		       loff_t *off)
+static ssize_t capi_write(struct file *file, const char *buf,
+		       size_t count, loff_t *ppos)
 {
-	unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
+        struct inode *inode = file->f_dentry->d_inode;
+	unsigned int minor = MINOR(inode->i_rdev);
 	struct capidev *cdev;
 	struct sk_buff *skb;
 	int retval;
@@ -161,6 +184,9 @@
 	__u8 subcmd;
 	__u16 mlen;
 
+       if (ppos != &file->f_pos)
+		return -ESPIPE;
+
 	if (!minor || minor > CAPI_MAXMINOR || !capidevs[minor].is_registered)
 		return -ENODEV;
 
@@ -200,7 +226,11 @@
 capi_poll(struct file *file, poll_table * wait)
 {
 	unsigned int mask = 0;
+#if (LINUX_VERSION_CODE >= 0x02012d)
 	unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
+#else
+	unsigned int minor = MINOR(file->f_inode->i_rdev);
+#endif
 	struct capidev *cdev;
 
 	if (!minor || minor > CAPI_MAXMINOR || !capidevs[minor].is_registered)
@@ -394,7 +424,7 @@
 	return 0;
 }
 
-static CLOSETYPE
+static int
 capi_release(struct inode *inode, struct file *file)
 {
 	unsigned int minor = MINOR(inode->i_rdev);
@@ -403,7 +433,7 @@
 
 	if (minor >= CAPI_MAXMINOR || !capidevs[minor].is_open) {
 		printk(KERN_ERR "capi20: release minor %d ???\n", minor);
-		return CLOSEVAL;
+		return 0;
 	}
 	cdev = &capidevs[minor];
 
@@ -421,7 +451,7 @@
 	cdev->is_open = 0;
 
 	MOD_DEC_USE_COUNT;
-	return CLOSEVAL;
+	return 0;
 }
 
 static struct file_operations capi_fops =
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/avmb1/capidrv.c linux/drivers/isdn/avmb1/capidrv.c
--- v2.1.91/linux/drivers/isdn/avmb1/capidrv.c	Thu Feb 12 20:56:06 1998
+++ linux/drivers/isdn/avmb1/capidrv.c	Wed Apr  1 16:20:57 1998
@@ -1,11 +1,42 @@
 /*
- * $Id: capidrv.c,v 1.3 1997/05/18 09:24:15 calle Exp $
+ * $Id: capidrv.c,v 1.11 1998/02/13 07:09:15 calle Exp $
  *
  * ISDN4Linux Driver, using capi20 interface (kernelcapi)
  *
  * Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de)
  *
  * $Log: capidrv.c,v $
+ * Revision 1.11  1998/02/13 07:09:15  calle
+ * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
+ *
+ * Revision 1.10  1998/02/02 19:52:23  calle
+ * Fixed vbox (audio) acceptb.
+ *
+ * Revision 1.9  1998/01/31 11:14:45  calle
+ * merged changes to 2.0 tree, prepare 2.1.82 to work.
+ *
+ * Revision 1.8  1997/11/04 06:12:09  calle
+ * capi.c: new read/write in file_ops since 2.1.60
+ * capidrv.c: prepared isdnlog interface for d2-trace in newer firmware.
+ * capiutil.c: needs config.h (CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON)
+ * compat.h: added #define LinuxVersionCode
+ *
+ * Revision 1.7  1997/10/11 10:36:34  calle
+ * Added isdnlog support. patch to isdnlog needed.
+ *
+ * Revision 1.6  1997/10/11 10:25:55  calle
+ * New interface for lowlevel drivers. BSENT with nr. of bytes sent,
+ * allow sending without ACK.
+ *
+ * Revision 1.5  1997/10/01 09:21:16  fritz
+ * Removed old compatibility stuff for 2.0.X kernels.
+ * From now on, this code is for 2.1.X ONLY!
+ * Old stuff is still in the separate branch.
+ *
+ * Revision 1.4  1997/07/13 12:22:43  calle
+ * bug fix for more than one controller in connect_req.
+ * debugoutput now with contrnr.
+ *
  * Revision 1.3  1997/05/18 09:24:15  calle
  * added verbose disconnect reason reporting to avmb1.
  * some fixes in capi20 interface.
@@ -48,13 +79,11 @@
 #include "capicmd.h"
 #include "capidrv.h"
 
-static char *revision = "$Revision: 1.3 $";
+static char *revision = "$Revision: 1.11 $";
 int debugmode = 0;
 
-#ifdef HAS_NEW_SYMTAB
 MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>");
 MODULE_PARM(debugmode, "i");
-#endif
 
 /* -------- type definitions ----------------------------------------- */
 
@@ -116,14 +145,26 @@
 				int oldstate;
 				/* */
 				__u16 datahandle;
+				struct ncci_datahandle_queue {
+				    struct ncci_datahandle_queue *next;
+				    __u16                         datahandle;
+				    int                           len;
+				} *ackqueue;
 			} *ncci_list;
 		} *plcip;
 		struct capidrv_ncci *nccip;
 	} *bchans;
 
 	struct capidrv_plci *plci_list;
+
+	/* for q931 data */
+	__u8  q931_buf[4096];
+	__u8 *q931_read;
+	__u8 *q931_write;
+	__u8 *q931_end;
 };
 
+
 struct capidrv_data {
 	__u16 appid;
 	int ncontr;
@@ -141,6 +182,9 @@
 static capidrv_data global;
 static struct capi_interface *capifuncs;
 
+static void handle_dtrace_data(capidrv_contr *card,
+	int send, int level2, __u8 *data, __u16 len);
+
 /* -------- convert functions ---------------------------------------- */
 
 static inline __u32 b1prot(int l2, int l3)
@@ -167,9 +211,8 @@
 	default:
 		return 0;
 	case ISDN_PROTO_L2_HDLC:
-		return 1;
 	case ISDN_PROTO_L2_TRANS:
-		return 0;
+		return 1;
 	}
 }
 
@@ -410,6 +453,42 @@
 	kfree(nccip);
 }
 
+static int capidrv_add_ack(struct capidrv_ncci *nccip,
+		           __u16 datahandle, int len)
+{
+	struct ncci_datahandle_queue *n, **pp;
+
+	n = (struct ncci_datahandle_queue *)
+		kmalloc(sizeof(struct ncci_datahandle_queue), GFP_ATOMIC);
+	if (!n) {
+	   printk(KERN_ERR "capidrv: kmalloc ncci_datahandle failed\n");
+	   return -1;
+	}
+	n->next = 0;
+	n->datahandle = datahandle;
+	n->len = len;
+	for (pp = &nccip->ackqueue; *pp; pp = &(*pp)->next) ;
+	*pp = n;
+	return 0;
+}
+
+static int capidrv_del_ack(struct capidrv_ncci *nccip, __u16 datahandle)
+{
+	struct ncci_datahandle_queue **pp, *p;
+	int len;
+
+	for (pp = &nccip->ackqueue; *pp; pp = &(*pp)->next) {
+ 		if ((*pp)->datahandle == datahandle) {
+			p = *pp;
+			len = p->len;
+			*pp = (*pp)->next;
+		        kfree(p);
+			return len;
+		}
+	}
+	return -1;
+}
+
 /* -------- convert and send capi message ---------------------------- */
 
 static void send_message(capidrv_contr * card, _cmsg * cmsg)
@@ -419,7 +498,6 @@
 	capi_cmsg2message(cmsg, cmsg->buf);
 	len = CAPIMSG_LEN(cmsg->buf);
 	skb = dev_alloc_skb(len);
-        SET_SKB_FREE(skb);
 	memcpy(skb_put(skb, len), cmsg->buf, len);
 	(*capifuncs->capi_put_message) (global.appid, skb);
 }
@@ -686,8 +764,53 @@
 		break;
 
 	case CAPI_MANUFACTURER_IND:	/* Controller */
+		if (   cmsg->ManuID == 0x214D5641
+		    && cmsg->Class == 0
+		    && cmsg->Function == 1) {
+		   __u8  *data = cmsg->ManuData+3;
+		   __u16  len = cmsg->ManuData[0];
+		   __u16 layer;
+		   int direction;
+		   if (len == 255) {
+		      len = (cmsg->ManuData[1] | (cmsg->ManuData[2] << 8));
+		      data += 2;
+		   }
+		   len -= 2;
+		   layer = ((*(data-1)) << 8) | *(data-2);
+		   if (layer & 0x300)
+			direction = (layer & 0x200) ? 0 : 1;
+		   else direction = (layer & 0x800) ? 0 : 1;
+		   if (layer & 0x0C00) {
+		   	if ((layer & 0xff) == 0x80) {
+		           handle_dtrace_data(card, direction, 1, data, len);
+		           break;
+		   	}
+		   } else if ((layer & 0xff) < 0x80) {
+		      handle_dtrace_data(card, direction, 0, data, len);
+		      break;
+		   }
+	           printk(KERN_INFO "capidrv: %s from controller 0x%x layer 0x%x, ignored\n",
+			capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+			cmsg->adr.adrController, layer);
+                   break;
+		}
 		goto ignored;
 	case CAPI_MANUFACTURER_CONF:	/* Controller */
+		if (cmsg->ManuID == 0x214D5641) {
+		   char *s = 0;
+		   switch (cmsg->Class) {
+		      case 0: break;
+		      case 1: s = "unknown class"; break;
+		      case 2: s = "unknown function"; break;
+		      default: s = "unkown error"; break;
+		   }
+		   if (s)
+	           printk(KERN_INFO "capidrv: %s from controller 0x%x function %d: %s\n",
+			capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+			cmsg->adr.adrController,
+			cmsg->Function, s);
+		   break;
+		}
 		goto ignored;
 	case CAPI_FACILITY_IND:	/* Controller/plci/ncci */
 		goto ignored;
@@ -996,6 +1119,7 @@
 	capidrv_plci *plcip;
 	capidrv_ncci *nccip;
 	isdn_ctrl cmd;
+	int len;
 
 	if (!card) {
 		printk(KERN_ERR "capidrv: %s from unknown controller 0x%x\n",
@@ -1093,11 +1217,14 @@
 		if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI)))
 			goto notfound;
 
-		cmd.command = ISDN_STAT_BSENT;
-		cmd.driver = card->myid;
-		cmd.arg = nccip->chan;
-		card->interface.statcallb(&cmd);
-
+		len = capidrv_del_ack(nccip, cmsg->DataHandle);
+		if (len < 0)
+			break;
+	        cmd.command = ISDN_STAT_BSENT;
+	        cmd.driver = card->myid;
+	        cmd.arg = nccip->chan;
+		cmd.parm.length = len;
+	        card->interface.statcallb(&cmd);
 		break;
 
 	case CAPI_DISCONNECT_B3_IND:	/* ncci */
@@ -1206,6 +1333,60 @@
 
 /* ------------------------------------------------------------------- */
 
+#define PUTBYTE_TO_STATUS(card, byte) \
+	do { \
+		*(card)->q931_write++ = (byte); \
+        	if ((card)->q931_write > (card)->q931_end) \
+	  		(card)->q931_write = (card)->q931_buf; \
+	} while (0)
+
+static void handle_dtrace_data(capidrv_contr *card,
+			     int send, int level2, __u8 *data, __u16 len)
+{
+    long flags;
+    __u8 *p, *end;
+    isdn_ctrl cmd;
+
+    if (!len) {
+       printk(KERN_DEBUG "avmb1_q931_data: len == %d\n", len);
+       return;
+    }
+
+    save_flags(flags);
+    cli();
+
+    if (level2) {
+        PUTBYTE_TO_STATUS(card, 'D');
+        PUTBYTE_TO_STATUS(card, '2');
+        PUTBYTE_TO_STATUS(card, send ? '>' : '<');
+        PUTBYTE_TO_STATUS(card, ':');
+    } else {
+        PUTBYTE_TO_STATUS(card, 'D');
+        PUTBYTE_TO_STATUS(card, '3');
+        PUTBYTE_TO_STATUS(card, send ? '>' : '<');
+        PUTBYTE_TO_STATUS(card, ':');
+    }
+
+    for (p = data, end = data+len; p < end; p++) {
+       __u8 w;
+       PUTBYTE_TO_STATUS(card, ' ');
+       w = (*p >> 4) & 0xf;
+       PUTBYTE_TO_STATUS(card, (w < 10) ? '0'+w : 'A'-10+w);
+       w = *p & 0xf;
+       PUTBYTE_TO_STATUS(card, (w < 10) ? '0'+w : 'A'-10+w);
+    }
+    PUTBYTE_TO_STATUS(card, '\n');
+
+    restore_flags(flags);
+    
+    cmd.command = ISDN_STAT_STAVAIL;
+    cmd.driver = card->myid;
+    cmd.arg = len*3+5;
+    card->interface.statcallb(&cmd);
+}
+
+/* ------------------------------------------------------------------- */
+
 static _cmsg cmdcmsg;
 
 static int capidrv_ioctl(isdn_ctrl * c, capidrv_contr * card)
@@ -1270,7 +1451,7 @@
 			capi_fill_CONNECT_REQ(&cmdcmsg,
 					      global.appid,
 					      card->msgid++,
-					      1,	/* adr */
+					      card->contrnr,	/* adr */
 					  si2cip(bchan->si1, bchan->si2),	/* cipvalue */
 					      called,	/* CalledPartyNumber */
 					      calling,	/* CallingPartyNumber */
@@ -1471,7 +1652,7 @@
 
 static _cmsg sendcmsg;
 
-static int if_sendbuf(int id, int channel, struct sk_buff *skb)
+static int if_sendbuf(int id, int channel, int doack, struct sk_buff *skb)
 {
 	capidrv_contr *card = findcontrbydriverid(id);
 	capidrv_bchan *bchan;
@@ -1479,6 +1660,7 @@
 	int len = skb->len;
 	size_t msglen;
 	__u16 errcode;
+	__u16 datahandle;
 
 	if (!card) {
 		printk(KERN_ERR "capidrv: if_sendbuf called with invalid driverId %d!\n",
@@ -1492,53 +1674,130 @@
 		       card->name, channel);
 		return 0;
 	}
+	datahandle = nccip->datahandle;
 	capi_fill_DATA_B3_REQ(&sendcmsg, global.appid, card->msgid++,
 			      nccip->ncci,	/* adr */
 			      (__u32) skb->data,	/* Data */
 			      skb->len,		/* DataLength */
-			      nccip->datahandle++,	/* DataHandle */
+			      datahandle,	/* DataHandle */
 			      0	/* Flags */
 	    );
+
+	if (capidrv_add_ack(nccip, datahandle, doack ? skb->len : -1) < 0)
+	   return 0;
+
 	capi_cmsg2message(&sendcmsg, sendcmsg.buf);
 	msglen = CAPIMSG_LEN(sendcmsg.buf);
 	if (skb_headroom(skb) < msglen) {
 		struct sk_buff *nskb = dev_alloc_skb(msglen + skb->len);
 		if (!nskb) {
 			printk(KERN_ERR "capidrv: if_sendbuf: no memory\n");
+		        (void)capidrv_del_ack(nccip, datahandle);
 			return 0;
 		}
 #if 0
 		printk(KERN_DEBUG "capidrv: only %d bytes headroom\n",
 		       skb_headroom(skb));
 #endif
-                SET_SKB_FREE(nskb);
 		memcpy(skb_put(nskb, msglen), sendcmsg.buf, msglen);
 		memcpy(skb_put(nskb, skb->len), skb->data, skb->len);
 		errcode = (*capifuncs->capi_put_message) (global.appid, nskb);
-		switch (errcode) {
-		case CAPI_NOERROR:
+		if (errcode == CAPI_NOERROR) {
 			dev_kfree_skb(skb);
+			nccip->datahandle++;
 			return len;
-		case CAPI_SENDQUEUEFULL:
-			dev_kfree_skb(nskb);
-			return 0;
-		default:
-			return -1;
 		}
+	        (void)capidrv_del_ack(nccip, datahandle);
+	        dev_kfree_skb(nskb);
+		return errcode == CAPI_SENDQUEUEFULL ? 0 : -1;
 	} else {
 		memcpy(skb_push(skb, msglen), sendcmsg.buf, msglen);
 		errcode = (*capifuncs->capi_put_message) (global.appid, skb);
-		switch (errcode) {
-		case CAPI_NOERROR:
+		if (errcode == CAPI_NOERROR) {
+			nccip->datahandle++;
 			return len;
-		case CAPI_SENDQUEUEFULL:
-			return 0;
-		default:
-			return -1;
 		}
+	        (void)capidrv_del_ack(nccip, datahandle);
+		return errcode == CAPI_SENDQUEUEFULL ? 0 : -1;
 	}
 }
 
+static int if_readstat(__u8 *buf, int len, int user, int id, int channel)
+{
+	capidrv_contr *card = findcontrbydriverid(id);
+	int count;
+	__u8 *p;
+
+	if (!card) {
+		printk(KERN_ERR "capidrv: if_readstat called with invalid driverId %d!\n",
+		       id);
+		return -ENODEV;
+	}
+
+	for (p=buf, count=0; count < len; p++, count++) {
+	        if (user)
+	                put_user(*card->q931_read++, p);
+	        else
+	                *p = *card->q931_read++;
+	        if (card->q931_read > card->q931_end)
+	                card->q931_read = card->q931_buf;
+	}
+	return count;
+
+}
+
+static void enable_dchannel_trace(capidrv_contr *card)
+{
+        __u8 manufacturer[CAPI_MANUFACTURER_LEN];
+        capi_version version;
+	__u16 contr = card->contrnr;
+	__u16 errcode;
+	__u16 avmversion[3];
+
+        errcode = (*capifuncs->capi_get_manufacturer)(contr, manufacturer);
+        if (errcode != CAPI_NOERROR) {
+	   printk(KERN_ERR "%s: can't get manufacturer (0x%x)\n",
+			card->name, errcode);
+	   return;
+	}
+	if (strstr(manufacturer, "AVM") == 0) {
+	   printk(KERN_ERR "%s: not from AVM, no d-channel trace possible\n",
+			card->name);
+	   return;
+	}
+        errcode = (*capifuncs->capi_get_version)(contr, &version);
+        if (errcode != CAPI_NOERROR) {
+	   printk(KERN_ERR "%s: can't get version (0x%x)\n",
+			card->name, errcode);
+	   return;
+	}
+	avmversion[0] = (version.majormanuversion >> 4) & 0x0f;
+	avmversion[1] = (version.majormanuversion << 4) & 0xf0;
+	avmversion[1] |= (version.minormanuversion >> 4) & 0x0f;
+	avmversion[2] |= version.minormanuversion & 0x0f;
+
+        if (avmversion[0] > 3 || (avmversion[0] == 3 && avmversion[1] > 5)) {
+		printk(KERN_INFO "%s: D2 trace enabled\n", card->name);
+		capi_fill_MANUFACTURER_REQ(&cmdcmsg, global.appid,
+					   card->msgid++,
+					   contr,
+					   0x214D5641,  /* ManuID */
+					   0,           /* Class */
+					   1,           /* Function */
+					   (_cstruct)"\004\200\014\000\000");
+	} else {
+		printk(KERN_INFO "%s: D3 trace enabled\n", card->name);
+		capi_fill_MANUFACTURER_REQ(&cmdcmsg, global.appid,
+					   card->msgid++,
+					   contr,
+					   0x214D5641,  /* ManuID */
+					   0,           /* Class */
+					   1,           /* Function */
+					   (_cstruct)"\004\002\003\000\000");
+	}
+	send_message(card, &cmdcmsg);
+}
+
 static int capidrv_addcontr(__u16 contr, struct capi_profile *profp)
 {
 	capidrv_contr *card;
@@ -1568,7 +1827,7 @@
 	card->interface.command = if_command;
 	card->interface.writebuf_skb = if_sendbuf;
 	card->interface.writecmd = 0;
-	card->interface.readstat = 0;
+	card->interface.readstat = if_readstat;
 	card->interface.features = ISDN_FEATURE_L2_X75I |
 	    ISDN_FEATURE_L2_X75UI |
 	    ISDN_FEATURE_L2_X75BUI |
@@ -1581,6 +1840,9 @@
 	card->next = global.contr_list;
 	global.contr_list = card;
 	global.ncontr++;
+	card->q931_read = card->q931_buf;
+	card->q931_write = card->q931_buf;
+	card->q931_end = card->q931_buf + sizeof(card->q931_buf) - 1;
 
 	if (!register_isdn(&card->interface)) {
 		global.contr_list = global.contr_list->next;
@@ -1600,7 +1862,7 @@
 	cmd.command = ISDN_STAT_RUN;
 	card->interface.statcallb(&cmd);
 
-	card->cipmask = 1;	/* any */
+	card->cipmask = 0x1FFF03FF;	/* any */
 	card->cipmask2 = 0;
 
 	capi_fill_LISTEN_REQ(&cmdcmsg, global.appid,
@@ -1616,6 +1878,8 @@
 	printk(KERN_INFO "%s: now up (%d B channels)\n",
 		card->name, card->nbchan);
 
+        enable_dchannel_trace(card);
+
 	return 0;
 }
 
@@ -1693,11 +1957,6 @@
 
 	if (!capifuncs)
 		return -EIO;
-
-#ifndef HAS_NEW_SYMTAB
-	/* No symbols to export, hide all symbols */
-	register_symtab(NULL);
-#endif
 
 	if ((p = strchr(revision, ':'))) {
 		strcpy(rev, p + 1);
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/avmb1/capiutil.c linux/drivers/isdn/avmb1/capiutil.c
--- v2.1.91/linux/drivers/isdn/avmb1/capiutil.c	Mon Jul  7 08:19:59 1997
+++ linux/drivers/isdn/avmb1/capiutil.c	Wed Apr  1 16:20:57 1998
@@ -1,5 +1,5 @@
 /*
- * $Id: capiutil.c,v 1.3 1997/05/18 09:24:18 calle Exp $
+ * $Id: capiutil.c,v 1.6 1997/11/04 06:12:12 calle Exp $
  *
  * CAPI 2.0 convert capi message to capi message struct
  *
@@ -7,6 +7,20 @@
  * Rewritten for Linux 1996 by Carsten Paeth (calle@calle.in-berlin.de)
  *
  * $Log: capiutil.c,v $
+ * Revision 1.6  1997/11/04 06:12:12  calle
+ * capi.c: new read/write in file_ops since 2.1.60
+ * capidrv.c: prepared isdnlog interface for d2-trace in newer firmware.
+ * capiutil.c: needs config.h (CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON)
+ * compat.h: added #define LinuxVersionCode
+ *
+ * Revision 1.5  1997/10/01 09:21:19  fritz
+ * Removed old compatibility stuff for 2.0.X kernels.
+ * From now on, this code is for 2.1.X ONLY!
+ * Old stuff is still in the separate branch.
+ *
+ * Revision 1.4  1997/08/10 07:43:55  calle
+ * forgot to export symbol capi_info2str for 2.1.x
+ *
  * Revision 1.3  1997/05/18 09:24:18  calle
  * added verbose disconnect reason reporting to avmb1.
  * some fixes in capi20 interface.
@@ -26,13 +40,13 @@
  *
  */
 #include <linux/module.h>
-#include <linux/config.h>
 #include <linux/string.h>
 #include <linux/ctype.h>
 #include <linux/stddef.h>
 #include <linux/kernel.h>
 #include <linux/mm.h>
 #include <asm/segment.h>
+#include <linux/config.h>
 
 #include "compat.h"
 #include "capiutil.h"
@@ -936,35 +950,18 @@
 }
 
 
-#ifdef HAS_NEW_SYMTAB
 EXPORT_SYMBOL(capi_cmsg2message);
 EXPORT_SYMBOL(capi_message2cmsg);
 EXPORT_SYMBOL(capi_cmsg_header);
 EXPORT_SYMBOL(capi_cmd2str);
 EXPORT_SYMBOL(capi_cmsg2str);
 EXPORT_SYMBOL(capi_message2str);
-#else
-static struct symbol_table capifunc_syms =
-{
-#include <linux/symtab_begin.h>
-	X(capi_cmsg2message),
-	X(capi_message2cmsg),
-	X(capi_cmsg_header),
-	X(capi_cmd2str),
-	X(capi_cmsg2str),
-	X(capi_message2str),
-	X(capi_info2str),
-#include <linux/symtab_end.h>
-};
-#endif
+EXPORT_SYMBOL(capi_info2str);
 
 #ifdef MODULE
 
 int init_module(void)
 {
-#ifndef HAS_NEW_SYMTAB
-	register_symtab(&capifunc_syms);
-#endif
 	return 0;
 }
 
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/avmb1/compat.h linux/drivers/isdn/avmb1/compat.h
--- v2.1.91/linux/drivers/isdn/avmb1/compat.h	Mon Nov  3 10:09:19 1997
+++ linux/drivers/isdn/avmb1/compat.h	Wed Apr  1 16:20:57 1998
@@ -1,11 +1,22 @@
 /*
- * $Id: compat.h,v 1.1 1997/03/04 21:50:36 calle Exp $
+ * $Id: compat.h,v 1.3 1997/11/04 06:12:15 calle Exp $
  * 
  * Headerfile for Compartibility between different kernel versions
  * 
  * (c) Copyright 1996 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
  * $Log: compat.h,v $
+ * Revision 1.3  1997/11/04 06:12:15  calle
+ * capi.c: new read/write in file_ops since 2.1.60
+ * capidrv.c: prepared isdnlog interface for d2-trace in newer firmware.
+ * capiutil.c: needs config.h (CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON)
+ * compat.h: added #define LinuxVersionCode
+ *
+ * Revision 1.2  1997/10/01 09:21:22  fritz
+ * Removed old compatibility stuff for 2.0.X kernels.
+ * From now on, this code is for 2.1.X ONLY!
+ * Old stuff is still in the separate branch.
+ *
  * Revision 1.1  1997/03/04 21:50:36  calle
  * Frirst version in isdn4linux
  *
@@ -23,8 +34,8 @@
 #include <linux/version.h>
 #include <linux/isdnif.h>
 
-#if LINUX_VERSION_CODE >= 0x020112	/* 2.1.18 */
-#define HAS_NEW_SYMTAB
+#ifndef LinuxVersionCode
+#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
 #endif
 
 #endif				/* __COMPAT_H__ */
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/Makefile linux/drivers/isdn/hisax/Makefile
--- v2.1.91/linux/drivers/isdn/hisax/Makefile	Thu May 29 21:53:05 1997
+++ linux/drivers/isdn/hisax/Makefile	Wed Apr  1 16:20:57 1998
@@ -1,7 +1,14 @@
 L_OBJS :=
 M_OBJS :=
-O_OBJS := isdnl1.o config.o tei.o isdnl2.o isdnl3.o \
-          q931.o callc.o fsm.o
+LX_OBJS :=
+MX_OBJS :=
+O_OBJS :=
+OX_OBJS :=
+L_TARGET :=
+O_TARGET :=
+
+O_OBJS := isdnl1.o tei.o isdnl2.o isdnl3.o \
+          lmgr.o q931.o callc.o fsm.o
 
 # EXTRA_CFLAGS += -S
 
@@ -17,31 +24,105 @@
 	 O_OBJS += l3_1tr6.o
 endif
 
+ISAC_OBJ :=
+ARCOFI_OBJ :=
+HSCX_OBJ :=
+HFC_OBJ :=
+HFC_2BDS0 :=
+RAWHDLC_OBJ :=
+
 ifeq ($(CONFIG_HISAX_16_0),y)
 	 O_OBJS += teles0.o
+	 ISAC_OBJ := isac.o
+	 HSCX_OBJ := hscx.o
 endif
 
 ifeq ($(CONFIG_HISAX_16_3),y)
 	 O_OBJS += teles3.o
+	 ISAC_OBJ := isac.o
+	 HSCX_OBJ := hscx.o
 endif
 
 ifeq ($(CONFIG_HISAX_AVM_A1),y)
 	 O_OBJS += avm_a1.o
+	 ISAC_OBJ := isac.o
+	 HSCX_OBJ := hscx.o
 endif
 
-ifeq ($(CONFIG_HISAX_ELSA_PCC),y)
-	 O_OBJS += elsa.o
-endif
-
-ifeq ($(CONFIG_HISAX_ELSA_PCMCIA),y)
+ifeq ($(CONFIG_HISAX_ELSA),y)
 	 O_OBJS += elsa.o
+	 ISAC_OBJ := isac.o
+	 HSCX_OBJ := hscx.o
+	 ARCOFI_OBJ := arcofi.o
 endif
 
 ifeq ($(CONFIG_HISAX_IX1MICROR2),y)
 	 O_OBJS += ix1_micro.o
+	 ISAC_OBJ := isac.o
+	 HSCX_OBJ := hscx.o
+endif
+
+ifeq ($(CONFIG_HISAX_DIEHLDIVA),y)
+	 O_OBJS += diva.o
+	 ISAC_OBJ := isac.o
+	 HSCX_OBJ := hscx.o
+endif
+
+ifeq ($(CONFIG_HISAX_ASUSCOM),y)
+	 O_OBJS += asuscom.o
+	 ISAC_OBJ := isac.o
+	 HSCX_OBJ := hscx.o
+endif
+
+ifeq ($(CONFIG_HISAX_TELEINT),y)
+	 O_OBJS += teleint.o
+	 ISAC_OBJ := isac.o
+	 HFC_OBJ := hfc_2bs0.o
+endif
+
+ifeq ($(CONFIG_HISAX_SEDLBAUER),y)
+	 O_OBJS += sedlbauer.o
+	 ISAC_OBJ := isac.o
+	 HSCX_OBJ := hscx.o
 endif
 
+ifeq ($(CONFIG_HISAX_SPORTSTER),y)
+	 O_OBJS += sportster.o
+	 ISAC_OBJ := isac.o
+	 HSCX_OBJ := hscx.o
+endif
+
+ifeq ($(CONFIG_HISAX_MIC),y)
+	 O_OBJS += mic.o
+	 ISAC_OBJ := isac.o
+	 HSCX_OBJ := hscx.o
+endif
+
+ifeq ($(CONFIG_HISAX_NETJET),y)
+	 O_OBJS += netjet.o
+	 ISAC_OBJ := isac.o
+endif
+
+ifeq ($(CONFIG_HISAX_TELES3C),y)
+	 O_OBJS += teles3c.o
+	 HFC_2BDS0 := hfc_2bds0.o
+endif
+ifeq ($(CONFIG_HISAX_AMD7930),y)
+	 O_OBJS += amd7930.o
+	 RAWHDLC_OBJ := rawhdlc.o
+endif
+
+ifeq ($(CONFIG_HISAX_NICCY),y)
+	 O_OBJS += niccy.o
+	 ISAC_OBJ := isac.o
+	 HSCX_OBJ := hscx.o
+endif
+
+O_OBJS += $(ISAC_OBJ) $(HSCX_OBJ) $(HFC_OBJ) $(ARCOFI_OBJ) $(HFC_2BDS0) $(RAWHDLC_OBJ)
+OX_OBJS += config.o
+
 O_TARGET :=
+
 ifeq ($(CONFIG_ISDN_DRV_HISAX),y)
   O_TARGET += hisax.o
 else
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/amd7930.c linux/drivers/isdn/hisax/amd7930.c
--- v2.1.91/linux/drivers/isdn/hisax/amd7930.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/amd7930.c	Wed Apr  1 16:20:57 1998
@@ -0,0 +1,768 @@
+/* $Id: amd7930.c,v 1.2 1998/02/12 23:07:10 keil Exp $
+ *
+ * HiSax ISDN driver - chip specific routines for AMD 7930
+ *
+ * Author       Brent Baccala (baccala@FreeSoft.org)
+ *
+ *
+ *
+ * $Log: amd7930.c,v $
+ * Revision 1.2  1998/02/12 23:07:10  keil
+ * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
+ *
+ * Revision 1.1  1998/02/03 23:20:51  keil
+ * New files for SPARC isdn support
+ *
+ * Revision 1.1  1998/01/08 04:17:12  baccala
+ * ISDN comes to the Sparc.  Key points:
+ *
+ *    - Existing ISDN HiSax driver provides all the smarts
+ *    - it compiles, runs, talks to an isolated phone switch, connects
+ *      to a Cisco, pings go through
+ *    - AMD 7930 support only (no DBRI yet)
+ *    - no US NI-1 support (may not work on US phone system - untested)
+ *    - periodic packet loss, apparently due to lost interrupts
+ *    - ISDN sometimes freezes, requiring reboot before it will work again
+ *
+ * The code is unreliable enough to be consider alpha
+ *
+ *
+ * Advanced Micro Devices' Am79C30A is an ISDN/audio chip used in the
+ * SparcStation 1+.  The chip provides microphone and speaker interfaces
+ * which provide mono-channel audio at 8K samples per second via either
+ * 8-bit A-law or 8-bit mu-law encoding.  Also, the chip features an
+ * ISDN BRI Line Interface Unit (LIU), I.430 S/T physical interface,
+ * which performs basic D channel LAPD processing and provides raw
+ * B channel data.  The digital audio channel, the two ISDN B channels,
+ * and two 64 Kbps channels to the microprocessor are all interconnected
+ * via a multiplexer.
+ *
+ * This driver interfaces to the Linux HiSax ISDN driver, which performs
+ * all high-level Q.921 and Q.931 ISDN functions.  The file is not
+ * itself a hardware driver; rather it uses functions exported by
+ * the AMD7930 driver in the sparcaudio subsystem (drivers/sbus/audio),
+ * allowing the chip to be simultaneously used for both audio and ISDN data.
+ * The hardware driver does _no_ buffering, but provides several callbacks
+ * which are called during interrupt service and should therefore run quickly.
+ *
+ * D channel transmission is performed by passing the hardware driver the
+ * address and size of an skb's data area, then waiting for a callback
+ * to signal successful transmission of the packet.  A task is then
+ * queued to notify the HiSax driver that another packet may be transmitted.
+ *
+ * D channel reception is quite simple, mainly because of:
+ *   1) the slow speed of the D channel - 16 kbps, and
+ *   2) the presence of an 8- or 32-byte (depending on chip version) FIFO
+ *      to buffer the D channel data on the chip
+ * Worst case scenario of back-to-back packets with the 8 byte buffer
+ * at 16 kbps yields an service time of 4 ms - long enough to preclude
+ * the need for fancy buffering.  We queue a background task that copies
+ * data out of the receive buffer into an skb, and the hardware driver
+ * simply does nothing until we're done with the receive buffer and
+ * reset it for a new packet.
+ *
+ * B channel processing is more complex, because of:
+ *   1) the faster speed - 64 kbps,
+ *   2) the lack of any on-chip buffering (it interrupts for every byte), and
+ *   3) the lack of any chip support for HDLC encapsulation
+ *
+ * The HiSax driver can put each B channel into one of three modes -
+ * L1_MODE_NULL (channel disabled), L1_MODE_TRANS (transparent data relay),
+ * and L1_MODE_HDLC (HDLC encapsulation by low-level driver).
+ * L1_MODE_HDLC is the most common, used for almost all "pure" digital
+ * data sessions.  L1_MODE_TRANS is used for ISDN audio.
+ *
+ * HDLC B channel transmission is performed via a large buffer into
+ * which the skb is copied while performing HDLC bit-stuffing.  A CRC
+ * is computed and attached to the end of the buffer, which is then
+ * passed to the low-level routines for raw transmission.  Once
+ * transmission is complete, the hardware driver is set to enter HDLC
+ * idle by successive transmission of mark (all 1) bytes, waiting for
+ * the ISDN driver to prepare another packet for transmission and
+ * deliver it.
+ *
+ * HDLC B channel reception is performed via an X-byte ring buffer
+ * divided into N sections of X/N bytes each.  Defaults: X=256 bytes, N=4.
+ * As the hardware driver notifies us that each section is full, we
+ * hand it the next section and schedule a background task to peruse
+ * the received section, bit-by-bit, with an HDLC decoder.  As
+ * packets are detected, they are copied into a large buffer while
+ * decoding HDLC bit-stuffing.  The ending CRC is verified, and if
+ * it is correct, we alloc a new skb of the correct length (which we
+ * now know), copy the packet into it, and hand it to the upper layers.
+ * Optimization: for large packets, we hand the buffer (which also
+ * happens to be an skb) directly to the upper layer after an skb_trim,
+ * and alloc a new large buffer for future packets, thus avoiding a copy.
+ * Then we return to HDLC processing; state is saved between calls.
+ * 
+ */
+
+#define __NO_VERSION__
+#include "hisax.h"
+#include "../../sbus/audio/amd7930.h"
+#include "isac.h"
+#include "isdnl1.h"
+#include "rawhdlc.h"
+#include <linux/interrupt.h>
+
+static const char *amd7930_revision = "$Revision: 1.2 $";
+
+#define RCV_BUFSIZE	1024	/* Size of raw receive buffer in bytes */
+#define RCV_BUFBLKS	4	/* Number of blocks to divide buffer into
+				 * (must divide RCV_BUFSIZE) */
+
+static void Bchan_fill_fifo(struct BCState *, struct sk_buff *);
+
+static void
+Bchan_xmt_bh(struct BCState *bcs)
+{
+	struct sk_buff *skb;
+
+	if (bcs->hw.amd7930.tx_skb != NULL) {
+		dev_kfree_skb(bcs->hw.amd7930.tx_skb);
+		bcs->hw.amd7930.tx_skb = NULL;
+	}
+
+	if ((skb = skb_dequeue(&bcs->squeue))) {
+		Bchan_fill_fifo(bcs, skb);
+	} else {
+		clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		bcs->event |= 1 << B_XMTBUFREADY;
+		queue_task(&bcs->tqueue, &tq_immediate);
+		mark_bh(IMMEDIATE_BH);
+	}
+}
+
+static void
+Bchan_xmit_callback(struct BCState *bcs)
+{
+	queue_task(&bcs->hw.amd7930.tq_xmt, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+}
+
+/* B channel transmission: two modes (three, if you count L1_MODE_NULL)
+ *
+ * L1_MODE_HDLC - We need to do HDLC encapsulation before transmiting
+ * the packet (i.e. make_raw_hdlc_data).  Since this can be a
+ * time-consuming operation, our completion callback just schedules
+ * a bottom half to do encapsulation for the next packet.  In between,
+ * the link will just idle
+ *
+ * L1_MODE_TRANS - Data goes through, well, transparent.  No HDLC encap,
+ * and we can't just let the link idle, so the "bottom half" actually
+ * gets called during the top half (it's our callback routine in this case),
+ * but it's a lot faster now since we don't call make_raw_hdlc_data
+ */
+
+static void
+Bchan_fill_fifo(struct BCState *bcs, struct sk_buff *skb)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int len;
+
+	if ((cs->debug & L1_DEB_HSCX) || (cs->debug & L1_DEB_HSCX_FIFO)) {
+		char tmp[1024];
+		char *t = tmp;
+
+		t += sprintf(t, "amd7930_fill_fifo %c cnt %d",
+			     bcs->channel ? 'B' : 'A', skb->len);
+		if (cs->debug & L1_DEB_HSCX_FIFO)
+			QuickHex(t, skb->data, skb->len);
+		debugl1(cs, tmp);
+	}
+
+	if (bcs->mode == L1_MODE_HDLC) {
+		len = make_raw_hdlc_data(skb->data, skb->len,
+					 bcs->hw.amd7930.tx_buff, RAW_BUFMAX);
+		if (len > 0)
+			amd7930_bxmit(0, bcs->channel,
+				      bcs->hw.amd7930.tx_buff, len,
+				      (void *) &Bchan_xmit_callback,
+				      (void *) bcs);
+		dev_kfree_skb(skb);
+	} else if (bcs->mode == L1_MODE_TRANS) {
+		amd7930_bxmit(0, bcs->channel,
+			      bcs->hw.amd7930.tx_buff, skb->len,
+			      (void *) &Bchan_xmt_bh,
+			      (void *) bcs);
+		bcs->hw.amd7930.tx_skb = skb;
+	} else {
+		dev_kfree_skb(skb);
+	}
+}
+
+static void
+Bchan_mode(struct BCState *bcs, int mode, int bc)
+{
+	struct IsdnCardState *cs = bcs->cs;
+
+	if (cs->debug & L1_DEB_HSCX) {
+		char tmp[40];
+		sprintf(tmp, "AMD 7930 mode %d bchan %d/%d",
+			mode, bc, bcs->channel);
+		debugl1(cs, tmp);
+	}
+	bcs->mode = mode;
+}
+
+/* Bchan_l2l1 is the entry point for upper layer routines that want to
+ * transmit on the B channel.  PH_DATA_REQ is a normal packet that
+ * we either start transmitting (if idle) or queue (if busy).
+ * PH_PULL_REQ can be called to request a callback message (PH_PULL_CNF)
+ * once the link is idle.  After a "pull" callback, the upper layer
+ * routines can use PH_PULL_IND to send data.
+ */
+
+static void
+Bchan_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+
+	switch (pr) {
+		case (PH_DATA_REQ):
+			if (test_bit(BC_FLG_BUSY, &st->l1.bcs->Flag)) {
+				skb_queue_tail(&st->l1.bcs->squeue, skb);
+			} else {
+				test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+				Bchan_fill_fifo(st->l1.bcs, skb);
+			}
+			break;
+		case (PH_PULL_IND):
+			if (test_bit(BC_FLG_BUSY, &st->l1.bcs->Flag)) {
+				printk(KERN_WARNING "amd7930: this shouldn't happen\n");
+				break;
+			}
+			test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+			Bchan_fill_fifo(st->l1.bcs, skb);
+			break;
+		case (PH_PULL_REQ):
+			if (!test_bit(BC_FLG_BUSY, &st->l1.bcs->Flag)) {
+				clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL_CNF, NULL);
+			} else
+				set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+	}
+}
+
+/* Receiver callback and bottom half - decodes HDLC at leisure (if
+ * L1_MODE_HDLC) and passes newly received skb on via bcs->rqueue.  If
+ * a large packet is received, stick rv_skb (the buffer that the
+ * packet has been decoded into) on the receive queue and alloc a new
+ * (large) skb to act as buffer for future receives.  If a small
+ * packet is received, leave rv_skb alone, alloc a new skb of the
+ * correct size, and copy the packet into it
+ */
+
+static void
+Bchan_recv_callback(struct BCState *bcs)
+{
+	struct amd7930_hw *hw = &bcs->hw.amd7930;
+
+	hw->rv_buff_in += RCV_BUFSIZE/RCV_BUFBLKS;
+	hw->rv_buff_in %= RCV_BUFSIZE;
+
+	if (hw->rv_buff_in != hw->rv_buff_out) {
+		amd7930_brecv(0, bcs->channel,
+			      hw->rv_buff + hw->rv_buff_in,
+			      RCV_BUFSIZE/RCV_BUFBLKS,
+			      (void *) &Bchan_recv_callback, (void *) bcs);
+	}
+
+	queue_task(&hw->tq_rcv, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+}
+
+static void
+Bchan_rcv_bh(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	struct amd7930_hw *hw = &bcs->hw.amd7930;
+	struct sk_buff *skb;
+	int len;
+
+	if (cs->debug & L1_DEB_HSCX) {
+		char tmp[1024];
+
+		sprintf(tmp, "amd7930_Bchan_rcv (%d/%d)",
+			hw->rv_buff_in, hw->rv_buff_out);
+		debugl1(cs, tmp);
+		QuickHex(tmp, hw->rv_buff + hw->rv_buff_out,
+			 RCV_BUFSIZE/RCV_BUFBLKS);
+		debugl1(cs, tmp);
+	}
+
+	do {
+		if (bcs->mode == L1_MODE_HDLC) {
+			while ((len = read_raw_hdlc_data(hw->hdlc_state,
+							 hw->rv_buff + hw->rv_buff_out, RCV_BUFSIZE/RCV_BUFBLKS,
+							 hw->rv_skb->tail, HSCX_BUFMAX))) {
+				if (len > 0 && (cs->debug & L1_DEB_HSCX_FIFO)) {
+					char tmp[1024];
+					char *t = tmp;
+
+					t += sprintf(t, "amd7930_Bchan_rcv %c cnt %d", bcs->channel ? 'B' : 'A', len);
+					QuickHex(t, hw->rv_skb->tail, len);
+					debugl1(cs, tmp);
+				}
+
+				if (len > HSCX_BUFMAX/2) {
+					/* Large packet received */
+
+					if (!(skb = dev_alloc_skb(HSCX_BUFMAX))) {
+						printk(KERN_WARNING "amd7930: receive out of memory");
+					} else {
+						skb_put(hw->rv_skb, len);
+						skb_queue_tail(&bcs->rqueue, hw->rv_skb);
+						hw->rv_skb = skb;
+						bcs->event |= 1 << B_RCVBUFREADY;
+						queue_task(&bcs->tqueue, &tq_immediate);
+					}
+				} else if (len > 0) {
+					/* Small packet received */
+
+					if (!(skb = dev_alloc_skb(len))) {
+						printk(KERN_WARNING "amd7930: receive out of memory\n");
+					} else {
+						memcpy(skb_put(skb, len), hw->rv_skb->tail, len);
+						skb_queue_tail(&bcs->rqueue, skb);
+						bcs->event |= 1 << B_RCVBUFREADY;
+						queue_task(&bcs->tqueue, &tq_immediate);
+						mark_bh(IMMEDIATE_BH);
+					}
+				} else {
+					/* Reception Error */
+					/* printk("amd7930: B channel receive error\n"); */
+				}
+			}
+		} else if (bcs->mode == L1_MODE_TRANS) {
+			if (!(skb = dev_alloc_skb(RCV_BUFSIZE/RCV_BUFBLKS))) {
+				printk(KERN_WARNING "amd7930: receive out of memory\n");
+			} else {
+				memcpy(skb_put(skb, RCV_BUFSIZE/RCV_BUFBLKS),
+				       hw->rv_buff + hw->rv_buff_out,
+				       RCV_BUFSIZE/RCV_BUFBLKS);
+				skb_queue_tail(&bcs->rqueue, skb);
+				bcs->event |= 1 << B_RCVBUFREADY;
+				queue_task(&bcs->tqueue, &tq_immediate);
+				mark_bh(IMMEDIATE_BH);
+			}
+		}
+
+		if (hw->rv_buff_in == hw->rv_buff_out) {
+			/* Buffer was filled up - need to restart receiver */
+			amd7930_brecv(0, bcs->channel,
+				      hw->rv_buff + hw->rv_buff_in,
+				      RCV_BUFSIZE/RCV_BUFBLKS,
+				      (void *) &Bchan_recv_callback,
+				      (void *) bcs);
+		}
+
+		hw->rv_buff_out += RCV_BUFSIZE/RCV_BUFBLKS;
+		hw->rv_buff_out %= RCV_BUFSIZE;
+
+	} while (hw->rv_buff_in != hw->rv_buff_out);
+}
+
+static void
+Bchan_close(struct BCState *bcs)
+{
+	struct sk_buff *skb;
+
+	Bchan_mode(bcs, 0, 0);
+	amd7930_bclose(0, bcs->channel);
+
+	if (test_bit(BC_FLG_INIT, &bcs->Flag)) {
+		while ((skb = skb_dequeue(&bcs->rqueue))) {
+			dev_kfree_skb(skb);
+		}
+		while ((skb = skb_dequeue(&bcs->squeue))) {
+			dev_kfree_skb(skb);
+		}
+	}
+	test_and_clear_bit(BC_FLG_INIT, &bcs->Flag);
+}
+
+static int
+Bchan_open(struct BCState *bcs)
+{
+	struct amd7930_hw *hw = &bcs->hw.amd7930;
+
+	if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+		skb_queue_head_init(&bcs->rqueue);
+		skb_queue_head_init(&bcs->squeue);
+	}
+	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+
+	amd7930_bopen(0, bcs->channel, 0xff);
+	hw->rv_buff_in = 0;
+	hw->rv_buff_out = 0;
+	hw->tx_skb = NULL;
+	init_hdlc_state(hw->hdlc_state, 0);
+	amd7930_brecv(0, bcs->channel,
+		      hw->rv_buff + hw->rv_buff_in, RCV_BUFSIZE/RCV_BUFBLKS,
+		      (void *) &Bchan_recv_callback, (void *) bcs);
+
+	bcs->event = 0;
+	bcs->tx_cnt = 0;
+	return (0);
+}
+
+static void
+Bchan_init(struct BCState *bcs)
+{
+	if (!(bcs->hw.amd7930.tx_buff = kmalloc(RAW_BUFMAX, GFP_ATOMIC))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for amd7930.tx_buff\n");
+		return;
+	}
+	if (!(bcs->hw.amd7930.rv_buff = kmalloc(RCV_BUFSIZE, GFP_ATOMIC))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for amd7930.rv_buff\n");
+		return;
+	}
+	if (!(bcs->hw.amd7930.rv_skb = dev_alloc_skb(HSCX_BUFMAX))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for amd7930.rv_skb\n");
+		return;
+	}
+	if (!(bcs->hw.amd7930.hdlc_state = kmalloc(sizeof(struct hdlc_state),
+						   GFP_ATOMIC))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for amd7930.hdlc_state\n");
+		return;
+	}
+
+	bcs->hw.amd7930.tq_rcv.sync = 0;
+	bcs->hw.amd7930.tq_rcv.routine = (void (*)(void *)) &Bchan_rcv_bh;
+	bcs->hw.amd7930.tq_rcv.data = (void *) bcs;
+
+	bcs->hw.amd7930.tq_xmt.sync = 0;
+	bcs->hw.amd7930.tq_xmt.routine = (void (*)(void *)) &Bchan_xmt_bh;
+	bcs->hw.amd7930.tq_xmt.data = (void *) bcs;
+}
+
+static void
+Bchan_manl1(struct PStack *st, int pr,
+	  void *arg)
+{
+	switch (pr) {
+		case (PH_ACTIVATE_REQ):
+			test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+			Bchan_mode(st->l1.bcs, st->l1.mode, st->l1.bc);
+			st->l1.l1man(st, PH_ACTIVATE_CNF, NULL);
+			break;
+		case (PH_DEACTIVATE_REQ):
+			if (!test_bit(BC_FLG_BUSY, &st->l1.bcs->Flag))
+				Bchan_mode(st->l1.bcs, 0, 0);
+			test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+			break;
+	}
+}
+
+int
+setstack_amd7930(struct PStack *st, struct BCState *bcs)
+{
+	if (Bchan_open(bcs))
+		return (-1);
+	st->l1.bcs = bcs;
+	st->l2.l2l1 = Bchan_l2l1;
+	st->ma.manl1 = Bchan_manl1;
+	setstack_manager(st);
+	bcs->st = st;
+	return (0);
+}
+
+
+static void
+amd7930_drecv_callback(void *arg, int error, unsigned int count)
+{
+	struct IsdnCardState *cs = (struct IsdnCardState *) arg;
+	static struct tq_struct task;
+	struct sk_buff *skb;
+
+        /* NOTE: This function is called directly from an interrupt handler */
+
+	if (1) {
+		if (!(skb = alloc_skb(count, GFP_ATOMIC)))
+			printk(KERN_WARNING "HiSax: D receive out of memory\n");
+		else {
+			memcpy(skb_put(skb, count), cs->rcvbuf, count);
+			skb_queue_tail(&cs->rq, skb);
+		}
+
+		task.routine = (void *) DChannel_proc_rcv;
+		task.data = (void *) cs;
+		queue_task(&task, &tq_immediate);
+		mark_bh(IMMEDIATE_BH);
+	}
+
+	if (cs->debug & L1_DEB_ISAC_FIFO) {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "amd7930 Drecv cnt %d", count);
+		if (error) t += sprintf(t, " ERR %x", error);
+		QuickHex(t, cs->rcvbuf, count);
+		debugl1(cs, tmp);
+	}
+
+	amd7930_drecv(0, cs->rcvbuf, MAX_DFRAME_LEN,
+		      &amd7930_drecv_callback, cs);
+}
+
+static void
+amd7930_dxmit_callback(void *arg, int error)
+{
+	struct IsdnCardState *cs = (struct IsdnCardState *) arg;
+	static struct tq_struct task;
+
+        /* NOTE: This function is called directly from an interrupt handler */
+
+	/* may wish to do retransmission here, if error indicates collision */
+
+	if (cs->debug & L1_DEB_ISAC_FIFO) {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "amd7930 Dxmit cnt %d", cs->tx_skb->len);
+		if (error) t += sprintf(t, " ERR %x", error);
+		QuickHex(t, cs->tx_skb->data, cs->tx_skb->len);
+		debugl1(cs, tmp);
+	}
+
+	cs->tx_skb = NULL;
+
+	task.routine = (void *) DChannel_proc_xmt;
+	task.data = (void *) cs;
+	queue_task(&task, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+}
+
+static void
+amd7930_Dchan_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
+	struct sk_buff *skb = arg;
+	char str[64];
+
+	switch (pr) {
+		case (PH_DATA_REQ):
+			if (cs->tx_skb) {
+				skb_queue_tail(&cs->sq, skb);
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "PH_DATA Queued", 0);
+#endif
+			} else {
+				if ((cs->dlogflag) && (!(skb->data[2] & 1))) {
+					/* I-FRAME */
+					LogFrame(cs, skb->data, skb->len);
+					sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei);
+					dlogframe(cs, skb->data+4, skb->len-4,
+						  str);
+				}
+				cs->tx_skb = skb;
+				cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "PH_DATA", 0);
+#endif
+				amd7930_dxmit(0, skb->data, skb->len,
+					      &amd7930_dxmit_callback, cs);
+			}
+			break;
+		case (PH_PULL_IND):
+			if (cs->tx_skb) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, " l2l1 tx_skb exist this shouldn't happen");
+				skb_queue_tail(&cs->sq, skb);
+				break;
+			}
+			if ((cs->dlogflag) && (!(skb->data[2] & 1))) {	/* I-FRAME */
+				LogFrame(cs, skb->data, skb->len);
+				sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei);
+				dlogframe(cs, skb->data + 4, skb->len - 4,
+					  str);
+			}
+			cs->tx_skb = skb;
+			cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (cs->debug & L1_DEB_LAPD)
+				Logl2Frame(cs, skb, "PH_DATA_PULLED", 0);
+#endif
+			amd7930_dxmit(0, cs->tx_skb->data, cs->tx_skb->len,
+				      &amd7930_dxmit_callback, cs);
+			break;
+		case (PH_PULL_REQ):
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (cs->debug & L1_DEB_LAPD)
+				debugl1(cs, "-> PH_REQUEST_PULL");
+#endif
+			if (!cs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL_CNF, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+	}
+}
+
+int
+setDstack_amd7930(struct PStack *st, struct IsdnCardState *cs)
+{
+	st->l2.l2l1 = amd7930_Dchan_l2l1;
+	if (! cs->rcvbuf) {
+		printk("setDstack_amd7930: No cs->rcvbuf!\n");
+	} else {
+		amd7930_drecv(0, cs->rcvbuf, MAX_DFRAME_LEN,
+			      &amd7930_drecv_callback, cs);
+	}
+	return (0);
+}
+
+static void
+manl1_msg(struct IsdnCardState *cs, int msg, void *arg) {
+	struct PStack *st;
+
+	st = cs->stlist;
+	while (st) {
+		st->ma.manl1(st, msg, arg);
+		st = st->next;
+	}
+}
+
+static void
+amd7930_new_ph(struct IsdnCardState *cs)
+{
+	switch (amd7930_get_liu_state(0)) {
+	        case 3:
+			manl1_msg(cs, PH_POWERUP_CNF, NULL);
+                        break;
+
+	        case 7:
+			manl1_msg(cs, PH_I4_P8_IND, NULL);
+			break;
+
+	        case 8:
+			manl1_msg(cs, PH_RSYNC_IND, NULL);
+			break;
+	}
+}
+
+/* amd7930 LIU state change callback */
+
+static void
+amd7930_liu_callback(struct IsdnCardState *cs)
+{
+	static struct tq_struct task;
+
+	if (!cs)
+		return;
+
+	if (cs->debug & L1_DEB_ISAC) {
+		char tmp[32];
+		sprintf(tmp, "amd7930_liu state %d", amd7930_get_liu_state(0));
+		debugl1(cs, tmp);
+	}
+
+	task.sync = 0;
+	task.routine = (void *) &amd7930_new_ph;
+	task.data = (void *) cs;
+	queue_task(&task, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+}
+
+void
+amd7930_l1cmd(struct IsdnCardState *cs, int msg, void *arg)
+{
+	u_char val;
+	char tmp[32];
+	
+	if (cs->debug & L1_DEB_ISAC) {
+		char tmp[32];
+		sprintf(tmp, "amd7930_l1cmd msg %x", msg);
+		debugl1(cs, tmp);
+	}
+
+	switch(msg) {
+		case PH_RESET_REQ:
+			if (amd7930_get_liu_state(0) <= 3)
+				amd7930_liu_activate(0,0);
+			else
+				amd7930_liu_deactivate(0);
+			break;
+		case PH_ENABLE_REQ:
+			break;
+		case PH_INFO3_REQ:
+			amd7930_liu_activate(0,0);
+			break;
+		case PH_TESTLOOP_REQ:
+			break;
+		default:
+			if (cs->debug & L1_DEB_WARN) {
+				sprintf(tmp, "amd7930_l1cmd unknown %4x", msg);
+				debugl1(cs, tmp);
+			}
+			break;
+	}
+}
+
+static void init_amd7930(struct IsdnCardState *cs)
+{
+	Bchan_init(&cs->bcs[0]);
+	Bchan_init(&cs->bcs[1]);
+	cs->bcs[0].BC_SetStack = setstack_amd7930;
+	cs->bcs[1].BC_SetStack = setstack_amd7930;
+	cs->bcs[0].BC_Close = Bchan_close;
+	cs->bcs[1].BC_Close = Bchan_close;
+	Bchan_mode(cs->bcs, 0, 0);
+	Bchan_mode(cs->bcs + 1, 0, 0);
+}
+
+void
+release_amd7930(struct IsdnCardState *cs)
+{
+}
+
+static int
+amd7930_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	switch (mt) {
+		case CARD_RESET:
+			return(0);
+		case CARD_RELEASE:
+			release_amd7930(cs);
+			return(0);
+		case CARD_SETIRQ:
+			return(0);
+		case CARD_INIT:
+			cs->l1cmd = amd7930_l1cmd;
+			amd7930_liu_init(0, &amd7930_liu_callback, (void *)cs);
+			init_amd7930(cs);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+__initfunc(int
+setup_amd7930(struct IsdnCard *card))
+{
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, amd7930_revision);
+	printk(KERN_INFO "HiSax: AMD7930 driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_AMD7930)
+		return (0);
+
+        cs->irq = amd7930_get_irqnum(0);
+        if (cs->irq == 0)
+		return (0);
+
+	cs->cardmsg = &amd7930_card_msg;
+
+	return (1);
+}
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/arcofi.c linux/drivers/isdn/hisax/arcofi.c
--- v2.1.91/linux/drivers/isdn/hisax/arcofi.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/arcofi.c	Wed Apr  1 16:20:57 1998
@@ -0,0 +1,51 @@
+/* $Id: arcofi.c,v 1.1 1997/10/29 18:51:20 keil Exp $
+
+ * arcofi.h   Ansteuerung ARCOFI 2165
+ *
+ * Author     Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ *
+ *
+ * $Log: arcofi.c,v $
+ * Revision 1.1  1997/10/29 18:51:20  keil
+ * New files
+ *
+ */
+ 
+#define __NO_VERSION__
+#include "hisax.h"
+#include "isdnl1.h"
+#include "isac.h"
+
+int
+send_arcofi(struct IsdnCardState *cs, const u_char *msg) {
+	u_char val;
+	char tmp[32];
+	long flags;
+	int cnt=2;
+	
+	cs->mon_txp = 0;
+	cs->mon_txc = msg[0];
+	memcpy(cs->mon_tx, &msg[1], cs->mon_txc);
+	cs->mocr &= 0x0f;
+	cs->mocr |= 0xa0;
+	test_and_clear_bit(HW_MON1_TX_END, &cs->HW_Flags);
+	cs->writeisac(cs, ISAC_MOCR, cs->mocr);
+	val = cs->readisac(cs, ISAC_MOSR);
+	cs->writeisac(cs, ISAC_MOX1, cs->mon_tx[cs->mon_txp++]);
+	cs->mocr |= 0x10;
+	cs->writeisac(cs, ISAC_MOCR, cs->mocr);
+	save_flags(flags);
+	sti();
+	while (cnt && !test_bit(HW_MON1_TX_END, &cs->HW_Flags)) {
+		cnt--;
+		current->state = TASK_INTERRUPTIBLE;
+		current->timeout = jiffies + (10 * HZ) / 1000;	/* Timeout 10ms */
+		schedule();
+	}
+	restore_flags(flags);
+	sprintf(tmp, "arcofi tout %d", cnt);
+	debugl1(cs, tmp);
+	return(cnt);	
+}
+
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/arcofi.h linux/drivers/isdn/hisax/arcofi.h
--- v2.1.91/linux/drivers/isdn/hisax/arcofi.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/arcofi.h	Wed Apr  1 16:20:57 1998
@@ -0,0 +1,17 @@
+/* $Id: arcofi.h,v 1.1 1997/10/29 18:51:20 keil Exp $
+
+ * arcofi.h   Ansteuerung ARCOFI 2165
+ *
+ * Author     Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ *
+ *
+ * $Log: arcofi.h,v $
+ * Revision 1.1  1997/10/29 18:51:20  keil
+ * New files
+ *
+ */
+ 
+#define ARCOFI_USE	1
+
+extern int send_arcofi(struct IsdnCardState *cs, const u_char *msg);
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/asuscom.c linux/drivers/isdn/hisax/asuscom.c
--- v2.1.91/linux/drivers/isdn/hisax/asuscom.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/asuscom.c	Wed Apr  1 16:20:57 1998
@@ -0,0 +1,292 @@
+/* $Id: asuscom.c,v 1.2 1998/02/02 13:27:06 keil Exp $
+
+ * asuscom.c     low level stuff for ASUSCOM NETWORK INC. ISDNLink cards
+ *
+ * Author     Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ * Thanks to  ASUSCOM NETWORK INC. Taiwan and  Dynalink NL for informations
+ *
+ *
+ * $Log: asuscom.c,v $
+ * Revision 1.2  1998/02/02 13:27:06  keil
+ * New
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+
+extern const char *CardType[];
+
+const char *Asuscom_revision = "$Revision: 1.2 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define ASUS_ISAC	0
+#define ASUS_HSCX	1
+#define ASUS_ADR	2
+#define ASUS_CTRL_U7	3
+#define ASUS_CTRL_POTS	5
+
+/* CARD_ADR (Write) */
+#define ASUS_RESET      0x80	/* Bit 7 Reset-Leitung */
+
+static inline u_char
+readreg(unsigned int ale, unsigned int adr, u_char off)
+{
+	register u_char ret;
+	long flags;
+
+	save_flags(flags);
+	cli();
+	byteout(ale, off);
+	ret = bytein(adr);
+	restore_flags(flags);
+	return (ret);
+}
+
+static inline void
+readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	/* fifo read without cli because it's allready done  */
+
+	byteout(ale, off);
+	insb(adr, data, size);
+}
+
+
+static inline void
+writereg(unsigned int ale, unsigned int adr, u_char off, u_char data)
+{
+	long flags;
+
+	save_flags(flags);
+	cli();
+	byteout(ale, off);
+	byteout(adr, data);
+	restore_flags(flags);
+}
+
+static inline void
+writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	/* fifo write without cli because it's allready done  */
+	byteout(ale, off);
+	outsb(adr, data, size);
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.asus.adr, cs->hw.asus.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.asus.adr, cs->hw.asus.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	readfifo(cs->hw.asus.adr, cs->hw.asus.isac, 0, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	writefifo(cs->hw.asus.adr, cs->hw.asus.isac, 0, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return (readreg(cs->hw.asus.adr,
+			cs->hw.asus.hscx, offset + (hscx ? 0x40 : 0)));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	writereg(cs->hw.asus.adr,
+		 cs->hw.asus.hscx, offset + (hscx ? 0x40 : 0), value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.asus.adr, \
+		cs->hw.asus.hscx, reg + (nr ? 0x40 : 0))
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.asus.adr, \
+		cs->hw.asus.hscx, reg + (nr ? 0x40 : 0), data)
+
+#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.asus.adr, \
+		cs->hw.asus.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.asus.adr, \
+		cs->hw.asus.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#include "hscx_irq.c"
+
+static void
+asuscom_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val, stat = 0;
+
+	if (!cs) {
+		printk(KERN_WARNING "ISDNLink: Spurious interrupt!\n");
+		return;
+	}
+	val = readreg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_ISTA + 0x40);
+      Start_HSCX:
+	if (val) {
+		hscx_int_main(cs, val);
+		stat |= 1;
+	}
+	val = readreg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_ISTA);
+      Start_ISAC:
+	if (val) {
+		isac_interrupt(cs, val);
+		stat |= 2;
+	}
+	val = readreg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_ISTA + 0x40);
+	if (val) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX IntStat after IntRoutine");
+		goto Start_HSCX;
+	}
+	val = readreg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_ISTA);
+	if (val) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	if (stat & 1) {
+		writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK, 0xFF);
+		writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK + 0x40, 0xFF);
+		writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK, 0x0);
+		writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK + 0x40, 0x0);
+	}
+	if (stat & 2) {
+		writereg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_MASK, 0xFF);
+		writereg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_MASK, 0x0);
+	}
+}
+
+void
+release_io_asuscom(struct IsdnCardState *cs)
+{
+	int bytecnt = 8;
+
+	if (cs->hw.asus.cfg_reg)
+		release_region(cs->hw.asus.cfg_reg, bytecnt);
+}
+
+static void
+reset_asuscom(struct IsdnCardState *cs)
+{
+	long flags;
+
+	byteout(cs->hw.asus.adr, ASUS_RESET);	/* Reset On */
+	save_flags(flags);
+	sti();
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + 1;
+	schedule();
+	byteout(cs->hw.asus.adr, 0);	/* Reset Off */
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + 1;
+	schedule();
+	restore_flags(flags);
+}
+
+static int
+Asus_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	switch (mt) {
+		case CARD_RESET:
+			reset_asuscom(cs);
+			return(0);
+		case CARD_RELEASE:
+			release_io_asuscom(cs);
+			return(0);
+		case CARD_SETIRQ:
+			return(request_irq(cs->irq, &asuscom_interrupt,
+					I4L_IRQ_FLAG, "HiSax", cs));
+		case CARD_INIT:
+			clear_pending_isac_ints(cs);
+			clear_pending_hscx_ints(cs);
+			initisac(cs);
+			inithscx(cs);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+__initfunc(int
+setup_asuscom(struct IsdnCard *card))
+{
+	int bytecnt;
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, Asuscom_revision);
+	printk(KERN_INFO "HiSax: Asuscom ISDNLink driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_ASUSCOM)
+		return (0);
+
+	bytecnt = 8;
+	cs->hw.asus.cfg_reg = card->para[1];
+	cs->irq = card->para[0];
+	cs->hw.asus.adr = cs->hw.asus.cfg_reg + ASUS_ADR;
+	cs->hw.asus.isac = cs->hw.asus.cfg_reg + ASUS_ISAC;
+	cs->hw.asus.hscx = cs->hw.asus.cfg_reg + ASUS_HSCX;
+	cs->hw.asus.u7 = cs->hw.asus.cfg_reg + ASUS_CTRL_U7;
+	cs->hw.asus.pots = cs->hw.asus.cfg_reg + ASUS_CTRL_POTS;
+
+	if (check_region((cs->hw.asus.cfg_reg), bytecnt)) {
+		printk(KERN_WARNING
+		       "HiSax: %s config port %x-%x already in use\n",
+		       CardType[card->typ],
+		       cs->hw.asus.cfg_reg,
+		       cs->hw.asus.cfg_reg + bytecnt);
+		return (0);
+	} else {
+		request_region(cs->hw.asus.cfg_reg, bytecnt, "asuscom isdn");
+	}
+
+	printk(KERN_INFO
+	       "ISDNLink: defined at 0x%x IRQ %d\n",
+	       cs->hw.asus.cfg_reg,
+	       cs->irq);
+	printk(KERN_INFO "ISDNLink: resetting card\n");
+	reset_asuscom(cs);
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &Asus_card_msg;
+	ISACVersion(cs, "ISDNLink:");
+	if (HscxVersion(cs, "ISDNLink:")) {
+		printk(KERN_WARNING
+		     "ISDNLink: wrong HSCX versions check IO address\n");
+		release_io_asuscom(cs);
+		return (0);
+	}
+	return (1);
+}
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/avm_a1.c linux/drivers/isdn/hisax/avm_a1.c
--- v2.1.91/linux/drivers/isdn/hisax/avm_a1.c	Thu Feb 12 20:56:06 1998
+++ linux/drivers/isdn/hisax/avm_a1.c	Wed Apr  1 16:20:58 1998
@@ -1,4 +1,4 @@
-/* $Id: avm_a1.c,v 1.6 1997/04/13 19:54:07 keil Exp $
+/* $Id: avm_a1.c,v 2.7 1998/02/02 13:29:37 keil Exp $
 
  * avm_a1.c     low level stuff for AVM A1 (Fritz) isdn cards
  *
@@ -6,6 +6,30 @@
  *
  *
  * $Log: avm_a1.c,v $
+ * Revision 2.7  1998/02/02 13:29:37  keil
+ * fast io
+ *
+ * Revision 2.6  1998/01/13 23:09:46  keil
+ * really disable timer
+ *
+ * Revision 2.5  1998/01/02 06:50:29  calle
+ * Perodic timer of A1 now disabled, no need for linux driver.
+ *
+ * Revision 2.4  1997/11/08 21:35:42  keil
+ * new l1 init
+ *
+ * Revision 2.3  1997/11/06 17:13:32  keil
+ * New 2.1 init code
+ *
+ * Revision 2.2  1997/10/29 18:55:48  keil
+ * changes for 2.1.60 (irq2dev_map)
+ *
+ * Revision 2.1  1997/07/27 21:47:13  keil
+ * new interface structures
+ *
+ * Revision 2.0  1997/06/26 11:02:48  keil
+ * New Layer and card interface
+ *
  * Revision 1.6  1997/04/13 19:54:07  keil
  * Change in IRQ check delay for SMP
  *
@@ -27,17 +51,20 @@
  *
  */
 #define __NO_VERSION__
-#include "siemens.h"
 #include "hisax.h"
-#include "avm_a1.h"
+#include "isac.h"
+#include "hscx.h"
 #include "isdnl1.h"
-#include <linux/kernel_stat.h>
 
 extern const char *CardType[];
-const char *avm_revision = "$Revision: 1.6 $";
+const char *avm_revision = "$Revision: 2.7 $";
 
-#define byteout(addr,val) outb_p(val,addr)
-#define bytein(addr) inb_p(addr)
+#define	 AVM_A1_STAT_ISAC	0x01
+#define	 AVM_A1_STAT_HSCX	0x02
+#define	 AVM_A1_STAT_TIMER	0x04
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
 
 static inline u_char
 readreg(unsigned int adr, u_char off)
@@ -55,906 +82,303 @@
 static inline void
 read_fifo(unsigned int adr, u_char * data, int size)
 {
-	insb(adr - 0x400, data, size);
+	insb(adr, data, size);
 }
 
 static void
 write_fifo(unsigned int adr, u_char * data, int size)
 {
-	outsb(adr - 0x400, data, size);
+	outsb(adr, data, size);
 }
 
-static inline void
-waitforCEC(int adr)
-{
-	int to = 50;
-
-	while ((readreg(adr, HSCX_STAR) & 0x04) && to) {
-		udelay(1);
-		to--;
-	}
-	if (!to)
-		printk(KERN_WARNING "AVM A1: waitforCEC timeout\n");
-}
-
-
-static inline void
-waitforXFW(int adr)
-{
-	int to = 50;
-
-	while ((!(readreg(adr, HSCX_STAR) & 0x44) == 0x40) && to) {
-		udelay(1);
-		to--;
-	}
-	if (!to)
-		printk(KERN_WARNING "AVM A1: waitforXFW timeout\n");
-}
+/* Interface functions */
 
-static inline void
-writehscxCMDR(int adr, u_char data)
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
 {
-	long flags;
-
-	save_flags(flags);
-	cli();
-	waitforCEC(adr);
-	writereg(adr, HSCX_CMDR, data);
-	restore_flags(flags);
+	return (readreg(cs->hw.avm.isac, offset));
 }
 
-/*
- * fast interrupt here
- */
-
 static void
-hscxreport(struct IsdnCardState *sp, int hscx)
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
 {
-	printk(KERN_DEBUG "HSCX %d\n", hscx);
-	printk(KERN_DEBUG "ISTA %x\n", readreg(sp->hscx[hscx], HSCX_ISTA));
-	printk(KERN_DEBUG "STAR %x\n", readreg(sp->hscx[hscx], HSCX_STAR));
-	printk(KERN_DEBUG "EXIR %x\n", readreg(sp->hscx[hscx], HSCX_EXIR));
-}
-
-void
-avm_a1_report(struct IsdnCardState *sp)
-{
-	printk(KERN_DEBUG "ISAC\n");
-	printk(KERN_DEBUG "ISTA %x\n", readreg(sp->isac, ISAC_ISTA));
-	printk(KERN_DEBUG "STAR %x\n", readreg(sp->isac, ISAC_STAR));
-	printk(KERN_DEBUG "EXIR %x\n", readreg(sp->isac, ISAC_EXIR));
-	hscxreport(sp, 0);
-	hscxreport(sp, 1);
+	writereg(cs->hw.avm.isac, offset, value);
 }
 
-/*
- * HSCX stuff goes here
- */
-
 static void
-hscx_empty_fifo(struct HscxState *hsp, int count)
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
 {
-	u_char *ptr;
-	struct IsdnCardState *sp = hsp->sp;
-	long flags;
-
-	if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO))
-		debugl1(sp, "hscx_empty_fifo");
-
-	if (hsp->rcvidx + count > HSCX_BUFMAX) {
-		if (sp->debug & L1_DEB_WARN)
-			debugl1(sp, "hscx_empty_fifo: incoming packet too large");
-		writehscxCMDR(sp->hscx[hsp->hscx], 0x80);
-		hsp->rcvidx = 0;
-		return;
-	}
-	ptr = hsp->rcvbuf + hsp->rcvidx;
-	hsp->rcvidx += count;
-	save_flags(flags);
-	cli();
-	read_fifo(sp->hscx[hsp->hscx], ptr, count);
-	writehscxCMDR(sp->hscx[hsp->hscx], 0x80);
-	restore_flags(flags);
-	if (sp->debug & L1_DEB_HSCX_FIFO) {
-		char tmp[128];
-		char *t = tmp;
-
-		t += sprintf(t, "hscx_empty_fifo %c cnt %d",
-			     hsp->hscx ? 'B' : 'A', count);
-		QuickHex(t, ptr, count);
-		debugl1(sp, tmp);
-	}
+	read_fifo(cs->hw.avm.isacfifo, data, size);
 }
 
 static void
-hscx_fill_fifo(struct HscxState *hsp)
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
 {
-	struct IsdnCardState *sp = hsp->sp;
-	int more, count;
-	u_char *ptr;
-	long flags;
-
-	if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO))
-		debugl1(sp, "hscx_fill_fifo");
-
-	if (!hsp->tx_skb)
-		return;
-	if (hsp->tx_skb->len <= 0)
-		return;
-
-	more = (hsp->mode == 1) ? 1 : 0;
-	if (hsp->tx_skb->len > 32) {
-		more = !0;
-		count = 32;
-	} else
-		count = hsp->tx_skb->len;
-
-	waitforXFW(sp->hscx[hsp->hscx]);
-	save_flags(flags);
-	cli();
-	ptr = hsp->tx_skb->data;
-	skb_pull(hsp->tx_skb, count);
-	hsp->tx_cnt -= count;
-	hsp->count += count;
-	write_fifo(sp->hscx[hsp->hscx], ptr, count);
-	writehscxCMDR(sp->hscx[hsp->hscx], more ? 0x8 : 0xa);
-	restore_flags(flags);
-	if (sp->debug & L1_DEB_HSCX_FIFO) {
-		char tmp[128];
-		char *t = tmp;
-
-		t += sprintf(t, "hscx_fill_fifo %c cnt %d",
-			     hsp->hscx ? 'B' : 'A', count);
-		QuickHex(t, ptr, count);
-		debugl1(sp, tmp);
-	}
+	write_fifo(cs->hw.avm.isacfifo, data, size);
 }
 
-static inline void
-hscx_interrupt(struct IsdnCardState *sp, u_char val, u_char hscx)
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
 {
-	u_char r;
-	struct HscxState *hsp = sp->hs + hscx;
-	struct sk_buff *skb;
-	int count;
-	char tmp[32];
-
-	if (!hsp->init)
-		return;
-
-	if (val & 0x80) {	/* RME */
-
-		r = readreg(sp->hscx[hsp->hscx], HSCX_RSTA);
-		if ((r & 0xf0) != 0xa0) {
-			if (!r & 0x80)
-				if (sp->debug & L1_DEB_WARN)
-					debugl1(sp, "HSCX invalid frame");
-			if ((r & 0x40) && hsp->mode)
-				if (sp->debug & L1_DEB_WARN) {
-					sprintf(tmp, "HSCX RDO mode=%d",
-						hsp->mode);
-					debugl1(sp, tmp);
-				}
-			if (!r & 0x20)
-				if (sp->debug & L1_DEB_WARN)
-					debugl1(sp, "HSCX CRC error");
-			writehscxCMDR(sp->hscx[hsp->hscx], 0x80);
-		} else {
-			count = readreg(sp->hscx[hsp->hscx], HSCX_RBCL) & 0x1f;
-			if (count == 0)
-				count = 32;
-			hscx_empty_fifo(hsp, count);
-			if ((count = hsp->rcvidx - 1) > 0) {
-				if (!(skb = dev_alloc_skb(count)))
-					printk(KERN_WARNING "AVM: receive out of memory\n");
-				else {
-					memcpy(skb_put(skb, count), hsp->rcvbuf, count);
-					skb_queue_tail(&hsp->rqueue, skb);
-				}
-			}
-		}
-		hsp->rcvidx = 0;
-		hscx_sched_event(hsp, HSCX_RCVBUFREADY);
-	}
-	if (val & 0x40) {	/* RPF */
-		hscx_empty_fifo(hsp, 32);
-		if (hsp->mode == 1) {
-			/* receive audio data */
-			if (!(skb = dev_alloc_skb(32)))
-				printk(KERN_WARNING "AVM: receive out of memory\n");
-			else {
-				memcpy(skb_put(skb, 32), hsp->rcvbuf, 32);
-				skb_queue_tail(&hsp->rqueue, skb);
-			}
-			hsp->rcvidx = 0;
-			hscx_sched_event(hsp, HSCX_RCVBUFREADY);
-		}
-	}
-	if (val & 0x10) {	/* XPR */
-		if (hsp->tx_skb)
-			if (hsp->tx_skb->len) {
-				hscx_fill_fifo(hsp);
-				return;
-			} else {
-				SET_SKB_FREE(hsp->tx_skb);
-				dev_kfree_skb(hsp->tx_skb);
-				hsp->count = 0;
-				if (hsp->st->l4.l1writewakeup)
-					hsp->st->l4.l1writewakeup(hsp->st);
-				hsp->tx_skb = NULL;
-			}
-		if ((hsp->tx_skb = skb_dequeue(&hsp->squeue))) {
-			hsp->count = 0;
-			hscx_fill_fifo(hsp);
-		} else
-			hscx_sched_event(hsp, HSCX_XMTBUFREADY);
-	}
+	return (readreg(cs->hw.avm.hscx[hscx], offset));
 }
 
-/*
- * ISAC stuff goes here
- */
-
 static void
-isac_empty_fifo(struct IsdnCardState *sp, int count)
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
 {
-	u_char *ptr;
-	long flags;
-
-	if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO))
-		if (sp->debug & L1_DEB_ISAC)
-			debugl1(sp, "isac_empty_fifo");
-
-	if ((sp->rcvidx + count) >= MAX_DFRAME_LEN) {
-		if (sp->debug & L1_DEB_WARN) {
-			char tmp[40];
-			sprintf(tmp, "isac_empty_fifo overrun %d",
-				sp->rcvidx + count);
-			debugl1(sp, tmp);
-		}
-		writereg(sp->isac, ISAC_CMDR, 0x80);
-		sp->rcvidx = 0;
-		return;
-	}
-	ptr = sp->rcvbuf + sp->rcvidx;
-	sp->rcvidx += count;
-	save_flags(flags);
-	cli();
-	read_fifo(sp->isac, ptr, count);
-	writereg(sp->isac, ISAC_CMDR, 0x80);
-	restore_flags(flags);
-	if (sp->debug & L1_DEB_ISAC_FIFO) {
-		char tmp[128];
-		char *t = tmp;
-
-		t += sprintf(t, "isac_empty_fifo cnt %d", count);
-		QuickHex(t, ptr, count);
-		debugl1(sp, tmp);
-	}
+	writereg(cs->hw.avm.hscx[hscx], offset, value);
 }
 
-static void
-isac_fill_fifo(struct IsdnCardState *sp)
-{
-	int count, more;
-	u_char *ptr;
-	long flags;
-
-	if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO))
-		debugl1(sp, "isac_fill_fifo");
-
-	if (!sp->tx_skb)
-		return;
-
-	count = sp->tx_skb->len;
-	if (count <= 0)
-		return;
-
-	more = 0;
-	if (count > 32) {
-		more = !0;
-		count = 32;
-	}
-	save_flags(flags);
-	cli();
-	ptr = sp->tx_skb->data;
-	skb_pull(sp->tx_skb, count);
-	sp->tx_cnt += count;
-	write_fifo(sp->isac, ptr, count);
-	writereg(sp->isac, ISAC_CMDR, more ? 0x8 : 0xa);
-	restore_flags(flags);
-	if (sp->debug & L1_DEB_ISAC_FIFO) {
-		char tmp[128];
-		char *t = tmp;
-
-		t += sprintf(t, "isac_fill_fifo cnt %d", count);
-		QuickHex(t, ptr, count);
-		debugl1(sp, tmp);
-	}
-}
-
-static void
-ph_command(struct IsdnCardState *sp, unsigned int command)
-{
-	if (sp->debug & L1_DEB_ISAC) {
-		char tmp[32];
-		sprintf(tmp, "ph_command %d", command);
-		debugl1(sp, tmp);
-	}
-	writereg(sp->isac, ISAC_CIX0, (command << 2) | 3);
-}
-
-
-static inline void
-isac_interrupt(struct IsdnCardState *sp, u_char val)
-{
-	u_char exval;
-	struct sk_buff *skb;
-	unsigned int count;
-	char tmp[32];
-
-	if (sp->debug & L1_DEB_ISAC) {
-		sprintf(tmp, "ISAC interrupt %x", val);
-		debugl1(sp, tmp);
-	}
-	if (val & 0x80) {	/* RME */
-		exval = readreg(sp->isac, ISAC_RSTA);
-		if ((exval & 0x70) != 0x20) {
-			if (exval & 0x40)
-				if (sp->debug & L1_DEB_WARN)
-					debugl1(sp, "ISAC RDO");
-			if (!exval & 0x20)
-				if (sp->debug & L1_DEB_WARN)
-					debugl1(sp, "ISAC CRC error");
-			writereg(sp->isac, ISAC_CMDR, 0x80);
-		} else {
-			count = readreg(sp->isac, ISAC_RBCL) & 0x1f;
-			if (count == 0)
-				count = 32;
-			isac_empty_fifo(sp, count);
-			if ((count = sp->rcvidx) > 0) {
-				if (!(skb = alloc_skb(count, GFP_ATOMIC)))
-					printk(KERN_WARNING "AVM: D receive out of memory\n");
-				else {
-					memcpy(skb_put(skb, count), sp->rcvbuf, count);
-					skb_queue_tail(&sp->rq, skb);
-				}
-			}
-		}
-		sp->rcvidx = 0;
-		isac_sched_event(sp, ISAC_RCVBUFREADY);
-	}
-	if (val & 0x40) {	/* RPF */
-		isac_empty_fifo(sp, 32);
-	}
-	if (val & 0x20) {	/* RSC */
-		/* never */
-		if (sp->debug & L1_DEB_WARN)
-			debugl1(sp, "ISAC RSC interrupt");
-	}
-	if (val & 0x10) {	/* XPR */
-		if (sp->tx_skb)
-			if (sp->tx_skb->len) {
-				isac_fill_fifo(sp);
-				goto afterXPR;
-			} else {
-				SET_SKB_FREE(sp->tx_skb);
-				dev_kfree_skb(sp->tx_skb);
-				sp->tx_cnt = 0;
-				sp->tx_skb = NULL;
-			}
-		if ((sp->tx_skb = skb_dequeue(&sp->sq))) {
-			sp->tx_cnt = 0;
-			isac_fill_fifo(sp);
-		} else
-			isac_sched_event(sp, ISAC_XMTBUFREADY);
-	}
-      afterXPR:
-	if (val & 0x04) {	/* CISQ */
-		sp->ph_state = (readreg(sp->isac, ISAC_CIX0) >> 2)
-		    & 0xf;
-		if (sp->debug & L1_DEB_ISAC) {
-			sprintf(tmp, "l1state %d", sp->ph_state);
-			debugl1(sp, tmp);
-		}
-		isac_new_ph(sp);
-	}
-	if (val & 0x02) {	/* SIN */
-		/* never */
-		if (sp->debug & L1_DEB_WARN)
-			debugl1(sp, "ISAC SIN interrupt");
-	}
-	if (val & 0x01) {	/* EXI */
-		exval = readreg(sp->isac, ISAC_EXIR);
-		if (sp->debug & L1_DEB_WARN) {
-			sprintf(tmp, "ISAC EXIR %02x", exval);
-			debugl1(sp, tmp);
-		}
-	}
-}
-
-static inline void
-hscx_int_main(struct IsdnCardState *sp, u_char val)
-{
-
-	u_char exval;
-	struct HscxState *hsp;
-	char tmp[32];
+/*
+ * fast interrupt HSCX stuff goes here
+ */
 
+#define READHSCX(cs, nr, reg) readreg(cs->hw.avm.hscx[nr], reg)
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.avm.hscx[nr], reg, data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo(cs->hw.avm.hscxfifo[nr], ptr, cnt)
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo(cs->hw.avm.hscxfifo[nr], ptr, cnt)
 
-	if (val & 0x01) {
-		hsp = sp->hs + 1;
-		exval = readreg(sp->hscx[1], HSCX_EXIR);
-		if (exval == 0x40) {
-			if (hsp->mode == 1)
-				hscx_fill_fifo(hsp);
-			else {
-				/* Here we lost an TX interrupt, so
-				   * restart transmitting the whole frame.
-				 */
-				if (hsp->tx_skb) {
-					skb_push(hsp->tx_skb, hsp->count);
-					hsp->tx_cnt += hsp->count;
-					hsp->count = 0;
-				}
-				writehscxCMDR(sp->hscx[hsp->hscx], 0x01);
-				if (sp->debug & L1_DEB_WARN) {
-					sprintf(tmp, "HSCX B EXIR %x Lost TX", exval);
-					debugl1(sp, tmp);
-				}
-			}
-		} else if (sp->debug & L1_DEB_HSCX) {
-			sprintf(tmp, "HSCX B EXIR %x", exval);
-			debugl1(sp, tmp);
-		}
-	}
-	if (val & 0xf8) {
-		if (sp->debug & L1_DEB_HSCX) {
-			sprintf(tmp, "HSCX B interrupt %x", val);
-			debugl1(sp, tmp);
-		}
-		hscx_interrupt(sp, val, 1);
-	}
-	if (val & 0x02) {
-		hsp = sp->hs;
-		exval = readreg(sp->hscx[0], HSCX_EXIR);
-		if (exval == 0x40) {
-			if (hsp->mode == 1)
-				hscx_fill_fifo(hsp);
-			else {
-				/* Here we lost an TX interrupt, so
-				   * restart transmitting the whole frame.
-				 */
-				if (hsp->tx_skb) {
-					skb_push(hsp->tx_skb, hsp->count);
-					hsp->tx_cnt += hsp->count;
-					hsp->count = 0;
-				}
-				writehscxCMDR(sp->hscx[hsp->hscx], 0x01);
-				if (sp->debug & L1_DEB_WARN) {
-					sprintf(tmp, "HSCX A EXIR %x Lost TX", exval);
-					debugl1(sp, tmp);
-				}
-			}
-		} else if (sp->debug & L1_DEB_HSCX) {
-			sprintf(tmp, "HSCX A EXIR %x", exval);
-			debugl1(sp, tmp);
-		}
-	}
-	if (val & 0x04) {
-		exval = readreg(sp->hscx[0], HSCX_ISTA);
-		if (sp->debug & L1_DEB_HSCX) {
-			sprintf(tmp, "HSCX A interrupt %x", exval);
-			debugl1(sp, tmp);
-		}
-		hscx_interrupt(sp, exval, 0);
-	}
-}
+#include "hscx_irq.c"
 
 static void
 avm_a1_interrupt(int intno, void *dev_id, struct pt_regs *regs)
 {
-	struct IsdnCardState *sp;
+	struct IsdnCardState *cs = dev_id;
 	u_char val, sval, stat = 0;
 	char tmp[32];
 
-	sp = (struct IsdnCardState *) dev_id;
-
-	if (!sp) {
+	if (!cs) {
 		printk(KERN_WARNING "AVM A1: Spurious interrupt!\n");
 		return;
 	}
-	while (((sval = bytein(sp->cfg_reg)) & 0xf) != 0x7) {
+	while (((sval = bytein(cs->hw.avm.cfg_reg)) & 0xf) != 0x7) {
 		if (!(sval & AVM_A1_STAT_TIMER)) {
-			byteout(sp->cfg_reg, 0x14);
-			byteout(sp->cfg_reg, 0x18);
-			sval = bytein(sp->cfg_reg);
-		} else if (sp->debug & L1_DEB_INTSTAT) {
+			byteout(cs->hw.avm.cfg_reg, 0x1E);
+			sval = bytein(cs->hw.avm.cfg_reg);
+		} else if (cs->debug & L1_DEB_INTSTAT) {
 			sprintf(tmp, "avm IntStatus %x", sval);
-			debugl1(sp, tmp);
+			debugl1(cs, tmp);
 		}
 		if (!(sval & AVM_A1_STAT_HSCX)) {
-			val = readreg(sp->hscx[1], HSCX_ISTA);
+			val = readreg(cs->hw.avm.hscx[1], HSCX_ISTA);
 			if (val) {
-				hscx_int_main(sp, val);
+				hscx_int_main(cs, val);
 				stat |= 1;
 			}
 		}
 		if (!(sval & AVM_A1_STAT_ISAC)) {
-			val = readreg(sp->isac, ISAC_ISTA);
+			val = readreg(cs->hw.avm.isac, ISAC_ISTA);
 			if (val) {
-				isac_interrupt(sp, val);
+				isac_interrupt(cs, val);
 				stat |= 2;
 			}
 		}
 	}
 	if (stat & 1) {
-		writereg(sp->hscx[0], HSCX_MASK, 0xFF);
-		writereg(sp->hscx[1], HSCX_MASK, 0xFF);
-		writereg(sp->hscx[0], HSCX_MASK, 0x0);
-		writereg(sp->hscx[1], HSCX_MASK, 0x0);
+		writereg(cs->hw.avm.hscx[0], HSCX_MASK, 0xFF);
+		writereg(cs->hw.avm.hscx[1], HSCX_MASK, 0xFF);
+		writereg(cs->hw.avm.hscx[0], HSCX_MASK, 0x0);
+		writereg(cs->hw.avm.hscx[1], HSCX_MASK, 0x0);
 	}
 	if (stat & 2) {
-		writereg(sp->isac, ISAC_MASK, 0xFF);
-		writereg(sp->isac, ISAC_MASK, 0x0);
+		writereg(cs->hw.avm.isac, ISAC_MASK, 0xFF);
+		writereg(cs->hw.avm.isac, ISAC_MASK, 0x0);
 	}
 }
 
-
-static void
-initisac(struct IsdnCardState *sp)
-{
-	unsigned int adr = sp->isac;
-
-	/* 16.3 IOM 2 Mode */
-	writereg(adr, ISAC_MASK, 0xff);
-	writereg(adr, ISAC_ADF2, 0x80);
-	writereg(adr, ISAC_SQXR, 0x2f);
-	writereg(adr, ISAC_SPCR, 0x0);
-	writereg(adr, ISAC_ADF1, 0x2);
-	writereg(adr, ISAC_STCR, 0x70);
-	writereg(adr, ISAC_MODE, 0xc9);
-	writereg(adr, ISAC_TIMR, 0x0);
-	writereg(adr, ISAC_ADF1, 0x0);
-	writereg(adr, ISAC_CMDR, 0x41);
-	writereg(adr, ISAC_CIX0, (1 << 2) | 3);
-	writereg(adr, ISAC_MASK, 0xff);
-	writereg(adr, ISAC_MASK, 0x0);
-}
-
-static void
-modehscx(struct HscxState *hs, int mode, int ichan)
-{
-	struct IsdnCardState *sp = hs->sp;
-	int hscx = hs->hscx;
-
-	if (sp->debug & L1_DEB_HSCX) {
-		char tmp[40];
-		sprintf(tmp, "hscx %c mode %d ichan %d",
-			'A' + hscx, mode, ichan);
-		debugl1(sp, tmp);
-	}
-	hs->mode = mode;
-	writereg(sp->hscx[hscx], HSCX_CCR1, 0x85);
-	writereg(sp->hscx[hscx], HSCX_XAD1, 0xFF);
-	writereg(sp->hscx[hscx], HSCX_XAD2, 0xFF);
-	writereg(sp->hscx[hscx], HSCX_RAH2, 0xFF);
-	writereg(sp->hscx[hscx], HSCX_XBCH, 0x0);
-	writereg(sp->hscx[hscx], HSCX_RLCR, 0x0);
-
-	switch (mode) {
-		case (0):
-			writereg(sp->hscx[hscx], HSCX_CCR2, 0x30);
-			writereg(sp->hscx[hscx], HSCX_TSAX, 0xff);
-			writereg(sp->hscx[hscx], HSCX_TSAR, 0xff);
-			writereg(sp->hscx[hscx], HSCX_XCCR, 7);
-			writereg(sp->hscx[hscx], HSCX_RCCR, 7);
-			writereg(sp->hscx[hscx], HSCX_MODE, 0x84);
-			break;
-		case (1):
-			if (ichan == 0) {
-				writereg(sp->hscx[hscx], HSCX_CCR2, 0x30);
-				writereg(sp->hscx[hscx], HSCX_TSAX, 0x2f);
-				writereg(sp->hscx[hscx], HSCX_TSAR, 0x2f);
-				writereg(sp->hscx[hscx], HSCX_XCCR, 7);
-				writereg(sp->hscx[hscx], HSCX_RCCR, 7);
-			} else {
-				writereg(sp->hscx[hscx], HSCX_CCR2, 0x30);
-				writereg(sp->hscx[hscx], HSCX_TSAX, 0x3);
-				writereg(sp->hscx[hscx], HSCX_TSAR, 0x3);
-				writereg(sp->hscx[hscx], HSCX_XCCR, 7);
-				writereg(sp->hscx[hscx], HSCX_RCCR, 7);
-			}
-			writereg(sp->hscx[hscx], HSCX_MODE, 0xe4);
-			writereg(sp->hscx[hscx], HSCX_CMDR, 0x41);
-			break;
-		case (2):
-			if (ichan == 0) {
-				writereg(sp->hscx[hscx], HSCX_CCR2, 0x30);
-				writereg(sp->hscx[hscx], HSCX_TSAX, 0x2f);
-				writereg(sp->hscx[hscx], HSCX_TSAR, 0x2f);
-				writereg(sp->hscx[hscx], HSCX_XCCR, 7);
-				writereg(sp->hscx[hscx], HSCX_RCCR, 7);
-			} else {
-				writereg(sp->hscx[hscx], HSCX_CCR2, 0x30);
-				writereg(sp->hscx[hscx], HSCX_TSAX, 0x3);
-				writereg(sp->hscx[hscx], HSCX_TSAR, 0x3);
-				writereg(sp->hscx[hscx], HSCX_XCCR, 7);
-				writereg(sp->hscx[hscx], HSCX_RCCR, 7);
-			}
-			writereg(sp->hscx[hscx], HSCX_MODE, 0x8c);
-			writereg(sp->hscx[hscx], HSCX_CMDR, 0x41);
-			break;
-	}
-	writereg(sp->hscx[hscx], HSCX_ISTA, 0x00);
-}
-
 inline static void
-release_ioregs(struct IsdnCard *card, int mask)
+release_ioregs(struct IsdnCardState *cs, int mask)
 {
-	release_region(card->sp->cfg_reg, 8);
+	release_region(cs->hw.avm.cfg_reg, 8);
 	if (mask & 1)
-		release_region(card->sp->isac, 32);
+		release_region(cs->hw.avm.isac + 32, 32);
 	if (mask & 2)
-		release_region(card->sp->isac - 0x400, 1);
+		release_region(cs->hw.avm.isacfifo, 1);
 	if (mask & 4)
-		release_region(card->sp->hscx[0], 32);
+		release_region(cs->hw.avm.hscx[0] + 32, 32);
 	if (mask & 8)
-		release_region(card->sp->hscx[0] - 0x400, 1);
+		release_region(cs->hw.avm.hscxfifo[0], 1);
 	if (mask & 0x10)
-		release_region(card->sp->hscx[1], 32);
+		release_region(cs->hw.avm.hscx[1] + 32, 32);
 	if (mask & 0x20)
-		release_region(card->sp->hscx[1] - 0x400, 1);
+		release_region(cs->hw.avm.hscxfifo[1], 1);
 }
 
-void
-release_io_avm_a1(struct IsdnCard *card)
-{
-	release_ioregs(card, 0x3f);
-}
-
-static void
-clear_pending_ints(struct IsdnCardState *sp)
+static int
+AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg)
 {
-	int val;
-	char tmp[64];
-
-	val = readreg(sp->hscx[1], HSCX_ISTA);
-	sprintf(tmp, "HSCX B ISTA %x", val);
-	debugl1(sp, tmp);
-	if (val & 0x01) {
-		val = readreg(sp->hscx[1], HSCX_EXIR);
-		sprintf(tmp, "HSCX B EXIR %x", val);
-		debugl1(sp, tmp);
-	} else if (val & 0x02) {
-		val = readreg(sp->hscx[0], HSCX_EXIR);
-		sprintf(tmp, "HSCX A EXIR %x", val);
-		debugl1(sp, tmp);
-	}
-	val = readreg(sp->hscx[0], HSCX_ISTA);
-	sprintf(tmp, "HSCX A ISTA %x", val);
-	debugl1(sp, tmp);
-	val = readreg(sp->hscx[1], HSCX_STAR);
-	sprintf(tmp, "HSCX B STAR %x", val);
-	debugl1(sp, tmp);
-	val = readreg(sp->hscx[0], HSCX_STAR);
-	sprintf(tmp, "HSCX A STAR %x", val);
-	debugl1(sp, tmp);
-	val = readreg(sp->isac, ISAC_STAR);
-	sprintf(tmp, "ISAC STAR %x", val);
-	debugl1(sp, tmp);
-	val = readreg(sp->isac, ISAC_MODE);
-	sprintf(tmp, "ISAC MODE %x", val);
-	debugl1(sp, tmp);
-	val = readreg(sp->isac, ISAC_ADF2);
-	sprintf(tmp, "ISAC ADF2 %x", val);
-	debugl1(sp, tmp);
-	val = readreg(sp->isac, ISAC_ISTA);
-	sprintf(tmp, "ISAC ISTA %x", val);
-	debugl1(sp, tmp);
-	if (val & 0x01) {
-		val = readreg(sp->isac, ISAC_EXIR);
-		sprintf(tmp, "ISAC EXIR %x", val);
-		debugl1(sp, tmp);
-	} else if (val & 0x04) {
-		val = readreg(sp->isac, ISAC_CIR0);
-		sprintf(tmp, "ISAC CIR0 %x", val);
-		debugl1(sp, tmp);
-	}
-	writereg(sp->isac, ISAC_MASK, 0);
-	writereg(sp->isac, ISAC_CMDR, 0x41);
-}
-
-int
-initavm_a1(struct IsdnCardState *sp)
-{
-	int ret;
-	int loop = 0;
-	char tmp[40];
-
-	sp->counter = kstat_irqs(sp->irq);
-	sprintf(tmp, "IRQ %d count %d", sp->irq, sp->counter);
-	debugl1(sp, tmp);
-	clear_pending_ints(sp);
-	ret = get_irq(sp->cardnr, &avm_a1_interrupt);
-	if (ret) {
-		initisac(sp);
-		sp->modehscx(sp->hs, 0, 0);
-		sp->modehscx(sp->hs + 1, 0, 0);
-		while (loop++ < 10) {
-			/* At least 1-3 irqs must happen
-			 * (one from HSCX A, one from HSCX B, 3rd from ISAC)
-			 */
-			if (kstat_irqs(sp->irq) > sp->counter)
-				break;
-			current->state = TASK_INTERRUPTIBLE;
-			current->timeout = jiffies + 1;
-			schedule();
-		}
-		sprintf(tmp, "IRQ %d count %d", sp->irq,
-			kstat_irqs(sp->irq));
-		debugl1(sp, tmp);
-		if (kstat_irqs(sp->irq) == sp->counter) {
-			printk(KERN_WARNING
-			       "AVM A1: IRQ(%d) getting no interrupts during init\n",
-			       sp->irq);
-			free_irq(sp->irq, sp);
-			return (0);
-		}
+	switch (mt) {
+		case CARD_RESET:
+			return(0);
+		case CARD_RELEASE:
+			release_ioregs(cs, 0x3f);
+			return(0);
+		case CARD_SETIRQ:
+			return(request_irq(cs->irq, &avm_a1_interrupt,
+					I4L_IRQ_FLAG, "HiSax", cs));
+		case CARD_INIT:
+			clear_pending_isac_ints(cs);
+			clear_pending_hscx_ints(cs);
+			initisac(cs);
+			inithscx(cs);
+			return(0);
+		case CARD_TEST:
+			return(0);
 	}
-	return (ret);
+	return(0);
 }
 
-int
-setup_avm_a1(struct IsdnCard *card)
+__initfunc(int
+setup_avm_a1(struct IsdnCard *card))
 {
-	u_char val, verA, verB;
-	struct IsdnCardState *sp = card->sp;
+	u_char val;
+	struct IsdnCardState *cs = card->cs;
 	long flags;
 	char tmp[64];
 
 	strcpy(tmp, avm_revision);
-	printk(KERN_NOTICE "HiSax: AVM driver Rev. %s\n", HiSax_getrev(tmp));
-	if (sp->typ != ISDN_CTYPE_A1)
+	printk(KERN_INFO "HiSax: AVM driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_A1)
 		return (0);
 
-	sp->cfg_reg = card->para[1] + 0x1800;
-	sp->isac = card->para[1] + 0x1400;
-	sp->hscx[0] = card->para[1] + 0x400;
-	sp->hscx[1] = card->para[1] + 0xc00;
-	sp->irq = card->para[0];
-	if (check_region((sp->cfg_reg), 8)) {
+	cs->hw.avm.cfg_reg = card->para[1] + 0x1800;
+	cs->hw.avm.isac = card->para[1] + 0x1400 - 0x20;
+	cs->hw.avm.hscx[0] = card->para[1] + 0x400 - 0x20;
+	cs->hw.avm.hscx[1] = card->para[1] + 0xc00 - 0x20;
+	cs->hw.avm.isacfifo = card->para[1] + 0x1000;
+	cs->hw.avm.hscxfifo[0] = card->para[1];
+	cs->hw.avm.hscxfifo[1] = card->para[1] + 0x800;
+	cs->irq = card->para[0];
+	if (check_region((cs->hw.avm.cfg_reg), 8)) {
 		printk(KERN_WARNING
 		       "HiSax: %s config port %x-%x already in use\n",
 		       CardType[card->typ],
-		       sp->cfg_reg,
-		       sp->cfg_reg + 8);
+		       cs->hw.avm.cfg_reg,
+		       cs->hw.avm.cfg_reg + 8);
 		return (0);
 	} else {
-		request_region(sp->cfg_reg, 8, "avm cfg");
+		request_region(cs->hw.avm.cfg_reg, 8, "avm cfg");
 	}
-	if (check_region((sp->isac), 32)) {
+	if (check_region((cs->hw.avm.isac + 32), 32)) {
 		printk(KERN_WARNING
 		       "HiSax: %s isac ports %x-%x already in use\n",
-		       CardType[sp->typ],
-		       sp->isac,
-		       sp->isac + 32);
-		release_ioregs(card, 0);
+		       CardType[cs->typ],
+		       cs->hw.avm.isac + 32,
+		       cs->hw.avm.isac + 64);
+		release_ioregs(cs, 0);
 		return (0);
 	} else {
-		request_region(sp->isac, 32, "HiSax isac");
+		request_region(cs->hw.avm.isac + 32, 32, "HiSax isac");
 	}
-	if (check_region((sp->isac - 0x400), 1)) {
+	if (check_region((cs->hw.avm.isacfifo), 1)) {
 		printk(KERN_WARNING
 		       "HiSax: %s isac fifo port %x already in use\n",
-		       CardType[sp->typ],
-		       sp->isac - 0x400);
-		release_ioregs(card, 1);
+		       CardType[cs->typ],
+		       cs->hw.avm.isacfifo);
+		release_ioregs(cs, 1);
 		return (0);
 	} else {
-		request_region(sp->isac - 0x400, 1, "HiSax isac fifo");
+		request_region(cs->hw.avm.isacfifo, 1, "HiSax isac fifo");
 	}
-	if (check_region((sp->hscx[0]), 32)) {
+	if (check_region((cs->hw.avm.hscx[0]) + 32, 32)) {
 		printk(KERN_WARNING
 		       "HiSax: %s hscx A ports %x-%x already in use\n",
-		       CardType[sp->typ],
-		       sp->hscx[0],
-		       sp->hscx[0] + 32);
-		release_ioregs(card, 3);
+		       CardType[cs->typ],
+		       cs->hw.avm.hscx[0] + 32,
+		       cs->hw.avm.hscx[0] + 64);
+		release_ioregs(cs, 3);
 		return (0);
 	} else {
-		request_region(sp->hscx[0], 32, "HiSax hscx A");
+		request_region(cs->hw.avm.hscx[0] + 32, 32, "HiSax hscx A");
 	}
-	if (check_region((sp->hscx[0] - 0x400), 1)) {
+	if (check_region(cs->hw.avm.hscxfifo[0], 1)) {
 		printk(KERN_WARNING
 		       "HiSax: %s hscx A fifo port %x already in use\n",
-		       CardType[sp->typ],
-		       sp->hscx[0] - 0x400);
-		release_ioregs(card, 7);
+		       CardType[cs->typ],
+		       cs->hw.avm.hscxfifo[0]);
+		release_ioregs(cs, 7);
 		return (0);
 	} else {
-		request_region(sp->hscx[0] - 0x400, 1, "HiSax hscx A fifo");
+		request_region(cs->hw.avm.hscxfifo[0], 1, "HiSax hscx A fifo");
 	}
-	if (check_region((sp->hscx[1]), 32)) {
+	if (check_region(cs->hw.avm.hscx[1] + 32, 32)) {
 		printk(KERN_WARNING
 		       "HiSax: %s hscx B ports %x-%x already in use\n",
-		       CardType[sp->typ],
-		       sp->hscx[1],
-		       sp->hscx[1] + 32);
-		release_ioregs(card, 0xf);
+		       CardType[cs->typ],
+		       cs->hw.avm.hscx[1] + 32,
+		       cs->hw.avm.hscx[1] + 64);
+		release_ioregs(cs, 0xf);
 		return (0);
 	} else {
-		request_region(sp->hscx[1], 32, "HiSax hscx B");
+		request_region(cs->hw.avm.hscx[1] + 32, 32, "HiSax hscx B");
 	}
-	if (check_region((sp->hscx[1] - 0x400), 1)) {
+	if (check_region(cs->hw.avm.hscxfifo[1], 1)) {
 		printk(KERN_WARNING
 		       "HiSax: %s hscx B fifo port %x already in use\n",
-		       CardType[sp->typ],
-		       sp->hscx[1] - 0x400);
-		release_ioregs(card, 0x1f);
+		       CardType[cs->typ],
+		       cs->hw.avm.hscxfifo[1]);
+		release_ioregs(cs, 0x1f);
 		return (0);
 	} else {
-		request_region(sp->hscx[1] - 0x400, 1, "HiSax hscx B fifo");
+		request_region(cs->hw.avm.hscxfifo[1], 1, "HiSax hscx B fifo");
 	}
 	save_flags(flags);
-	byteout(sp->cfg_reg, 0x0);
+	byteout(cs->hw.avm.cfg_reg, 0x0);
 	sti();
 	HZDELAY(HZ / 5 + 1);
-	byteout(sp->cfg_reg, 0x1);
+	byteout(cs->hw.avm.cfg_reg, 0x1);
 	HZDELAY(HZ / 5 + 1);
-	byteout(sp->cfg_reg, 0x0);
+	byteout(cs->hw.avm.cfg_reg, 0x0);
 	HZDELAY(HZ / 5 + 1);
-	val = sp->irq;
+	val = cs->irq;
 	if (val == 9)
 		val = 2;
-	byteout(sp->cfg_reg + 1, val);
+	byteout(cs->hw.avm.cfg_reg + 1, val);
 	HZDELAY(HZ / 5 + 1);
-	byteout(sp->cfg_reg, 0x0);
+	byteout(cs->hw.avm.cfg_reg, 0x0);
 	HZDELAY(HZ / 5 + 1);
 	restore_flags(flags);
 
-	val = bytein(sp->cfg_reg);
+	val = bytein(cs->hw.avm.cfg_reg);
 	printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
-	       sp->cfg_reg, val);
-	val = bytein(sp->cfg_reg + 3);
+	       cs->hw.avm.cfg_reg, val);
+	val = bytein(cs->hw.avm.cfg_reg + 3);
 	printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
-	       sp->cfg_reg + 3, val);
-	val = bytein(sp->cfg_reg + 2);
+	       cs->hw.avm.cfg_reg + 3, val);
+	val = bytein(cs->hw.avm.cfg_reg + 2);
 	printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
-	       sp->cfg_reg + 2, val);
-	byteout(sp->cfg_reg, 0x14);
-	byteout(sp->cfg_reg, 0x18);
-	val = bytein(sp->cfg_reg);
+	       cs->hw.avm.cfg_reg + 2, val);
+	byteout(cs->hw.avm.cfg_reg, 0x1E);
+	val = bytein(cs->hw.avm.cfg_reg);
 	printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
-	       sp->cfg_reg, val);
+	       cs->hw.avm.cfg_reg, val);
 
-	printk(KERN_NOTICE
-	       "HiSax: %s config irq:%d cfg:%x\n",
-	       CardType[sp->typ], sp->irq,
-	       sp->cfg_reg);
-	printk(KERN_NOTICE
-	       "HiSax: isac:%x/%x\n",
-	       sp->isac, sp->isac - 0x400);
-	printk(KERN_NOTICE
-	       "HiSax: hscx A:%x/%x  hscx B:%x/%x\n",
-	       sp->hscx[0], sp->hscx[0] - 0x400,
-	       sp->hscx[1], sp->hscx[1] - 0x400);
-	verA = readreg(sp->hscx[0], HSCX_VSTR) & 0xf;
-	verB = readreg(sp->hscx[1], HSCX_VSTR) & 0xf;
-	printk(KERN_INFO "AVM A1: HSCX version A: %s  B: %s\n",
-	       HscxVersion(verA), HscxVersion(verB));
-	val = readreg(sp->isac, ISAC_RBCH);
-	printk(KERN_INFO "AVM A1: ISAC %s\n",
-	       ISACVersion(val));
-	if ((verA == 0) | (verA == 0xf) | (verB == 0) | (verB == 0xf)) {
+	printk(KERN_INFO
+	       "HiSax: %s config irq:%d cfg:0x%X\n",
+	       CardType[cs->typ], cs->irq,
+	       cs->hw.avm.cfg_reg);
+	printk(KERN_INFO
+	       "HiSax: isac:0x%X/0x%X\n",
+	       cs->hw.avm.isac + 32, cs->hw.avm.isacfifo);
+	printk(KERN_INFO
+	       "HiSax: hscx A:0x%X/0x%X  hscx B:0x%X/0x%X\n",
+	       cs->hw.avm.hscx[0] + 32, cs->hw.avm.hscxfifo[0],
+	       cs->hw.avm.hscx[1] + 32, cs->hw.avm.hscxfifo[1]);
+
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &AVM_card_msg;
+	ISACVersion(cs, "AVM A1:");
+	if (HscxVersion(cs, "AVM A1:")) {
 		printk(KERN_WARNING
 		       "AVM A1: wrong HSCX versions check IO address\n");
-		release_io_avm_a1(card);
+		release_ioregs(cs, 0x3f);
 		return (0);
 	}
-	sp->modehscx = &modehscx;
-	sp->ph_command = &ph_command;
-	sp->hscx_fill_fifo = &hscx_fill_fifo;
-	sp->isac_fill_fifo = &isac_fill_fifo;
 	return (1);
 }
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/avm_a1.h linux/drivers/isdn/hisax/avm_a1.h
--- v2.1.91/linux/drivers/isdn/hisax/avm_a1.h	Thu Feb 27 10:57:29 1997
+++ linux/drivers/isdn/hisax/avm_a1.h	Wed Dec 31 16:00:00 1969
@@ -1,25 +0,0 @@
-/* $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.91/linux/drivers/isdn/hisax/buffers.c linux/drivers/isdn/hisax/buffers.c
--- v2.1.91/linux/drivers/isdn/hisax/buffers.c	Fri Jan 16 20:35:48 1998
+++ linux/drivers/isdn/hisax/buffers.c	Wed Dec 31 16:00:00 1969
@@ -1,326 +0,0 @@
-/* $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);
-	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.91/linux/drivers/isdn/hisax/callc.c linux/drivers/isdn/hisax/callc.c
--- v2.1.91/linux/drivers/isdn/hisax/callc.c	Thu Feb 12 20:56:06 1998
+++ linux/drivers/isdn/hisax/callc.c	Wed Apr  1 16:20:58 1998
@@ -1,4 +1,4 @@
-/* $Id: callc.c,v 1.30 1997/05/29 10:40:43 keil Exp $
+/* $Id: callc.c,v 2.13 1998/02/12 23:07:16 keil Exp $
 
  * Author       Karsten Keil (keil@temic-ech.spacenet.de)
  *              based on the teles driver from Jan den Ouden
@@ -7,96 +7,54 @@
  *              Fritz Elfert
  *
  * $Log: callc.c,v $
- * Revision 1.30  1997/05/29 10:40:43  keil
- * chanp->impair was uninitialised
+ * Revision 2.13  1998/02/12 23:07:16  keil
+ * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
  *
- * Revision 1.29  1997/04/23 20:09:49  fritz
- * Removed tmp, used by removed debugging code.
+ * Revision 2.12  1998/02/09 10:55:54  keil
+ * New leased line mode
  *
- * Revision 1.28  1997/04/21 13:42:25  keil
- * Remove unneeded debug
+ * Revision 2.11  1998/02/02 13:35:19  keil
+ * config B-channel delay
  *
- * Revision 1.27  1997/04/16 14:21:01  keil
- * remove unused variable
+ * Revision 2.10  1997/11/06 17:09:15  keil
+ * New 2.1 init code
  *
- * Revision 1.26  1997/04/13 19:55:21  keil
- * Changes in debugging code
+ * Revision 2.9  1997/10/29 19:01:58  keil
+ * new LL interface
  *
- * Revision 1.25  1997/04/06 22:54:08  keil
- * Using SKB's
+ * Revision 2.8  1997/10/10 20:56:44  fritz
+ * New HL interface.
  *
- * Revision 1.24  1997/03/05 11:28:03  keil
- * fixed undefined l2tei procedure
- * a layer1 release delete now the drel timer
+ * Revision 2.7  1997/10/01 09:21:28  fritz
+ * Removed old compatibility stuff for 2.0.X kernels.
+ * From now on, this code is for 2.1.X ONLY!
+ * Old stuff is still in the separate branch.
  *
- * Revision 1.23  1997/03/04 23:07:42  keil
- * bugfix dial parameter
+ * Revision 2.6  1997/09/11 17:26:58  keil
+ * Open B-channel if here are incomming packets
  *
- * Revision 1.22  1997/02/27 13:51:55  keil
- * Reset B-channel (dlc) statemachine in every release
+ * Revision 2.5  1997/08/07 17:46:05  keil
+ * Fix Incomming Call without broadcast
  *
- * Revision 1.21  1997/02/19 09:24:27  keil
- * Bugfix: Hangup to LL if a ttyI rings
+ * Revision 2.4  1997/08/03 14:37:58  keil
+ * Activate Layer2 in PtP mode
  *
- * Revision 1.20  1997/02/17 00:32:47  keil
- * Bugfix: No Busy reported to LL
+ * Revision 2.3  1997/07/31 19:23:40  keil
+ * LAYER2_WATCHING for PtP
  *
- * Revision 1.19  1997/02/14 12:23:10  fritz
- * Added support for new insmod parameter handling.
+ * Revision 2.2  1997/07/31 11:48:18  keil
+ * experimental REJECT after ALERTING
  *
- * Revision 1.18  1997/02/11 01:36:58  keil
- * Changed setup-interface (incoming and outgoing), cause reporting
+ * Revision 2.1  1997/07/30 17:12:59  keil
+ * more changes for 'One TEI per card'
  *
- * 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 2.0  1997/07/27 21:12:21  keil
+ * CRef based L3; new channel handling; many other stuff
  *
- * Revision 1.16  1997/01/27 23:17:03  keil
- * delete timers while unloading
+ * Revision 1.31  1997/06/26 11:09:23  keil
+ * New managment and minor changes
  *
- * 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
+ * old logs removed /KKe
  *
  */
 
@@ -104,53 +62,63 @@
 #include "hisax.h"
 
 #ifdef MODULE
-#if (LINUX_VERSION_CODE < 0x020111)
-extern long mod_use_count_;
-#define MOD_USE_COUNT mod_use_count_
-#else
 #define MOD_USE_COUNT ((&__this_module)->usecount)
-#endif
 #endif				/* MODULE */
 
-const char *l4_revision = "$Revision: 1.30 $";
+const char *lli_revision = "$Revision: 2.13 $";
 
 extern struct IsdnCard cards[];
 extern int nrcards;
 extern void HiSax_mod_dec_use_count(void);
 extern void HiSax_mod_inc_use_count(void);
 
-static int init_ds(struct Channel *chanp, int incoming);
-static void release_ds(struct Channel *chanp);
+static int init_b_st(struct Channel *chanp, int incoming);
+static void release_b_st(struct Channel *chanp);
 
 static struct Fsm callcfsm =
-{NULL, 0, 0};
+{NULL, 0, 0, NULL, NULL};
 static struct Fsm lcfsm =
-{NULL, 0, 0};
+{NULL, 0, 0, NULL, NULL};
 
 static int chancount = 0;
 
-/* Flags for remembering action done in l4 */
+/* experimental REJECT after ALERTING for CALLBACK to beat the 4s delay */ 
+#define ALERT_REJECT 1
 
-#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
+/* Value to delay the sending of the first B-channel paket after CONNECT
+ * here is no value given by ITU, but experience shows that 300 ms will
+ * work on many networks, if you or your other side is behind local exchanges
+ * a greater value may be recommented. If the delay is to short the first paket
+ * will be lost and autodetect on many comercial routers goes wrong !
+ * You can adjust this value on runtime with 
+ * hisaxctrl <id> 2 <value>
+ * value is in milliseconds
+ */
+#define DEFAULT_B_DELAY	300
+
+/* Flags for remembering action done in lli */
 
-#define  SETBIT(flg, item)  flg |= item
-#define  RESBIT(flg, item)  flg &= (~item)
+#define  FLG_START_D	0
+#define  FLG_ESTAB_D	1
+#define  FLG_CALL_SEND	2
+#define  FLG_CALL_REC   3
+#define  FLG_CALL_ALERT	4
+#define  FLG_START_B	5
+#define  FLG_CONNECT_B	6
+#define  FLG_LL_DCONN	7
+#define  FLG_LL_BCONN	8
+#define  FLG_DISC_SEND	9
+#define  FLG_DISC_REC	10
+#define  FLG_REL_REC	11
+#define  FLG_DO_ALERT	12
+#define  FLG_DO_HANGUP	13
+#define  FLG_DO_CONNECT	14
+#define  FLG_DO_ESTAB	15
 
 /*
  * Because of callback it's a good idea to delay the shutdown of the d-channel
  */
-#define	DREL_TIMER_VALUE 30000
+#define	DREL_TIMER_VALUE 10000
 
 /*
  * Find card with given driverId
@@ -162,9 +130,9 @@
 	int i;
 
 	for (i = 0; i < nrcards; i++)
-		if (cards[i].sp)
-			if (cards[i].sp->myid == driverid)
-				return (cards[i].sp);
+		if (cards[i].cs)
+			if (cards[i].cs->myid == driverid)
+				return (cards[i].cs);
 	return (struct IsdnCardState *) 0;
 }
 
@@ -176,7 +144,7 @@
 	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);
+	HiSax_putstatus(chanp->cs, tmp);
 }
 
 
@@ -233,7 +201,7 @@
 	EV_SETUP_CMPL_IND,	/* 10 */
 	EV_BC_EST,		/* 11 */
 	EV_WRITEBUF,		/* 12 */
-	EV_DATAIN,		/* 13 */
+	EV_ESTABLISH,		/* 13 */
 	EV_HANGUP,		/* 14 */
 	EV_BC_REL,		/* 15 */
 	EV_CINF,		/* 16 */
@@ -263,7 +231,7 @@
 	"EV_SETUP_CMPL_IND",
 	"EV_BC_EST",
 	"EV_WRITEBUF",
-	"EV_DATAIN",
+	"EV_ESTABLISH",
 	"EV_HANGUP",
 	"EV_BC_REL",
 	"EV_CINF",
@@ -283,7 +251,6 @@
 	ST_LC_ESTABLISH_WAIT,
 	ST_LC_CONNECTED,
 	ST_LC_FLUSH_WAIT,
-	ST_LC_FLUSH_DELAY,
 	ST_LC_RELEASE_WAIT,
 };
 
@@ -297,7 +264,6 @@
 	"ST_LC_ESTABLISH_WAIT",
 	"ST_LC_CONNECTED",
 	"ST_LC_FLUSH_WAIT",
-	"ST_LC_FLUSH_DELAY",
 	"ST_LC_RELEASE_WAIT",
 };
 
@@ -307,9 +273,7 @@
 	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,
 };
 
@@ -322,9 +286,7 @@
 	"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",
 };
 
@@ -332,788 +294,962 @@
 #define LC_B  1
 
 static inline void
-l4_deliver_cause(struct Channel *chanp)
+lli_deliver_cause(struct Channel *chanp)
 {
 	isdn_ctrl ic;
 
-	if (chanp->para.cause < 0)
+	if (chanp->proc->para.cause < 0)
 		return;
-	ic.driver = chanp->sp->myid;
+	ic.driver = chanp->cs->myid;
 	ic.command = ISDN_STAT_CAUSE;
 	ic.arg = chanp->chan;
-	if (chanp->sp->protocol == ISDN_PTYPE_EURO)
-		sprintf(ic.parm.num, "E%02X%02X", chanp->para.loc & 0x7f,
-			chanp->para.cause & 0x7f);
+	if (chanp->cs->protocol == ISDN_PTYPE_EURO)
+		sprintf(ic.parm.num, "E%02X%02X", chanp->proc->para.loc & 0x7f,
+			chanp->proc->para.cause & 0x7f);
 	else
-		sprintf(ic.parm.num, "%02X%02X", chanp->para.loc & 0x7f,
-			chanp->para.cause & 0x7f);
-	chanp->sp->iif.statcallb(&ic);
+		sprintf(ic.parm.num, "%02X%02X", chanp->proc->para.loc & 0x7f,
+			chanp->proc->para.cause & 0x7f);
+	chanp->cs->iif.statcallb(&ic);
+}
+
+static void
+lli_d_established(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	test_and_set_bit(FLG_ESTAB_D, &chanp->Flags);
+	if (chanp->leased) {
+		isdn_ctrl ic;
+		int ret;
+		char txt[32];
+
+		chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) chanp->chan);
+		FsmChangeState(fi, ST_IN_WAIT_LL);
+		test_and_set_bit(FLG_CALL_REC, &chanp->Flags);
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_ICALL_LEASED", 0);
+		ic.driver = chanp->cs->myid;
+		ic.command = ISDN_STAT_ICALL;
+		ic.arg = chanp->chan;
+		ic.parm.setup.si1 = 7;
+		ic.parm.setup.si2 = 0;
+		ic.parm.setup.plan = 0;
+		ic.parm.setup.screen = 0;
+		sprintf(ic.parm.setup.eazmsn,"%d", chanp->chan + 1); 
+		sprintf(ic.parm.setup.phone,"LEASED%d", chanp->cs->myid);
+		ret = chanp->cs->iif.statcallb(&ic);
+		if (chanp->debug & 1) {
+			sprintf(txt, "statcallb ret=%d", ret);
+			link_debug(chanp, txt, 1);
+		}
+		if (!ret) {
+			chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) chanp->chan);
+			FsmChangeState(fi, ST_NULL);
+		}
+	} else if (fi->state == ST_WAIT_DSHUTDOWN)
+		FsmChangeState(fi, ST_NULL);
+}
+
+static void
+lli_d_released(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	test_and_clear_bit(FLG_START_D, &chanp->Flags);
 }
 
 /*
  * Dial out
  */
 static void
-l4_prep_dialout(struct FsmInst *fi, int event, void *arg)
+lli_prep_dialout(struct FsmInst *fi, int event, void *arg)
 {
 	struct Channel *chanp = fi->userdata;
 
 	FsmChangeState(fi, ST_OUT_WAIT_D);
 	FsmDelTimer(&chanp->drel_timer, 60);
 	FsmDelTimer(&chanp->dial_timer, 73);
-
 	chanp->l2_active_protocol = chanp->l2_protocol;
 	chanp->incoming = 0;
-	chanp->lc_b.l2_start = !0;
+	chanp->lc_b->l2_start = !0;
 	switch (chanp->l2_active_protocol) {
 		case (ISDN_PROTO_L2_X75I):
-			chanp->lc_b.l2_establish = !0;
+			chanp->lc_b->l2_establish = !0;
 			break;
 		case (ISDN_PROTO_L2_HDLC):
 		case (ISDN_PROTO_L2_TRANS):
-			chanp->lc_b.l2_establish = 0;
+			chanp->lc_b->l2_establish = 0;
 			break;
 		default:
-			printk(KERN_WARNING "l4_prep_dialout unknown protocol\n");
+			printk(KERN_WARNING "lli_prep_dialout unknown protocol\n");
 			break;
 	}
-	if (chanp->Flags & FLG_ESTAB_D) {
+	if (test_bit(FLG_ESTAB_D, &chanp->Flags)) {
 		FsmEvent(fi, EV_DLEST, NULL);
 	} else {
-		chanp->Flags = FLG_START_D;
+		chanp->Flags = 0;
+		test_and_set_bit(FLG_START_D, &chanp->Flags);
 		if (chanp->leased) {
-			chanp->lc_d.l2_establish = 0;
+			chanp->lc_d->l2_establish = 0;
 		}
-		FsmEvent(&chanp->lc_d.lcfi, EV_LC_ESTABLISH, NULL);
+		FsmEvent(&chanp->lc_d->lcfi, EV_LC_ESTABLISH, NULL);
 	}
 }
 
 static void
-l4_do_dialout(struct FsmInst *fi, int event, void *arg)
+lli_do_dialout(struct FsmInst *fi, int event, void *arg)
 {
 	struct Channel *chanp = fi->userdata;
 
 	FsmChangeState(fi, ST_OUT_DIAL);
+	chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) chanp->chan);
 	if (chanp->leased) {
-		chanp->para.bchannel = (chanp->chan & 1) + 1;
 		FsmEvent(&chanp->fi, EV_SETUP_CNF, NULL);
 	} else {
-		SETBIT(chanp->Flags, FLG_ESTAB_D);
-		chanp->para.callref = chanp->outcallref;
-		chanp->outcallref++;
-		if (chanp->outcallref == 128)
-			chanp->outcallref = 64;
-		chanp->is.l4.l4l3(&chanp->is, CC_SETUP_REQ, NULL);
-		SETBIT(chanp->Flags, FLG_CALL_SEND);
+		test_and_set_bit(FLG_ESTAB_D, &chanp->Flags);
+		chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP_REQ, chanp);
+		test_and_set_bit(FLG_CALL_SEND, &chanp->Flags);
 	}
 }
 
 static void
-l4_init_bchan_out(struct FsmInst *fi, int event, void *arg)
+lli_init_bchan_out(struct FsmInst *fi, int event, void *arg)
 {
 	struct Channel *chanp = fi->userdata;
 	isdn_ctrl ic;
 
 	FsmChangeState(fi, ST_WAIT_BCONN);
-	SETBIT(chanp->Flags, FLG_LL_DCONN);
+	test_and_set_bit(FLG_LL_DCONN, &chanp->Flags);
 	if (chanp->debug & 1)
 		link_debug(chanp, "STAT_DCONN", 0);
-	ic.driver = chanp->sp->myid;
+	ic.driver = chanp->cs->myid;
 	ic.command = ISDN_STAT_DCONN;
 	ic.arg = chanp->chan;
-	chanp->sp->iif.statcallb(&ic);
-	init_ds(chanp, 0);
-	SETBIT(chanp->Flags, FLG_START_B);
-	FsmEvent(&chanp->lc_b.lcfi, EV_LC_ESTABLISH, NULL);
+	chanp->cs->iif.statcallb(&ic);
+	init_b_st(chanp, 0);
+	test_and_set_bit(FLG_START_B, &chanp->Flags);
+	FsmEvent(&chanp->lc_b->lcfi, EV_LC_ESTABLISH, NULL);
 }
 
 static void
-l4_go_active(struct FsmInst *fi, int event, void *arg)
+lli_go_active(struct FsmInst *fi, int event, void *arg)
 {
 	struct Channel *chanp = fi->userdata;
 	isdn_ctrl ic;
 
 	FsmChangeState(fi, ST_ACTIVE);
 	chanp->data_open = !0;
-	SETBIT(chanp->Flags, FLG_CONNECT_B);
+	test_and_set_bit(FLG_CONNECT_B, &chanp->Flags);
 	if (chanp->debug & 1)
 		link_debug(chanp, "STAT_BCONN", 0);
-	SETBIT(chanp->Flags, FLG_LL_BCONN);
-	ic.driver = chanp->sp->myid;
+	test_and_set_bit(FLG_LL_BCONN, &chanp->Flags);
+	ic.driver = chanp->cs->myid;
 	ic.command = ISDN_STAT_BCONN;
 	ic.arg = chanp->chan;
-	chanp->sp->iif.statcallb(&ic);
+	chanp->cs->iif.statcallb(&ic);
+	chanp->cs->cardmsg(chanp->cs, MDL_INFO_CONN, (void *) chanp->chan);
 }
 
 /* incomming call */
 
 static void
-l4_start_dchan(struct FsmInst *fi, int event, void *arg)
+lli_start_dchan(struct FsmInst *fi, int event, void *arg)
 {
 	struct Channel *chanp = fi->userdata;
 
 	FsmChangeState(fi, ST_IN_WAIT_D);
 	FsmDelTimer(&chanp->drel_timer, 61);
-	if (chanp->Flags & FLG_ESTAB_D) {
+	if (event == EV_ACCEPTD)
+		test_and_set_bit(FLG_DO_CONNECT, &chanp->Flags);
+	else if (event == EV_HANGUP) {
+		test_and_set_bit(FLG_DO_HANGUP, &chanp->Flags);
+#ifdef ALERT_REJECT		
+		test_and_set_bit(FLG_DO_ALERT, &chanp->Flags);
+#endif
+	} 
+	if (test_bit(FLG_ESTAB_D, &chanp->Flags)) {
 		FsmEvent(fi, EV_DLEST, NULL);
-	} else {
-		chanp->Flags = FLG_START_D;
-		FsmEvent(&chanp->lc_d.lcfi, EV_LC_ESTABLISH, NULL);
-	}
+	} else if (!test_and_set_bit(FLG_START_D, &chanp->Flags))
+		FsmEvent(&chanp->lc_d->lcfi, EV_LC_ESTABLISH, NULL);
 }
 
 static void
-l4_deliver_call(struct FsmInst *fi, int event, void *arg)
+lli_deliver_call(struct FsmInst *fi, int event, void *arg)
 {
 	struct Channel *chanp = fi->userdata;
 	isdn_ctrl ic;
 	int ret;
 	char txt[32];
 
+	chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) chanp->chan);
 	/*
 	 * Report incoming calls only once to linklevel, use CallFlags
 	 * which is set to 3 with each broadcast message in isdnl1.c
 	 * and resetted if a interface  answered the STAT_ICALL.
 	 */
-	if ((chanp->sp) && (chanp->sp->CallFlags == 3)) {
+	if (1) { /* for only one TEI */
 		FsmChangeState(fi, ST_IN_WAIT_LL);
-		SETBIT(chanp->Flags, FLG_ESTAB_D);
-		SETBIT(chanp->Flags, FLG_CALL_REC);
+		test_and_set_bit(FLG_CALL_REC, &chanp->Flags);
 		if (chanp->debug & 1)
 			link_debug(chanp, "STAT_ICALL", 0);
-		ic.driver = chanp->sp->myid;
+		ic.driver = chanp->cs->myid;
 		ic.command = ISDN_STAT_ICALL;
 		ic.arg = chanp->chan;
 		/*
 		 * No need to return "unknown" for calls without OAD,
 		 * cause that's handled in linklevel now (replaced by '0')
 		 */
-		ic.parm.setup = chanp->para.setup;
-		ret = chanp->sp->iif.statcallb(&ic);
+		ic.parm.setup = chanp->proc->para.setup;
+		ret = chanp->cs->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);
+				FsmDelTimer(&chanp->drel_timer, 61);
+				if (test_bit(FLG_ESTAB_D, &chanp->Flags)) {
+					FsmChangeState(fi, ST_IN_ALERT_SEND);
+					test_and_set_bit(FLG_CALL_ALERT, &chanp->Flags);
+					chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING_REQ, chanp->proc);
+				} else {
+					test_and_set_bit(FLG_DO_ALERT, &chanp->Flags);
+					FsmChangeState(fi, ST_IN_WAIT_D);
+					test_and_set_bit(FLG_START_D, &chanp->Flags);
+					FsmEvent(&chanp->lc_d->lcfi, EV_LC_ESTABLISH, NULL);
+				}
 				break;
 			case 2:	/* Rejecting Call */
-				RESBIT(chanp->Flags, FLG_CALL_REC);
+				test_and_clear_bit(FLG_CALL_REC, &chanp->Flags);
 				break;
 			case 0:	/* OK, nobody likes this call */
 			default:	/* statcallb problems */
-				chanp->is.l4.l4l3(&chanp->is, CC_IGNORE, NULL);
+				chanp->d_st->lli.l4l3(chanp->d_st, CC_IGNORE, chanp->proc);
+				chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) chanp->chan);
 				FsmChangeState(fi, ST_NULL);
-				chanp->Flags = FLG_ESTAB_D;
-				FsmAddTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 61);
+#ifndef LAYER2_WATCHING
+				if (test_bit(FLG_ESTAB_D, &chanp->Flags))
+					FsmRestartTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 61);
+#endif
 				break;
 		}
 	} else {
-		chanp->is.l4.l4l3(&chanp->is, CC_IGNORE, NULL);
+		chanp->d_st->lli.l4l3(chanp->d_st, CC_IGNORE, chanp->proc);
+		chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) chanp->chan);
+		FsmChangeState(fi, ST_NULL);
+#ifndef LAYER2_WATCHING
+		if (test_bit(FLG_ESTAB_D, &chanp->Flags))
+			FsmRestartTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 62);
+#endif
+	}
+}
+
+static void
+lli_establish_d(struct FsmInst *fi, int event, void *arg)
+{
+	/* This establish the D-channel for pending L3 messages 
+	 * without blocking th channel
+	 */
+	struct Channel *chanp = fi->userdata;
+
+	test_and_set_bit(FLG_DO_ESTAB, &chanp->Flags);
+	FsmChangeState(fi, ST_IN_WAIT_D);
+	test_and_set_bit(FLG_START_D, &chanp->Flags);
+	FsmEvent(&chanp->lc_d->lcfi, EV_LC_ESTABLISH, NULL);
+}
+
+static void
+lli_do_action(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	test_and_set_bit(FLG_ESTAB_D, &chanp->Flags);
+	if (chanp->leased) {
+		FsmChangeState(fi, ST_IN_WAIT_CONN_ACK);
+		test_and_clear_bit(FLG_DO_ALERT, &chanp->Flags);
+		test_and_clear_bit(FLG_DO_CONNECT, &chanp->Flags);
+		FsmEvent(&chanp->fi, EV_SETUP_CMPL_IND, NULL);
+	} else if (test_and_clear_bit(FLG_DO_CONNECT, &chanp->Flags) &&
+		!test_bit(FLG_DO_HANGUP, &chanp->Flags)) {
+		FsmChangeState(fi, ST_IN_WAIT_CONN_ACK);
+		test_and_clear_bit(FLG_DO_ALERT, &chanp->Flags);
+		chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP_RSP, chanp->proc);
+	} else if (test_and_clear_bit(FLG_DO_ALERT, &chanp->Flags)) {
+		if (test_bit(FLG_DO_HANGUP, &chanp->Flags))
+			FsmRestartTimer(&chanp->drel_timer, 40, EV_HANGUP, NULL, 63);
+		FsmChangeState(fi, ST_IN_ALERT_SEND);
+		test_and_set_bit(FLG_CALL_ALERT, &chanp->Flags);
+		chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING_REQ, chanp->proc);
+	} else if (test_and_clear_bit(FLG_DO_HANGUP, &chanp->Flags)) {
+		FsmChangeState(fi, ST_WAIT_DRELEASE);
+		chanp->proc->para.cause = 0x15;		/* Call Rejected */
+		chanp->d_st->lli.l4l3(chanp->d_st, CC_REJECT_REQ, chanp->proc);
+		test_and_set_bit(FLG_DISC_SEND, &chanp->Flags);
+	} else if (test_and_clear_bit(FLG_DO_ESTAB, &chanp->Flags)) {
 		FsmChangeState(fi, ST_NULL);
-		chanp->Flags = FLG_ESTAB_D;
-		FsmAddTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 62);
+		chanp->Flags = 0;
+		test_and_set_bit(FLG_ESTAB_D, &chanp->Flags);
+		chanp->d_st->lli.l4l3(chanp->d_st, CC_ESTABLISH, chanp->proc);
+		chanp->proc = NULL;
+#ifndef LAYER2_WATCHING
+		FsmAddTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 60);
+#endif
 	}
 }
 
 static void
-l4_send_dconnect(struct FsmInst *fi, int event, void *arg)
+lli_send_dconnect(struct FsmInst *fi, int event, void *arg)
 {
 	struct Channel *chanp = fi->userdata;
 
 	FsmChangeState(fi, ST_IN_WAIT_CONN_ACK);
-	chanp->is.l4.l4l3(&chanp->is, CC_SETUP_RSP, NULL);
+	chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP_RSP, chanp->proc);
 }
 
 static void
-l4_init_bchan_in(struct FsmInst *fi, int event, void *arg)
+lli_init_bchan_in(struct FsmInst *fi, int event, void *arg)
 {
 	struct Channel *chanp = fi->userdata;
 	isdn_ctrl ic;
 
 	FsmChangeState(fi, ST_WAIT_BCONN);
-	SETBIT(chanp->Flags, FLG_LL_DCONN);
+	test_and_set_bit(FLG_LL_DCONN, &chanp->Flags);
 	if (chanp->debug & 1)
 		link_debug(chanp, "STAT_DCONN", 0);
-	ic.driver = chanp->sp->myid;
+	ic.driver = chanp->cs->myid;
 	ic.command = ISDN_STAT_DCONN;
 	ic.arg = chanp->chan;
-	chanp->sp->iif.statcallb(&ic);
+	chanp->cs->iif.statcallb(&ic);
 	chanp->l2_active_protocol = chanp->l2_protocol;
 	chanp->incoming = !0;
-	chanp->lc_b.l2_start = 0;
+	chanp->lc_b->l2_start = 0;
 	switch (chanp->l2_active_protocol) {
 		case (ISDN_PROTO_L2_X75I):
-			chanp->lc_b.l2_establish = !0;
+			chanp->lc_b->l2_establish = !0;
 			break;
 		case (ISDN_PROTO_L2_HDLC):
 		case (ISDN_PROTO_L2_TRANS):
-			chanp->lc_b.l2_establish = 0;
+			chanp->lc_b->l2_establish = 0;
 			break;
 		default:
-			printk(KERN_WARNING "r9 unknown protocol\n");
+			printk(KERN_WARNING "bchannel unknown protocol\n");
 			break;
 	}
-	init_ds(chanp, !0);
-	SETBIT(chanp->Flags, FLG_START_B);
-	FsmEvent(&chanp->lc_b.lcfi, EV_LC_ESTABLISH, NULL);
+	init_b_st(chanp, !0);
+	test_and_set_bit(FLG_START_B, &chanp->Flags);
+	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)
+lli_cancel_call(struct FsmInst *fi, int event, void *arg)
 {
 	struct Channel *chanp = fi->userdata;
 	isdn_ctrl ic;
 
 	FsmChangeState(fi, ST_WAIT_DRELEASE);
-	if (chanp->Flags & FLG_LL_BCONN) {
+	if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) {
 		if (chanp->debug & 1)
 			link_debug(chanp, "STAT_BHUP", 0);
-		RESBIT(chanp->Flags, FLG_LL_BCONN);
-		ic.driver = chanp->sp->myid;
+		ic.driver = chanp->cs->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->cs->iif.statcallb(&ic);
 	}
-	chanp->para.cause = 0x10;	/* Normal Call Clearing */
-	chanp->is.l4.l4l3(&chanp->is, CC_DISCONNECT_REQ, NULL);
-	SETBIT(chanp->Flags, FLG_DISC_SEND);
+	if (test_and_clear_bit(FLG_START_B, &chanp->Flags))
+		release_b_st(chanp);
+	chanp->proc->para.cause = 0x10;		/* Normal Call Clearing */
+	chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT_REQ, chanp->proc);
+	test_and_set_bit(FLG_DISC_SEND, &chanp->Flags);
 }
 
 static void
-l4_shutdown_d(struct FsmInst *fi, int event, void *arg)
+lli_shutdown_d(struct FsmInst *fi, int event, void *arg)
 {
 	struct Channel *chanp = fi->userdata;
 
-	FsmChangeState(fi, ST_WAIT_DSHUTDOWN);
 	FsmDelTimer(&chanp->drel_timer, 62);
-	RESBIT(chanp->Flags, FLG_ESTAB_D);
-	FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL);
+#ifdef LAYER2_WATCHING
+	FsmChangeState(fi, ST_NULL);
+#else
+	if (!test_bit(FLG_TWO_DCHAN, &chanp->cs->HW_Flags)) {
+		if (chanp->chan) {
+			if (chanp->cs->channel[0].fi.state != ST_NULL)
+				return;
+		} else {
+			if (chanp->cs->channel[1].fi.state != ST_NULL)
+				return;
+		}
+	}
+	FsmChangeState(fi, ST_WAIT_DSHUTDOWN);
+	test_and_clear_bit(FLG_ESTAB_D, &chanp->Flags);
+	FsmEvent(&chanp->lc_d->lcfi, EV_LC_RELEASE, NULL);
+#endif
 }
 
 static void
-l4_timeout_d(struct FsmInst *fi, int event, void *arg)
+lli_timeout_d(struct FsmInst *fi, int event, void *arg)
 {
 	struct Channel *chanp = fi->userdata;
 	isdn_ctrl ic;
 
-	if (chanp->Flags & FLG_LL_DCONN) {
+	if (test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags)) {
 		if (chanp->debug & 1)
 			link_debug(chanp, "STAT_DHUP", 0);
-		RESBIT(chanp->Flags, FLG_LL_DCONN);
-		l4_deliver_cause(chanp);
-		ic.driver = chanp->sp->myid;
+		lli_deliver_cause(chanp);
+		ic.driver = chanp->cs->myid;
 		ic.command = ISDN_STAT_DHUP;
 		ic.arg = chanp->chan;
-		chanp->sp->iif.statcallb(&ic);
+		chanp->cs->iif.statcallb(&ic);
 	}
 	FsmChangeState(fi, ST_NULL);
-	chanp->Flags = FLG_ESTAB_D;
+	chanp->Flags = 0;
+	test_and_set_bit(FLG_ESTAB_D, &chanp->Flags);
+#ifndef LAYER2_WATCHING
 	FsmAddTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 60);
+#endif
+	chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) chanp->chan);
 }
 
 static void
-l4_go_null(struct FsmInst *fi, int event, void *arg)
+lli_go_null(struct FsmInst *fi, int event, void *arg)
 {
 	struct Channel *chanp = fi->userdata;
 
 	FsmChangeState(fi, ST_NULL);
 	chanp->Flags = 0;
 	FsmDelTimer(&chanp->drel_timer, 63);
+	chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) chanp->chan);
 }
 
 static void
-l4_disconn_bchan(struct FsmInst *fi, int event, void *arg)
+lli_disconn_bchan(struct FsmInst *fi, int event, void *arg)
 {
 	struct Channel *chanp = fi->userdata;
 
 	chanp->data_open = 0;
 	FsmChangeState(fi, ST_WAIT_BRELEASE);
-	RESBIT(chanp->Flags, FLG_CONNECT_B);
-	FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL);
+	test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags);
+	FsmEvent(&chanp->lc_b->lcfi, EV_LC_RELEASE, NULL);
 }
 
 static void
-l4_send_d_disc(struct FsmInst *fi, int event, void *arg)
+lli_send_d_disc(struct FsmInst *fi, int event, void *arg)
 {
 	struct Channel *chanp = fi->userdata;
 	isdn_ctrl ic;
 
-
-	if (chanp->Flags & (FLG_DISC_REC | FLG_REL_REC))
+	if (test_bit(FLG_DISC_REC, &chanp->Flags) ||
+		test_bit(FLG_REL_REC, &chanp->Flags))
 		return;
 	FsmChangeState(fi, ST_WAIT_DRELEASE);
-	if (chanp->Flags & FLG_LL_BCONN) {
+	if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) {
 		if (chanp->debug & 1)
 			link_debug(chanp, "STAT_BHUP", 0);
-		RESBIT(chanp->Flags, FLG_LL_BCONN);
-		ic.driver = chanp->sp->myid;
+		ic.driver = chanp->cs->myid;
 		ic.command = ISDN_STAT_BHUP;
 		ic.arg = chanp->chan;
-		chanp->sp->iif.statcallb(&ic);
+		chanp->cs->iif.statcallb(&ic);
 	}
-	if (chanp->Flags & FLG_START_B) {
-		release_ds(chanp);
-		RESBIT(chanp->Flags, FLG_START_B);
+	if (test_and_clear_bit(FLG_START_B, &chanp->Flags))
+		release_b_st(chanp);
+	if (chanp->leased) {
+		ic.command = ISDN_STAT_CAUSE;
+		ic.arg = chanp->chan;
+		sprintf(ic.parm.num, "L0010");
+		chanp->cs->iif.statcallb(&ic);
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_DHUP", 0);
+		ic.driver = chanp->cs->myid;
+		ic.command = ISDN_STAT_DHUP;
+		ic.arg = chanp->chan;
+		chanp->cs->iif.statcallb(&ic);
+		FsmChangeState(fi, ST_WAIT_DSHUTDOWN);
+		test_and_clear_bit(FLG_ESTAB_D, &chanp->Flags);
+		FsmEvent(&chanp->lc_d->lcfi, EV_LC_RELEASE, NULL);
+	} else {
+		if (test_and_clear_bit(FLG_DO_HANGUP, &chanp->Flags))
+			chanp->proc->para.cause = 0x15;		/* Call Reject */
+		else
+			chanp->proc->para.cause = 0x10;		/* Normal Call Clearing */
+		chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT_REQ, chanp->proc);
+		test_and_set_bit(FLG_DISC_SEND, &chanp->Flags);
 	}
-	chanp->para.cause = 0x10;	/* Normal Call Clearing */
-	chanp->is.l4.l4l3(&chanp->is, CC_DISCONNECT_REQ, NULL);
-	SETBIT(chanp->Flags, FLG_DISC_SEND);
 }
 
 static void
-l4_released_bchan(struct FsmInst *fi, int event, void *arg)
+lli_released_bchan(struct FsmInst *fi, int event, void *arg)
 {
 	struct Channel *chanp = fi->userdata;
 	isdn_ctrl ic;
 
 	FsmChangeState(fi, ST_WAIT_DCOMMAND);
 	chanp->data_open = 0;
-	if (chanp->Flags & FLG_LL_BCONN) {
+	if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) {
 		if (chanp->debug & 1)
 			link_debug(chanp, "STAT_BHUP", 0);
-		RESBIT(chanp->Flags, FLG_LL_BCONN);
-		ic.driver = chanp->sp->myid;
+		ic.driver = chanp->cs->myid;
 		ic.command = ISDN_STAT_BHUP;
 		ic.arg = chanp->chan;
-		chanp->sp->iif.statcallb(&ic);
+		chanp->cs->iif.statcallb(&ic);
 	}
-	release_ds(chanp);
-	RESBIT(chanp->Flags, FLG_START_B);
+	release_b_st(chanp);
+	test_and_clear_bit(FLG_START_B, &chanp->Flags);
 }
 
 
 static void
-l4_release_bchan(struct FsmInst *fi, int event, void *arg)
+lli_release_bchan(struct FsmInst *fi, int event, void *arg)
 {
 	struct Channel *chanp = fi->userdata;
 
 	chanp->data_open = 0;
-	SETBIT(chanp->Flags, FLG_DISC_REC);
+	test_and_set_bit(FLG_DISC_REC, &chanp->Flags);
 	FsmChangeState(fi, ST_WAIT_BREL_DISC);
-	RESBIT(chanp->Flags, FLG_CONNECT_B);
-	FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL);
+	test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags);
+	FsmEvent(&chanp->lc_b->lcfi, EV_LC_RELEASE, NULL);
 }
 
 static void
-l4_received_d_rel(struct FsmInst *fi, int event, void *arg)
+lli_received_d_rel(struct FsmInst *fi, int event, void *arg)
 {
 	struct Channel *chanp = fi->userdata;
 	isdn_ctrl ic;
 
 	chanp->data_open = 0;
-	FsmChangeState(fi, ST_WAIT_DSHUTDOWN);
-	SETBIT(chanp->Flags, FLG_REL_REC);
-	if (chanp->Flags & FLG_CONNECT_B) {
-		chanp->lc_b.l2_establish = 0;	/* direct reset in lc_b.lcfi */
-		FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL);
-		RESBIT(chanp->Flags, FLG_CONNECT_B);
+	FsmChangeState(fi, ST_NULL);
+	test_and_set_bit(FLG_REL_REC, &chanp->Flags);
+	if (test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags)) {
+		chanp->lc_b->l2_establish = 0;	/* direct reset in lc_b->lcfi */
+		FsmEvent(&chanp->lc_b->lcfi, EV_LC_RELEASE, NULL);
 	}
-	if (chanp->Flags & FLG_LL_BCONN) {
+	if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) {
 		if (chanp->debug & 1)
 			link_debug(chanp, "STAT_BHUP", 0);
-		ic.driver = chanp->sp->myid;
+		ic.driver = chanp->cs->myid;
 		ic.command = ISDN_STAT_BHUP;
 		ic.arg = chanp->chan;
-		chanp->sp->iif.statcallb(&ic);
-		RESBIT(chanp->Flags, FLG_LL_BCONN);
+		chanp->cs->iif.statcallb(&ic);
 	}
-	if (chanp->Flags & FLG_START_B) {
-		release_ds(chanp);
-		RESBIT(chanp->Flags, FLG_START_B);
-	}
-	if (chanp->Flags & (FLG_LL_DCONN | FLG_CALL_SEND | FLG_CALL_ALERT)) {
+	if (test_and_clear_bit(FLG_START_B, &chanp->Flags))
+		release_b_st(chanp);
+	if (test_bit(FLG_LL_DCONN, &chanp->Flags) ||
+		test_bit(FLG_CALL_SEND, &chanp->Flags) ||
+		test_bit(FLG_CALL_ALERT, &chanp->Flags)) {
 		if (chanp->debug & 1)
 			link_debug(chanp, "STAT_DHUP", 0);
-		l4_deliver_cause(chanp);
-		ic.driver = chanp->sp->myid;
+		lli_deliver_cause(chanp);
+		ic.driver = chanp->cs->myid;
 		ic.command = ISDN_STAT_DHUP;
 		ic.arg = chanp->chan;
-		chanp->sp->iif.statcallb(&ic);
+		chanp->cs->iif.statcallb(&ic);
 	}
-	FsmEvent(&chanp->lc_d.lcfi, EV_LC_FLUSH, NULL);
-	RESBIT(chanp->Flags, FLG_ESTAB_D);
-	RESBIT(chanp->Flags, FLG_DISC_SEND);
-	RESBIT(chanp->Flags, FLG_CALL_REC);
-	RESBIT(chanp->Flags, FLG_CALL_ALERT);
-	RESBIT(chanp->Flags, FLG_LL_DCONN);
-	RESBIT(chanp->Flags, FLG_CALL_SEND);
+	test_and_clear_bit(FLG_DISC_SEND, &chanp->Flags);
+	test_and_clear_bit(FLG_CALL_REC, &chanp->Flags);
+	test_and_clear_bit(FLG_CALL_ALERT, &chanp->Flags);
+	test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags);
+	test_and_clear_bit(FLG_CALL_SEND, &chanp->Flags);
+	lli_timeout_d(fi, event, arg);
 }
 
 static void
-l4_received_d_relcnf(struct FsmInst *fi, int event, void *arg)
+lli_received_d_relcnf(struct FsmInst *fi, int event, void *arg)
 {
 	struct Channel *chanp = fi->userdata;
 	isdn_ctrl ic;
 
 	chanp->data_open = 0;
-	FsmChangeState(fi, ST_WAIT_DSHUTDOWN);
-	if (chanp->Flags & FLG_CONNECT_B) {
-		chanp->lc_b.l2_establish = 0;	/* direct reset in lc_b.lcfi */
-		FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL);
-		RESBIT(chanp->Flags, FLG_CONNECT_B);
+	FsmChangeState(fi, ST_NULL);
+	if (test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags)) {
+		chanp->lc_b->l2_establish = 0;	/* direct reset in lc_b->lcfi */
+		FsmEvent(&chanp->lc_b->lcfi, EV_LC_RELEASE, NULL);
 	}
-	if (chanp->Flags & FLG_LL_BCONN) {
+	if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) {
 		if (chanp->debug & 1)
 			link_debug(chanp, "STAT_BHUP", 0);
-		ic.driver = chanp->sp->myid;
+		ic.driver = chanp->cs->myid;
 		ic.command = ISDN_STAT_BHUP;
 		ic.arg = chanp->chan;
-		chanp->sp->iif.statcallb(&ic);
-		RESBIT(chanp->Flags, FLG_LL_BCONN);
-	}
-	if (chanp->Flags & FLG_START_B) {
-		release_ds(chanp);
-		RESBIT(chanp->Flags, FLG_START_B);
+		chanp->cs->iif.statcallb(&ic);
 	}
-	if (chanp->Flags & (FLG_LL_DCONN | FLG_CALL_SEND | FLG_CALL_ALERT)) {
+	if (test_and_clear_bit(FLG_START_B, &chanp->Flags))
+		release_b_st(chanp);
+	if (test_bit(FLG_LL_DCONN, &chanp->Flags) ||
+		test_bit(FLG_CALL_SEND, &chanp->Flags) ||
+		test_bit(FLG_CALL_ALERT, &chanp->Flags)) {
 		if (chanp->debug & 1)
 			link_debug(chanp, "STAT_DHUP", 0);
-		l4_deliver_cause(chanp);
-		ic.driver = chanp->sp->myid;
+		lli_deliver_cause(chanp);
+		ic.driver = chanp->cs->myid;
 		ic.command = ISDN_STAT_DHUP;
 		ic.arg = chanp->chan;
-		chanp->sp->iif.statcallb(&ic);
+		chanp->cs->iif.statcallb(&ic);
 	}
-	FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL);
-	RESBIT(chanp->Flags, FLG_ESTAB_D);
-	RESBIT(chanp->Flags, FLG_DISC_SEND);
-	RESBIT(chanp->Flags, FLG_CALL_REC);
-	RESBIT(chanp->Flags, FLG_CALL_ALERT);
-	RESBIT(chanp->Flags, FLG_LL_DCONN);
-	RESBIT(chanp->Flags, FLG_CALL_SEND);
+	test_and_clear_bit(FLG_DISC_SEND, &chanp->Flags);
+	test_and_clear_bit(FLG_CALL_REC, &chanp->Flags);
+	test_and_clear_bit(FLG_CALL_ALERT, &chanp->Flags);
+	test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags);
+	test_and_clear_bit(FLG_CALL_SEND, &chanp->Flags);
+	lli_timeout_d(fi, event, arg);
 }
 
 static void
-l4_received_d_disc(struct FsmInst *fi, int event, void *arg)
+lli_received_d_disc(struct FsmInst *fi, int event, void *arg)
 {
 	struct Channel *chanp = fi->userdata;
 	isdn_ctrl ic;
 
 	chanp->data_open = 0;
 	FsmChangeState(fi, ST_WAIT_D_REL_CNF);
-	SETBIT(chanp->Flags, FLG_DISC_REC);
-	if (chanp->Flags & FLG_LL_BCONN) {
+	test_and_set_bit(FLG_DISC_REC, &chanp->Flags);
+	if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) {
 		if (chanp->debug & 1)
 			link_debug(chanp, "STAT_BHUP", 0);
-		ic.driver = chanp->sp->myid;
+		ic.driver = chanp->cs->myid;
 		ic.command = ISDN_STAT_BHUP;
 		ic.arg = chanp->chan;
-		chanp->sp->iif.statcallb(&ic);
-		RESBIT(chanp->Flags, FLG_LL_BCONN);
+		chanp->cs->iif.statcallb(&ic);
 	}
-	if (chanp->Flags & FLG_START_B) {
-		release_ds(chanp);
-		RESBIT(chanp->Flags, FLG_START_B);
-	}
-	if (chanp->Flags & (FLG_LL_DCONN | FLG_CALL_SEND | FLG_CALL_ALERT)) {
+	if (test_and_clear_bit(FLG_START_B, &chanp->Flags))
+		release_b_st(chanp);
+	if (test_bit(FLG_LL_DCONN, &chanp->Flags) ||
+		test_bit(FLG_CALL_SEND, &chanp->Flags) ||
+		test_bit(FLG_CALL_ALERT, &chanp->Flags)) {
 		if (chanp->debug & 1)
 			link_debug(chanp, "STAT_DHUP", 0);
-		l4_deliver_cause(chanp);
-		ic.driver = chanp->sp->myid;
+		lli_deliver_cause(chanp);
+		ic.driver = chanp->cs->myid;
 		ic.command = ISDN_STAT_DHUP;
 		ic.arg = chanp->chan;
-		chanp->sp->iif.statcallb(&ic);
+		chanp->cs->iif.statcallb(&ic);
 	}
-	RESBIT(chanp->Flags, FLG_LL_DCONN);
-	RESBIT(chanp->Flags, FLG_CALL_SEND);
-	RESBIT(chanp->Flags, FLG_CALL_ALERT);
-	chanp->is.l4.l4l3(&chanp->is, CC_RELEASE_REQ, NULL);
+	test_and_clear_bit(FLG_CALL_ALERT, &chanp->Flags);
+	test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags);
+	test_and_clear_bit(FLG_CALL_SEND, &chanp->Flags);
+	chanp->d_st->lli.l4l3(chanp->d_st, CC_RELEASE_REQ, chanp->proc);
 }
 
 /* processing charge info */
 static void
-l4_charge_info(struct FsmInst *fi, int event, void *arg)
+lli_charge_info(struct FsmInst *fi, int event, void *arg)
 {
 	struct Channel *chanp = fi->userdata;
 	isdn_ctrl ic;
 
-	ic.driver = chanp->sp->myid;
+	ic.driver = chanp->cs->myid;
 	ic.command = ISDN_STAT_CINF;
 	ic.arg = chanp->chan;
-	sprintf(ic.parm.num, "%d", chanp->para.chargeinfo);
-	chanp->sp->iif.statcallb(&ic);
+	sprintf(ic.parm.num, "%d", chanp->proc->para.chargeinfo);
+	chanp->cs->iif.statcallb(&ic);
 }
 
 /* error procedures */
 
 static void
-l4_no_dchan(struct FsmInst *fi, int event, void *arg)
+lli_no_dchan(struct FsmInst *fi, int event, void *arg)
 {
 	struct Channel *chanp = fi->userdata;
 	isdn_ctrl ic;
 
 	if (chanp->debug & 1)
 		link_debug(chanp, "STAT_NODCH", 0);
-	ic.driver = chanp->sp->myid;
+	ic.driver = chanp->cs->myid;
 	ic.command = ISDN_STAT_NODCH;
 	ic.arg = chanp->chan;
-	chanp->sp->iif.statcallb(&ic);
+	chanp->cs->iif.statcallb(&ic);
 	chanp->Flags = 0;
 	FsmChangeState(fi, ST_NULL);
-	FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL);
+	FsmEvent(&chanp->lc_d->lcfi, EV_LC_RELEASE, NULL);
 }
 
 static void
-l4_no_dchan_ready(struct FsmInst *fi, int event, void *arg)
+lli_no_dchan_ready(struct FsmInst *fi, int event, void *arg)
 {
 	struct Channel *chanp = fi->userdata;
 	isdn_ctrl ic;
 
 	if (chanp->debug & 1)
 		link_debug(chanp, "STAT_DHUP", 0);
-	ic.driver = chanp->sp->myid;
+	ic.driver = chanp->cs->myid;
 	ic.command = ISDN_STAT_DHUP;
 	ic.arg = chanp->chan;
-	chanp->sp->iif.statcallb(&ic);
+	chanp->cs->iif.statcallb(&ic);
 }
 
 static void
-l4_no_dchan_in(struct FsmInst *fi, int event, void *arg)
+lli_no_dchan_in(struct FsmInst *fi, int event, void *arg)
 {
 	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
 
-	chanp->is.l4.l4l3(&chanp->is, CC_DLRL, NULL);
+	if (chanp->debug & 1)
+		link_debug(chanp, "STAT_DHUP", 0);
+	ic.driver = chanp->cs->myid;
+	ic.command = ISDN_STAT_DHUP;
+	ic.arg = chanp->chan;
+	chanp->cs->iif.statcallb(&ic);
+	chanp->d_st->lli.l4l3(chanp->d_st, CC_DLRL, chanp->proc);
 	chanp->Flags = 0;
 	FsmChangeState(fi, ST_NULL);
-	FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL);
+	FsmEvent(&chanp->lc_d->lcfi, EV_LC_RELEASE, NULL);
 }
 
 static void
-l4_no_setup_rsp(struct FsmInst *fi, int event, void *arg)
+lli_no_setup_rsp(struct FsmInst *fi, int event, void *arg)
 {
 	struct Channel *chanp = fi->userdata;
 	isdn_ctrl ic;
 
-	chanp->Flags = 0;
 	FsmChangeState(fi, ST_NULL);
-	FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL);
+	test_and_clear_bit(FLG_CALL_SEND, &chanp->Flags);
 	if (chanp->debug & 1)
 		link_debug(chanp, "STAT_DHUP", 0);
-	ic.driver = chanp->sp->myid;
+	ic.driver = chanp->cs->myid;
 	ic.command = ISDN_STAT_DHUP;
 	ic.arg = chanp->chan;
-	chanp->sp->iif.statcallb(&ic);
+	chanp->cs->iif.statcallb(&ic);
+	lli_shutdown_d(fi, event, arg);
 }
 
 static void
-l4_setup_err(struct FsmInst *fi, int event, void *arg)
+lli_setup_err(struct FsmInst *fi, int event, void *arg)
 {
 	struct Channel *chanp = fi->userdata;
 	isdn_ctrl ic;
 
 	FsmChangeState(fi, ST_WAIT_DRELEASE);
-	if (chanp->Flags & FLG_LL_DCONN) {
+	if (test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags)) {
 		if (chanp->debug & 1)
 			link_debug(chanp, "STAT_DHUP", 0);
-		RESBIT(chanp->Flags, FLG_LL_DCONN);
-		l4_deliver_cause(chanp);
-		ic.driver = chanp->sp->myid;
+		lli_deliver_cause(chanp);
+		ic.driver = chanp->cs->myid;
 		ic.command = ISDN_STAT_DHUP;
 		ic.arg = chanp->chan;
-		chanp->sp->iif.statcallb(&ic);
+		chanp->cs->iif.statcallb(&ic);
 	}
-	SETBIT(chanp->Flags, FLG_DISC_SEND);	/* DISCONN was sent from L3 */
+	test_and_set_bit(FLG_DISC_SEND, &chanp->Flags);	/* DISCONN was sent from L3 */
 }
 
 static void
-l4_connect_err(struct FsmInst *fi, int event, void *arg)
+lli_connect_err(struct FsmInst *fi, int event, void *arg)
 {
 	struct Channel *chanp = fi->userdata;
 	isdn_ctrl ic;
 
 	FsmChangeState(fi, ST_WAIT_DRELEASE);
-	if (chanp->Flags & FLG_LL_DCONN) {
+	if (test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags)) {
 		if (chanp->debug & 1)
 			link_debug(chanp, "STAT_DHUP", 0);
-		RESBIT(chanp->Flags, FLG_LL_DCONN);
-		l4_deliver_cause(chanp);
-		ic.driver = chanp->sp->myid;
+		lli_deliver_cause(chanp);
+		ic.driver = chanp->cs->myid;
 		ic.command = ISDN_STAT_DHUP;
 		ic.arg = chanp->chan;
-		chanp->sp->iif.statcallb(&ic);
+		chanp->cs->iif.statcallb(&ic);
 	}
-	SETBIT(chanp->Flags, FLG_DISC_SEND);	/* DISCONN was sent from L3 */
+	test_and_set_bit(FLG_DISC_SEND, &chanp->Flags);	/* DISCONN was sent from L3 */
 }
 
 static void
-l4_active_dlrl(struct FsmInst *fi, int event, void *arg)
+lli_got_dlrl(struct FsmInst *fi, int event, void *arg)
 {
 	struct Channel *chanp = fi->userdata;
 	isdn_ctrl ic;
 
 	chanp->data_open = 0;
 	FsmChangeState(fi, ST_NULL);
-	if (chanp->Flags & FLG_CONNECT_B) {
-		chanp->lc_b.l2_establish = 0;	/* direct reset in lc_b.lcfi */
-		FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL);
-		RESBIT(chanp->Flags, FLG_CONNECT_B);
+	if (test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags)) {
+		chanp->lc_b->l2_establish = 0;	/* direct reset in lc_b->lcfi */
+		FsmEvent(&chanp->lc_b->lcfi, EV_LC_RELEASE, NULL);
 	}
-	if (chanp->Flags & FLG_LL_BCONN) {
+	if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) {
 		if (chanp->debug & 1)
 			link_debug(chanp, "STAT_BHUP", 0);
-		ic.driver = chanp->sp->myid;
+		ic.driver = chanp->cs->myid;
 		ic.command = ISDN_STAT_BHUP;
 		ic.arg = chanp->chan;
-		chanp->sp->iif.statcallb(&ic);
-		RESBIT(chanp->Flags, FLG_LL_BCONN);
+		chanp->cs->iif.statcallb(&ic);
 	}
-	if (chanp->Flags & FLG_START_B) {
-		release_ds(chanp);
-		RESBIT(chanp->Flags, FLG_START_B);
-	}
-	if (chanp->Flags & FLG_LL_DCONN) {
-		if (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;
+	if (test_and_clear_bit(FLG_START_B, &chanp->Flags))
+		release_b_st(chanp);
+	if (chanp->leased) {
+		ic.driver = chanp->cs->myid;
+		ic.command = ISDN_STAT_CAUSE;
+		ic.arg = chanp->chan;
+		sprintf(ic.parm.num, "L%02X%02X", 0, 0x2f);
+		chanp->cs->iif.statcallb(&ic);
+		ic.driver = chanp->cs->myid;
 		ic.command = ISDN_STAT_DHUP;
 		ic.arg = chanp->chan;
-		chanp->sp->iif.statcallb(&ic);
+		chanp->cs->iif.statcallb(&ic);
+		chanp->Flags = 0;
+	} else {
+		if (test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags)) {
+			if (chanp->debug & 1)
+				link_debug(chanp, "STAT_DHUP", 0);
+			if (chanp->cs->protocol == ISDN_PTYPE_EURO) {
+				chanp->proc->para.cause = 0x2f;
+				chanp->proc->para.loc = 0;
+			} else {
+				chanp->proc->para.cause = 0x70;
+				chanp->proc->para.loc = 0;
+			}
+			lli_deliver_cause(chanp);
+			ic.driver = chanp->cs->myid;
+			ic.command = ISDN_STAT_DHUP;
+			ic.arg = chanp->chan;
+			chanp->cs->iif.statcallb(&ic);
+		}
+		chanp->d_st->lli.l4l3(chanp->d_st, CC_DLRL, chanp->proc);
+		chanp->Flags = 0;
+		FsmEvent(&chanp->lc_d->lcfi, EV_LC_RELEASE, NULL);
 	}
-	chanp->Flags = 0;
-	chanp->is.l4.l4l3(&chanp->is, CC_DLRL, NULL);
-	FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL);
+	chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) chanp->chan);
 }
+
 /* *INDENT-OFF* */
-static struct FsmNode fnlist[] =
+static struct FsmNode fnlist[] HISAX_INITDATA =
 {
-	{ST_NULL,		EV_DIAL,		l4_prep_dialout},
-	{ST_NULL,		EV_SETUP_IND,		l4_start_dchan},
-	{ST_NULL,		EV_SHUTDOWN_D,		l4_shutdown_d},
-	{ST_NULL,		EV_DLRL,		l4_go_null},
-	{ST_OUT_WAIT_D,		EV_DLEST,		l4_do_dialout},
-	{ST_OUT_WAIT_D,		EV_DLRL,		l4_no_dchan},
-	{ST_OUT_WAIT_D,		EV_HANGUP,		l4_no_dchan},
-	{ST_IN_WAIT_D,		EV_DLEST,		l4_deliver_call},
-	{ST_IN_WAIT_D,		EV_DLRL,		l4_no_dchan_in},
-	{ST_IN_WAIT_D,		EV_HANGUP,		l4_no_dchan_in},
-	{ST_OUT_DIAL,		EV_SETUP_CNF,		l4_init_bchan_out},
-	{ST_OUT_DIAL,		EV_HANGUP,		l4_cancel_call},
-	{ST_OUT_DIAL,		EV_DISCONNECT_IND,	l4_received_d_disc},
-	{ST_OUT_DIAL,		EV_RELEASE_IND,		l4_received_d_rel},
-	{ST_OUT_DIAL,		EV_RELEASE_CNF,		l4_received_d_relcnf},
-	{ST_OUT_DIAL,		EV_NOSETUP_RSP,		l4_no_setup_rsp},
-	{ST_OUT_DIAL,		EV_SETUP_ERR,		l4_setup_err},
-	{ST_IN_WAIT_LL,		EV_SETUP_CMPL_IND,	l4_init_bchan_in},
-	{ST_IN_WAIT_LL,		EV_ACCEPTD,		l4_send_dconnect},
-	{ST_IN_WAIT_LL,		EV_HANGUP,		l4_reject_call},
-	{ST_IN_WAIT_LL,		EV_DISCONNECT_IND,	l4_received_d_disc},
-	{ST_IN_WAIT_LL,		EV_RELEASE_IND,		l4_received_d_rel},
-	{ST_IN_WAIT_LL,		EV_RELEASE_CNF,		l4_received_d_relcnf},
-	{ST_IN_ALERT_SEND,	EV_SETUP_CMPL_IND,	l4_init_bchan_in},
-	{ST_IN_ALERT_SEND,	EV_ACCEPTD,		l4_send_dconnect},
-	{ST_IN_ALERT_SEND,	EV_HANGUP,		l4_send_d_disc},
-	{ST_IN_ALERT_SEND,	EV_DISCONNECT_IND,	l4_received_d_disc},
-	{ST_IN_ALERT_SEND,	EV_RELEASE_IND,		l4_received_d_rel},
-	{ST_IN_ALERT_SEND,	EV_RELEASE_CNF,		l4_received_d_relcnf},
-	{ST_IN_WAIT_CONN_ACK,	EV_SETUP_CMPL_IND,	l4_init_bchan_in},
-	{ST_IN_WAIT_CONN_ACK,	EV_HANGUP,		l4_send_d_disc},
-	{ST_IN_WAIT_CONN_ACK,	EV_DISCONNECT_IND,	l4_received_d_disc},
-	{ST_IN_WAIT_CONN_ACK,	EV_RELEASE_IND,		l4_received_d_rel},
-	{ST_IN_WAIT_CONN_ACK,	EV_RELEASE_CNF,		l4_received_d_relcnf},
-	{ST_IN_WAIT_CONN_ACK,	EV_CONNECT_ERR,		l4_connect_err},
-	{ST_WAIT_BCONN,		EV_BC_EST,		l4_go_active},
-	{ST_WAIT_BCONN,		EV_BC_REL,		l4_send_d_disc},
-	{ST_WAIT_BCONN,		EV_HANGUP,		l4_send_d_disc},
-	{ST_WAIT_BCONN,		EV_DISCONNECT_IND,	l4_received_d_disc},
-	{ST_WAIT_BCONN,		EV_RELEASE_IND,		l4_received_d_rel},
-	{ST_WAIT_BCONN,		EV_RELEASE_CNF,		l4_received_d_relcnf},
-	{ST_ACTIVE,		EV_CINF,		l4_charge_info},
-	{ST_ACTIVE,		EV_BC_REL,		l4_released_bchan},
-	{ST_ACTIVE,		EV_HANGUP,		l4_disconn_bchan},
-	{ST_ACTIVE,		EV_DISCONNECT_IND,	l4_release_bchan},
-	{ST_ACTIVE,		EV_RELEASE_CNF,		l4_received_d_relcnf},
-	{ST_ACTIVE,		EV_RELEASE_IND,		l4_received_d_rel},
-	{ST_ACTIVE,		EV_DLRL,		l4_active_dlrl},
-	{ST_WAIT_BRELEASE,	EV_BC_REL,		l4_send_d_disc},
-	{ST_WAIT_BRELEASE,	EV_DISCONNECT_IND,	l4_received_d_disc},
-	{ST_WAIT_BRELEASE,	EV_RELEASE_CNF,		l4_received_d_relcnf},
-	{ST_WAIT_BRELEASE,	EV_RELEASE_IND,		l4_received_d_rel},
-	{ST_WAIT_BREL_DISC,	EV_BC_REL,		l4_received_d_disc},
-	{ST_WAIT_BREL_DISC,	EV_RELEASE_CNF,		l4_received_d_relcnf},
-	{ST_WAIT_BREL_DISC,	EV_RELEASE_IND,		l4_received_d_rel},
-	{ST_WAIT_DCOMMAND,	EV_HANGUP,		l4_send_d_disc},
-	{ST_WAIT_DCOMMAND,	EV_DISCONNECT_IND,	l4_received_d_disc},
-	{ST_WAIT_DCOMMAND,	EV_RELEASE_CNF,		l4_received_d_relcnf},
-	{ST_WAIT_DCOMMAND,	EV_RELEASE_IND,		l4_received_d_rel},
-	{ST_WAIT_DRELEASE,	EV_RELEASE_IND,		l4_timeout_d},
-	{ST_WAIT_DRELEASE,	EV_RELEASE_CNF,		l4_timeout_d},
-	{ST_WAIT_DRELEASE,	EV_RELEASE_ERR,		l4_timeout_d},
-	{ST_WAIT_DRELEASE,	EV_DIAL,		l4_no_dchan_ready},
-	{ST_WAIT_D_REL_CNF,	EV_RELEASE_CNF,		l4_timeout_d},
-	{ST_WAIT_D_REL_CNF,	EV_RELEASE_ERR,		l4_timeout_d},
-	{ST_WAIT_D_REL_CNF,	EV_DIAL,		l4_no_dchan_ready},
-	{ST_WAIT_DSHUTDOWN,	EV_DLRL,		l4_go_null},
-	{ST_WAIT_DSHUTDOWN,	EV_DIAL,		l4_prep_dialout},
-	{ST_WAIT_DSHUTDOWN,	EV_SETUP_IND,		l4_start_dchan},
+	{ST_NULL,		EV_DIAL,		lli_prep_dialout},
+	{ST_NULL,		EV_SETUP_IND,		lli_deliver_call},
+	{ST_NULL,		EV_SHUTDOWN_D,		lli_shutdown_d},
+	{ST_NULL,		EV_DLRL,		lli_go_null},
+	{ST_NULL,		EV_DLEST,		lli_d_established},
+	{ST_NULL,		EV_ESTABLISH,		lli_establish_d},
+	{ST_OUT_WAIT_D,		EV_DLEST,		lli_do_dialout},
+	{ST_OUT_WAIT_D,		EV_DLRL,		lli_no_dchan},
+	{ST_OUT_WAIT_D,		EV_HANGUP,		lli_no_dchan},
+	{ST_IN_WAIT_D,		EV_DLEST,		lli_do_action},
+	{ST_IN_WAIT_D,		EV_DLRL,		lli_no_dchan_in},
+	{ST_IN_WAIT_D,		EV_ACCEPTD,		lli_start_dchan},
+	{ST_IN_WAIT_D,		EV_HANGUP,		lli_start_dchan},
+	{ST_OUT_DIAL,		EV_SETUP_CNF,		lli_init_bchan_out},
+	{ST_OUT_DIAL,		EV_HANGUP,		lli_cancel_call},
+	{ST_OUT_DIAL,		EV_DISCONNECT_IND,	lli_received_d_disc},
+	{ST_OUT_DIAL,		EV_RELEASE_IND,		lli_received_d_rel},
+	{ST_OUT_DIAL,		EV_RELEASE_CNF,		lli_received_d_relcnf},
+	{ST_OUT_DIAL,		EV_NOSETUP_RSP,		lli_no_setup_rsp},
+	{ST_OUT_DIAL,		EV_SETUP_ERR,		lli_setup_err},
+	{ST_OUT_DIAL,		EV_DLRL,		lli_got_dlrl},
+	{ST_IN_WAIT_LL,		EV_DLEST,		lli_d_established},
+	{ST_IN_WAIT_LL,		EV_DLRL,		lli_d_released},
+	{ST_IN_WAIT_LL,		EV_ACCEPTD,		lli_start_dchan},
+	{ST_IN_WAIT_LL,		EV_HANGUP,		lli_start_dchan},
+	{ST_IN_WAIT_LL,		EV_DISCONNECT_IND,	lli_received_d_disc},
+	{ST_IN_WAIT_LL,		EV_RELEASE_IND,		lli_received_d_rel},
+	{ST_IN_WAIT_LL,		EV_RELEASE_CNF,		lli_received_d_relcnf},
+	{ST_IN_ALERT_SEND,	EV_SETUP_CMPL_IND,	lli_init_bchan_in},
+	{ST_IN_ALERT_SEND,	EV_ACCEPTD,		lli_send_dconnect},
+	{ST_IN_ALERT_SEND,	EV_HANGUP,		lli_send_d_disc},
+	{ST_IN_ALERT_SEND,	EV_DISCONNECT_IND,	lli_received_d_disc},
+	{ST_IN_ALERT_SEND,	EV_RELEASE_IND,		lli_received_d_rel},
+	{ST_IN_ALERT_SEND,	EV_RELEASE_CNF,		lli_received_d_relcnf},
+	{ST_IN_ALERT_SEND,	EV_DLRL,		lli_got_dlrl},
+	{ST_IN_WAIT_CONN_ACK,	EV_SETUP_CMPL_IND,	lli_init_bchan_in},
+	{ST_IN_WAIT_CONN_ACK,	EV_HANGUP,		lli_send_d_disc},
+	{ST_IN_WAIT_CONN_ACK,	EV_DISCONNECT_IND,	lli_received_d_disc},
+	{ST_IN_WAIT_CONN_ACK,	EV_RELEASE_IND,		lli_received_d_rel},
+	{ST_IN_WAIT_CONN_ACK,	EV_RELEASE_CNF,		lli_received_d_relcnf},
+	{ST_IN_WAIT_CONN_ACK,	EV_CONNECT_ERR,		lli_connect_err},
+	{ST_IN_WAIT_CONN_ACK,	EV_DLRL,		lli_got_dlrl},
+	{ST_WAIT_BCONN,		EV_BC_EST,		lli_go_active},
+	{ST_WAIT_BCONN,		EV_BC_REL,		lli_send_d_disc},
+	{ST_WAIT_BCONN,		EV_HANGUP,		lli_send_d_disc},
+	{ST_WAIT_BCONN,		EV_DISCONNECT_IND,	lli_received_d_disc},
+	{ST_WAIT_BCONN,		EV_RELEASE_IND,		lli_received_d_rel},
+	{ST_WAIT_BCONN,		EV_RELEASE_CNF,		lli_received_d_relcnf},
+	{ST_WAIT_BCONN,		EV_DLRL,		lli_got_dlrl},
+	{ST_WAIT_BCONN,		EV_CINF,		lli_charge_info},
+	{ST_ACTIVE,		EV_CINF,		lli_charge_info},
+	{ST_ACTIVE,		EV_BC_REL,		lli_released_bchan},
+	{ST_ACTIVE,		EV_HANGUP,		lli_disconn_bchan},
+	{ST_ACTIVE,		EV_DISCONNECT_IND,	lli_release_bchan},
+	{ST_ACTIVE,		EV_RELEASE_CNF,		lli_received_d_relcnf},
+	{ST_ACTIVE,		EV_RELEASE_IND,		lli_received_d_rel},
+	{ST_ACTIVE,		EV_DLRL,		lli_got_dlrl},
+	{ST_WAIT_BRELEASE,	EV_BC_REL,		lli_send_d_disc},
+	{ST_WAIT_BRELEASE,	EV_DISCONNECT_IND,	lli_received_d_disc},
+	{ST_WAIT_BRELEASE,	EV_RELEASE_CNF,		lli_received_d_relcnf},
+	{ST_WAIT_BRELEASE,	EV_RELEASE_IND,		lli_received_d_rel},
+	{ST_WAIT_BRELEASE,	EV_DLRL,		lli_got_dlrl},
+	{ST_WAIT_BREL_DISC,	EV_BC_REL,		lli_received_d_disc},
+	{ST_WAIT_BREL_DISC,	EV_RELEASE_CNF,		lli_received_d_relcnf},
+	{ST_WAIT_BREL_DISC,	EV_RELEASE_IND,		lli_received_d_rel},
+	{ST_WAIT_BREL_DISC,	EV_DLRL,		lli_got_dlrl},
+	{ST_WAIT_DCOMMAND,	EV_HANGUP,		lli_send_d_disc},
+	{ST_WAIT_DCOMMAND,	EV_DISCONNECT_IND,	lli_received_d_disc},
+	{ST_WAIT_DCOMMAND,	EV_RELEASE_CNF,		lli_received_d_relcnf},
+	{ST_WAIT_DCOMMAND,	EV_RELEASE_IND,		lli_received_d_rel},
+	{ST_WAIT_DCOMMAND,	EV_DLRL,		lli_got_dlrl},
+	{ST_WAIT_DRELEASE,	EV_RELEASE_IND,		lli_timeout_d},
+	{ST_WAIT_DRELEASE,	EV_RELEASE_CNF,		lli_timeout_d},
+	{ST_WAIT_DRELEASE,	EV_RELEASE_ERR,		lli_timeout_d},
+	{ST_WAIT_DRELEASE,	EV_DIAL,		lli_no_dchan_ready},
+	{ST_WAIT_DRELEASE,	EV_DLRL,		lli_got_dlrl},
+	{ST_WAIT_D_REL_CNF,	EV_RELEASE_CNF,		lli_timeout_d},
+	{ST_WAIT_D_REL_CNF,	EV_RELEASE_ERR,		lli_timeout_d},
+/* ETS 300-104 16.1 */
+	{ST_WAIT_D_REL_CNF,     EV_RELEASE_IND,         lli_timeout_d},
+	{ST_WAIT_D_REL_CNF,	EV_DIAL,		lli_no_dchan_ready},
+	{ST_WAIT_D_REL_CNF,	EV_DLRL,		lli_got_dlrl},
+	{ST_WAIT_DSHUTDOWN,	EV_DLRL,		lli_go_null},
+	{ST_WAIT_DSHUTDOWN,	EV_DLEST,		lli_d_established},
+	{ST_WAIT_DSHUTDOWN,	EV_DIAL,		lli_prep_dialout},
+	{ST_WAIT_DSHUTDOWN,	EV_SETUP_IND,		lli_deliver_call},
 };
 /* *INDENT-ON* */
 
 
-
-
 #define FNCOUNT (sizeof(fnlist)/sizeof(struct FsmNode))
 
 static void
-lc_r1(struct FsmInst *fi, int event, void *arg)
+lc_activate_l1(struct FsmInst *fi, int event, void *arg)
 {
 	struct LcFsm *lf = fi->userdata;
 
+	FsmDelTimer(&lf->act_timer, 50);
 	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);
+	/* This timeout is to avoid a hang if no L1 activation is possible */
+	FsmAddTimer(&lf->act_timer, 30000, EV_LC_TIMER, NULL, 50);
+	lf->st->ma.manl1(lf->st, PH_ACTIVATE_REQ, NULL);
+}
 
+static void
+lc_activated_from_l1(struct FsmInst *fi, int event, void *arg)
+{
+	struct LcFsm *lf = fi->userdata;
+
+	if (lf->l2_establish)
+		FsmChangeState(fi, ST_LC_DELAY);
+	else {
+		FsmChangeState(fi, ST_LC_CONNECTED);
+		lf->lccall(lf, LC_ESTABLISH, NULL);
+	}
 }
 
 static void
-lc_r6(struct FsmInst *fi, int event, void *arg)
+lc_l1_activated(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);
+	/* This timer is needed for delay the first paket on a channel
+	   to be shure that the other side is ready too */
+	if (lf->delay)
+		FsmAddTimer(&lf->act_timer, lf->delay, EV_LC_TIMER, NULL, 51);
+	else
+		FsmEvent(fi, EV_LC_TIMER, NULL);
 }
 
 static void
-lc_r2(struct FsmInst *fi, int event, void *arg)
+lc_start_l2(struct FsmInst *fi, int event, void *arg)
 {
 	struct LcFsm *lf = fi->userdata;
 
-	if (lf->l2_establish) {
+/*	if (!lf->st->l1.act_state)
+		lf->st->l1.act_state = 2;
+*/	if (lf->l2_establish) {
 		FsmChangeState(fi, ST_LC_ESTABLISH_WAIT);
 		if (lf->l2_start)
 			lf->st->ma.manl2(lf->st, DL_ESTABLISH, NULL);
@@ -1124,107 +1260,97 @@
 }
 
 static void
-lc_r3(struct FsmInst *fi, int event, void *arg)
+lc_connected(struct FsmInst *fi, int event, void *arg)
 {
 	struct LcFsm *lf = fi->userdata;
 
+	FsmDelTimer(&lf->act_timer, 50);
 	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)
+lc_release_l2(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->st->ma.manl1(lf->st, PH_DEACTIVATE_REQ, NULL);
 		lf->lccall(lf, LC_RELEASE, NULL);
 	}
 }
 
 static void
-lc_r4_1(struct FsmInst *fi, int event, void *arg)
+lc_l2_released(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);
+	FsmChangeState(fi, ST_LC_RELEASE_WAIT);
+	FsmDelTimer(&lf->act_timer, 51);
+	/* This delay is needed for send out the UA frame before
+	 * PH_DEACTIVATE the interface
+	 */
+	FsmAddTimer(&lf->act_timer, 20, EV_LC_TIMER, NULL, 54);
 }
 
 static void
-lc_r5_1(struct FsmInst *fi, int event, void *arg)
+lc_release_l1(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);
+	FsmDelTimer(&lf->act_timer, 54);
+	FsmChangeState(fi, ST_LC_NULL);
+	lf->st->ma.manl1(lf->st, PH_DEACTIVATE_REQ, NULL);
+	lf->lccall(lf, LC_RELEASE, NULL);
 }
 
 static void
-lc_r5(struct FsmInst *fi, int event, void *arg)
+lc_l1_deactivated(struct FsmInst *fi, int event, void *arg)
 {
 	struct LcFsm *lf = fi->userdata;
 
 	FsmDelTimer(&lf->act_timer, 54);
 	FsmChangeState(fi, ST_LC_NULL);
-	lf->st->ma.manl1(lf->st, PH_DEACTIVATE, NULL);
 	lf->lccall(lf, LC_RELEASE, NULL);
 }
 /* *INDENT-OFF* */
-static struct FsmNode LcFnList[] =
+static struct FsmNode LcFnList[] HISAX_INITDATA =
 {
-	{ST_LC_NULL,  		EV_LC_ESTABLISH,	lc_r1},
-	{ST_LC_ACTIVATE_WAIT,	EV_LC_PH_ACTIVATE,	lc_r6},
-	{ST_LC_DELAY,		EV_LC_TIMER,		lc_r2},
-	{ST_LC_DELAY,		EV_LC_DL_ESTABLISH,	lc_r3},
-	{ST_LC_ESTABLISH_WAIT,	EV_LC_DL_ESTABLISH,	lc_r3},
-	{ST_LC_ESTABLISH_WAIT,	EV_LC_RELEASE,		lc_r5},
-	{ST_LC_CONNECTED,	EV_LC_FLUSH,		lc_r7},
-	{ST_LC_CONNECTED,	EV_LC_RELEASE,		lc_r4},
-	{ST_LC_CONNECTED,	EV_LC_DL_RELEASE,	lc_r5_1},
-	{ST_LC_FLUSH_WAIT,	EV_LC_DL_FLUSH,		lc_r4_1},
-	{ST_LC_FLUSH_DELAY,	EV_LC_TIMER,		lc_r4},
-	{ST_LC_RELEASE_WAIT,	EV_LC_DL_RELEASE,	lc_r5},
-	{ST_LC_RELEASE_WAIT,	EV_LC_TIMER,		lc_r5},
-	{ST_LC_ACTIVATE_WAIT,	EV_LC_TIMER,		lc_r5},
-	{ST_LC_ESTABLISH_WAIT,	EV_LC_DL_RELEASE,	lc_r5},
+	{ST_LC_NULL,  		EV_LC_ESTABLISH,	lc_activate_l1},
+	{ST_LC_NULL,  		EV_LC_PH_ACTIVATE,	lc_activated_from_l1},
+	{ST_LC_NULL,  		EV_LC_DL_ESTABLISH,	lc_connected},
+	{ST_LC_ACTIVATE_WAIT,	EV_LC_PH_ACTIVATE,	lc_l1_activated},
+	{ST_LC_ACTIVATE_WAIT,	EV_LC_TIMER,		lc_release_l1},
+	{ST_LC_ACTIVATE_WAIT,  	EV_LC_PH_DEACTIVATE,	lc_l1_deactivated},
+	{ST_LC_DELAY,  		EV_LC_ESTABLISH,	lc_start_l2},
+	{ST_LC_DELAY,		EV_LC_TIMER,		lc_start_l2},
+	{ST_LC_DELAY,		EV_LC_DL_ESTABLISH,	lc_connected},
+	{ST_LC_DELAY,  		EV_LC_PH_DEACTIVATE,	lc_l1_deactivated},
+	{ST_LC_ESTABLISH_WAIT,	EV_LC_DL_ESTABLISH,	lc_connected},
+	{ST_LC_ESTABLISH_WAIT,	EV_LC_RELEASE,		lc_release_l1},
+	{ST_LC_ESTABLISH_WAIT,	EV_LC_DL_RELEASE,	lc_release_l1},
+	{ST_LC_ESTABLISH_WAIT,  EV_LC_PH_DEACTIVATE,	lc_l1_deactivated},
+	{ST_LC_CONNECTED,	EV_LC_ESTABLISH,	lc_connected},
+	{ST_LC_CONNECTED,	EV_LC_RELEASE,		lc_release_l2},
+	{ST_LC_CONNECTED,	EV_LC_DL_RELEASE,	lc_l2_released},
+	{ST_LC_CONNECTED,  	EV_LC_PH_DEACTIVATE,	lc_l1_deactivated},
+	{ST_LC_FLUSH_WAIT,	EV_LC_TIMER,		lc_release_l2},
+	{ST_LC_FLUSH_WAIT,  	EV_LC_PH_DEACTIVATE,	lc_l1_deactivated},
+	{ST_LC_RELEASE_WAIT,	EV_LC_DL_RELEASE,	lc_release_l1},
+	{ST_LC_RELEASE_WAIT,	EV_LC_TIMER,		lc_release_l1},
+	{ST_LC_FLUSH_WAIT,  	EV_LC_PH_DEACTIVATE,	lc_l1_deactivated},
 };
 /* *INDENT-ON* */
 
 
-
-
-
-
-
-
-
-
 #define LC_FN_COUNT (sizeof(LcFnList)/sizeof(struct FsmNode))
 
-void
-CallcNew(void)
+HISAX_INITFUNC(void
+CallcNew(void))
 {
 	callcfsm.state_count = STATE_COUNT;
 	callcfsm.event_count = EVENT_COUNT;
@@ -1247,17 +1373,11 @@
 }
 
 static void
-release_ds(struct Channel *chanp)
+release_b_st(struct Channel *chanp)
 {
-	struct PStack *st = &chanp->ds;
-	struct IsdnCardState *sp;
-	struct HscxState *hsp;
-
-	sp = st->l1.hardware;
-	hsp = sp->hs + chanp->hscx;
-
-	close_hscxstate(hsp);
+	struct PStack *st = chanp->b_st;
 
+	chanp->bcs->BC_Close(chanp->bcs);
 	switch (chanp->l2_active_protocol) {
 		case (ISDN_PROTO_L2_X75I):
 			releasestack_isdnl2(st);
@@ -1268,90 +1388,145 @@
 			break;
 	}
 	/* Reset B-Channel Statemachine */
-	FsmDelTimer(&chanp->lc_b.act_timer, 79);
-	FsmChangeState(&chanp->lc_b.lcfi, ST_LC_NULL);
+	FsmDelTimer(&chanp->lc_b->act_timer, 79);
+	FsmChangeState(&chanp->lc_b->lcfi, ST_LC_NULL);
 }
 
 static void
-cc_l1man(struct PStack *st, int pr, void *arg)
+dc_l1man(struct PStack *st, int pr, void *arg)
 {
-	struct Channel *chanp = (struct Channel *) st->l4.userdata;
+	struct Channel *chanp;
 
+	chanp = (struct Channel *) st->lli.userdata;
 	switch (pr) {
-		case (PH_ACTIVATE):
-			FsmEvent(&chanp->lc_d.lcfi, EV_LC_PH_ACTIVATE, NULL);
+		case (PH_ACTIVATE_CNF):
+		case (PH_ACTIVATE_IND):
+			FsmEvent(&chanp->lc_d->lcfi, EV_LC_PH_ACTIVATE, NULL);
 			break;
-		case (PH_DEACTIVATE):
-			FsmEvent(&chanp->lc_d.lcfi, EV_LC_PH_DEACTIVATE, NULL);
+		case (PH_DEACTIVATE_IND):
+			FsmEvent(&chanp->lc_d->lcfi, EV_LC_PH_DEACTIVATE, NULL);
 			break;
 	}
 }
 
 static void
-cc_l2man(struct PStack *st, int pr, void *arg)
+dc_l2man(struct PStack *st, int pr, void *arg)
 {
-	struct Channel *chanp = (struct Channel *) st->l4.userdata;
+	struct Channel *chanp = (struct Channel *) st->lli.userdata;
 
 	switch (pr) {
 		case (DL_ESTABLISH):
-			FsmEvent(&chanp->lc_d.lcfi, EV_LC_DL_ESTABLISH, NULL);
+			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);
+			FsmEvent(&chanp->lc_d->lcfi, EV_LC_DL_RELEASE, NULL);
 			break;
 	}
 }
 
 static void
-dcc_l1man(struct PStack *st, int pr, void *arg)
+bc_l1man(struct PStack *st, int pr, void *arg)
 {
-	struct Channel *chanp = (struct Channel *) st->l4.userdata;
+	struct Channel *chanp = (struct Channel *) st->lli.userdata;
 
 	switch (pr) {
-		case (PH_ACTIVATE):
-			FsmEvent(&chanp->lc_b.lcfi, EV_LC_PH_ACTIVATE, NULL);
+		case (PH_ACTIVATE_IND):
+		case (PH_ACTIVATE_CNF):
+			FsmEvent(&chanp->lc_b->lcfi, EV_LC_PH_ACTIVATE, NULL);
 			break;
-		case (PH_DEACTIVATE):
-			FsmEvent(&chanp->lc_b.lcfi, EV_LC_PH_DEACTIVATE, NULL);
+		case (PH_DEACTIVATE_IND):
+			FsmEvent(&chanp->lc_b->lcfi, EV_LC_PH_DEACTIVATE, NULL);
 			break;
 	}
 }
 
 static void
-dcc_l2man(struct PStack *st, int pr, void *arg)
+bc_l2man(struct PStack *st, int pr, void *arg)
 {
-	struct Channel *chanp = (struct Channel *) st->l4.userdata;
+	struct Channel *chanp = (struct Channel *) st->lli.userdata;
 
 	switch (pr) {
 		case (DL_ESTABLISH):
-			FsmEvent(&chanp->lc_b.lcfi, EV_LC_DL_ESTABLISH, NULL);
+			FsmEvent(&chanp->lc_b->lcfi, EV_LC_DL_ESTABLISH, NULL);
 			break;
 		case (DL_RELEASE):
-			FsmEvent(&chanp->lc_b.lcfi, EV_LC_DL_RELEASE, NULL);
+			FsmEvent(&chanp->lc_b->lcfi, EV_LC_DL_RELEASE, NULL);
 			break;
 	}
 }
 
-static void
-l2tei_dummy(struct PStack *st, int pr, void *arg)
+struct Channel
+*selectfreechannel(struct PStack *st)
 {
-	struct Channel *chanp = (struct Channel *) st->l4.userdata;
-	char tmp[64], tm[32];
+	struct IsdnCardState *cs = st->l1.hardware;
+	struct Channel *chanp = st->lli.userdata;
+	int i;
 
-	jiftime(tm, jiffies);
-	sprintf(tmp, "%s Channel %d Warning! Dummy l2tei called pr=%d\n", tm, chanp->chan, pr);
-	HiSax_putstatus(chanp->sp, tmp);
+	if (test_bit(FLG_TWO_DCHAN, &cs->HW_Flags))
+		i=1;
+	else
+		i=0;
+	while (i<2) {
+		if (chanp->fi.state == ST_NULL)
+			return (chanp);
+		chanp++;
+		i++;
+	}		
+	return (NULL);
+}
+
+int
+is_activ(struct PStack *st)
+{
+	struct IsdnCardState *cs = st->l1.hardware;
+	struct Channel *chanp = st->lli.userdata;
+	int i;
+
+	if (test_bit(FLG_TWO_DCHAN, &cs->HW_Flags))
+		i=1;
+	else
+		i=0;
+	while (i<2) {
+		if (test_bit(FLG_ESTAB_D, &chanp->Flags))
+			return (1);
+		chanp++;
+		i++;
+	}		
+	return (0);
 }
 
 static void
-ll_handler(struct PStack *st, int pr, void *arg)
+ll_handler(struct l3_process *pc, int pr, void *arg)
 {
-	struct Channel *chanp = (struct Channel *) st->l4.userdata;
+	struct Channel *chanp;
 	char tmp[64], tm[32];
 
+	if (pr == CC_SETUP_IND) {
+		if (!(chanp = selectfreechannel(pc->st))) {
+			pc->st->lli.l4l3(pc->st, CC_DLRL, pc);
+			return;
+		} else {
+			chanp->proc = pc;
+			pc->chan = chanp;
+			FsmEvent(&chanp->fi, EV_SETUP_IND, NULL);
+			return;
+		}
+	} else if (pr == CC_ESTABLISH) {
+		if (is_activ(pc->st)) {
+			pc->st->lli.l4l3(pc->st, CC_ESTABLISH, pc);
+			return;
+		} else if (!(chanp = selectfreechannel(pc->st))) {
+			pc->st->lli.l4l3(pc->st, CC_DLRL, pc);
+			return;
+		} else {
+			chanp->proc = pc;
+			FsmEvent(&chanp->fi, EV_ESTABLISH, NULL);
+			return;
+		}
+
+			
+	}
+	chanp = pc->chan;
 	switch (pr) {
 		case (CC_DISCONNECT_IND):
 			FsmEvent(&chanp->fi, EV_DISCONNECT_IND, NULL);
@@ -1359,9 +1534,6 @@
 		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;
@@ -1386,49 +1558,51 @@
 		case (CC_RELEASE_ERR):
 			FsmEvent(&chanp->fi, EV_RELEASE_ERR, NULL);
 			break;
+		case (CC_PROCEEDING_IND):
+		case (CC_ALERTING_IND):
+			break;
 		default:
-			if (chanp->debug & 2048) {
+			if (chanp->debug & 0x800) {
 				jiftime(tm, jiffies);
 				sprintf(tmp, "%s Channel %d L3->L4 unknown primitiv %d\n",
 					tm, chanp->chan, pr);
-				HiSax_putstatus(chanp->sp, tmp);
+				HiSax_putstatus(chanp->cs, tmp);
 			}
 	}
 }
 
 static void
-init_is(struct Channel *chanp, unsigned int ces)
+init_d_st(struct Channel *chanp)
 {
-	struct PStack *st = &chanp->is;
-	struct IsdnCardState *sp = chanp->sp;
+	struct PStack *st = chanp->d_st;
+	struct IsdnCardState *cs = chanp->cs;
 	char tmp[128];
 
-	setstack_HiSax(st, sp);
+	HiSax_addlist(cs, st);
+	setstack_HiSax(st, cs);
 	st->l2.sap = 0;
-	st->l2.tei = 255;
-	st->l2.ces = ces;
-	st->l2.extended = !0;
-	st->l2.laptype = LAPD;
+	st->l2.tei = -1;
+	st->l2.flag = 0;
+	test_and_set_bit(FLG_MOD128, &st->l2.flag);
+	test_and_set_bit(FLG_LAPD, &st->l2.flag);
+	test_and_set_bit(FLG_ORIG, &st->l2.flag);
+	st->l2.maxlen = MAX_DFRAME_LEN;
 	st->l2.window = 1;
-	st->l2.orig = !0;
-	st->l2.t200 = 1000;	/* 1000 milliseconds  */
-	if (st->protocol == ISDN_PTYPE_1TR6) {
-		st->l2.n200 = 3;	/* try 3 times        */
-		st->l2.t203 = 10000;	/* 10000 milliseconds */
-	} else {
-		st->l2.n200 = 4;	/* try 4 times        */
-		st->l2.t203 = 5000;	/* 5000 milliseconds  */
-	}
+	st->l2.T200 = 1000;	/* 1000 milliseconds  */
+	st->l2.N200 = 3;	/* try 3 times        */
+	if (st->protocol == ISDN_PTYPE_1TR6)
+		st->l2.T203 = 10000;	/* 10000 milliseconds */
+	else
+		st->l2.T203 = 10000;	/* 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->lli.userdata = chanp;
+	st->lli.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);
+	st->l1.l1man = dc_l1man;
+	st->l2.l2man = dc_l2man;
 }
 
 static void
@@ -1439,7 +1613,7 @@
 
 	jiftime(tm, jiffies);
 	sprintf(str, "%s Channel %d callc %s\n", tm, chanp->chan, s);
-	HiSax_putstatus(chanp->sp, str);
+	HiSax_putstatus(chanp->cs, str);
 }
 
 static void
@@ -1449,8 +1623,8 @@
 	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);
+	sprintf(str, "%s Channel %d dc %s\n", tm, lf->ch->chan, s);
+	HiSax_putstatus(lf->ch->cs, str);
 }
 
 static void
@@ -1460,22 +1634,35 @@
 	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);
+	sprintf(str, "%s Channel %d bc %s\n", tm, lf->ch->chan, s);
+	HiSax_putstatus(lf->ch->cs, str);
 }
 
 static void
 lccall_d(struct LcFsm *lf, int pr, void *arg)
 {
-	struct Channel *chanp = lf->ch;
+	struct IsdnCardState *cs = lf->st->l1.hardware;
+	struct Channel *chanp;
+	int i;
 
-	switch (pr) {
-		case (LC_ESTABLISH):
-			FsmEvent(&chanp->fi, EV_DLEST, NULL);
-			break;
-		case (LC_RELEASE):
-			FsmEvent(&chanp->fi, EV_DLRL, NULL);
-			break;
+	if (test_bit(FLG_TWO_DCHAN, &cs->HW_Flags)) {
+		chanp = lf->ch;
+		i = 1;
+	} else {
+		chanp = cs->channel;
+		i = 0;
+	}
+	while (i < 2) {
+		switch (pr) {
+			case (LC_ESTABLISH):
+				FsmEvent(&chanp->fi, EV_DLEST, NULL);
+				break;
+			case (LC_RELEASE):
+				FsmEvent(&chanp->fi, EV_DLRL, NULL);
+				break;
+		}
+		chanp++;
+		i++;
 	}
 }
 
@@ -1495,20 +1682,19 @@
 }
 
 static void
-init_chan(int chan, struct IsdnCardState *csta, int hscx,
-	  unsigned int ces)
+init_chan(int chan, struct IsdnCardState *csta)
 {
 	struct Channel *chanp = csta->channel + chan;
 
-	chanp->sp = csta;
-	chanp->hscx = hscx;
+	chanp->cs = csta;
+	chanp->bcs = csta->bcs + chan;
 	chanp->chan = chan;
 	chanp->incoming = 0;
 	chanp->debug = 0;
 	chanp->Flags = 0;
 	chanp->leased = 0;
-	chanp->impair = 0;
-	init_is(chanp, ces);
+	chanp->b_st = kmalloc(sizeof(struct PStack), GFP_ATOMIC);
+	chanp->b_st->next = NULL;
 
 	chanp->fi.fsm = &callcfsm;
 	chanp->fi.state = ST_NULL;
@@ -1517,58 +1703,72 @@
 	chanp->fi.printdebug = callc_debug;
 	FsmInitTimer(&chanp->fi, &chanp->dial_timer);
 	FsmInitTimer(&chanp->fi, &chanp->drel_timer);
-
-	chanp->lc_d.lcfi.fsm = &lcfsm;
-	chanp->lc_d.lcfi.state = ST_LC_NULL;
-	chanp->lc_d.lcfi.debug = 0;
-	chanp->lc_d.lcfi.userdata = &chanp->lc_d;
-	chanp->lc_d.lcfi.printdebug = lc_debug;
-	chanp->lc_d.type = LC_D;
-	chanp->lc_d.ch = chanp;
-	chanp->lc_d.st = &chanp->is;
-	chanp->lc_d.l2_establish = !0;
-	chanp->lc_d.l2_start = !0;
-	chanp->lc_d.lccall = lccall_d;
-	FsmInitTimer(&chanp->lc_d.lcfi, &chanp->lc_d.act_timer);
-
-	chanp->lc_b.lcfi.fsm = &lcfsm;
-	chanp->lc_b.lcfi.state = ST_LC_NULL;
-	chanp->lc_b.lcfi.debug = 0;
-	chanp->lc_b.lcfi.userdata = &chanp->lc_b;
-	chanp->lc_b.lcfi.printdebug = dlc_debug;
-	chanp->lc_b.type = LC_B;
-	chanp->lc_b.ch = chanp;
-	chanp->lc_b.st = &chanp->ds;
-	chanp->lc_b.l2_establish = !0;
-	chanp->lc_b.l2_start = !0;
-	chanp->lc_b.lccall = lccall_b;
-	FsmInitTimer(&chanp->lc_b.lcfi, &chanp->lc_b.act_timer);
-	chanp->outcallref = 64;
+	if (!chan || test_bit(FLG_TWO_DCHAN, &csta->HW_Flags)) {
+		chanp->d_st = kmalloc(sizeof(struct PStack), GFP_ATOMIC);
+		chanp->d_st->next = NULL;
+		init_d_st(chanp);
+		chanp->lc_d = kmalloc(sizeof(struct LcFsm), GFP_ATOMIC);
+		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->delay = 0;
+		chanp->lc_d->ch = chanp;
+		chanp->lc_d->st = chanp->d_st;
+		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);
+	} else {
+		chanp->d_st = csta->channel->d_st;
+		chanp->lc_d = csta->channel->lc_d;
+	}
+	chanp->lc_b = kmalloc(sizeof(struct LcFsm), GFP_ATOMIC);
+	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->delay = DEFAULT_B_DELAY;
+	chanp->lc_b->ch = chanp;
+	chanp->lc_b->st = chanp->b_st;
+	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->data_open = 0;
 }
 
 int
 CallcNewChan(struct IsdnCardState *csta)
 {
-	int ces;
-
 	chancount += 2;
-	ces = randomces();
-	init_chan(0, csta, 1, ces++);
-	ces %= 0xffff;
-	init_chan(1, csta, 0, ces++);
+	init_chan(0, csta);
+	init_chan(1, csta);
 	printk(KERN_INFO "HiSax: 2 channels added\n");
+#ifdef LAYER2_WATCHING
+	printk(KERN_INFO "LAYER2 ESTABLISH\n");
+	test_and_set_bit(FLG_START_D, &csta->channel->Flags);
+	FsmEvent(&csta->channel->lc_d->lcfi, EV_LC_ESTABLISH, NULL);
+#endif
 	return (2);
 }
 
 static void
-release_is(struct Channel *chanp)
+release_d_st(struct Channel *chanp)
 {
-	struct PStack *st = &chanp->is;
+	struct PStack *st = chanp->d_st;
 
+	if (!st)
+		return;
 	releasestack_isdnl2(st);
 	releasestack_isdnl3(st);
 	HiSax_rmlist(st->l1.hardware, st);
+	kfree(st);
+	chanp->d_st = NULL;
 }
 
 void
@@ -1579,27 +1779,45 @@
 	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);
+		FsmDelTimer(&csta->channel[i].lc_d->act_timer, 77);
+		FsmDelTimer(&csta->channel[i].lc_b->act_timer, 76);
+		if (i || test_bit(FLG_TWO_DCHAN, &csta->HW_Flags))
+			release_d_st(csta->channel + i);
+		if (csta->channel[i].b_st) {
+			if (test_and_clear_bit(FLG_START_B, &csta->channel[i].Flags))
+				release_b_st(csta->channel + i);
+			kfree(csta->channel[i].b_st);
+			csta->channel[i].b_st = NULL;
+		} else
+			printk(KERN_WARNING "CallcFreeChan b_st ch%d allready freed\n", i);
+		if (csta->channel[i].lc_b) {
+			kfree(csta->channel[i].lc_b);
+			csta->channel[i].b_st = NULL;
 		}
-		release_is(csta->channel + i);
+		if (i || test_bit(FLG_TWO_DCHAN, &csta->HW_Flags)) {
+			release_d_st(csta->channel + i);
+			FsmDelTimer(&csta->channel[i].lc_d->act_timer, 77);
+			if (csta->channel[i].lc_d) {
+				kfree(csta->channel[i].lc_d);
+				csta->channel[i].d_st = NULL;
+			} else
+				printk(KERN_WARNING "CallcFreeChan lc_d ch%d allready freed\n", i);
+		} else
+			csta->channel[i].d_st = NULL;
 	}
 }
 
 static void
 lldata_handler(struct PStack *st, int pr, void *arg)
 {
-	struct Channel *chanp = (struct Channel *) st->l4.userdata;
+	struct Channel *chanp = (struct Channel *) st->lli.userdata;
 	struct sk_buff *skb = arg;
 
 	switch (pr) {
 		case (DL_DATA):
 			if (chanp->data_open)
-				chanp->sp->iif.rcvcallb_skb(chanp->sp->myid, chanp->chan, skb);
+				chanp->cs->iif.rcvcallb_skb(chanp->cs->myid, chanp->chan, skb);
 			else {
-				SET_SKB_FREE(skb);
 				dev_kfree_skb(skb);
 			}
 			break;
@@ -1613,16 +1831,22 @@
 static void
 lltrans_handler(struct PStack *st, int pr, void *arg)
 {
-	struct Channel *chanp = (struct Channel *) st->l4.userdata;
+	struct Channel *chanp = (struct Channel *) st->lli.userdata;
 	struct sk_buff *skb = arg;
 
 	switch (pr) {
-		case (PH_DATA):
+		case (PH_DATA_IND):
 			if (chanp->data_open)
-				chanp->sp->iif.rcvcallb_skb(chanp->sp->myid, chanp->chan, skb);
+				chanp->cs->iif.rcvcallb_skb(chanp->cs->myid, chanp->chan, skb);
 			else {
-				SET_SKB_FREE(skb);
-				dev_kfree_skb(skb);
+				if (chanp->lc_b->lcfi.state == ST_LC_DELAY)
+					FsmEvent(&chanp->lc_b->lcfi, EV_LC_DL_ESTABLISH, NULL);
+				if (chanp->data_open) {
+					link_debug(chanp, "channel now open", 0);
+					chanp->cs->iif.rcvcallb_skb(chanp->cs->myid,
+						chanp->chan, skb);
+				} else
+					dev_kfree_skb(skb);
 			}
 			break;
 		default:
@@ -1633,73 +1857,79 @@
 }
 
 static void
-ll_writewakeup(struct PStack *st)
+ll_writewakeup(struct PStack *st, int len)
 {
-	struct Channel *chanp = st->l4.userdata;
+	struct Channel *chanp = st->lli.userdata;
 	isdn_ctrl ic;
 
-	ic.driver = chanp->sp->myid;
+	ic.driver = chanp->cs->myid;
 	ic.command = ISDN_STAT_BSENT;
 	ic.arg = chanp->chan;
-	chanp->sp->iif.statcallb(&ic);
+	ic.parm.length = len;
+	chanp->cs->iif.statcallb(&ic);
 }
 
 static int
-init_ds(struct Channel *chanp, int incoming)
+init_b_st(struct Channel *chanp, int incoming)
 {
-	struct PStack *st = &chanp->ds;
-	struct IsdnCardState *sp = chanp->sp;
-	struct HscxState *hsp = sp->hs + chanp->hscx;
+	struct PStack *st = chanp->b_st;
+	struct IsdnCardState *cs = chanp->cs;
 	char tmp[128];
 
-	st->l1.hardware = sp;
-
-	hsp->mode = 2;
-
-	if (setstack_hscx(st, hsp))
+	st->l1.hardware = cs;
+	chanp->bcs->mode = 2;
+	if (chanp->bcs->BC_SetStack(st, chanp->bcs))
 		return (-1);
-
-	st->l2.extended = 0;
-	st->l2.laptype = LAPB;
-	st->l2.orig = !incoming;
-	st->l2.t200 = 1000;	/* 1000 milliseconds */
+	st->l2.flag = 0;
+	test_and_set_bit(FLG_LAPB, &st->l2.flag);
+	st->l2.maxlen = MAX_DATA_SIZE;
+	if (!incoming)
+		test_and_set_bit(FLG_ORIG, &st->l2.flag);
+	st->l2.T200 = 1000;	/* 1000 milliseconds */
 	st->l2.window = 7;
-	st->l2.n200 = 4;	/* try 4 times       */
-	st->l2.t203 = 5000;	/* 5000 milliseconds */
-
+	st->l2.N200 = 4;	/* try 4 times       */
+	st->l2.T203 = 5000;	/* 5000 milliseconds */
 	st->l3.debug = 0;
 	switch (chanp->l2_active_protocol) {
 		case (ISDN_PROTO_L2_X75I):
 			sprintf(tmp, "Channel %d x.75", chanp->chan);
 			setstack_isdnl2(st, tmp);
 			st->l2.l2l3 = lldata_handler;
-			st->l1.l1man = dcc_l1man;
-			st->l2.l2man = dcc_l2man;
-			st->l2.l2tei = l2tei_dummy;
-			st->l4.userdata = chanp;
-			st->l4.l1writewakeup = NULL;
-			st->l4.l2writewakeup = ll_writewakeup;
+			st->l1.l1man = bc_l1man;
+			st->l2.l2man = bc_l2man;
+			st->lli.userdata = chanp;
+			st->lli.l1writewakeup = NULL;
+			st->lli.l2writewakeup = ll_writewakeup;
 			st->l2.l2m.debug = chanp->debug & 16;
 			st->l2.debug = chanp->debug & 64;
 			st->ma.manl2(st, MDL_NOTEIPROC, NULL);
-			st->l1.hscxmode = 2;	/* Packet-Mode ? */
-			st->l1.hscxchannel = chanp->para.bchannel - 1;
+			st->l1.mode = L1_MODE_HDLC;
+			if (chanp->leased)
+				st->l1.bc = chanp->chan & 1;
+			else
+				st->l1.bc = chanp->proc->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;
+			st->l1.l1man = bc_l1man;
+			st->lli.userdata = chanp;
+			st->lli.l1writewakeup = ll_writewakeup;
+			st->l1.mode = L1_MODE_HDLC;
+			if (chanp->leased)
+				st->l1.bc = chanp->chan & 1;
+			else
+				st->l1.bc = chanp->proc->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;
+			st->l1.l1man = bc_l1man;
+			st->lli.userdata = chanp;
+			st->lli.l1writewakeup = ll_writewakeup;
+			st->l1.mode = L1_MODE_TRANS;
+			if (chanp->leased)
+				st->l1.bc = chanp->chan & 1;
+			else
+				st->l1.bc = chanp->proc->para.bchannel - 1;
 			break;
 	}
 	return (0);
@@ -1719,15 +1949,17 @@
 	for (i = 0; i < 2; i++) {
 		chanp[i].debug = debugflags;
 		chanp[i].fi.debug = debugflags & 2;
-		chanp[i].is.l2.l2m.debug = debugflags & 8;
-		chanp[i].ds.l2.l2m.debug = debugflags & 16;
-		chanp[i].is.l2.debug = debugflags & 32;
-		chanp[i].ds.l2.debug = debugflags & 64;
-		chanp[i].lc_d.lcfi.debug = debugflags & 128;
-		chanp[i].lc_b.lcfi.debug = debugflags & 256;
+		chanp[i].d_st->l2.l2m.debug = debugflags & 8;
+		chanp[i].b_st->l2.l2m.debug = debugflags & 0x10;
+		chanp[i].d_st->l2.debug = debugflags & 0x20;
+		chanp[i].b_st->l2.debug = debugflags & 0x40;
+		chanp[i].lc_d->lcfi.debug = debugflags & 0x80;
+		chanp[i].lc_b->lcfi.debug = debugflags & 0x100;
+		chanp[i].b_st->ma.tei_m.debug = debugflags & 0x200;
+		chanp[i].b_st->ma.debug = debugflags & 0x200;
+		chanp[i].d_st->l1.l1m.debug = debugflags & 0x1000;
 	}
 	csta->dlogflag = debugflags & 4;
-	csta->teistack->l2.l2m.debug = debugflags & 512;
 }
 
 int
@@ -1748,8 +1980,11 @@
 	switch (ic->command) {
 		case (ISDN_CMD_SETEAZ):
 			chanp = csta->channel + ic->arg;
-			if (chanp->debug & 1)
-				link_debug(chanp, "SETEAZ", 1);
+			if (chanp->debug & 1) {
+				sprintf(tmp, "SETEAZ card %d %s", csta->cardnr + 1,
+					ic->parm.num);
+				link_debug(chanp, tmp, 1);
+			}
 			break;
 		case (ISDN_CMD_SETL2):
 			chanp = csta->channel + (ic->arg & 0xff);
@@ -1768,9 +2003,9 @@
 				 ic->parm.setup.si1, ic->parm.setup.si2);
 				link_debug(chanp, tmp, 1);
 			}
-			chanp->para.setup = ic->parm.setup;
-			if (!strcmp(chanp->para.setup.eazmsn, "0"))
-				chanp->para.setup.eazmsn[0] = '\0';
+			chanp->setup = ic->parm.setup;
+			if (!strcmp(chanp->setup.eazmsn, "0"))
+				chanp->setup.eazmsn[0] = '\0';
 			/* this solution is dirty and may be change, if
 			 * we make a callreference based callmanager */
 			if (chanp->fi.state == ST_NULL) {
@@ -1817,7 +2052,7 @@
 		case (ISDN_CMD_LOCK):
 			HiSax_mod_inc_use_count();
 #ifdef MODULE
-			if (csta->channel[0].debug & 1024) {
+			if (csta->channel[0].debug & 0x400) {
 				jiftime(tmp, jiffies);
 				i = strlen(tmp);
 				sprintf(tmp + i, "   LOCK modcnt %lx\n", MOD_USE_COUNT);
@@ -1828,7 +2063,7 @@
 		case (ISDN_CMD_UNLOCK):
 			HiSax_mod_dec_use_count();
 #ifdef MODULE
-			if (csta->channel[0].debug & 1024) {
+			if (csta->channel[0].debug & 0x400) {
 				jiftime(tmp, jiffies);
 				i = strlen(tmp);
 				sprintf(tmp + i, " UNLOCK modcnt %lx\n", MOD_USE_COUNT);
@@ -1852,16 +2087,13 @@
 					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);
-					}
+					num = *(unsigned int *) ic->parm.num; 
+					csta->channel[0].lc_b->delay = num;
+					csta->channel[1].lc_b->delay = num;
+					sprintf(tmp, "delay card %d set to %d ms\n",
+						csta->cardnr + 1, num);
+					HiSax_putstatus(csta, tmp);
+					printk(KERN_DEBUG "HiSax: %s", tmp);
 					break;
 				case (3):
 					for (i = 0; i < *(unsigned int *) ic->parm.num; i++)
@@ -1872,35 +2104,47 @@
 						HiSax_mod_inc_use_count();
 					break;
 				case (5):	/* set card in leased mode */
-					csta->channel[0].leased = 1;
-					csta->channel[1].leased = 1;
-					sprintf(tmp, "card %d set into leased mode\n",
-						csta->cardnr + 1);
-					HiSax_putstatus(csta, tmp);
+					num = *(unsigned int *) ic->parm.num;
+					if ((num <1) || (num > 2)) {
+						sprintf(tmp, "Set LEASED wrong channel %d\n",
+							num);
+						HiSax_putstatus(csta, tmp);
+						printk(KERN_WARNING "HiSax: %s", tmp);
+					} else {
+						num--;
+						csta->channel[num].leased = 1;
+						csta->channel[num].lc_d->l2_establish = 0;
+						sprintf(tmp, "card %d channel %d set leased mode\n",
+							csta->cardnr + 1, num + 1);
+						HiSax_putstatus(csta, tmp);
+						FsmEvent(&csta->channel[num].lc_d->lcfi, EV_LC_ESTABLISH, NULL);
+					}
+					break;
+				case (6):	/* set B-channel test loop */
+					num = *(unsigned int *) ic->parm.num;
+					if (csta->stlist)
+						csta->stlist->ma.manl1(csta->stlist,
+							PH_TESTLOOP_REQ, (void *) num);
 					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);
+						csta->cardnr + 1, csta->debug);
+					HiSax_putstatus(cards[0].cs, 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;
+					csta->channel[0].d_st->l3.debug = *(unsigned int *) ic->parm.num;
+					csta->channel[1].d_st->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);
+					HiSax_putstatus(cards[0].cs, tmp);
 					printk(KERN_DEBUG "HiSax: %s", tmp);
 					break;
 				default:
@@ -1917,7 +2161,7 @@
 }
 
 int
-HiSax_writebuf_skb(int id, int chan, struct sk_buff *skb)
+HiSax_writebuf_skb(int id, int chan, int ack, struct sk_buff *skb)
 {
 	struct IsdnCardState *csta = hisax_findcard(id);
 	struct Channel *chanp;
@@ -1933,7 +2177,7 @@
 		return -ENODEV;
 	}
 	chanp = csta->channel + chan;
-	st = &chanp->ds;
+	st = chanp->b_st;
 	if (!chanp->data_open) {
 		link_debug(chanp, "writebuf: channel not open", 1);
 		return -EIO;
@@ -1945,11 +2189,11 @@
 		return -EINVAL;
 	}
 	if (len) {
-		if ((len + csta->hs[chanp->hscx].tx_cnt) > MAX_DATA_MEM) {
+		if ((len + chanp->bcs->tx_cnt) > MAX_DATA_MEM) {
 			/* Must return 0 here, since this is not an error
 			 * but a temporary lack of resources.
 			 */
-			if (chanp->debug & 2048) {
+			if (chanp->debug & 0x800) {
 				sprintf(tmp, "writebuf: no buffers for %d bytes", len);
 				link_debug(chanp, tmp, 1);
 			}
@@ -1959,12 +2203,13 @@
 		cli();
 		nskb = skb_clone(skb, GFP_ATOMIC);
 		if (nskb) {
-			if (chanp->lc_b.l2_establish) {
-				csta->hs[chanp->hscx].tx_cnt += len + st->l2.ihsize;
-				chanp->ds.l3.l3l2(&chanp->ds, DL_DATA, nskb);
-			} else {
-				csta->hs[chanp->hscx].tx_cnt += len;
-				chanp->ds.l2.l2l1(&chanp->ds, PH_DATA, nskb);
+			if (!ack)
+				nskb->pkt_type = PACKET_NOACK;
+			if (chanp->lc_b->l2_establish)
+				st->l3.l3l2(st, DL_DATA, nskb);
+			else {
+				chanp->bcs->tx_cnt += len;
+				st->l2.l2l1(st, PH_DATA_REQ, nskb);
 			}
 			dev_kfree_skb(skb);
 		} else
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/config.c linux/drivers/isdn/hisax/config.c
--- v2.1.91/linux/drivers/isdn/hisax/config.c	Fri Jan 30 11:28:07 1998
+++ linux/drivers/isdn/hisax/config.c	Wed Apr  1 16:20:58 1998
@@ -1,58 +1,53 @@
-/* $Id: config.c,v 1.15 1997/04/06 22:57:24 keil Exp $
+/* $Id: config.c,v 2.12 1998/02/11 17:28:02 keil Exp $
 
  * Author       Karsten Keil (keil@temic-ech.spacenet.de)
  *              based on the teles driver from Jan den Ouden
  *
  *
  * $Log: config.c,v $
- * Revision 1.15  1997/04/06 22:57:24  keil
- * Hisax version 2.1
+ * Revision 2.12  1998/02/11 17:28:02  keil
+ * Niccy PnP/PCI support
  *
- * Revision 1.14  1997/03/25 23:11:22  keil
- * US NI-1 protocol
+ * Revision 2.11  1998/02/09 21:26:13  keil
+ * fix export module for 2.1
  *
- * Revision 1.13  1997/03/23 21:45:49  keil
- * Add support for ELSA PCMCIA
+ * Revision 2.10  1998/02/09 18:46:05  keil
+ * Support for Sedlbauer PCMCIA (Marcus Niemann)
  *
- * Revision 1.12  1997/03/11 21:01:43  keil
- * nzproto is only used with modules
+ * Revision 2.9  1998/02/03 23:31:28  keil
+ * add AMD7930 support
  *
- * Revision 1.11  1997/02/14 12:23:12  fritz
- * Added support for new insmod parameter handling.
+ * Revision 2.8  1998/02/02 13:32:59  keil
+ * New card support
  *
- * Revision 1.10  1997/02/14 09:22:09  keil
- * Final 2.0 version
+ * Revision 2.7  1998/01/31 21:41:44  keil
+ * changes for newer 2.1 kernels
  *
- * Revision 1.9  1997/02/10 11:45:09  fritz
- * More changes for Kernel 2.1.X compatibility.
+ * Revision 2.6  1997/11/08 21:35:43  keil
+ * new l1 init
  *
- * Revision 1.8  1997/02/09 00:28:05  keil
- * new interface handling, one interface per card
- * default protocol now works again
+ * Revision 2.5  1997/11/06 17:15:08  keil
+ * New 2.1 init; PCMCIA wrapper changes
  *
- * Revision 1.7  1997/01/27 15:56:57  keil
- * Teles PCMCIA ITK ix1 micro added
+ * Revision 2.4  1997/10/29 19:07:52  keil
+ * changes for 2.1
  *
- * Revision 1.6  1997/01/21 22:17:56  keil
- * new module load syntax
+ * Revision 2.3  1997/10/01 09:21:33  fritz
+ * Removed old compatibility stuff for 2.0.X kernels.
+ * From now on, this code is for 2.1.X ONLY!
+ * Old stuff is still in the separate branch.
  *
- * Revision 1.5  1997/01/09 18:28:20  keil
- * cosmetic cleanups
+ * Revision 2.2  1997/09/11 17:24:46  keil
+ * Add new cards
  *
- * 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
+ * Revision 2.1  1997/07/27 21:41:35  keil
+ * version change
  *
+ * Revision 2.0  1997/06/26 11:06:28  keil
+ * New card and L1 interface.
+ * Eicon.Diehl Diva and Dynalink IS64PH support
  *
+ * old changes removed /KKe
  *
  */
 #include <linux/types.h>
@@ -68,16 +63,30 @@
  * { type, protocol, p0, p1, p2, NULL }
  *
  * type
- *    1 Teles 16.0      p0=irq p1=membase p2=iobase
- *    2 Teles  8.0      p0=irq p1=membase
- *    3 Teles 16.3      p0=irq p1=iobase
- *    4 Creatix PNP     p0=irq p1=IO0 (ISAC)  p2=IO1 (HSCX)
- *    5 AVM A1 (Fritz)  p0=irq p1=iobase
- *    6 ELSA PC         [p0=iobase] or nothing (autodetect)
- *    7 ELSA Quickstep  p0=irq p1=iobase
- *      ELSA PCMCIA     p0=irq p1=iobase
- *    8 Teles PCMCIA    p0=irq p1=iobase
- *    9 ITK ix1-micro   p0=irq p1=iobase
+ *    1 Teles 16.0       p0=irq p1=membase p2=iobase
+ *    2 Teles  8.0       p0=irq p1=membase
+ *    3 Teles 16.3       p0=irq p1=iobase
+ *    4 Creatix PNP      p0=irq p1=IO0 (ISAC)  p2=IO1 (HSCX)
+ *    5 AVM A1 (Fritz)   p0=irq p1=iobase
+ *    6 ELSA PC          [p0=iobase] or nothing (autodetect)
+ *    7 ELSA Quickstep   p0=irq p1=iobase
+ *    8 Teles PCMCIA     p0=irq p1=iobase
+ *    9 ITK ix1-micro    p0=irq p1=iobase
+ *   10 ELSA PCMCIA      p0=irq p1=iobase
+ *   11 Eicon.Diehl Diva p0=irq p1=iobase
+ *   12 Asuscom ISDNLink p0=irq p1=iobase
+ *   13 Teleint          p0=irq p1=iobase
+ *   14 Teles 16.3c      p0=irq p1=iobase
+ *   15 Sedlbauer speed  p0=irq p1=iobase
+ *   16 USR Sportster internal  p0=irq  p1=iobase
+ *   17 MIC card                p0=irq  p1=iobase
+ *   18 ELSA Quickstep 1000PCI  no parameter
+ *   19 Compaq ISDN S0 ISA card p0=irq  p1=IO0 (HSCX)  p2=IO1 (ISAC) p3=IO2
+ *   20 Travers Technologies NETjet PCI card
+ *   21 reserved TELES PCI
+ *   22 Sedlbauer Speed Star    p0=irq p1=iobase
+ *   23 reserved
+ *   24 Dr Neuhaus Niccy PnP/PCI card p0=irq p1=IO0 p2=IO1 (PnP only)
  *
  *
  * protocol can be either ISDN_PTYPE_EURO or ISDN_PTYPE_1TR6 or ISDN_PTYPE_NI1
@@ -85,38 +94,108 @@
  *
  */
 
-#ifdef CONFIG_HISAX_ELSA_PCC
+#ifdef CONFIG_HISAX_ELSA
 #define DEFAULT_CARD ISDN_CTYPE_ELSA
-#define DEFAULT_CFG {0,0,0}
-#endif
-#ifdef CONFIG_HISAX_ELSA_PCMCIA
-#define DEFAULT_CARD ISDN_CTYPE_ELSA_QS1000
-#define DEFAULT_CFG {3,0x2f8,0}
+#define DEFAULT_CFG {0,0,0,0}
+int elsa_init_pcmcia(void*, int, int*, int);
+EXPORT_SYMBOL(elsa_init_pcmcia);
 #endif
 #ifdef CONFIG_HISAX_AVM_A1
 #undef DEFAULT_CARD
 #undef DEFAULT_CFG
 #define DEFAULT_CARD ISDN_CTYPE_A1
-#define DEFAULT_CFG {10,0x340,0}
+#define DEFAULT_CFG {10,0x340,0,0}
 #endif
 #ifdef CONFIG_HISAX_16_3
 #undef DEFAULT_CARD
 #undef DEFAULT_CFG
 #define DEFAULT_CARD ISDN_CTYPE_16_3
-#define DEFAULT_CFG {15,0x180,0}
+#define DEFAULT_CFG {15,0x180,0,0}
 #endif
 #ifdef CONFIG_HISAX_16_0
 #undef DEFAULT_CARD
 #undef DEFAULT_CFG
 #define DEFAULT_CARD ISDN_CTYPE_16_0
-#define DEFAULT_CFG {15,0xd0000,0xd80}
+#define DEFAULT_CFG {15,0xd0000,0xd80,0}
 #endif
 
 #ifdef CONFIG_HISAX_IX1MICROR2
 #undef DEFAULT_CARD
 #undef DEFAULT_CFG
 #define DEFAULT_CARD ISDN_CTYPE_IX1MICROR2
-#define DEFAULT_CFG {5,0x390,0}
+#define DEFAULT_CFG {5,0x390,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_DIEHLDIVA
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_DIEHLDIVA
+#define DEFAULT_CFG {0,0x0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_ASUSCOM
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_ASUSCOM
+#define DEFAULT_CFG {5,0x200,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_TELEINT
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_TELEINT
+#define DEFAULT_CFG {5,0x300,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_SEDLBAUER
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_SEDLBAUER
+#define DEFAULT_CFG {11,0x270,0,0}
+int sedl_init_pcmcia(void*, int, int*, int);
+EXPORT_SYMBOL(sedl_init_pcmcia);
+#endif
+
+#ifdef CONFIG_HISAX_SPORTSTER
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_SPORTSTER
+#define DEFAULT_CFG {7,0x268,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_MIC
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_MIC
+#define DEFAULT_CFG {12,0x3e0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_NETJET
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_NETJET
+#define DEFAULT_CFG {0,0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_TELES3C
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_TELES3C
+#define DEFAULT_CFG {5,0x500,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_AMD7930
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_AMD7930
+#define DEFAULT_CFG {12,0x3e0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_NICCY
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_NICCY
+#define DEFAULT_CFG {0,0x0,0,0}
 #endif
 
 #ifdef CONFIG_HISAX_1TR6
@@ -150,7 +229,7 @@
   NULL, \
 }
 
-#define EMPTY_CARD	{0, DEFAULT_PROTO, {0, 0, 0}, NULL}
+#define EMPTY_CARD	{0, DEFAULT_PROTO, {0, 0, 0, 0}, NULL}
 
 struct IsdnCard cards[] =
 {
@@ -172,55 +251,63 @@
 	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" \
+static char HiSaxID[96] HISAX_INITDATA = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \
 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \
 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \
 "\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;
+char *HiSax_id HISAX_INITDATA = HiSaxID;
 #ifdef MODULE
 /* Variables for insmod */
-static int type[] =
+static int type[] HISAX_INITDATA =
 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-static int protocol[] =
+static int protocol[] HISAX_INITDATA =
 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-static int io[] =
+static int io[] HISAX_INITDATA =
 {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 */
-static int io0[] =
+#undef IO0_IO1
+#ifdef CONFIG_HISAX_16_3
+#define IO0_IO1
+#endif
+#ifdef CONFIG_HISAX_NICCY
+#undef IO0_IO1
+#define IO0_IO1
+#endif
+#ifdef IO0_IO1
+static int io0[] HISAX_INITDATA =
 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-static int io1[] =
+static int io1[] HISAX_INITDATA =
 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
 #endif
-static int irq[] =
+static int irq[] HISAX_INITDATA =
 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-static int mem[] =
+static int mem[] HISAX_INITDATA =
 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-static char *id = HiSaxID;
+static char *id HISAX_INITDATA = HiSaxID;
 
-#if (LINUX_VERSION_CODE > 0x020111)
 MODULE_AUTHOR("Karsten Keil");
-MODULE_PARM(type, "1-16i");
-MODULE_PARM(protocol, "1-16i");
-MODULE_PARM(io, "1-16i");
-MODULE_PARM(irq, "1-16i");
-MODULE_PARM(mem, "1-16i");
+MODULE_PARM(type, "1-3i");
+MODULE_PARM(protocol, "1-2i");
+MODULE_PARM(io, "1-8i");
+MODULE_PARM(irq, "1-2i");
+MODULE_PARM(mem, "1-12i");
 MODULE_PARM(id, "s");
 #ifdef CONFIG_HISAX_16_3	/* For Creatix/Teles PnP */
-MODULE_PARM(io0, "1-16i");
-MODULE_PARM(io1, "1-16i");
-#endif
+MODULE_PARM(io0, "1-8i");
+MODULE_PARM(io1, "1-8i");
 #endif
 
 #endif
 
+int nrcards;
+
 extern char *l1_revision;
 extern char *l2_revision;
 extern char *l3_revision;
-extern char *l4_revision;
+extern char *lli_revision;
 extern char *tei_revision;
 
-char *
-HiSax_getrev(const char *revision)
+HISAX_INITFUNC(char *
+HiSax_getrev(const char *revision))
 {
 	char *rev;
 	char *p;
@@ -234,7 +321,27 @@
 	return rev;
 }
 
-int nrcards;
+HISAX_INITFUNC(void
+HiSaxVersion(void))
+{
+	char tmp[64], rev[64];
+	char *r = rev;
+
+	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, lli_revision);
+	r += sprintf(r, "%s/", HiSax_getrev(tmp));
+	strcpy(tmp, tei_revision);
+	r += sprintf(r, "%s", HiSax_getrev(tmp));
+
+	printk(KERN_INFO "HiSax: Driver for Siemens chip set ISDN cards\n");
+	printk(KERN_INFO "HiSax: Version 2.8\n");
+	printk(KERN_INFO "HiSax: Revisions %s\n", rev);
+}
 
 void
 HiSax_mod_dec_use_count(void)
@@ -251,8 +358,8 @@
 #ifdef MODULE
 #define HiSax_init init_module
 #else
-void
-HiSax_setup(char *str, int *ints)
+__initfunc(void
+HiSax_setup(char *str, int *ints))
 {
 	int i, j, argc;
 
@@ -297,31 +404,28 @@
 }
 #endif
 
-int
-HiSax_init(void)
+__initfunc(int
+HiSax_init(void))
 {
 	int i;
-	char tmp[64], rev[64];
-	char *r = rev;
+	
 #ifdef MODULE
 	int nzproto = 0;
+#ifdef CONFIG_HISAX_ELSA
+	if (type[0] == ISDN_CTYPE_ELSA_PCMCIA) {
+		/* we have exported  and return in this case */
+		return 0;
+	}
+#endif
+#ifdef CONFIG_HISAX_SEDLBAUER
+	if (type[0] == ISDN_CTYPE_SEDLBAUER_PCMCIA) {
+		/* we have to export  and return in this case */
+		return 0;
+	}
+#endif
 #endif
+	HiSaxVersion();
 	nrcards = 0;
-	strcpy(tmp, l1_revision);
-	r += sprintf(r, "%s/", HiSax_getrev(tmp));
-	strcpy(tmp, l2_revision);
-	r += sprintf(r, "%s/", HiSax_getrev(tmp));
-	strcpy(tmp, l3_revision);
-	r += sprintf(r, "%s/", HiSax_getrev(tmp));
-	strcpy(tmp, l4_revision);
-	r += sprintf(r, "%s/", HiSax_getrev(tmp));
-	strcpy(tmp, tei_revision);
-	r += sprintf(r, "%s", HiSax_getrev(tmp));
-
-	printk(KERN_NOTICE "HiSax: Driver for Siemens chip set ISDN cards\n");
-	printk(KERN_NOTICE "HiSax: Version 2.1\n");
-	printk(KERN_NOTICE "HiSax: Revisions %s\n", rev);
-
 #ifdef MODULE
 	if (id)			/* If id= string used */
 		HiSax_id = id;
@@ -343,37 +447,44 @@
 				cards[i].para[1] = mem[i];
 				break;
 
-			case ISDN_CTYPE_16_3:
-			case ISDN_CTYPE_TELESPCMCIA:
-				cards[i].para[0] = irq[i];
-				cards[i].para[1] = io[i];
-				break;
-
-#ifdef CONFIG_HISAX_16_3	/* For Creatix/Teles PnP */
+#ifdef IO0_IO1
 			case ISDN_CTYPE_PNP:
+			case ISDN_CTYPE_NICCY:
 				cards[i].para[0] = irq[i];
 				cards[i].para[1] = io0[i];
 				cards[i].para[2] = io1[i];
 				break;
-#endif
-			case ISDN_CTYPE_A1:
+			case ISDN_CTYPE_COMPAQ_ISA:
 				cards[i].para[0] = irq[i];
-				cards[i].para[1] = io[i];
+				cards[i].para[1] = io0[i];
+				cards[i].para[2] = io1[i];
+				cards[i].para[3] = io[i];
 				break;
-
+#endif
 			case ISDN_CTYPE_ELSA:
 				cards[i].para[0] = io[i];
 				break;
-			case ISDN_CTYPE_ELSA_QS1000:
-				cards[i].para[0] = irq[i];
-				cards[i].para[1] = io[i];
-				break;
-
+			case ISDN_CTYPE_16_3:
+			case ISDN_CTYPE_TELESPCMCIA:
+			case ISDN_CTYPE_A1:
+			case ISDN_CTYPE_ELSA_PNP:
+			case ISDN_CTYPE_ELSA_PCMCIA:
 			case ISDN_CTYPE_IX1MICROR2:
+			case ISDN_CTYPE_DIEHLDIVA:
+			case ISDN_CTYPE_ASUSCOM:
+			case ISDN_CTYPE_TELEINT:
+			case ISDN_CTYPE_SEDLBAUER:
+			case ISDN_CTYPE_SEDLBAUER_PCMCIA:
+			case ISDN_CTYPE_SPORTSTER:
+			case ISDN_CTYPE_MIC:
+			case ISDN_CTYPE_TELES3C:
 				cards[i].para[0] = irq[i];
 				cards[i].para[1] = io[i];
 				break;
-
+			case ISDN_CTYPE_ELSA_PCI:
+			case ISDN_CTYPE_NETJET:
+			case ISDN_CTYPE_AMD7930:
+				break;
 		}
 	}
 	if (!nzproto) {
@@ -394,20 +505,20 @@
 
 	CallcNew();
 	Isdnl2New();
-	if (HiSax_inithardware()) {
+	TeiNew();
+	Isdnl1New();
+	if (HiSax_inithardware(NULL)) {
 		/* Install only, if at least one card found */
 		/* No symbols to export, hide all symbols */
 
 #ifdef MODULE
-#if (LINUX_VERSION_CODE < 0x020111)
-		register_symtab(NULL);
-#else
 		EXPORT_NO_SYMBOLS;
-#endif
-		printk(KERN_NOTICE "HiSax: module installed\n");
+		printk(KERN_INFO "HiSax: module installed\n");
 #endif
 		return (0);
 	} else {
+		Isdnl1Free();
+		TeiFree();
 		Isdnl2Free();
 		CallcFree();
 		return -EIO;
@@ -419,7 +530,101 @@
 cleanup_module(void)
 {
 	HiSax_closehardware();
-	printk(KERN_NOTICE "HiSax module removed\n");
+	printk(KERN_INFO "HiSax module removed\n");
 }
 
+#ifdef CONFIG_HISAX_ELSA
+int elsa_init_pcmcia(void *pcm_iob, int pcm_irq, int *busy_flag, int prot)
+{
+	int i;
+	int nzproto = 0;
+
+	nrcards = 0;
+	HiSaxVersion();
+	if (id)			/* If id= string used */
+		HiSax_id = id;
+	/* Initialize all 16 structs, even though we only accept
+	   two pcmcia cards
+	   */
+	for (i = 0; i < 16; i++) {
+		cards[i].para[0] = irq[i];
+		cards[i].para[1] = io[i];
+		cards[i].typ = type[i];
+		if (protocol[i]) {
+			cards[i].protocol = protocol[i];
+			nzproto++;
+		}
+	}
+	cards[0].para[0] = pcm_irq;
+	cards[0].para[1] = (int)pcm_iob;
+	cards[0].protocol = prot;
+	cards[0].typ = 10;
+	nzproto = 1;
+
+	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" : "");
+
+	Isdnl1New();
+	CallcNew();
+	Isdnl2New();
+	TeiNew();
+	HiSax_inithardware(busy_flag);
+	printk(KERN_NOTICE "HiSax: module installed\n");
+	return (0);
+}
+#endif
+#ifdef CONFIG_HISAX_SEDLBAUER
+int sedl_init_pcmcia(void *pcm_iob, int pcm_irq, int *busy_flag, int prot)
+{
+	int i;
+	int nzproto = 0;
+
+	nrcards = 0;
+	HiSaxVersion();
+	if (id)			/* If id= string used */
+		HiSax_id = id;
+	/* Initialize all 16 structs, even though we only accept
+	   two pcmcia cards
+	   */
+	for (i = 0; i < 16; i++) {
+		cards[i].para[0] = irq[i];
+		cards[i].para[1] = io[i];
+		cards[i].typ = type[i];
+		if (protocol[i]) {
+			cards[i].protocol = protocol[i];
+			nzproto++;
+		}
+	}
+	cards[0].para[0] = pcm_irq;
+	cards[0].para[1] = (int)pcm_iob;
+	cards[0].protocol = prot;
+	cards[0].typ = ISDN_CTYPE_SEDLBAUER_PCMCIA;
+	nzproto = 1;
+
+	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" : "");
+
+	Isdnl1New();
+	CallcNew();
+	Isdnl2New();
+	TeiNew();
+	HiSax_inithardware(busy_flag);
+	printk(KERN_NOTICE "HiSax: module installed\n");
+	return (0);
+}
 #endif
+#endif 
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/diva.c linux/drivers/isdn/hisax/diva.c
--- v2.1.91/linux/drivers/isdn/hisax/diva.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/diva.c	Wed Apr  1 16:20:58 1998
@@ -0,0 +1,465 @@
+/* $Id: diva.c,v 1.5 1998/02/02 13:29:38 keil Exp $
+
+ * diva.c     low level stuff for Eicon.Diehl Diva Family ISDN cards
+ *
+ * Author     Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ * Thanks to Eicon Technology Diehl GmbH & Co. oHG for documents and informations
+ *
+ *
+ * $Log: diva.c,v $
+ * Revision 1.5  1998/02/02 13:29:38  keil
+ * fast io
+ *
+ * Revision 1.4  1997/11/08 21:35:44  keil
+ * new l1 init
+ *
+ * Revision 1.3  1997/11/06 17:13:33  keil
+ * New 2.1 init code
+ *
+ * Revision 1.2  1997/10/29 18:55:55  keil
+ * changes for 2.1.60 (irq2dev_map)
+ *
+ * Revision 1.1  1997/09/18 17:11:20  keil
+ * first version
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include <linux/config.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include <linux/bios32.h>
+
+extern const char *CardType[];
+
+const char *Diva_revision = "$Revision: 1.5 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define DIVA_HSCX_DATA		0
+#define DIVA_HSCX_ADR		4
+#define DIVA_ISA_ISAC_DATA	2
+#define DIVA_ISA_ISAC_ADR	6
+#define DIVA_ISA_CTRL		7
+
+#define DIVA_PCI_ISAC_DATA	8
+#define DIVA_PCI_ISAC_ADR	0xc
+#define DIVA_PCI_CTRL		0x10
+
+/* SUB Types */
+#define DIVA_ISA	1
+#define DIVA_PCI	2
+
+/* PCI stuff */
+#define PCI_VENDOR_EICON_DIEHL	0x1133
+#define PCI_DIVA20PRO_ID	0xe001
+#define PCI_DIVA20_ID		0xe002
+#define PCI_DIVA20PRO_U_ID	0xe003
+#define PCI_DIVA20_U_ID		0xe004
+
+/* CTRL (Read) */
+#define DIVA_IRQ_STAT	0x01
+#define DIVA_EEPROM_SDA	0x02
+/* CTRL (Write) */
+#define DIVA_IRQ_REQ	0x01
+#define DIVA_RESET	0x08
+#define DIVA_EEPROM_CLK	0x40
+#define DIVA_PCI_LED_A	0x10
+#define DIVA_PCI_LED_B	0x20
+#define DIVA_ISA_LED_A	0x20
+#define DIVA_ISA_LED_B	0x40
+#define DIVA_IRQ_CLR	0x80
+
+static inline u_char
+readreg(unsigned int ale, unsigned int adr, u_char off)
+{
+	register u_char ret;
+	long flags;
+
+	save_flags(flags);
+	cli();
+	byteout(ale, off);
+	ret = bytein(adr);
+	restore_flags(flags);
+	return (ret);
+}
+
+static inline void
+readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	/* fifo read without cli because it's allready done  */
+
+	byteout(ale, off);
+	insb(adr, data, size);
+}
+
+
+static inline void
+writereg(unsigned int ale, unsigned int adr, u_char off, u_char data)
+{
+	long flags;
+
+	save_flags(flags);
+	cli();
+	byteout(ale, off);
+	byteout(adr, data);
+	restore_flags(flags);
+}
+
+static inline void
+writefifo(unsigned int ale, unsigned int adr, u_char off, u_char *data, int size)
+{
+	/* fifo write without cli because it's allready done  */
+	byteout(ale, off);
+	outsb(adr, data, size);
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return(readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char *data, int size)
+{
+	readfifo(cs->hw.diva.isac_adr, cs->hw.diva.isac, 0, data, size);
+}
+
+static void 
+WriteISACfifo(struct IsdnCardState *cs, u_char *data, int size)
+{
+	writefifo(cs->hw.diva.isac_adr, cs->hw.diva.isac, 0, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return(readreg(cs->hw.diva.hscx_adr,
+		cs->hw.diva.hscx, offset + (hscx ? 0x40 : 0)));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	writereg(cs->hw.diva.hscx_adr,
+		cs->hw.diva.hscx, offset + (hscx ? 0x40 : 0), value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.diva.hscx_adr, \
+		cs->hw.diva.hscx, reg + (nr ? 0x40 : 0))
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.diva.hscx_adr, \
+                cs->hw.diva.hscx, reg + (nr ? 0x40 : 0), data)
+                
+#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.diva.hscx_adr, \
+		cs->hw.diva.hscx, (nr ? 0x40 : 0), ptr, cnt)
+		
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.diva.hscx_adr, \
+		cs->hw.diva.hscx, (nr ? 0x40 : 0), ptr, cnt)
+		
+#include "hscx_irq.c"
+
+static void
+diva_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val, sval, stat = 0;
+	int cnt=8;
+
+	if (!cs) {
+		printk(KERN_WARNING "Diva: Spurious interrupt!\n");
+		return;
+	}
+	while (((sval = bytein(cs->hw.diva.ctrl)) & DIVA_IRQ_REQ) && cnt) {
+		val = readreg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_ISTA + 0x40);
+		if (val) {
+			hscx_int_main(cs, val);
+			stat |= 1;
+		}
+		val = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_ISTA);
+		if (val) {
+			isac_interrupt(cs, val);
+			stat |= 2;
+		}
+		cnt--;
+	}
+	if (!cnt)
+		printk(KERN_WARNING "Diva: IRQ LOOP\n");
+	if (stat & 1) {
+		writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK, 0xFF);
+		writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK + 0x40, 0xFF);
+		writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK, 0x0);
+		writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK + 0x40, 0x0);
+	}
+	if (stat & 2) {
+		writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_MASK, 0xFF);
+		writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_MASK, 0x0);
+	}
+}
+
+void
+release_io_diva(struct IsdnCardState *cs)
+{
+	int bytecnt;
+	
+	del_timer(&cs->hw.diva.tl);
+	if (cs->subtyp == DIVA_ISA)
+		bytecnt = 8;
+	else
+		bytecnt = 32;
+	if (cs->hw.diva.cfg_reg) {
+		byteout(cs->hw.diva.ctrl, 0); /* LED off, Reset */
+		release_region(cs->hw.diva.cfg_reg, bytecnt);
+	}
+}
+
+static void
+reset_diva(struct IsdnCardState *cs)
+{
+	long flags;
+
+	save_flags(flags);
+	sti();
+	cs->hw.diva.ctrl_reg = 0;        /* Reset On */
+	byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg);
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + (10 * HZ) / 1000;	/* Timeout 10ms */
+	schedule();
+	cs->hw.diva.ctrl_reg |= DIVA_RESET;  /* Reset Off */
+	byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg);
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + (10 * HZ) / 1000;	/* Timeout 10ms */
+	schedule();
+	if (cs->subtyp == DIVA_ISA)
+		cs->hw.diva.ctrl_reg |= DIVA_ISA_LED_A;
+	else
+		cs->hw.diva.ctrl_reg |= DIVA_PCI_LED_A;
+	byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg);
+}
+
+#define DIVA_ASSIGN 1
+
+static void
+diva_led_handler(struct IsdnCardState *cs)
+{
+	int blink = 0;
+
+	del_timer(&cs->hw.diva.tl);
+	if (cs->hw.diva.status & DIVA_ASSIGN)
+		cs->hw.diva.ctrl_reg |= (DIVA_ISA == cs->subtyp) ?
+			DIVA_ISA_LED_A : DIVA_PCI_LED_A;
+	else {
+		cs->hw.diva.ctrl_reg ^= (DIVA_ISA == cs->subtyp) ?
+			DIVA_ISA_LED_A : DIVA_PCI_LED_A;
+		blink = 250;
+	}
+	if (cs->hw.diva.status & 0xf000)
+		cs->hw.diva.ctrl_reg |= (DIVA_ISA == cs->subtyp) ?
+			DIVA_ISA_LED_B : DIVA_PCI_LED_B;
+	else if (cs->hw.diva.status & 0x0f00) { 
+		cs->hw.diva.ctrl_reg ^= (DIVA_ISA == cs->subtyp) ?
+			DIVA_ISA_LED_B : DIVA_PCI_LED_B;
+		blink = 500;
+	} else
+		cs->hw.diva.ctrl_reg &= ~((DIVA_ISA == cs->subtyp) ?
+			DIVA_ISA_LED_B : DIVA_PCI_LED_B);
+	
+	byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg);
+	if (blink) {
+		init_timer(&cs->hw.diva.tl);
+		cs->hw.diva.tl.expires = jiffies + ((blink * HZ) / 1000);
+		add_timer(&cs->hw.diva.tl);
+	}
+}
+
+static int
+Diva_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	switch (mt) {
+		case CARD_RESET:
+			reset_diva(cs);
+			return(0);
+		case CARD_RELEASE:
+			release_io_diva(cs);
+			return(0);
+		case CARD_SETIRQ:
+			return(request_irq(cs->irq, &diva_interrupt,
+					I4L_IRQ_FLAG, "HiSax", cs));
+		case CARD_INIT:
+			clear_pending_isac_ints(cs);
+			clear_pending_hscx_ints(cs);
+			initisac(cs);
+			inithscx(cs);
+			return(0);
+		case CARD_TEST:
+			return(0);
+		case MDL_REMOVE_REQ:
+			cs->hw.diva.status = 0;
+			break;
+		case MDL_ASSIGN_REQ:
+			cs->hw.diva.status |= DIVA_ASSIGN;
+			break;
+		case MDL_INFO_SETUP:
+			if ((int)arg) 
+				cs->hw.diva.status |=  0x0200;
+			else
+				cs->hw.diva.status |=  0x0100;
+			break;
+		case MDL_INFO_CONN:
+			if ((int)arg) 
+				cs->hw.diva.status |=  0x2000;
+			else
+				cs->hw.diva.status |=  0x1000;
+			break;
+		case MDL_INFO_REL:
+			if ((int)arg) {
+				cs->hw.diva.status &=  ~0x2000;
+				cs->hw.diva.status &=  ~0x0200;
+			} else {
+				cs->hw.diva.status &=  ~0x1000;
+				cs->hw.diva.status &=  ~0x0100;
+			}
+			break;
+	}
+	diva_led_handler(cs);
+	return(0);
+}
+
+
+
+static 	int pci_index __initdata = 0;
+
+__initfunc(int
+setup_diva(struct IsdnCard *card))
+{
+	int bytecnt;
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, Diva_revision);
+	printk(KERN_INFO "HiSax: Eicon.Diehl Diva driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_DIEHLDIVA)
+		return(0);
+	cs->hw.diva.status = 0;
+	if (card->para[1]) {
+		cs->subtyp = DIVA_ISA;
+		cs->hw.diva.ctrl_reg = 0;
+		cs->hw.diva.cfg_reg = card->para[1];
+		cs->hw.diva.ctrl = card->para[1] + DIVA_ISA_CTRL;
+		cs->hw.diva.isac = card->para[1] + DIVA_ISA_ISAC_DATA;
+		cs->hw.diva.hscx = card->para[1] + DIVA_HSCX_DATA;
+		cs->hw.diva.isac_adr = card->para[1] + DIVA_ISA_ISAC_ADR;
+		cs->hw.diva.hscx_adr = card->para[1] + DIVA_HSCX_ADR;
+		cs->irq = card->para[0];
+		bytecnt = 8;
+	} else {
+#if CONFIG_PCI
+		u_char pci_bus, pci_device_fn, pci_irq;
+		u_int pci_ioaddr;
+
+		cs->subtyp = 0;
+		for (; pci_index < 0xff; pci_index++) {
+			if (pcibios_find_device(PCI_VENDOR_EICON_DIEHL,
+			   PCI_DIVA20_ID, pci_index, &pci_bus, &pci_device_fn)
+			   == PCIBIOS_SUCCESSFUL)
+				cs->subtyp = DIVA_PCI;
+			else if (pcibios_find_device(PCI_VENDOR_EICON_DIEHL,
+			   PCI_DIVA20_ID, pci_index, &pci_bus, &pci_device_fn)
+			   == PCIBIOS_SUCCESSFUL)
+			   	cs->subtyp = DIVA_PCI;
+			else
+				break;
+			/* get IRQ */
+			pcibios_read_config_byte(pci_bus, pci_device_fn,
+				PCI_INTERRUPT_LINE, &pci_irq);
+
+			/* get IO address */
+			pcibios_read_config_dword(pci_bus, pci_device_fn,
+				PCI_BASE_ADDRESS_2, &pci_ioaddr);
+			if (cs->subtyp)
+				break;
+		}
+		if (!cs->subtyp) {
+			printk(KERN_WARNING "Diva: No PCI card found\n");
+			return(0);
+		}
+		if (!pci_irq) {
+			printk(KERN_WARNING "Diva: No IRQ for PCI card found\n");
+			return(0);
+		}
+
+		if (!pci_ioaddr) {
+			printk(KERN_WARNING "Diva: No IO-Adr for PCI card found\n");
+			return(0);
+		}
+		pci_ioaddr &= ~3; /* remove io/mem flag */
+		cs->hw.diva.cfg_reg = pci_ioaddr; 
+		cs->hw.diva.ctrl = pci_ioaddr + DIVA_PCI_CTRL;
+		cs->hw.diva.isac = pci_ioaddr + DIVA_PCI_ISAC_DATA;
+		cs->hw.diva.hscx = pci_ioaddr + DIVA_HSCX_DATA;
+		cs->hw.diva.isac_adr = pci_ioaddr + DIVA_PCI_ISAC_ADR;
+		cs->hw.diva.hscx_adr = pci_ioaddr + DIVA_HSCX_ADR;
+		cs->irq = pci_irq;
+		bytecnt = 32;
+#else
+		printk(KERN_WARNING "Diva: cfgreg 0 and NO_PCI_BIOS\n");
+		printk(KERN_WARNING "Diva: unable to config DIVA PCI\n");
+		return (0);
+#endif /* CONFIG_PCI */
+	}
+
+	printk(KERN_INFO
+		"Diva: %s card configured at 0x%x IRQ %d\n",
+		(cs->subtyp == DIVA_ISA) ? "ISA" : "PCI",
+		cs->hw.diva.cfg_reg, cs->irq);
+	if (check_region(cs->hw.diva.cfg_reg, bytecnt)) {
+		printk(KERN_WARNING
+		       "HiSax: %s config port %x-%x already in use\n",
+		       CardType[card->typ],
+		       cs->hw.diva.cfg_reg,
+		       cs->hw.diva.cfg_reg + bytecnt);
+		return (0);
+	} else {
+		request_region(cs->hw.diva.cfg_reg, bytecnt, "diva isdn");
+	}
+
+	reset_diva(cs);
+	cs->hw.diva.tl.function = (void *) diva_led_handler;
+	cs->hw.diva.tl.data = (long) cs;
+	init_timer(&cs->hw.diva.tl);
+	cs->readisac  = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo  = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg  = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &Diva_card_msg;
+
+	ISACVersion(cs, "Diva:");
+	if (HscxVersion(cs, "Diva:")) {
+		printk(KERN_WARNING
+		       "Diva: wrong HSCX versions check IO address\n");
+		release_io_diva(cs);
+		return (0);
+	}
+	return (1);
+}
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/elsa.c linux/drivers/isdn/hisax/elsa.c
--- v2.1.91/linux/drivers/isdn/hisax/elsa.c	Thu Feb 12 20:56:06 1998
+++ linux/drivers/isdn/hisax/elsa.c	Wed Apr  1 16:20:58 1998
@@ -1,4 +1,4 @@
-/* $Id: elsa.c,v 1.14 1997/04/13 19:53:25 keil Exp $
+/* $Id: elsa.c,v 2.6 1998/02/02 13:29:40 keil Exp $
 
  * elsa.c     low level stuff for Elsa isdn cards
  *
@@ -8,1241 +8,726 @@
  *
  *
  * $Log: elsa.c,v $
- * Revision 1.14  1997/04/13 19:53:25  keil
- * Fixed QS1000 init, change in IRQ check delay for SMP
+ * Revision 2.6  1998/02/02 13:29:40  keil
+ * fast io
  *
- * Revision 1.13  1997/04/07 22:58:07  keil
- * need include config.h
- *
- * Revision 1.12  1997/04/06 22:54:14  keil
- * Using SKB's
+ * Revision 2.5  1998/01/31 21:41:45  keil
+ * changes for newer 2.1 kernels
  *
- * Revision 1.11  1997/03/23 21:45:46  keil
- * Add support for ELSA PCMCIA
+ * Revision 2.4  1997/11/08 21:35:46  keil
+ * new l1 init
  *
- * Revision 1.10  1997/03/12 21:42:19  keil
- * Bugfix: IRQ hangs with QS1000
+ * Revision 2.3  1997/11/06 17:15:09  keil
+ * New 2.1 init; PCMCIA wrapper changes
  *
- * Revision 1.9  1997/03/04 15:57:39  keil
- * bugfix IRQ reset Quickstep, ELSA PC changes, some stuff for new cards
+ * Revision 2.2  1997/10/29 18:57:09  keil
+ * changes for 2.1.60, arcofi support
  *
- * Revision 1.8  1997/01/27 15:51:48  keil
- * SMP proof,cosmetics
+ * Revision 2.1  1997/07/27 21:47:08  keil
+ * new interface structures
  *
- * Revision 1.7  1997/01/21 22:20:48  keil
- * Elsa Quickstep support
+ * Revision 2.0  1997/06/26 11:02:40  keil
+ * New Layer and card interface
  *
- * 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.14  1997/04/13 19:53:25  keil
+ * Fixed QS1000 init, change in IRQ check delay for SMP
  *
- * Revision 1.2  1996/10/27 22:08:03  keil
- * cosmetic changes
+ * Revision 1.13  1997/04/07 22:58:07  keil
+ * need include config.h
  *
- * Revision 1.1  1996/10/13 20:04:52  keil
- * Initial revision
+ * Revision 1.12  1997/04/06 22:54:14  keil
+ * Using SKB's
  *
+ * old changes removed KKe
  *
  */
 
-#define ARCOFI_USE	0
-
 #define __NO_VERSION__
 #include <linux/config.h>
-#include "siemens.h"
 #include "hisax.h"
-#include "elsa.h"
+#include "arcofi.h"
+#include "isac.h"
+#include "ipac.h"
+#include "hscx.h"
 #include "isdnl1.h"
-#include <linux/kernel_stat.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
 
 extern const char *CardType[];
 
-const char *Elsa_revision = "$Revision: 1.14 $";
+const char *Elsa_revision = "$Revision: 2.6 $";
 const char *Elsa_Types[] =
 {"None", "PC", "PCC-8", "PCC-16", "PCF", "PCF-Pro",
- "PCMCIA", "QS 1000", "QS 3000"};
+ "PCMCIA", "QS 1000", "QS 3000", "QS 1000 PCI"};
 
 const char *ITACVer[] =
 {"?0?", "?1?", "?2?", "?3?", "?4?", "V2.2",
  "B1", "A1"};
 
-#define byteout(addr,val) outb_p(val,addr)
-#define bytein(addr) inb_p(addr)
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define ELSA_ISAC	0
+#define ELSA_ISAC_PCM	1
+#define ELSA_ITAC	1
+#define ELSA_HSCX	2
+#define ELSA_ALE	3
+#define ELSA_ALE_PCM	4
+#define ELSA_CONTROL	4
+#define ELSA_CONFIG	5
+#define ELSA_START_TIMER 6
+#define ELSA_TRIG_IRQ	7
+
+#define ELSA_PC      1
+#define ELSA_PCC8    2
+#define ELSA_PCC16   3
+#define ELSA_PCF     4
+#define ELSA_PCFPRO  5
+#define ELSA_PCMCIA  6
+#define ELSA_QS1000  7
+#define ELSA_QS3000  8
+#define ELSA_QS1000PCI 9
+
+/* PCI stuff */
+#define PCI_VENDOR_ELSA	0x1048
+#define PCI_QS1000_ID	0x1000
+
+
+/* ITAC Registeradressen (only Microlink PC) */
+#define ITAC_SYS	0x34
+#define ITAC_ISEN	0x48
+#define ITAC_RFIE	0x4A
+#define ITAC_XFIE	0x4C
+#define ITAC_SCIE	0x4E
+#define ITAC_STIE	0x46
+
+/***                                                                    ***
+ ***   Makros als Befehle fuer die Kartenregister                       ***
+ ***   (mehrere Befehle werden durch Bit-Oderung kombiniert)            ***
+ ***                                                                    ***/
+
+/* Config-Register (Read) */
+#define ELSA_TIMER_RUN       0x02	/* Bit 1 des Config-Reg     */
+#define ELSA_TIMER_RUN_PCC8  0x01	/* Bit 0 des Config-Reg  bei PCC */
+#define ELSA_IRQ_IDX       0x38	/* Bit 3,4,5 des Config-Reg */
+#define ELSA_IRQ_IDX_PCC8  0x30	/* Bit 4,5 des Config-Reg */
+#define ELSA_IRQ_IDX_PC    0x0c	/* Bit 2,3 des Config-Reg */
+
+/* Control-Register (Write) */
+#define ELSA_LINE_LED        0x02	/* Bit 1 Gelbe LED */
+#define ELSA_STAT_LED        0x08	/* Bit 3 Gruene LED */
+#define ELSA_ISDN_RESET      0x20	/* Bit 5 Reset-Leitung */
+#define ELSA_ENA_TIMER_INT   0x80	/* Bit 7 Freigabe Timer Interrupt */
+
+/* ALE-Register (Read) */
+#define ELSA_HW_RELEASE      0x07	/* Bit 0-2 Hardwarerkennung */
+#define ELSA_S0_POWER_BAD    0x08	/* Bit 3 S0-Bus Spannung fehlt */
+
+/* Status Flags */
+#define ELSA_TIMER_AKTIV 1
+#define ELSA_BAD_PWR     2
+#define ELSA_ASSIGN      4
 
 static inline u_char
-readhscx(unsigned int adr, int hscx, u_char off)
+readreg(unsigned int ale, unsigned int adr, u_char off)
 {
 	register u_char ret;
 	long flags;
 
 	save_flags(flags);
 	cli();
-	byteout(adr + CARD_ALE, off + (hscx ? 0x60 : 0x20));
-	ret = bytein(adr + CARD_HSCX);
+	byteout(ale, off);
+	ret = bytein(adr);
 	restore_flags(flags);
 	return (ret);
 }
 
 static inline void
-read_fifo_hscx(unsigned int adr, int hscx, u_char * data, int size)
+readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
 {
 	/* fifo read without cli because it's allready done  */
 
-	byteout(adr + CARD_ALE, (hscx ? 0x40 : 0));
-	insb(adr + CARD_HSCX, data, size);
+	byteout(ale, off);
+	insb(adr, data, size);
 }
 
 
 static inline void
-writehscx(unsigned int adr, int hscx, u_char off, u_char data)
+writereg(unsigned int ale, unsigned int adr, u_char off, u_char data)
 {
 	long flags;
 
 	save_flags(flags);
 	cli();
-	byteout(adr + CARD_ALE, off + (hscx ? 0x60 : 0x20));
-	byteout(adr + CARD_HSCX, data);
+	byteout(ale, off);
+	byteout(adr, data);
 	restore_flags(flags);
 }
 
 static inline void
-write_fifo_hscx(unsigned int adr, int hscx, u_char * data, int size)
+writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
 {
 	/* fifo write without cli because it's allready done  */
-	byteout(adr + CARD_ALE, (hscx ? 0x40 : 0));
-	outsb(adr + CARD_HSCX, data, size);
+	byteout(ale, off);
+	outsb(adr, data, size);
 }
 
-static inline u_char
-readisac(unsigned int adr, u_char off)
-{
-	register u_char ret;
-	long flags;
+/* Interface functions */
 
-	save_flags(flags);
-	cli();
-	byteout(adr + CARD_ALE, off + 0x20);
-	ret = bytein(adr + CARD_ISAC);
-	restore_flags(flags);
-	return (ret);
-}
-
-static inline void
-read_fifo_isac(unsigned int adr, u_char * data, int size)
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
 {
-	/* fifo read without cli because it's allready done  */
-
-	byteout(adr + CARD_ALE, 0);
-	insb(adr + CARD_ISAC, data, size);
+	return (readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, offset));
 }
 
-
-static inline void
-writeisac(unsigned int adr, u_char off, u_char data)
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
 {
-	long flags;
-
-	save_flags(flags);
-	cli();
-	byteout(adr + CARD_ALE, off + 0x20);
-	byteout(adr + CARD_ISAC, data);
-	restore_flags(flags);
+	writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, offset, value);
 }
 
-static inline void
-write_fifo_isac(unsigned int adr, u_char * data, int size)
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
 {
-	/* fifo write without cli because it's allready done  */
-
-	byteout(adr + CARD_ALE, 0);
-	outsb(adr + CARD_ISAC, data, size);
+	readfifo(cs->hw.elsa.ale, cs->hw.elsa.isac, 0, data, size);
 }
 
-#ifdef CONFIG_HISAX_ELSA_PCC
-static inline u_char
-readitac(unsigned int adr, u_char off)
-{
-	register u_char ret;
-	long flags;
-
-	save_flags(flags);
-	cli();
-	byteout(adr + CARD_ALE, off);
-	ret = bytein(adr + CARD_ITAC);
-	restore_flags(flags);
-	return (ret);
-}
-
-static inline void
-writeitac(unsigned int adr, u_char off, u_char data)
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
 {
-	long flags;
-
-	save_flags(flags);
-	cli();
-	byteout(adr + CARD_ALE, off);
-	byteout(adr + CARD_ITAC, data);
-	restore_flags(flags);
+	writefifo(cs->hw.elsa.ale, cs->hw.elsa.isac, 0, data, size);
 }
 
-static inline int
-TimerRun(struct IsdnCardState *sp)
+static u_char
+ReadISAC_IPAC(struct IsdnCardState *cs, u_char offset)
 {
-	register u_char val;
-
-	val = bytein(sp->cfg_reg + CARD_CONFIG);
-	if (sp->subtyp == ELSA_QS1000)
-		return (0 == (val & TIMER_RUN));
-	else if (sp->subtyp == ELSA_PCC8)
-		return (val & TIMER_RUN_PCC8);
-	return (val & TIMER_RUN);
+	return (readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, offset+0x80));
 }
 
-static inline void
-elsa_led_handler(struct IsdnCardState *sp)
+static void
+WriteISAC_IPAC(struct IsdnCardState *cs, u_char offset, u_char value)
 {
-
-	u_char outval = 0xf0;
-	int stat = 0, cval;
-
-
-	if ((sp->ph_state == 0) || (sp->ph_state == 15)) {
-		stat = 1;
-	} else {
-		if (sp->hs[0].mode != 0)
-			stat |= 2;
-		if (sp->hs[1].mode != 0)
-			stat |= 4;
-	}
-	cval = (sp->counter >> 6) & 3;
-	switch (cval) {
-		case 0:
-			if (!stat)
-				outval |= STAT_LED;
-			else if (stat == 1)
-				outval |= LINE_LED | STAT_LED;
-			else {
-				if (stat & 2)
-					outval |= STAT_LED;
-				if (stat & 4)
-					outval |= LINE_LED;
-			}
-			break;
-		case 1:
-			if (!stat)
-				outval |= LINE_LED;
-			else if (stat == 1)
-				outval |= LINE_LED | STAT_LED;
-			else {
-				if (stat & 2)
-					outval |= STAT_LED;
-				if (stat & 4)
-					outval |= LINE_LED;
-			}
-			break;
-		case 2:
-			if (!stat)
-				outval |= STAT_LED;
-			else if (stat == 1)
-				outval |= 0;
-			else {
-				if (stat & 2)
-					outval |= STAT_LED;
-				if (stat & 4)
-					outval |= LINE_LED;
-			}
-			break;
-		case 3:
-			if (!stat)
-				outval |= LINE_LED;
-			break;
-	}
-	byteout(sp->cfg_reg + CARD_CONTROL, outval);
+	writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, offset|0x80, value);
 }
-#endif
 
-static inline void
-waitforCEC(int adr, int hscx)
+static void
+ReadISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
 {
-	int to = 50;
-
-	while ((readhscx(adr, hscx, HSCX_STAR) & 0x04) && to) {
-		udelay(1);
-		to--;
-	}
-	if (!to)
-		printk(KERN_WARNING "Elsa: waitforCEC timeout\n");
+	readfifo(cs->hw.elsa.ale, cs->hw.elsa.isac, 0x80, data, size);
 }
 
-
-static inline void
-waitforXFW(int adr, int hscx)
+static void
+WriteISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
 {
-	int to = 50;
-
-	while ((!(readhscx(adr, hscx, HSCX_STAR) & 0x44) == 0x40) && to) {
-		udelay(1);
-		to--;
-	}
-	if (!to)
-		printk(KERN_WARNING "Elsa: waitforXFW timeout\n");
+	writefifo(cs->hw.elsa.ale, cs->hw.elsa.isac, 0x80, data, size);
 }
 
-static inline void
-writehscxCMDR(int adr, int hscx, u_char data)
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
 {
-	long flags;
-
-	save_flags(flags);
-	cli();
-	waitforCEC(adr, hscx);
-	writehscx(adr, hscx, HSCX_CMDR, data);
-	restore_flags(flags);
+	return (readreg(cs->hw.elsa.ale,
+			cs->hw.elsa.hscx, offset + (hscx ? 0x40 : 0)));
 }
 
-/*
- * fast interrupt here
- */
-
-
 static void
-hscxreport(struct IsdnCardState *sp, int hscx)
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
 {
-	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));
+	writereg(cs->hw.elsa.ale,
+		 cs->hw.elsa.hscx, offset + (hscx ? 0x40 : 0), value);
 }
 
-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)
+static inline u_char
+readitac(struct IsdnCardState *cs, u_char off)
 {
-	u_char *ptr;
-	struct IsdnCardState *sp = hsp->sp;
+	register u_char ret;
 	long flags;
 
-	if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO))
-		debugl1(sp, "hscx_empty_fifo");
-
-	if (hsp->rcvidx + count > HSCX_BUFMAX) {
-		if (sp->debug & L1_DEB_WARN)
-			debugl1(sp, "hscx_empty_fifo: incoming packet too large");
-		writehscxCMDR(sp->cfg_reg, hsp->hscx, 0x80);
-		hsp->rcvidx = 0;
-		return;
-	}
-	ptr = hsp->rcvbuf + hsp->rcvidx;
-	hsp->rcvidx += count;
 	save_flags(flags);
 	cli();
-	read_fifo_hscx(sp->cfg_reg, hsp->hscx, ptr, count);
-	writehscxCMDR(sp->cfg_reg, hsp->hscx, 0x80);
+	byteout(cs->hw.elsa.ale, off);
+	ret = bytein(cs->hw.elsa.itac);
 	restore_flags(flags);
-	if (sp->debug & L1_DEB_HSCX_FIFO) {
-		char tmp[128];
-		char *t = tmp;
-
-		t += sprintf(t, "hscx_empty_fifo %c cnt %d",
-			     hsp->hscx ? 'B' : 'A', count);
-		QuickHex(t, ptr, count);
-		debugl1(sp, tmp);
-	}
+	return (ret);
 }
 
-static void
-hscx_fill_fifo(struct HscxState *hsp)
+static inline void
+writeitac(struct IsdnCardState *cs, u_char off, u_char data)
 {
-	struct IsdnCardState *sp = hsp->sp;
-	int more, count;
-	u_char *ptr;
 	long flags;
 
-
-	if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO))
-		debugl1(sp, "hscx_fill_fifo");
-
-	if (!hsp->tx_skb)
-		return;
-	if (hsp->tx_skb->len <= 0)
-		return;
-
-	more = (hsp->mode == 1) ? 1 : 0;
-	if (hsp->tx_skb->len > 32) {
-		more = !0;
-		count = 32;
-	} else
-		count = hsp->tx_skb->len;
-
-	waitforXFW(sp->cfg_reg, hsp->hscx);
 	save_flags(flags);
 	cli();
-	ptr = hsp->tx_skb->data;
-	skb_pull(hsp->tx_skb, count);
-	hsp->tx_cnt -= count;
-	hsp->count += count;
-	write_fifo_hscx(sp->cfg_reg, hsp->hscx, ptr, count);
-	writehscxCMDR(sp->cfg_reg, hsp->hscx, more ? 0x8 : 0xa);
+	byteout(cs->hw.elsa.ale, off);
+	byteout(cs->hw.elsa.itac, data);
 	restore_flags(flags);
-	if (sp->debug & L1_DEB_HSCX_FIFO) {
-		char tmp[128];
-		char *t = tmp;
-
-		t += sprintf(t, "hscx_fill_fifo %c cnt %d",
-			     hsp->hscx ? 'B' : 'A', count);
-		QuickHex(t, ptr, count);
-		debugl1(sp, tmp);
-	}
 }
 
-static inline void
-hscx_interrupt(struct IsdnCardState *sp, u_char val, u_char hscx)
+static inline int
+TimerRun(struct IsdnCardState *cs)
 {
-	u_char r;
-	struct HscxState *hsp = sp->hs + hscx;
-	struct sk_buff *skb;
-	int count;
-	char tmp[32];
-
-	if (!hsp->init)
-		return;
-
-	if (val & 0x80) {	/* RME */
+	register u_char v;
 
-		r = readhscx(sp->cfg_reg, hsp->hscx, HSCX_RSTA);
-		if ((r & 0xf0) != 0xa0) {
-			if (!r & 0x80)
-				if (sp->debug & L1_DEB_WARN)
-					debugl1(sp, "HSCX invalid frame");
-			if ((r & 0x40) && hsp->mode)
-				if (sp->debug & L1_DEB_WARN) {
-					sprintf(tmp, "HSCX RDO mode=%d",
-						hsp->mode);
-					debugl1(sp, tmp);
-				}
-			if (!r & 0x20)
-				if (sp->debug & L1_DEB_WARN)
-					debugl1(sp, "HSCX CRC error");
-			writehscxCMDR(sp->cfg_reg, hsp->hscx, 0x80);
-		} else {
-			count = readhscx(sp->cfg_reg, hsp->hscx, HSCX_RBCL) & 0x1f;
-			if (count == 0)
-				count = 32;
-			hscx_empty_fifo(hsp, count);
-			if ((count = hsp->rcvidx - 1) > 0) {
-				if (sp->debug & L1_DEB_HSCX_FIFO) {
-					sprintf(tmp, "HX Frame %d", count);
-					debugl1(sp, tmp);
-				}
-				if (!(skb = dev_alloc_skb(count)))
-					printk(KERN_WARNING "Elsa: receive out of memory\n");
-				else {
-					memcpy(skb_put(skb, count), hsp->rcvbuf, count);
-					skb_queue_tail(&hsp->rqueue, skb);
-				}
-			}
-		}
-		hsp->rcvidx = 0;
-		hscx_sched_event(hsp, HSCX_RCVBUFREADY);
-	}
-	if (val & 0x40) {	/* RPF */
-		hscx_empty_fifo(hsp, 32);
-		if (hsp->mode == 1) {
-			/* receive audio data */
-			if (!(skb = dev_alloc_skb(32)))
-				printk(KERN_WARNING "elsa: receive out of memory\n");
-			else {
-				memcpy(skb_put(skb, 32), hsp->rcvbuf, 32);
-				skb_queue_tail(&hsp->rqueue, skb);
-			}
-			hsp->rcvidx = 0;
-			hscx_sched_event(hsp, HSCX_RCVBUFREADY);
-		}
-	}
-	if (val & 0x10) {	/* XPR */
-		if (hsp->tx_skb)
-			if (hsp->tx_skb->len) {
-				hscx_fill_fifo(hsp);
-				return;
-			} else {
-				SET_SKB_FREE(hsp->tx_skb);
-				dev_kfree_skb(hsp->tx_skb);
-				hsp->count = 0;
-				if (hsp->st->l4.l1writewakeup)
-					hsp->st->l4.l1writewakeup(hsp->st);
-				hsp->tx_skb = NULL;
-			}
-		if ((hsp->tx_skb = skb_dequeue(&hsp->squeue))) {
-			hsp->count = 0;
-			hscx_fill_fifo(hsp);
-		} else
-			hscx_sched_event(hsp, HSCX_XMTBUFREADY);
-	}
+	v = bytein(cs->hw.elsa.cfg);
+	if ((cs->subtyp == ELSA_QS1000) || (cs->subtyp == ELSA_QS3000))
+		return (0 == (v & ELSA_TIMER_RUN));
+	else if (cs->subtyp == ELSA_PCC8)
+		return (v & ELSA_TIMER_RUN_PCC8);
+	return (v & ELSA_TIMER_RUN);
 }
-
 /*
- * ISAC stuff goes here
+ * fast interrupt HSCX stuff goes here
  */
 
-static void
-isac_empty_fifo(struct IsdnCardState *sp, int count)
-{
-	u_char *ptr;
-	long flags;
-
-	if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO))
-		debugl1(sp, "isac_empty_fifo");
-
-	if ((sp->rcvidx + count) >= MAX_DFRAME_LEN) {
-		if (sp->debug & L1_DEB_WARN) {
-			char tmp[40];
-			sprintf(tmp, "isac_empty_fifo overrun %d",
-				sp->rcvidx + count);
-			debugl1(sp, tmp);
-		}
-		writeisac(sp->cfg_reg, ISAC_CMDR, 0x80);
-		sp->rcvidx = 0;
-		return;
-	}
-	ptr = sp->rcvbuf + sp->rcvidx;
-	sp->rcvidx += count;
-	save_flags(flags);
-	cli();
-	read_fifo_isac(sp->cfg_reg, ptr, count);
-	writeisac(sp->cfg_reg, ISAC_CMDR, 0x80);
-	restore_flags(flags);
-	if (sp->debug & L1_DEB_ISAC_FIFO) {
-		char tmp[128];
-		char *t = tmp;
-
-		t += sprintf(t, "isac_empty_fifo cnt %d", count);
-		QuickHex(t, ptr, count);
-		debugl1(sp, tmp);
-	}
-}
-
-static void
-isac_fill_fifo(struct IsdnCardState *sp)
-{
-	int count, more;
-	u_char *ptr;
-	long flags;
-
-	if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO))
-		debugl1(sp, "isac_fill_fifo");
+#define READHSCX(cs, nr, reg) readreg(cs->hw.elsa.ale, \
+		cs->hw.elsa.hscx, reg + (nr ? 0x40 : 0))
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.elsa.ale, \
+		cs->hw.elsa.hscx, reg + (nr ? 0x40 : 0), data)
 
-	if (!sp->tx_skb)
-		return;
+#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.elsa.ale, \
+		cs->hw.elsa.hscx, (nr ? 0x40 : 0), ptr, cnt)
 
-	count = sp->tx_skb->len;
-	if (count <= 0)
-		return;
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.elsa.ale, \
+		cs->hw.elsa.hscx, (nr ? 0x40 : 0), ptr, cnt)
 
-	more = 0;
-	if (count > 32) {
-		more = !0;
-		count = 32;
-	}
-	save_flags(flags);
-	cli();
-	ptr = sp->tx_skb->data;
-	skb_pull(sp->tx_skb, count);
-	sp->tx_cnt += count;
-	write_fifo_isac(sp->cfg_reg, ptr, count);
-	writeisac(sp->cfg_reg, ISAC_CMDR, more ? 0x8 : 0xa);
-	restore_flags(flags);
-	if (sp->debug & L1_DEB_ISAC_FIFO) {
-		char tmp[128];
-		char *t = tmp;
-
-		t += sprintf(t, "isac_fill_fifo cnt %d", count);
-		QuickHex(t, ptr, count);
-		debugl1(sp, tmp);
-	}
-}
-
-static void
-ph_command(struct IsdnCardState *sp, unsigned int command)
-{
-	if (sp->debug & L1_DEB_ISAC) {
-		char tmp[32];
-		sprintf(tmp, "ph_command %d", command);
-		debugl1(sp, tmp);
-	}
-	writeisac(sp->cfg_reg, ISAC_CIX0, (command << 2) | 3);
-}
-
-static inline void
-isac_interrupt(struct IsdnCardState *sp, u_char val)
-{
-	u_char exval, v1;
-	struct sk_buff *skb;
-	unsigned int count;
-	char tmp[32];
-#if ARCOFI_USE
-	struct BufHeader *ibh;
-	u_char *ptr;
-#endif
-
-	if (sp->debug & L1_DEB_ISAC) {
-		sprintf(tmp, "ISAC interrupt %x", val);
-		debugl1(sp, tmp);
-	}
-	if (val & 0x80) {	/* RME */
-		exval = readisac(sp->cfg_reg, ISAC_RSTA);
-		if ((exval & 0x70) != 0x20) {
-			if (exval & 0x40)
-				if (sp->debug & L1_DEB_WARN)
-					debugl1(sp, "ISAC RDO");
-			if (!exval & 0x20)
-				if (sp->debug & L1_DEB_WARN)
-					debugl1(sp, "ISAC CRC error");
-			writeisac(sp->cfg_reg, ISAC_CMDR, 0x80);
-		} else {
-			count = readisac(sp->cfg_reg, ISAC_RBCL) & 0x1f;
-			if (count == 0)
-				count = 32;
-			isac_empty_fifo(sp, count);
-			if ((count = sp->rcvidx) > 0) {
-				sp->rcvidx = 0;
-				if (!(skb = alloc_skb(count, GFP_ATOMIC)))
-					printk(KERN_WARNING "Elsa: D receive out of memory\n");
-				else {
-					memcpy(skb_put(skb, count), sp->rcvbuf, count);
-					skb_queue_tail(&sp->rq, skb);
-				}
-			}
-		}
-		sp->rcvidx = 0;
-		isac_sched_event(sp, ISAC_RCVBUFREADY);
-	}
-	if (val & 0x40) {	/* RPF */
-		isac_empty_fifo(sp, 32);
-	}
-	if (val & 0x20) {	/* RSC */
-		/* never */
-		if (sp->debug & L1_DEB_WARN)
-			debugl1(sp, "ISAC RSC interrupt");
-	}
-	if (val & 0x10) {	/* XPR */
-		if (sp->tx_skb)
-			if (sp->tx_skb->len) {
-				isac_fill_fifo(sp);
-				goto afterXPR;
-			} else {
-				SET_SKB_FREE(sp->tx_skb);
-				dev_kfree_skb(sp->tx_skb);
-				sp->tx_cnt = 0;
-				sp->tx_skb = NULL;
-			}
-		if ((sp->tx_skb = skb_dequeue(&sp->sq))) {
-			sp->tx_cnt = 0;
-			isac_fill_fifo(sp);
-		} else
-			isac_sched_event(sp, ISAC_XMTBUFREADY);
-	}
-      afterXPR:
-	if (val & 0x04) {	/* CISQ */
-		sp->ph_state = (readisac(sp->cfg_reg, ISAC_CIX0) >> 2)
-		    & 0xf;
-		if (sp->debug & L1_DEB_ISAC) {
-			sprintf(tmp, "l1state %d", sp->ph_state);
-			debugl1(sp, tmp);
-		}
-		isac_new_ph(sp);
-	}
-	if (val & 0x02) {	/* SIN */
-		/* never */
-		if (sp->debug & L1_DEB_WARN)
-			debugl1(sp, "ISAC SIN interrupt");
-	}
-	if (val & 0x01) {	/* EXI */
-		exval = readisac(sp->cfg_reg, ISAC_EXIR);
-		if (sp->debug & L1_DEB_WARN) {
-			sprintf(tmp, "ISAC EXIR %02x", exval);
-			debugl1(sp, tmp);
-		}
-		if (exval & 0x08) {
-			v1 = readisac(sp->cfg_reg, ISAC_MOSR);
-			if (sp->debug & L1_DEB_WARN) {
-				sprintf(tmp, "ISAC MOSR %02x", v1);
-				debugl1(sp, tmp);
-			}
-#if ARCOFI_USE
-			if (v1 & 0x08) {
-				if (!sp->mon_rx)
-					if (BufPoolGet(&(sp->mon_rx), &(sp->rbufpool),
-					    GFP_ATOMIC, (void *) 1, 3)) {
-						if (sp->debug & L1_DEB_WARN)
-							debugl1(sp, "ISAC MON RX out of buffers!");
-						writeisac(sp->cfg_reg, ISAC_MOCR, 0x0a);
-						goto afterMONR0;
-					} else
-						sp->mon_rxp = 0;
-				ibh = sp->mon_rx;
-				ptr = DATAPTR(ibh);
-				ptr += sp->mon_rxp;
-				sp->mon_rxp++;
-				if (sp->mon_rxp >= 3072) {
-					writeisac(sp->cfg_reg, ISAC_MOCR, 0x0a);
-					sp->mon_rxp = 0;
-					if (sp->debug & L1_DEB_WARN)
-						debugl1(sp, "ISAC MON RX overflow!");
-					goto afterMONR0;
-				}
-				*ptr = readisac(sp->cfg_reg, ISAC_MOR0);
-				if (sp->debug & L1_DEB_WARN) {
-					sprintf(tmp, "ISAC MOR0 %02x", *ptr);
-					debugl1(sp, tmp);
-				}
-			}
-		      afterMONR0:
-			if (v1 & 0x80) {
-				if (!sp->mon_rx)
-					if (BufPoolGet(&(sp->mon_rx), &(sp->rbufpool),
-					    GFP_ATOMIC, (void *) 1, 3)) {
-						if (sp->debug & L1_DEB_WARN)
-							debugl1(sp, "ISAC MON RX out of buffers!");
-						writeisac(sp->cfg_reg, ISAC_MOCR, 0xa0);
-						goto afterMONR1;
-					} else
-						sp->mon_rxp = 0;
-				ibh = sp->mon_rx;
-				ptr = DATAPTR(ibh);
-				ptr += sp->mon_rxp;
-				sp->mon_rxp++;
-				if (sp->mon_rxp >= 3072) {
-					writeisac(sp->cfg_reg, ISAC_MOCR, 0xa0);
-					sp->mon_rxp = 0;
-					if (sp->debug & L1_DEB_WARN)
-						debugl1(sp, "ISAC MON RX overflow!");
-					goto afterMONR1;
-				}
-				*ptr = readisac(sp->cfg_reg, ISAC_MOR1);
-				if (sp->debug & L1_DEB_WARN) {
-					sprintf(tmp, "ISAC MOR1 %02x", *ptr);
-					debugl1(sp, tmp);
-				}
-			}
-		      afterMONR1:
-			if (v1 & 0x04) {
-				writeisac(sp->cfg_reg, ISAC_MOCR, 0x0a);
-				sp->mon_rx->datasize = sp->mon_rxp;
-				sp->mon_flg |= MON0_RX;
-			}
-			if (v1 & 0x40) {
-				writeisac(sp->cfg_reg, ISAC_MOCR, 0xa0);
-				sp->mon_rx->datasize = sp->mon_rxp;
-				sp->mon_flg |= MON1_RX;
-			}
-			if (v1 == 0x02) {
-				ibh = sp->mon_tx;
-				if (!ibh) {
-					writeisac(sp->cfg_reg, ISAC_MOCR, 0x0a);
-					goto AfterMOX0;
-				}
-				count = ibh->datasize - sp->mon_txp;
-				if (count <= 0) {
-					writeisac(sp->cfg_reg, ISAC_MOCR, 0x0f);
-					BufPoolRelease(sp->mon_tx);
-					sp->mon_tx = NULL;
-					sp->mon_txp = 0;
-					sp->mon_flg |= MON0_TX;
-					goto AfterMOX0;
-				}
-				ptr = DATAPTR(ibh);
-				ptr += sp->mon_txp;
-				sp->mon_txp++;
-				writeisac(sp->cfg_reg, ISAC_MOX0, *ptr);
-			}
-		      AfterMOX0:
-			if (v1 == 0x20) {
-				ibh = sp->mon_tx;
-				if (!ibh) {
-					writeisac(sp->cfg_reg, ISAC_MOCR, 0xa0);
-					goto AfterMOX1;
-				}
-				count = ibh->datasize - sp->mon_txp;
-				if (count <= 0) {
-					writeisac(sp->cfg_reg, ISAC_MOCR, 0xf0);
-					BufPoolRelease(sp->mon_tx);
-					sp->mon_tx = NULL;
-					sp->mon_txp = 0;
-					sp->mon_flg |= MON1_TX;
-					goto AfterMOX1;
-				}
-				ptr = DATAPTR(ibh);
-				ptr += sp->mon_txp;
-				sp->mon_txp++;
-				writeisac(sp->cfg_reg, ISAC_MOX1, *ptr);
-			}
-		      AfterMOX1:
-#endif
-		}
-	}
-}
-
-static inline void
-hscx_int_main(struct IsdnCardState *sp, u_char val)
-{
-
-	u_char exval;
-	struct HscxState *hsp;
-	char tmp[32];
-
-	if (val & 0x01) {
-		hsp = sp->hs + 1;
-		exval = readhscx(sp->cfg_reg, 1, HSCX_EXIR);
-		if (exval == 0x40) {
-			if (hsp->mode == 1)
-				hscx_fill_fifo(hsp);
-			else {
-				/* Here we lost an TX interrupt, so
-				   * restart transmitting the whole frame.
-				 */
-				if (hsp->tx_skb) {
-					skb_push(hsp->tx_skb, hsp->count);
-					hsp->tx_cnt += hsp->count;
-					hsp->count = 0;
-				}
-				writehscxCMDR(sp->cfg_reg, hsp->hscx, 0x01);
-				if (sp->debug & L1_DEB_WARN) {
-					sprintf(tmp, "HSCX B EXIR %x Lost TX", exval);
-					debugl1(sp, tmp);
-				}
-			}
-		} else if (sp->debug & L1_DEB_HSCX) {
-			sprintf(tmp, "HSCX B EXIR %x", exval);
-			debugl1(sp, tmp);
-		}
-	}
-	if (val & 0xf8) {
-		if (sp->debug & L1_DEB_HSCX) {
-			sprintf(tmp, "HSCX B interrupt %x", val);
-			debugl1(sp, tmp);
-		}
-		hscx_interrupt(sp, val, 1);
-	}
-	if (val & 0x02) {
-		hsp = sp->hs;
-		exval = readhscx(sp->cfg_reg, 0, HSCX_EXIR);
-		if (exval == 0x40) {
-			if (hsp->mode == 1)
-				hscx_fill_fifo(hsp);
-			else {
-				/* Here we lost an TX interrupt, so
-				   * restart transmitting the whole frame.
-				 */
-				if (hsp->tx_skb) {
-					skb_push(hsp->tx_skb, hsp->count);
-					hsp->tx_cnt += hsp->count;
-					hsp->count = 0;
-				}
-				writehscxCMDR(sp->cfg_reg, hsp->hscx, 0x01);
-				if (sp->debug & L1_DEB_WARN) {
-					sprintf(tmp, "HSCX A EXIR %x Lost TX", exval);
-					debugl1(sp, tmp);
-				}
-			}
-		} else if (sp->debug & L1_DEB_HSCX) {
-			sprintf(tmp, "HSCX A EXIR %x", exval);
-			debugl1(sp, tmp);
-		}
-	}
-	if (val & 0x04) {
-		exval = readhscx(sp->cfg_reg, 0, HSCX_ISTA);
-		if (sp->debug & L1_DEB_HSCX) {
-			sprintf(tmp, "HSCX A interrupt %x", exval);
-			debugl1(sp, tmp);
-		}
-		hscx_interrupt(sp, exval, 0);
-	}
-}
+#include "hscx_irq.c"
 
 static void
 elsa_interrupt(int intno, void *dev_id, struct pt_regs *regs)
 {
-	struct IsdnCardState *sp;
+	struct IsdnCardState *cs = dev_id;
 	u_char val;
+	int icnt=20;
 
-	sp = (struct IsdnCardState *) dev_id;
-
-	if (!sp) {
+	if (!cs) {
 		printk(KERN_WARNING "Elsa: Spurious interrupt!\n");
 		return;
 	}
-#ifdef CONFIG_HISAX_ELSA_PCC
-      INT_RESTART:
-	if (!TimerRun(sp)) {
-		/* Timer Restart */
-		byteout(sp->cfg_reg + CARD_START_TIMER, 0);
-		if (!(sp->counter++ & 0x3f)) {
-			/* Call LEDs all 64 tics */
-			elsa_led_handler(sp);
-		}
-	}
-#endif
-	val = readhscx(sp->cfg_reg, 1, HSCX_ISTA);
+	val = readreg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_ISTA + 0x40);
       Start_HSCX:
 	if (val) {
-		hscx_int_main(sp, val);
+		hscx_int_main(cs, val);
 	}
-	val = readisac(sp->cfg_reg, ISAC_ISTA);
+	val = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, ISAC_ISTA);
       Start_ISAC:
 	if (val) {
-		isac_interrupt(sp, val);
+		isac_interrupt(cs, val);
 	}
-#ifdef CONFIG_HISAX_ELSA_PCC
-	if (!TimerRun(sp))
-		goto INT_RESTART;
-#endif
-	val = readhscx(sp->cfg_reg, 1, HSCX_ISTA);
-	if (val) {
-		if (sp->debug & L1_DEB_HSCX)
-			debugl1(sp, "HSCX IntStat after IntRoutine");
+	val = readreg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_ISTA + 0x40);
+	if (val && icnt) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX IntStat after IntRoutine");
+		icnt--;
 		goto Start_HSCX;
 	}
-	val = readisac(sp->cfg_reg, ISAC_ISTA);
-	if (val) {
-		if (sp->debug & L1_DEB_ISAC)
-			debugl1(sp, "ISAC IntStat after IntRoutine");
+	val = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, ISAC_ISTA);
+	if (val && icnt) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		icnt--;
 		goto Start_ISAC;
 	}
-	writehscx(sp->cfg_reg, 0, HSCX_MASK, 0xFF);
-	writehscx(sp->cfg_reg, 1, HSCX_MASK, 0xFF);
-	writeisac(sp->cfg_reg, ISAC_MASK, 0xFF);
-#ifdef CONFIG_HISAX_ELSA_PCC
-	if (sp->subtyp == ELSA_QS1000) {
-		byteout(sp->cfg_reg + CARD_START_TIMER, 0);
-		byteout(sp->cfg_reg + CARD_TRIG_IRQ, 0xff);
+	if (!icnt)
+		printk(KERN_WARNING"ELSA IRQ LOOP\n");
+	writereg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_MASK, 0xFF);
+	writereg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_MASK + 0x40, 0xFF);
+	writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, ISAC_MASK, 0xFF);
+	if (cs->hw.elsa.status & ELSA_TIMER_AKTIV) {
+		if (!TimerRun(cs)) {
+			/* Timer Restart */
+			byteout(cs->hw.elsa.timer, 0);
+			cs->hw.elsa.counter++;
+		}
 	}
-#endif
-	writehscx(sp->cfg_reg, 0, HSCX_MASK, 0x0);
-	writehscx(sp->cfg_reg, 1, HSCX_MASK, 0x0);
-	writeisac(sp->cfg_reg, ISAC_MASK, 0x0);
-}
-
-
-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);
+	if (cs->hw.elsa.trig)
+		byteout(cs->hw.elsa.trig, 0x00);
+	writereg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_MASK, 0x0);
+	writereg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_MASK + 0x40, 0x0);
+	writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, ISAC_MASK, 0x0);
 }
 
 static void
-modehscx(struct HscxState *hs, int mode, int ichan)
+elsa_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs)
 {
-	struct IsdnCardState *sp = hs->sp;
-	int hscx = hs->hscx;
+	struct IsdnCardState *cs = dev_id;
+	u_char ista,val;
+	char   tmp[64];
+	int icnt=20;
 
-	if (sp->debug & L1_DEB_HSCX) {
-		char tmp[40];
-		sprintf(tmp, "hscx %c mode %d ichan %d",
-			'A' + hscx, mode, ichan);
-		debugl1(sp, tmp);
-	}
-	hs->mode = mode;
-	writehscx(sp->cfg_reg, hscx, HSCX_CCR1, 0x85);
-	writehscx(sp->cfg_reg, hscx, HSCX_XAD1, 0xFF);
-	writehscx(sp->cfg_reg, hscx, HSCX_XAD2, 0xFF);
-	writehscx(sp->cfg_reg, hscx, HSCX_RAH2, 0xFF);
-	writehscx(sp->cfg_reg, hscx, HSCX_XBCH, 0x0);
-	writehscx(sp->cfg_reg, hscx, HSCX_RLCR, 0x0);
-	writehscx(sp->cfg_reg, hscx, HSCX_CCR2, 0x30);
-
-	switch (mode) {
-		case (0):
-			writehscx(sp->cfg_reg, hscx, HSCX_TSAX, 0xff);
-			writehscx(sp->cfg_reg, hscx, HSCX_TSAR, 0xff);
-			writehscx(sp->cfg_reg, hscx, HSCX_XCCR, 7);
-			writehscx(sp->cfg_reg, hscx, HSCX_RCCR, 7);
-			writehscx(sp->cfg_reg, hscx, HSCX_MODE, 0x84);
-			break;
-		case (1):
-			if (ichan == 0) {
-				writehscx(sp->cfg_reg, hscx, HSCX_TSAX, 0x2f);
-				writehscx(sp->cfg_reg, hscx, HSCX_TSAR, 0x2f);
-			} else {
-				writehscx(sp->cfg_reg, hscx, HSCX_TSAX, 0x3);
-				writehscx(sp->cfg_reg, hscx, HSCX_TSAR, 0x3);
-			}
-			writehscx(sp->cfg_reg, hscx, HSCX_XCCR, 7);
-			writehscx(sp->cfg_reg, hscx, HSCX_RCCR, 7);
-			writehscx(sp->cfg_reg, hscx, HSCX_MODE, 0xe4);
-			writehscx(sp->cfg_reg, hscx, HSCX_CMDR, 0x41);
-			break;
-		case (2):
-			if (ichan == 0) {
-				writehscx(sp->cfg_reg, hscx, HSCX_TSAX, 0x2f);
-				writehscx(sp->cfg_reg, hscx, HSCX_TSAR, 0x2f);
-			} else {
-				writehscx(sp->cfg_reg, hscx, HSCX_TSAX, 0x3);
-				writehscx(sp->cfg_reg, hscx, HSCX_TSAR, 0x3);
-			}
-			writehscx(sp->cfg_reg, hscx, HSCX_XCCR, 7);
-			writehscx(sp->cfg_reg, hscx, HSCX_RCCR, 7);
-			writehscx(sp->cfg_reg, hscx, HSCX_MODE, 0x8c);
-			writehscx(sp->cfg_reg, hscx, HSCX_CMDR, 0x41);
-			break;
+	if (!cs) {
+		printk(KERN_WARNING "Elsa: Spurious interrupt!\n");
+		return;
+	}
+	if ((cs->typ == ISDN_CTYPE_ELSA_PCMCIA) && (*cs->busy_flag == 1)) {
+	  /* The card tends to generate interrupts while being removed
+	     causing us to just crash the kernel. bad. */
+	  printk(KERN_WARNING "Elsa: card not available!\n");
+	  return;
+	}
+	ista = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ISTA);
+Start_IPAC:
+	if (cs->debug & L1_DEB_IPAC) {
+		sprintf(tmp, "IPAC ISTA %02X", ista);
+		debugl1(cs, tmp);
+	}
+	if (ista & 0x0f) {
+		val = readreg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_ISTA + 0x40);
+		if (ista & 0x01)
+			val |= 0x01;
+		if (ista & 0x04)
+			val |= 0x02;
+		if (ista & 0x08)
+			val |= 0x04;
+		if (val)
+			hscx_int_main(cs, val);
+	}
+	if (ista & 0x20) {
+		val = 0xfe & readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, ISAC_ISTA + 0x80);
+		if (val) {
+			isac_interrupt(cs, val);
+		}
 	}
-	writehscx(sp->cfg_reg, hscx, HSCX_ISTA, 0x00);
+	if (ista & 0x10) {
+		val = 0x01;
+		isac_interrupt(cs, val);
+	}
+	ista  = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ISTA);
+	if ((ista & 0x3f) && icnt) {
+		icnt--;
+		goto Start_IPAC;
+	}
+	if (!icnt)
+		printk(KERN_WARNING "ELSA IRQ LOOP\n");
+	writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_MASK, 0xFF);
+	writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_MASK, 0xC0);
 }
 
 void
-release_io_elsa(struct IsdnCard *card)
+release_io_elsa(struct IsdnCardState *cs)
 {
 	int bytecnt = 8;
 
-	if (card->sp->subtyp == ELSA_PCFPRO)
+	del_timer(&cs->hw.elsa.tl);
+	if (cs->hw.elsa.ctrl)
+		byteout(cs->hw.elsa.ctrl, 0);	/* LEDs Out */
+	if ((cs->subtyp == ELSA_PCFPRO) || 
+		(cs->subtyp == ELSA_QS3000) ||
+		(cs->subtyp == ELSA_PCF))
 		bytecnt = 16;
-	if (card->sp->cfg_reg)
-		release_region(card->sp->cfg_reg, bytecnt);
-}
-
-static void
-reset_elsa(struct IsdnCardState *sp)
-{
-#ifdef CONFIG_HISAX_ELSA_PCC
-	/* Wait 1 Timer */
-	byteout(sp->cfg_reg + CARD_START_TIMER, 0);
-	while (TimerRun(sp));
-	byteout(sp->cfg_reg + CARD_CONTROL, 0x00);	/* Reset On */
-	/* Wait 1 Timer */
-	byteout(sp->cfg_reg + CARD_START_TIMER, 0);
-	while (TimerRun(sp));
-	byteout(sp->cfg_reg + CARD_CONTROL, ISDN_RESET);	/* Reset Off */
-	/* Wait 1 Timer */
-	byteout(sp->cfg_reg + CARD_START_TIMER, 0);
-	while (TimerRun(sp));
-	byteout(sp->cfg_reg + CARD_TRIG_IRQ, 0xff);
-#endif
+	if (cs->subtyp == ELSA_QS1000PCI) {
+		byteout(cs->hw.elsa.cfg + 0x4c, 0x01);  /* disable IRQ */
+		bytecnt = 2;
+		release_region(cs->hw.elsa.cfg, 0x80);
+	}
+	if (cs->hw.elsa.base)
+		release_region(cs->hw.elsa.base, bytecnt);
 }
 
 static void
-clear_pending_ints(struct IsdnCardState *sp)
+reset_elsa(struct IsdnCardState *cs)
 {
-#ifdef CONFIG_HISAX_ELSA_PCMCIA
-	int val;
-	char tmp[64];
+	long flags;
 
-	val = readhscx(sp->cfg_reg, 1, HSCX_ISTA);
-	sprintf(tmp, "HSCX B ISTA %x", val);
-	debugl1(sp, tmp);
-	if (val & 0x01) {
-		val = readhscx(sp->cfg_reg, 1, HSCX_EXIR);
-		sprintf(tmp, "HSCX B EXIR %x", val);
-		debugl1(sp, tmp);
-	} else if (val & 0x02) {
-		val = readhscx(sp->cfg_reg, 0, HSCX_EXIR);
-		sprintf(tmp, "HSCX A EXIR %x", val);
-		debugl1(sp, tmp);
-	}
-	val = readhscx(sp->cfg_reg, 0, HSCX_ISTA);
-	sprintf(tmp, "HSCX A ISTA %x", val);
-	debugl1(sp, tmp);
-	val = readhscx(sp->cfg_reg, 1, HSCX_STAR);
-	sprintf(tmp, "HSCX B STAR %x", val);
-	debugl1(sp, tmp);
-	val = readhscx(sp->cfg_reg, 0, HSCX_STAR);
-	sprintf(tmp, "HSCX A STAR %x", val);
-	debugl1(sp, tmp);
-	val = readisac(sp->cfg_reg, ISAC_STAR);
-	sprintf(tmp, "ISAC STAR %x", val);
-	debugl1(sp, tmp);
-	val = readisac(sp->cfg_reg, ISAC_MODE);
-	sprintf(tmp, "ISAC MODE %x", val);
-	debugl1(sp, tmp);
-	val = readisac(sp->cfg_reg, ISAC_ADF2);
-	sprintf(tmp, "ISAC ADF2 %x", val);
-	debugl1(sp, tmp);
-	val = readisac(sp->cfg_reg, ISAC_ISTA);
-	sprintf(tmp, "ISAC ISTA %x", val);
-	debugl1(sp, tmp);
-	if (val & 0x01) {
-		val = readisac(sp->cfg_reg, ISAC_EXIR);
-		sprintf(tmp, "ISAC EXIR %x", val);
-		debugl1(sp, tmp);
-	} else if (val & 0x04) {
-		val = readisac(sp->cfg_reg, ISAC_CIR0);
-		sprintf(tmp, "ISAC CIR0 %x", val);
-		debugl1(sp, tmp);
+	if (cs->hw.elsa.timer) {
+		/* Wait 1 Timer */
+		byteout(cs->hw.elsa.timer, 0);
+		while (TimerRun(cs));
+		cs->hw.elsa.ctrl_reg |= 0x50;
+		cs->hw.elsa.ctrl_reg &= ~ELSA_ISDN_RESET;	/* Reset On */
+		byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg);
+		/* Wait 1 Timer */
+		byteout(cs->hw.elsa.timer, 0);
+		while (TimerRun(cs));
+		cs->hw.elsa.ctrl_reg |= ELSA_ISDN_RESET;	/* Reset Off */
+		byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg);
+		/* Wait 1 Timer */
+		byteout(cs->hw.elsa.timer, 0);
+		while (TimerRun(cs));
+		if (cs->hw.elsa.trig)
+			byteout(cs->hw.elsa.trig, 0xff);
 	}
-#endif
-	writehscx(sp->cfg_reg, 0, HSCX_MASK, 0xFF);
-	writehscx(sp->cfg_reg, 1, HSCX_MASK, 0xFF);
-	writeisac(sp->cfg_reg, ISAC_MASK, 0xFF);
-#ifdef CONFIG_HISAX_ELSA_PCC
-	if (sp->subtyp == ELSA_QS1000) {
-		byteout(sp->cfg_reg + CARD_START_TIMER, 0);
-		byteout(sp->cfg_reg + CARD_TRIG_IRQ, 0xff);
+	if (cs->subtyp == ELSA_QS1000PCI) {
+		save_flags(flags);
+		sti();
+		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_POTA2, 0x20);
+		current->state = TASK_INTERRUPTIBLE;
+		current->timeout = jiffies + (10 * HZ) / 1000;	/* Timeout 10ms */
+		schedule();
+		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_POTA2, 0x00);
+		current->state = TASK_INTERRUPTIBLE;
+		current->timeout = jiffies + (10 * HZ) / 1000;	/* Timeout 10ms */
+		schedule();
+		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_MASK, 0xc0);
+		schedule();
+		restore_flags(flags);
+		byteout(cs->hw.elsa.cfg + 0x4c, 0x41); /* enable ELSA PCI IRQ */
 	}
-#endif
-	writehscx(sp->cfg_reg, 0, HSCX_MASK, 0x0);
-	writehscx(sp->cfg_reg, 1, HSCX_MASK, 0x0);
-	writeisac(sp->cfg_reg, ISAC_MASK, 0x0);
-	writeisac(sp->cfg_reg, ISAC_CMDR, 0x41);
+}
+
+const u_char ARCOFI_VERSION[] = {2,0xa0,0};
+const u_char ARCOFI_COP_5[] = {4,0xa1,0x25,0xbb,0x4a}; /* GTX */
+const u_char ARCOFI_COP_6[] = {6,0xa1,0x26,0,0,0x82,0x7c}; /* GRL GRH */
+const u_char ARCOFI_COP_7[] = {4,0xa1,0x27,0x80,0x80}; /* GZ */
+const u_char ARCOFI_COP_8[] = {10,0xa1,0x28,0x49,0x31,0x8,0x13,0x6e,0x88,0x2a,0x61}; /* TX */
+const u_char ARCOFI_COP_9[] = {10,0xa1,0x29,0x80,0xcb,0x9e,0x88,0x00,0xc8,0xd8,0x80}; /* RX */
+const u_char ARCOFI_XOP_0[] = {2,0xa1,0x30}; /* PWR Down */
+const u_char ARCOFI_XOP_1[] = {2,0xa1,0x31}; /* PWR Down */
+const u_char ARCOFI_XOP_F[] = {2,0xa1,0x3f}; /* PWR Down */
+const u_char ARCOFI_SOP_F[] = {10,0xa1,0x1f,0x00,0x50,0x10,0x00,0x00,0x80,0x02,0x12};
+ 
+static void
+init_arcofi(struct IsdnCardState *cs) {
+	send_arcofi(cs, ARCOFI_COP_5);
+	send_arcofi(cs, ARCOFI_COP_6);
+	send_arcofi(cs, ARCOFI_COP_7);
+	send_arcofi(cs, ARCOFI_COP_8);
+	send_arcofi(cs, ARCOFI_COP_9);
+	send_arcofi(cs, ARCOFI_SOP_F);
+	send_arcofi(cs, ARCOFI_XOP_F);
 }
 
 static void
-check_arcofi(struct IsdnCardState *sp)
+check_arcofi(struct IsdnCardState *cs)
 {
-#if 0
-	u_char val;
+#if ARCOFI_USE
+	int arcofi_present = 0;
 	char tmp[40];
 	char *t;
-	long flags;
 	u_char *p;
 
-	if (BufPoolGet(&(sp->mon_tx), &(sp->sbufpool),
-		       GFP_ATOMIC, (void *) 1, 3)) {
-		if (sp->debug & L1_DEB_WARN)
-			debugl1(sp, "ISAC MON TX out of buffers!");
-		return;
-	} else
-		sp->mon_txp = 0;
-	p = DATAPTR(sp->mon_tx);
-	*p++ = 0xa0;
-	*p++ = 0x0;
-	sp->mon_tx->datasize = 2;
-	sp->mon_txp = 1;
-	sp->mon_flg = 0;
-	writeisac(sp->cfg_reg, ISAC_MOCR, 0xa0);
-	val = readisac(sp->cfg_reg, ISAC_MOSR);
-	writeisac(sp->cfg_reg, ISAC_MOX1, 0xa0);
-	writeisac(sp->cfg_reg, ISAC_MOCR, 0xb0);
-	save_flags(flags);
-	sti();
-	HZDELAY(3);
-	restore_flags(flags);
-	if (sp->mon_flg & MON1_TX) {
-		if (sp->mon_flg & MON1_RX) {
-			sprintf(tmp, "Arcofi response received %d bytes", sp->mon_rx->datasize);
-			debugl1(sp, tmp);
-			p = DATAPTR(sp->mon_rx);
+	if (!cs->mon_tx)
+		if (!(cs->mon_tx=kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "ISAC MON TX out of buffers!");
+			return;
+		}
+	send_arcofi(cs, ARCOFI_VERSION);
+	if (test_and_clear_bit(HW_MON1_TX_END, &cs->HW_Flags)) {
+		if (test_and_clear_bit(HW_MON1_RX_END, &cs->HW_Flags)) {
+			sprintf(tmp, "Arcofi response received %d bytes", cs->mon_rxp);
+			debugl1(cs, tmp);
+			p = cs->mon_rx;
 			t = tmp;
 			t += sprintf(tmp, "Arcofi data");
-			QuickHex(t, p, sp->mon_rx->datasize);
-			debugl1(sp, tmp);
-			BufPoolRelease(sp->mon_rx);
-			sp->mon_rx = NULL;
-			sp->mon_rxp = 0;
-			sp->mon_flg = 0;
+			QuickHex(t, p, cs->mon_rxp);
+			debugl1(cs, tmp);
+			if ((cs->mon_rxp == 2) && (cs->mon_rx[0] == 0xa0)) {
+				switch(cs->mon_rx[1]) {
+					case 0x80:
+						debugl1(cs, "Arcofi 2160 detected");
+						arcofi_present = 1;
+						break;
+					case 0x82:
+						debugl1(cs, "Arcofi 2165 detected");
+						arcofi_present = 2;
+						break;
+					case 0x84:
+						debugl1(cs, "Arcofi 2163 detected");
+						arcofi_present = 3;
+						break;
+					default:
+						debugl1(cs, "unknown Arcofi response");
+						break;
+				}
+			} else
+				debugl1(cs, "undefined Monitor response");
+			cs->mon_rxp = 0;
 		}
-	} else if (sp->mon_tx) {
-		BufPoolRelease(sp->mon_tx);
-		sp->mon_tx = NULL;
-		sp->mon_txp = 0;
+	} else if (cs->mon_tx) {
 		sprintf(tmp, "Arcofi not detected");
-		debugl1(sp, tmp);
+		debugl1(cs, tmp);
+	}
+	if (arcofi_present) {
+		if (cs->subtyp==ELSA_QS1000) {
+			cs->subtyp = ELSA_QS3000;
+			printk(KERN_INFO
+				"Elsa: %s detected modem at 0x%x\n",
+				Elsa_Types[cs->subtyp],
+				cs->hw.elsa.base+8);
+			release_region(cs->hw.elsa.base, 8);
+			if (check_region(cs->hw.elsa.base, 16)) {
+				printk(KERN_WARNING
+				"HiSax: %s config port %x-%x already in use\n",
+				Elsa_Types[cs->subtyp],
+				cs->hw.elsa.base + 8,
+				cs->hw.elsa.base + 16);
+			} else
+				request_region(cs->hw.elsa.base, 16,
+					"elsa isdn modem");
+		} else if (cs->subtyp==ELSA_PCC16) {
+			cs->subtyp = ELSA_PCF;
+			printk(KERN_INFO
+				"Elsa: %s detected modem at 0x%x\n",
+				Elsa_Types[cs->subtyp],
+				cs->hw.elsa.base+8);
+			release_region(cs->hw.elsa.base, 8);
+			if (check_region(cs->hw.elsa.base, 16)) {
+				printk(KERN_WARNING
+				"HiSax: %s config port %x-%x already in use\n",
+				Elsa_Types[cs->subtyp],
+				cs->hw.elsa.base + 8,
+				cs->hw.elsa.base + 16);
+			} else
+				request_region(cs->hw.elsa.base, 16,
+					"elsa isdn modem");
+		} else
+			printk(KERN_INFO
+				"Elsa: %s detected modem at 0x%x\n",
+				Elsa_Types[cs->subtyp],
+				cs->hw.elsa.base+8);
+		init_arcofi(cs);
 	}
-	sp->mon_flg = 0;
 #endif
 }
 
-int
-initelsa(struct IsdnCardState *sp)
+static void
+elsa_led_handler(struct IsdnCardState *cs)
 {
-	int ret, irq_cnt, cnt = 3;
-	long flags;
+	int blink = 0;
 
-	irq_cnt = kstat_irqs(sp->irq);
-	printk(KERN_INFO "Elsa: IRQ %d count %d\n", sp->irq, irq_cnt);
-	ret = get_irq(sp->cardnr, &elsa_interrupt);
-#ifdef CONFIG_HISAX_ELSA_PCC
-	byteout(sp->cfg_reg + CARD_TRIG_IRQ, 0xff);
-#endif
-	while (ret && cnt) {
-		sp->counter = 0;
-		clear_pending_ints(sp);
-		initisac(sp);
-		sp->modehscx(sp->hs, 0, 0);
-		sp->modehscx(sp->hs + 1, 0, 0);
-		save_flags(flags);
-		sp->counter = 0;
-		sti();
-#ifdef CONFIG_HISAX_ELSA_PCC
-		byteout(sp->cfg_reg + CARD_CONTROL, ISDN_RESET | ENABLE_TIM_INT);
-		byteout(sp->cfg_reg + CARD_START_TIMER, 0);
-		current->state = TASK_INTERRUPTIBLE;
-		current->timeout = jiffies + (110 * HZ) / 1000;		/* Timeout 110ms */
-		schedule();
-		restore_flags(flags);
-		printk(KERN_INFO "Elsa: %d timer tics in 110 msek\n",
-		       sp->counter);
-		if (abs(sp->counter - 13) < 3) {
-			printk(KERN_INFO "Elsa: timer and irq OK\n");
-		} else {
-			printk(KERN_WARNING
-			       "Elsa: timer tic problem (%d/12) maybe an IRQ(%d) conflict\n",
-			       sp->counter, sp->irq);
-		}
-#endif
-		printk(KERN_INFO "Elsa: IRQ %d count %d\n", sp->irq,
-		       kstat_irqs(sp->irq));
-		if (kstat_irqs(sp->irq) == irq_cnt) {
-			printk(KERN_WARNING
-			       "Elsa: IRQ(%d) getting no interrupts during init %d\n",
-			       sp->irq, 4 - cnt);
-			if (cnt == 1) {
-				free_irq(sp->irq, sp);
-				return (0);
+	if ((cs->subtyp == ELSA_PCMCIA) &&
+		(cs->subtyp == ELSA_QS1000PCI))
+		return;
+	del_timer(&cs->hw.elsa.tl);
+	if (cs->hw.elsa.status & ELSA_ASSIGN)
+		cs->hw.elsa.ctrl_reg |= ELSA_STAT_LED;
+	else if (cs->hw.elsa.status & ELSA_BAD_PWR)
+		cs->hw.elsa.ctrl_reg &= ~ELSA_STAT_LED;
+	else {
+		cs->hw.elsa.ctrl_reg ^= ELSA_STAT_LED;
+		blink = 250;
+	}
+	if (cs->hw.elsa.status & 0xf000)
+		cs->hw.elsa.ctrl_reg |= ELSA_LINE_LED;
+	else if (cs->hw.elsa.status & 0x0f00) {
+		cs->hw.elsa.ctrl_reg ^= ELSA_LINE_LED;
+		blink = 500;
+	} else
+		cs->hw.elsa.ctrl_reg &= ~ELSA_LINE_LED;
+
+	byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg);
+	if (blink) {
+		init_timer(&cs->hw.elsa.tl);
+		cs->hw.elsa.tl.expires = jiffies + ((blink * HZ) / 1000);
+		add_timer(&cs->hw.elsa.tl);
+	}
+}
+
+static int
+Elsa_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	int pwr, ret = 0;
+	long flags;	
+
+	switch (mt) {
+		case CARD_RESET:
+			reset_elsa(cs);
+			return(0);
+		case CARD_RELEASE:
+			release_io_elsa(cs);
+			return(0);
+		case CARD_SETIRQ:
+			if (cs->subtyp == ELSA_QS1000PCI)
+				ret = request_irq(cs->irq, &elsa_interrupt_ipac,
+					I4L_IRQ_FLAG, "HiSax", cs);
+			else
+				ret = request_irq(cs->irq, &elsa_interrupt,
+					I4L_IRQ_FLAG, "HiSax", cs);
+			return(ret);
+		case CARD_INIT:
+			if (cs->hw.elsa.trig)
+				byteout(cs->hw.elsa.trig, 0xff);
+			clear_pending_isac_ints(cs);
+			clear_pending_hscx_ints(cs);
+			initisac(cs);
+			inithscx(cs);
+			if (cs->subtyp == ELSA_QS1000) {
+				byteout(cs->hw.elsa.timer, 0);
+				byteout(cs->hw.elsa.trig, 0xff);
+			}
+			return(0);
+		case CARD_TEST:
+			if ((cs->subtyp != ELSA_PCMCIA) &&
+				(cs->subtyp != ELSA_QS1000PCI)) {
+				save_flags(flags);
+				cs->hw.elsa.counter = 0;
+				sti();
+				cs->hw.elsa.ctrl_reg |= ELSA_ENA_TIMER_INT;
+				cs->hw.elsa.status |= ELSA_TIMER_AKTIV;
+				byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg);
+				byteout(cs->hw.elsa.timer, 0);
+			} else
+				return(0);
+			current->state = TASK_INTERRUPTIBLE;
+			current->timeout = jiffies + (110 * HZ) / 1000;		/* Timeout 110ms */
+			schedule();
+			restore_flags(flags);
+			cs->hw.elsa.ctrl_reg &= ~ELSA_ENA_TIMER_INT;
+			byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg);
+			cs->hw.elsa.status &= ~ELSA_TIMER_AKTIV;
+			printk(KERN_INFO "Elsa: %d timer tics in 110 msek\n",
+			       cs->hw.elsa.counter);
+			if (abs(cs->hw.elsa.counter - 13) < 3) {
+				printk(KERN_INFO "Elsa: timer and irq OK\n");
+				ret = 0;
+			} else {
+				printk(KERN_WARNING
+				       "Elsa: timer tic problem (%d/12) maybe an IRQ(%d) conflict\n",
+				       cs->hw.elsa.counter, cs->irq);
+				ret = 1;
+			}
+			check_arcofi(cs);
+			elsa_led_handler(cs);
+			return(ret);
+		case MDL_REMOVE_REQ:
+			cs->hw.elsa.status &= 0;
+			break;
+		case MDL_ASSIGN_REQ:
+			cs->hw.elsa.status |= ELSA_ASSIGN;
+			break;
+		case MDL_INFO_SETUP:
+			if ((int) arg)
+				cs->hw.elsa.status |= 0x0200;
+			else
+				cs->hw.elsa.status |= 0x0100;
+			break;
+		case MDL_INFO_CONN:
+			if ((int) arg)
+				cs->hw.elsa.status |= 0x2000;
+			else
+				cs->hw.elsa.status |= 0x1000;
+			break;
+		case MDL_INFO_REL:
+			if ((int) arg) {
+				cs->hw.elsa.status &= ~0x2000;
+				cs->hw.elsa.status &= ~0x0200;
 			} else {
-				reset_elsa(sp);
-				cnt--;
+				cs->hw.elsa.status &= ~0x1000;
+				cs->hw.elsa.status &= ~0x0100;
 			}
-		} else {
-			check_arcofi(sp);
-			cnt = 0;
-		}
+			break;
+		case CARD_AUX_IND:
+			break;
 	}
-	sp->counter = 0;
-	return (ret);
+	pwr = bytein(cs->hw.elsa.ale);
+	if (pwr & 0x08)
+		cs->hw.elsa.status |= ELSA_BAD_PWR;
+	else
+		cs->hw.elsa.status &= ~ELSA_BAD_PWR;
+	elsa_led_handler(cs);
+	return(ret);
 }
 
-#ifdef CONFIG_HISAX_ELSA_PCC
 static unsigned char
-probe_elsa_adr(unsigned int adr)
+probe_elsa_adr(unsigned int adr, int typ)
 {
 	int i, in1, in2, p16_1 = 0, p16_2 = 0, p8_1 = 0, p8_2 = 0, pc_1 = 0,
 	 pc_2 = 0, pfp_1 = 0, pfp_2 = 0;
 	long flags;
 
-	if (check_region(adr, 8)) {
+	/* In case of the elsa pcmcia card, this region is in use,
+	   reserved for us by the card manager. So we do not check it
+	   here, it would fail. */
+	if (typ != ISDN_CTYPE_ELSA_PCMCIA && check_region(adr, 8)) {
 		printk(KERN_WARNING
 		       "Elsa: Probing Port 0x%x: already in use\n",
 		       adr);
@@ -1251,8 +736,8 @@
 	save_flags(flags);
 	cli();
 	for (i = 0; i < 16; i++) {
-		in1 = inb(adr + CARD_CONFIG);	/* 'toggelt' bei */
-		in2 = inb(adr + CARD_CONFIG);	/* jedem Zugriff */
+		in1 = inb(adr + ELSA_CONFIG);	/* 'toggelt' bei */
+		in2 = inb(adr + ELSA_CONFIG);	/* jedem Zugriff */
 		p16_1 += 0x04 & in1;
 		p16_2 += 0x04 & in2;
 		p8_1 += 0x02 & in1;
@@ -1283,205 +768,295 @@
 }
 
 static unsigned int
-probe_elsa(struct IsdnCardState *sp)
+probe_elsa(struct IsdnCardState *cs)
 {
 	int i;
 	unsigned int CARD_portlist[] =
 	{0x160, 0x170, 0x260, 0x360, 0};
 
 	for (i = 0; CARD_portlist[i]; i++) {
-		if ((sp->subtyp = probe_elsa_adr(CARD_portlist[i])))
+		if ((cs->subtyp = probe_elsa_adr(CARD_portlist[i], cs->typ)))
 			break;
 	}
 	return (CARD_portlist[i]);
 }
-#endif
+
+static 	int pci_index __initdata = 0;
 
 int
 setup_elsa(struct IsdnCard *card)
 {
-#ifdef CONFIG_HISAX_ELSA_PCC
 	long flags;
-#endif
 	int bytecnt;
-	u_char val, verA, verB;
-	struct IsdnCardState *sp = card->sp;
+	u_char val;
+	struct IsdnCardState *cs = card->cs;
 	char tmp[64];
 
 	strcpy(tmp, Elsa_revision);
-	printk(KERN_NOTICE "HiSax: Elsa driver Rev. %s\n", HiSax_getrev(tmp));
-#ifdef CONFIG_HISAX_ELSA_PCC
-	if (sp->typ == ISDN_CTYPE_ELSA) {
-		sp->cfg_reg = card->para[0];
+	printk(KERN_INFO "HiSax: Elsa driver Rev. %s\n", HiSax_getrev(tmp));
+	cs->hw.elsa.ctrl_reg = 0;
+	cs->hw.elsa.status = 0;
+	if (cs->typ == ISDN_CTYPE_ELSA) {
+		cs->hw.elsa.base = card->para[0];
 		printk(KERN_INFO "Elsa: Microlink IO probing\n");
-		if (sp->cfg_reg) {
-			if (!(sp->subtyp = probe_elsa_adr(sp->cfg_reg))) {
+		if (cs->hw.elsa.base) {
+			if (!(cs->subtyp = probe_elsa_adr(cs->hw.elsa.base,
+							  cs->typ))) {
 				printk(KERN_WARNING
 				     "Elsa: no Elsa Microlink at 0x%x\n",
-				       sp->cfg_reg);
+				       cs->hw.elsa.base);
 				return (0);
 			}
 		} else
-			sp->cfg_reg = probe_elsa(sp);
-		if (sp->cfg_reg) {
-			val = bytein(sp->cfg_reg + CARD_CONFIG);
-			if (sp->subtyp == ELSA_PC) {
+			cs->hw.elsa.base = probe_elsa(cs);
+		if (cs->hw.elsa.base) {
+			cs->hw.elsa.cfg = cs->hw.elsa.base + ELSA_CONFIG;
+			cs->hw.elsa.ctrl = cs->hw.elsa.base + ELSA_CONTROL;
+			cs->hw.elsa.ale = cs->hw.elsa.base + ELSA_ALE;
+			cs->hw.elsa.isac = cs->hw.elsa.base + ELSA_ISAC;
+			cs->hw.elsa.itac = cs->hw.elsa.base + ELSA_ITAC;
+			cs->hw.elsa.hscx = cs->hw.elsa.base + ELSA_HSCX;
+			cs->hw.elsa.trig = cs->hw.elsa.base + ELSA_TRIG_IRQ;
+			cs->hw.elsa.timer = cs->hw.elsa.base + ELSA_START_TIMER;
+			val = bytein(cs->hw.elsa.cfg);
+			if (cs->subtyp == ELSA_PC) {
 				const u_char CARD_IrqTab[8] =
 				{7, 3, 5, 9, 0, 0, 0, 0};
-				sp->irq = CARD_IrqTab[(val & IRQ_INDEX_PC) >> 2];
-			} else if (sp->subtyp == ELSA_PCC8) {
+				cs->irq = CARD_IrqTab[(val & ELSA_IRQ_IDX_PC) >> 2];
+			} else if (cs->subtyp == ELSA_PCC8) {
 				const u_char CARD_IrqTab[8] =
 				{7, 3, 5, 9, 0, 0, 0, 0};
-				sp->irq = CARD_IrqTab[(val & IRQ_INDEX_PCC8) >> 4];
+				cs->irq = CARD_IrqTab[(val & ELSA_IRQ_IDX_PCC8) >> 4];
 			} else {
 				const u_char CARD_IrqTab[8] =
 				{15, 10, 15, 3, 11, 5, 11, 9};
-				sp->irq = CARD_IrqTab[(val & IRQ_INDEX) >> 3];
+				cs->irq = CARD_IrqTab[(val & ELSA_IRQ_IDX) >> 3];
 			}
-			val = bytein(sp->cfg_reg + CARD_ALE) & 0x7;
+			val = bytein(cs->hw.elsa.ale) & ELSA_HW_RELEASE;
 			if (val < 3)
 				val |= 8;
 			val += 'A' - 3;
 			if (val == 'B' || val == 'C')
 				val ^= 1;
-			if ((sp->subtyp == ELSA_PCFPRO) && (val = 'G'))
+			if ((cs->subtyp == ELSA_PCFPRO) && (val = 'G'))
 				val = 'C';
 			printk(KERN_INFO
 			       "Elsa: %s found at 0x%x Rev.:%c IRQ %d\n",
-			       Elsa_Types[sp->subtyp],
-			       sp->cfg_reg,
-			       val, sp->irq);
-			val = bytein(sp->cfg_reg + CARD_ALE) & 0x08;
-			if (val)
+			       Elsa_Types[cs->subtyp],
+			       cs->hw.elsa.base,
+			       val, cs->irq);
+			val = bytein(cs->hw.elsa.ale) & ELSA_S0_POWER_BAD;
+			if (val) {
 				printk(KERN_WARNING
 				   "Elsa: Microlink S0 bus power bad\n");
+				cs->hw.elsa.status |= ELSA_BAD_PWR;
+			}
 		} else {
 			printk(KERN_WARNING
 			       "No Elsa Microlink found\n");
 			return (0);
 		}
-	} else if (sp->typ == ISDN_CTYPE_ELSA_QS1000) {
-		sp->cfg_reg = card->para[1];
-		sp->irq = card->para[0];
-		sp->subtyp = ELSA_QS1000;
+	} else if (cs->typ == ISDN_CTYPE_ELSA_PNP) {
+		cs->hw.elsa.base = card->para[1];
+		cs->irq = card->para[0];
+		cs->subtyp = ELSA_QS1000;
+		cs->hw.elsa.cfg = cs->hw.elsa.base + ELSA_CONFIG;
+		cs->hw.elsa.ale = cs->hw.elsa.base + ELSA_ALE;
+		cs->hw.elsa.isac = cs->hw.elsa.base + ELSA_ISAC;
+		cs->hw.elsa.hscx = cs->hw.elsa.base + ELSA_HSCX;
+		cs->hw.elsa.trig = cs->hw.elsa.base + ELSA_TRIG_IRQ;
+		cs->hw.elsa.timer = cs->hw.elsa.base + ELSA_START_TIMER;
+		cs->hw.elsa.ctrl = cs->hw.elsa.base + ELSA_CONTROL;
 		printk(KERN_INFO
-		       "Elsa: %s found at 0x%x IRQ %d\n",
-		       Elsa_Types[sp->subtyp],
-		       sp->cfg_reg,
-		       sp->irq);
-	} else
-		return (0);
-#endif
-#ifdef CONFIG_HISAX_ELSA_PCMCIA
-	if (sp->typ == ISDN_CTYPE_ELSA_QS1000) {
-		sp->cfg_reg = card->para[1];
-		sp->irq = card->para[0];
-		sp->subtyp = ELSA_PCMCIA;
+		       "Elsa: %s defined at 0x%x IRQ %d\n",
+		       Elsa_Types[cs->subtyp],
+		       cs->hw.elsa.base,
+		       cs->irq);
+	} else if (cs->typ == ISDN_CTYPE_ELSA_PCMCIA) {
+		cs->hw.elsa.base = card->para[1];
+		cs->irq = card->para[0];
+		cs->subtyp = ELSA_PCMCIA;
+		cs->hw.elsa.ale = cs->hw.elsa.base + ELSA_ALE_PCM;
+		cs->hw.elsa.isac = cs->hw.elsa.base + ELSA_ISAC_PCM;
+		cs->hw.elsa.hscx = cs->hw.elsa.base + ELSA_HSCX;
+		cs->hw.elsa.timer = 0;
+		cs->hw.elsa.trig = 0;
+		cs->hw.elsa.ctrl = 0;
 		printk(KERN_INFO
-		       "Elsa: %s found at 0x%x IRQ %d\n",
-		       Elsa_Types[sp->subtyp],
-		       sp->cfg_reg,
-		       sp->irq);
-	} else
+		       "Elsa: %s defined at 0x%x IRQ %d\n",
+		       Elsa_Types[cs->subtyp],
+		       cs->hw.elsa.base,
+		       cs->irq);
+	} else if (cs->typ == ISDN_CTYPE_ELSA_PCI) {
+#if CONFIG_PCI
+		u_char pci_bus, pci_device_fn, pci_irq;
+		u_int pci_ioaddr;
+
+		cs->subtyp = 0;
+		for (; pci_index < 0xff; pci_index++) {
+			if (pcibios_find_device(PCI_VENDOR_ELSA,
+			   PCI_QS1000_ID, pci_index, &pci_bus, &pci_device_fn)
+			   == PCIBIOS_SUCCESSFUL)
+				cs->subtyp = ELSA_QS1000PCI;
+			else
+				break;
+			/* get IRQ */
+			pcibios_read_config_byte(pci_bus, pci_device_fn,
+				PCI_INTERRUPT_LINE, &pci_irq);
+
+			/* get IO address */
+			pcibios_read_config_dword(pci_bus, pci_device_fn,
+				PCI_BASE_ADDRESS_1, &pci_ioaddr);
+			pci_ioaddr &= ~3; /* remove io/mem flag */
+			cs->hw.elsa.cfg = pci_ioaddr;
+			pcibios_read_config_dword(pci_bus, pci_device_fn,
+				PCI_BASE_ADDRESS_3, &pci_ioaddr);
+			if (cs->subtyp)
+				break;
+		}
+		if (!cs->subtyp) {
+			printk(KERN_WARNING "Elsa: No PCI card found\n");
+			return(0);
+		}
+		if (!pci_irq) {
+			printk(KERN_WARNING "Elsa: No IRQ for PCI card found\n");
+			return(0);
+		}
+
+		if (!pci_ioaddr) {
+			printk(KERN_WARNING "Elsa: No IO-Adr for PCI card found\n");
+			return(0);
+		}
+		pci_ioaddr &= ~3; /* remove io/mem flag */
+		cs->hw.elsa.base = pci_ioaddr;
+		cs->hw.elsa.ale  = pci_ioaddr;
+		cs->hw.elsa.isac = pci_ioaddr +1;
+		cs->hw.elsa.hscx = pci_ioaddr +1; 
+		cs->irq = pci_irq;
+		test_and_set_bit(HW_IPAC, &cs->HW_Flags);
+		cs->hw.elsa.timer = 0;
+		cs->hw.elsa.trig  = 0;
+		printk(KERN_INFO
+		       "Elsa: %s defined at 0x%x/0x%x IRQ %d\n",
+		       Elsa_Types[cs->subtyp],
+		       cs->hw.elsa.base,
+		       cs->hw.elsa.cfg,
+		       cs->irq);
+#else
+		printk(KERN_WARNING "Elsa: Elsa PCI and NO_PCI_BIOS\n");
+		printk(KERN_WARNING "Elsa: unable to config Elsa PCI\n");
+		return (0);
+#endif /* CONFIG_PCI */
+	} else 
 		return (0);
-#endif
 
-	switch (sp->subtyp) {
+	switch (cs->subtyp) {
 		case ELSA_PC:
-			bytecnt = 8;
-			break;
 		case ELSA_PCC8:
-			bytecnt = 8;
-			break;
-		case ELSA_PCFPRO:
-			bytecnt = 16;
-			break;
 		case ELSA_PCC16:
+		case ELSA_QS1000:
+		case ELSA_PCMCIA:
 			bytecnt = 8;
 			break;
+		case ELSA_PCFPRO:
 		case ELSA_PCF:
 			bytecnt = 16;
 			break;
-		case ELSA_QS1000:
-			bytecnt = 8;
-			break;
-		case ELSA_PCMCIA:
-			bytecnt = 8;
+		case ELSA_QS1000PCI:
+			bytecnt = 2;
 			break;
 		default:
 			printk(KERN_WARNING
-			       "Unknown ELSA subtype %d\n", sp->subtyp);
+			       "Unknown ELSA subtype %d\n", cs->subtyp);
 			return (0);
 	}
-
-	if (check_region((sp->cfg_reg), bytecnt)) {
+	/* In case of the elsa pcmcia card, this region is in use,
+	   reserved for us by the card manager. So we do not check it
+	   here, it would fail. */
+	if (cs->typ != ISDN_CTYPE_ELSA_PCMCIA && check_region(cs->hw.elsa.base, bytecnt)) {
 		printk(KERN_WARNING
 		       "HiSax: %s config port %x-%x already in use\n",
 		       CardType[card->typ],
-		       sp->cfg_reg,
-		       sp->cfg_reg + bytecnt);
+		       cs->hw.elsa.base,
+		       cs->hw.elsa.base + bytecnt);
 		return (0);
 	} else {
-		request_region(sp->cfg_reg, bytecnt, "elsa isdn");
+		request_region(cs->hw.elsa.base, bytecnt, "elsa isdn");
 	}
-
-	/* Teste Timer */
-#ifdef CONFIG_HISAX_ELSA_PCC
-	byteout(sp->cfg_reg + CARD_TRIG_IRQ, 0xff);
-	byteout(sp->cfg_reg + CARD_START_TIMER, 0);
-	if (!TimerRun(sp)) {
-		byteout(sp->cfg_reg + CARD_START_TIMER, 0);	/* 2. Versuch */
-		if (!TimerRun(sp)) {
+	if (cs->subtyp == ELSA_QS1000PCI) {
+		if (check_region(cs->hw.elsa.cfg, 0x80)) {
 			printk(KERN_WARNING
-			       "Elsa: timer do not start\n");
-			release_io_elsa(card);
+			       "HiSax: %s pci port %x-%x already in use\n",
+				CardType[card->typ],
+				cs->hw.elsa.cfg,
+				cs->hw.elsa.cfg + 0x80);
+			release_region(cs->hw.elsa.base, bytecnt);
 			return (0);
+		} else {
+			request_region(cs->hw.elsa.cfg, 0x80, "elsa isdn pci");
 		}
 	}
-	save_flags(flags);
-	sti();
-	HZDELAY(1);		/* wait >=10 ms */
-	restore_flags(flags);
-	if (TimerRun(sp)) {
-		printk(KERN_WARNING "Elsa: timer do not run down\n");
-		release_io_elsa(card);
-		return (0);
+	cs->hw.elsa.tl.function = (void *) elsa_led_handler;
+	cs->hw.elsa.tl.data = (long) cs;
+	init_timer(&cs->hw.elsa.tl);
+	/* Teste Timer */
+	if (cs->hw.elsa.timer) {
+		byteout(cs->hw.elsa.trig, 0xff);
+		byteout(cs->hw.elsa.timer, 0);
+		if (!TimerRun(cs)) {
+			byteout(cs->hw.elsa.timer, 0);	/* 2. Versuch */
+			if (!TimerRun(cs)) {
+				printk(KERN_WARNING
+				       "Elsa: timer do not start\n");
+				release_io_elsa(cs);
+				return (0);
+			}
+		}
+		save_flags(flags);
+		sti();
+		HZDELAY(1);	/* wait >=10 ms */
+		restore_flags(flags);
+		if (TimerRun(cs)) {
+			printk(KERN_WARNING "Elsa: timer do not run down\n");
+			release_io_elsa(cs);
+			return (0);
+		}
+		printk(KERN_INFO "Elsa: timer OK; resetting card\n");
 	}
-	printk(KERN_INFO "Elsa: timer OK; resetting card\n");
-	reset_elsa(sp);
-#endif
-	verA = readhscx(sp->cfg_reg, 0, HSCX_VSTR) & 0xf;
-	verB = readhscx(sp->cfg_reg, 1, HSCX_VSTR) & 0xf;
-	printk(KERN_INFO "Elsa: HSCX version A: %s  B: %s\n",
-	       HscxVersion(verA), HscxVersion(verB));
-	val = readisac(sp->cfg_reg, ISAC_RBCH);
-	printk(KERN_INFO "Elsa: ISAC %s\n",
-	       ISACVersion(val));
-
-#ifdef CONFIG_HISAX_ELSA_PCMCIA
-	if ((verA == 0) | (verA == 0xf) | (verB == 0) | (verB == 0xf)) {
-		printk(KERN_WARNING
-		       "Elsa: wrong HSCX versions check IO address\n");
-		release_io_elsa(card);
-		return (0);
+	reset_elsa(cs);
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &Elsa_card_msg;
+	if (cs->subtyp == ELSA_QS1000PCI) {
+		cs->readisac = &ReadISAC_IPAC;
+		cs->writeisac = &WriteISAC_IPAC;
+		cs->readisacfifo = &ReadISACfifo_IPAC;
+		cs->writeisacfifo = &WriteISACfifo_IPAC;
+		val = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ID);
+		printk(KERN_INFO "Elsa: IPAC version %x\n", val);
+	} else {
+		cs->readisac = &ReadISAC;
+		cs->writeisac = &WriteISAC;
+		cs->readisacfifo = &ReadISACfifo;
+		cs->writeisacfifo = &WriteISACfifo;
+		ISACVersion(cs, "Elsa:");
+		if (HscxVersion(cs, "Elsa:")) {
+			printk(KERN_WARNING
+				"Elsa: wrong HSCX versions check IO address\n");
+			release_io_elsa(cs);
+			return (0);
+		}
 	}
-#endif
-
-#ifdef CONFIG_HISAX_ELSA_PCC
-	if (sp->subtyp == ELSA_PC) {
-		val = readitac(sp->cfg_reg, ITAC_SYS);
+	if (cs->subtyp == ELSA_PC) {
+		val = readitac(cs, ITAC_SYS);
 		printk(KERN_INFO "Elsa: ITAC version %s\n", ITACVer[val & 7]);
-		writeitac(sp->cfg_reg, ITAC_ISEN, 0);
-		writeitac(sp->cfg_reg, ITAC_RFIE, 0);
-		writeitac(sp->cfg_reg, ITAC_XFIE, 0);
-		writeitac(sp->cfg_reg, ITAC_SCIE, 0);
-		writeitac(sp->cfg_reg, ITAC_STIE, 0);
+		writeitac(cs, ITAC_ISEN, 0);
+		writeitac(cs, ITAC_RFIE, 0);
+		writeitac(cs, ITAC_XFIE, 0);
+		writeitac(cs, ITAC_SCIE, 0);
+		writeitac(cs, ITAC_STIE, 0);
 	}
-#endif
-	sp->modehscx = &modehscx;
-	sp->ph_command = &ph_command;
-	sp->hscx_fill_fifo = &hscx_fill_fifo;
-	sp->isac_fill_fifo = &isac_fill_fifo;
-
 	return (1);
 }
+
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/elsa.h linux/drivers/isdn/hisax/elsa.h
--- v2.1.91/linux/drivers/isdn/hisax/elsa.h	Mon Nov  3 10:08:48 1997
+++ linux/drivers/isdn/hisax/elsa.h	Wed Dec 31 16:00:00 1969
@@ -1,90 +0,0 @@
-/* $Id: elsa.h,v 1.6 1997/03/23 21:45:48 keil Exp $
- *
- * elsa.h   Header for Elsa ISDN cards
- *
- * Author	Karsten Keil (keil@temic-ech.spacenet.de)
- *
- * Thanks to    Elsa GmbH for documents and informations
- *
- *
- * $Log: elsa.h,v $
- * Revision 1.6  1997/03/23 21:45:48  keil
- * Add support for ELSA PCMCIA
- *
- * Revision 1.5  1997/03/04 15:58:13  keil
- * ELSA PC changes, some stuff for new cards
- *
- * Revision 1.4  1997/01/21 22:21:05  keil
- * Elsa Quickstep support
- *
- * Revision 1.3  1996/12/08 19:47:38  keil
- * ARCOFI support
- *
- * Revision 1.2  1996/11/18 15:33:35  keil
- * PCC and PCFPro support
- *
- * Revision 1.1  1996/10/13 20:03:45  keil
- * Initial revision
- *
- *
-*/
-#include <linux/config.h>
-
-#ifdef CONFIG_HISAX_ELSA_PCMCIA
-#define CARD_ISAC	1
-#define CARD_HSCX	2
-#define CARD_ALE	4
-#else
-#define CARD_ISAC	0
-#define CARD_ITAC	1
-#define CARD_HSCX	2
-#define CARD_ALE	3
-#define CARD_CONTROL	4
-#define CARD_CONFIG	5
-#define CARD_START_TIMER 6
-#define CARD_TRIG_IRQ	7
-#endif
-
-#define ELSA_PC      1
-#define ELSA_PCC8    2
-#define ELSA_PCC16   3
-#define ELSA_PCF     4
-#define ELSA_PCFPRO  5
-#define ELSA_PCMCIA  6
-#define ELSA_QS1000  7
-#define ELSA_QS3000  8
-
-/* ITAC Registeradressen (only Microlink PC) */
-#define ITAC_SYS	0x34
-#define ITAC_ISEN	0x48
-#define ITAC_RFIE	0x4A
-#define ITAC_XFIE	0x4C
-#define ITAC_SCIE	0x4E
-#define ITAC_STIE	0x46
-
-/***                                                                    ***
- ***   Makros als Befehle fuer die Kartenregister                       ***
- ***   (mehrere Befehle werden durch Bit-Oderung kombiniert)            ***
- ***                                                                    ***/
-
-/* Config-Register (Read) */
-#define TIMER_RUN       0x02    /* Bit 1 des Config-Reg     */
-#define TIMER_RUN_PCC8  0x01    /* Bit 0 des Config-Reg  bei PCC */
-#define IRQ_INDEX       0x38    /* Bit 3,4,5 des Config-Reg */
-#define IRQ_INDEX_PCC8  0x30    /* Bit 4,5 des Config-Reg */
-#define IRQ_INDEX_PC    0x0c    /* Bit 2,3 des Config-Reg */
-
-/* Control-Register (Write) */
-#define LINE_LED        0x02    /* Bit 1 Gelbe LED */
-#define STAT_LED        0x08    /* Bit 3 Gruene LED */
-#define ISDN_RESET      0x20    /* Bit 5 Reset-Leitung */
-#define ENABLE_TIM_INT  0x80    /* Bit 7 Freigabe Timer Interrupt */
-
-/* ALE-Register (Read) */
-#define HW_RELEASE      0x07    /* Bit 0-2 Hardwarerkennung */
-#define S0_POWER_BAD    0x08    /* Bit 3 S0-Bus Spannung fehlt */
-
-extern	void elsa_report(struct IsdnCardState *sp);
-extern  void release_io_elsa(struct IsdnCard *card);
-extern	int  setup_elsa(struct IsdnCard *card);
-extern  int  initelsa(struct IsdnCardState *sp);
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/fsm.c linux/drivers/isdn/hisax/fsm.c
--- v2.1.91/linux/drivers/isdn/hisax/fsm.c	Thu May 29 21:53:05 1997
+++ linux/drivers/isdn/hisax/fsm.c	Wed Apr  1 16:20:58 1998
@@ -1,4 +1,4 @@
-/* $Id: fsm.c,v 1.4 1997/04/06 22:56:42 keil Exp $
+/* $Id: fsm.c,v 1.7 1997/11/06 17:09:13 keil Exp $
 
  * Author       Karsten Keil (keil@temic-ech.spacenet.de)
  *              based on the teles driver from Jan den Ouden
@@ -7,6 +7,15 @@
  *              Fritz Elfert
  *
  * $Log: fsm.c,v $
+ * Revision 1.7  1997/11/06 17:09:13  keil
+ * New 2.1 init code
+ *
+ * Revision 1.6  1997/07/27 21:42:25  keil
+ * proof Fsm routines
+ *
+ * Revision 1.5  1997/06/26 11:10:05  keil
+ * Restart timer function added
+ *
  * Revision 1.4  1997/04/06 22:56:42  keil
  * Some cosmetic changes
  *
@@ -26,9 +35,9 @@
 
 #define FSM_TIMER_DEBUG 0
 
-void
+HISAX_INITFUNC(void
 FsmNew(struct Fsm *fsm,
-       struct FsmNode *fnlist, int fncount)
+       struct FsmNode *fnlist, int fncount))
 {
 	int i;
 
@@ -36,9 +45,14 @@
 	    kmalloc(4L * fsm->state_count * fsm->event_count, GFP_KERNEL);
 	memset(fsm->jumpmatrix, 0, 4L * fsm->state_count * fsm->event_count);
 
-	for (i = 0; i < fncount; i++)
-		fsm->jumpmatrix[fsm->state_count * fnlist[i].event +
-			      fnlist[i].state] = (int) fnlist[i].routine;
+	for (i = 0; i < fncount; i++) 
+		if ((fnlist[i].state>=fsm->state_count) || (fnlist[i].event>=fsm->event_count)) {
+			printk(KERN_ERR "FsmNew Error line %d st(%d/%d) ev(%d/%d)\n",
+				i,fnlist[i].state,fsm->state_count,
+				fnlist[i].event,fsm->event_count);
+		} else		
+			fsm->jumpmatrix[fsm->state_count * fnlist[i].event +
+				fnlist[i].state] = (int) fnlist[i].routine;
 }
 
 void
@@ -53,6 +67,11 @@
 	void (*r) (struct FsmInst *, int, void *);
 	char str[80];
 
+	if ((fi->state>=fi->fsm->state_count) || (event >= fi->fsm->event_count)) {
+		printk(KERN_ERR "FsmEvent Error st(%d/%d) ev(%d/%d)\n",
+			fi->state,fi->fsm->state_count,event,fi->fsm->event_count);
+		return(1);
+	}
 	r = (void (*)) fi->fsm->jumpmatrix[fi->fsm->state_count * event + fi->state];
 	if (r) {
 		if (fi->debug) {
@@ -155,10 +174,26 @@
 	return 0;
 }
 
-int
-FsmTimerRunning(struct FsmTimer *ft)
+void
+FsmRestartTimer(struct FsmTimer *ft,
+	    int millisec, int event, void *arg, int where)
 {
-	return (ft->tl.next != NULL);
+
+#if FSM_TIMER_DEBUG
+	if (ft->fi->debug) {
+		char str[40];
+		sprintf(str, "FsmRestartTimer %lx %d %d", (long) ft, millisec, where);
+		ft->fi->printdebug(ft->fi, str);
+	}
+#endif
+
+	if (ft->tl.next || ft->tl.prev)
+		del_timer(&ft->tl);
+	init_timer(&ft->tl);
+	ft->event = event;
+	ft->arg = arg;
+	ft->tl.expires = jiffies + (millisec * HZ) / 1000;
+	add_timer(&ft->tl);
 }
 
 void
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/hfc_2bds0.c linux/drivers/isdn/hisax/hfc_2bds0.c
--- v2.1.91/linux/drivers/isdn/hisax/hfc_2bds0.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/hfc_2bds0.c	Wed Apr  1 16:20:58 1998
@@ -0,0 +1,1297 @@
+/* $Id: hfc_2bds0.c,v 1.3 1998/02/12 23:07:22 keil Exp $
+ *
+ *  specific routines for CCD's HFC 2BDS0
+ *
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ *
+ * $Log: hfc_2bds0.c,v $
+ * Revision 1.3  1998/02/12 23:07:22  keil
+ * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
+ *
+ * Revision 1.2  1998/02/02 13:26:13  keil
+ * New
+ *
+ *
+ *
+ */
+#define __NO_VERSION__
+#include "hisax.h"
+#include "hfc_2bds0.h"
+#include "isdnl1.h"
+#include <linux/interrupt.h>
+/*
+#define KDEBUG_DEF
+#include "kdebug.h"
+*/
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+static void
+dummyf(struct IsdnCardState *cs, u_char * data, int size)
+{
+	printk(KERN_WARNING "HiSax: hfcd dummy fifo called\n");
+}
+
+static inline u_char
+ReadReg(struct IsdnCardState *cs, int data, u_char reg)
+{
+	register u_char ret;
+
+	if (data) {
+		if (cs->hw.hfcD.cip != reg) { 
+			cs->hw.hfcD.cip = reg;
+			byteout(cs->hw.hfcD.addr | 1, reg);
+		}
+		ret = bytein(cs->hw.hfcD.addr);
+#if HFC_REG_DEBUG
+		if (cs->debug & L1_DEB_HSCX_FIFO && (data != 2)) {
+			char tmp[32];
+			sprintf(tmp, "t3c RD %02x %02x", reg, ret);
+			debugl1(cs, tmp);
+		}
+#endif
+	} else
+		ret = bytein(cs->hw.hfcD.addr | 1);
+	return (ret);
+}
+
+static inline void
+WriteReg(struct IsdnCardState *cs, int data, u_char reg, u_char value)
+{
+	if (cs->hw.hfcD.cip != reg) { 
+		cs->hw.hfcD.cip = reg;
+		byteout(cs->hw.hfcD.addr | 1, reg);
+	}
+	if (data)
+		byteout(cs->hw.hfcD.addr, value);
+#if HFC_REG_DEBUG
+	if (cs->debug & L1_DEB_HSCX_FIFO && (data != HFCD_DATA_NODEB)) {
+		char tmp[16];
+		sprintf(tmp, "t3c W%c %02x %02x", data ? 'D' : 'C', reg, value);
+		debugl1(cs, tmp);
+	}
+#endif
+}
+
+/* Interface functions */
+
+static u_char
+readreghfcd(struct IsdnCardState *cs, u_char offset)
+{
+	return(ReadReg(cs, HFCD_DATA, offset));
+}
+
+static void
+writereghfcd(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	WriteReg(cs, HFCD_DATA, offset, value);
+}
+
+void
+set_cs_func(struct IsdnCardState *cs)
+{
+	cs->readisac = &readreghfcd;
+	cs->writeisac = &writereghfcd;
+	cs->readisacfifo = &dummyf;
+	cs->writeisacfifo = &dummyf;
+	cs->BC_Read_Reg = &ReadReg;
+	cs->BC_Write_Reg = &WriteReg;
+}
+
+static inline int
+WaitForBusy(struct IsdnCardState *cs)
+{
+	int to = 130;
+
+	while (!(ReadReg(cs, HFCD_DATA, HFCD_STAT) & HFCD_BUSY) && to) {
+		udelay(1);
+		to--;
+	}
+	if (!to)
+		printk(KERN_WARNING "HiSax: WaitForBusy timeout\n");
+	return (to);
+}
+
+static inline int
+WaitNoBusy(struct IsdnCardState *cs)
+{
+	long flags;
+	int to = 130;
+
+	while ((ReadReg(cs, HFCD_STATUS, HFCD_STATUS) & HFCD_BUSY) && to) {
+		save_flags(flags);
+		sti();
+		udelay(1);
+		to--;
+		restore_flags(flags);
+	}
+	if (!to) 
+		printk(KERN_WARNING "HiSax: WaitNoBusy timeout\n");
+	return (to);
+}
+
+static int
+SelFiFo(struct IsdnCardState *cs, u_char FiFo)
+{
+	u_char cip;
+	long flags;
+
+
+	if (cs->hw.hfcD.fifo == FiFo)
+		return(1);
+	save_flags(flags);
+	cli();
+	switch(FiFo) {
+		case 0: cip = HFCB_FIFO | HFCB_Z1 | HFCB_SEND | HFCB_B1;
+			break;
+		case 1: cip = HFCB_FIFO | HFCB_Z1 | HFCB_REC | HFCB_B1;
+			break;
+		case 2: cip = HFCB_FIFO | HFCB_Z1 | HFCB_SEND | HFCB_B2;
+			break;
+		case 3: cip = HFCB_FIFO | HFCB_Z1 | HFCB_REC | HFCB_B2;
+			break;
+		case 4: cip = HFCD_FIFO | HFCD_Z1 | HFCD_SEND;
+			break;
+		case 5: cip = HFCD_FIFO | HFCD_Z1 | HFCD_REC;
+			break;
+		default:
+			restore_flags(flags);
+			debugl1(cs, "SelFiFo Error");
+			return(0);
+	}
+	cs->hw.hfcD.fifo = FiFo;
+	WaitNoBusy(cs);
+	cs->BC_Write_Reg(cs, HFCD_DATA, cip, 0);
+	sti();
+	WaitForBusy(cs);
+	restore_flags(flags);
+	return(2);
+}
+static int
+GetFreeFifoBytes_B(struct BCState *bcs)
+{
+	int s;
+
+	if (bcs->hw.hfc.f1 == bcs->hw.hfc.f2)
+		return (bcs->cs->hw.hfcD.bfifosize);
+	s = bcs->hw.hfc.send[bcs->hw.hfc.f1] - bcs->hw.hfc.send[bcs->hw.hfc.f2];
+	if (s <= 0)
+		s += bcs->cs->hw.hfcD.bfifosize;
+	s = bcs->cs->hw.hfcD.bfifosize - s;
+	return (s);
+}
+
+static int
+GetFreeFifoBytes_D(struct IsdnCardState *cs)
+{
+	int s;
+
+	if (cs->hw.hfcD.f1 == cs->hw.hfcD.f2)
+		return (cs->hw.hfcD.dfifosize);
+	s = cs->hw.hfcD.send[cs->hw.hfcD.f1] - cs->hw.hfcD.send[cs->hw.hfcD.f2];
+	if (s <= 0)
+		s += cs->hw.hfcD.dfifosize;
+	s = cs->hw.hfcD.dfifosize - s;
+	return (s);
+}
+
+static int
+ReadZReg(struct IsdnCardState *cs, u_char reg)
+{
+	int val;
+
+	WaitNoBusy(cs);
+	val = 256 * ReadReg(cs, HFCD_DATA, reg | HFCB_Z_HIGH);
+	WaitNoBusy(cs);
+	val += ReadReg(cs, HFCD_DATA, reg | HFCB_Z_LOW);
+	return (val);
+}
+
+static void
+hfc_sched_event(struct BCState *bcs, int event)
+{
+	bcs->event |= 1 << event;
+	queue_task(&bcs->tqueue, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+}
+
+static struct sk_buff
+*hfc_empty_fifo(struct BCState *bcs, int count)
+{
+	u_char *ptr;
+	struct sk_buff *skb;
+	struct IsdnCardState *cs = bcs->cs;
+	int idx;
+	int chksum;
+	long flags;
+	u_char stat, cip;
+	char tmp[64];
+	
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "hfc_empty_fifo");
+	idx = 0;
+	save_flags(flags);
+	if (count > HSCX_BUFMAX + 3) {
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "hfc_empty_fifo: incoming packet too large");
+		cip = HFCB_FIFO | HFCB_FIFO_OUT | HFCB_REC | HFCB_CHANNEL(bcs->channel);
+		while (idx++ < count) {
+			cli();
+			WaitNoBusy(cs);
+			ReadReg(cs, HFCD_DATA_NODEB, cip);
+			sti();
+		}
+		skb = NULL;
+	} else if (count < 4) {
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "hfc_empty_fifo: incoming packet too small");
+		cip = HFCB_FIFO | HFCB_FIFO_OUT | HFCB_REC | HFCB_CHANNEL(bcs->channel);
+		cli();
+		while ((idx++ < count) && WaitNoBusy(cs))
+			ReadReg(cs, HFCD_DATA_NODEB, cip);
+		skb = NULL;
+	} else if (!(skb = dev_alloc_skb(count - 3)))
+		printk(KERN_WARNING "HFC: receive out of memory\n");
+	else {
+		ptr = skb_put(skb, count - 3);
+		idx = 0;
+		cip = HFCB_FIFO | HFCB_FIFO_OUT | HFCB_REC | HFCB_CHANNEL(bcs->channel);
+		cli();
+		while (idx < (count - 3)) {
+			cli();
+			if (!WaitNoBusy(cs))
+				break;
+			*ptr = ReadReg(cs,  HFCD_DATA_NODEB, cip);
+			sti();
+			ptr++;
+			idx++;
+		}
+		if (idx != count - 3) {
+			sti();
+			debugl1(cs, "RFIFO BUSY error");
+			printk(KERN_WARNING "HFC FIFO channel %d BUSY Error\n", bcs->channel);
+			dev_kfree_skb(skb);
+			skb = NULL;
+		} else {
+			cli();
+			WaitNoBusy(cs);
+			chksum = (ReadReg(cs, HFCD_DATA, cip) << 8);
+			WaitNoBusy(cs);
+			chksum += ReadReg(cs, HFCD_DATA, cip);
+			WaitNoBusy(cs);
+			stat = ReadReg(cs, HFCD_DATA, cip);
+			sti();
+			if (cs->debug & L1_DEB_HSCX) {
+				sprintf(tmp, "hfc_empty_fifo %d chksum %x stat %x",
+					bcs->channel, chksum, stat);
+				debugl1(cs, tmp);
+			}
+			if (stat) {
+				debugl1(cs, "FIFO CRC error");
+				dev_kfree_skb(skb);
+				skb = NULL;
+			}
+		}
+	}
+	sti();
+	WaitForBusy(cs);
+	cli();
+	WaitNoBusy(cs);
+	stat = ReadReg(cs, HFCD_DATA, HFCB_FIFO | HFCB_F2_INC |
+		HFCB_REC | HFCB_CHANNEL(bcs->channel));
+	sti();
+	WaitForBusy(cs);
+	restore_flags(flags);
+	return (skb);
+}
+
+static void
+hfc_fill_fifo(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	long flags;
+	int idx, fcnt;
+	int count;
+	u_char cip;
+	char tmp[64];
+	
+
+	if (!bcs->hw.hfc.tx_skb)
+		return;
+	if (bcs->hw.hfc.tx_skb->len <= 0)
+		return;
+
+	save_flags(flags);
+	cli();
+	SelFiFo(cs, HFCB_SEND | HFCB_CHANNEL(bcs->channel)); 
+	cip = HFCB_FIFO | HFCB_F1 | HFCB_SEND | HFCB_CHANNEL(bcs->channel);
+	WaitNoBusy(cs);
+	bcs->hw.hfc.f1 = ReadReg(cs, HFCD_DATA, cip);
+	WaitNoBusy(cs);
+	cip = HFCB_FIFO | HFCB_F2 | HFCB_SEND | HFCB_CHANNEL(bcs->channel);
+	WaitNoBusy(cs);
+	bcs->hw.hfc.f2 = ReadReg(cs, HFCD_DATA, cip);
+	bcs->hw.hfc.send[bcs->hw.hfc.f1] = ReadZReg(cs, HFCB_FIFO | HFCB_Z1 | HFCB_SEND | HFCB_CHANNEL(bcs->channel));
+	sti();
+ 	if (cs->debug & L1_DEB_HSCX) {
+		sprintf(tmp, "hfc_fill_fifo %d f1(%d) f2(%d) z1(%x)",
+			bcs->channel, bcs->hw.hfc.f1, bcs->hw.hfc.f2,
+			bcs->hw.hfc.send[bcs->hw.hfc.f1]);
+		debugl1(cs, tmp);
+	}
+	fcnt = bcs->hw.hfc.f1 - bcs->hw.hfc.f2;
+	if (fcnt < 0)
+		fcnt += 32;
+	if (fcnt > 30) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfc_fill_fifo more as 30 frames");
+		restore_flags(flags);
+		return;
+	}
+	count = GetFreeFifoBytes_B(bcs);
+	if (cs->debug & L1_DEB_HSCX) {
+		sprintf(tmp, "hfc_fill_fifo %d count(%d/%d),%lx",
+			bcs->channel, bcs->hw.hfc.tx_skb->len,
+			count, current->state);
+		debugl1(cs, tmp);
+	}
+	if (count < bcs->hw.hfc.tx_skb->len) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfc_fill_fifo no fifo mem");
+		restore_flags(flags);
+		return;
+	}
+	cip = HFCB_FIFO | HFCB_FIFO_IN | HFCB_SEND | HFCB_CHANNEL(bcs->channel);
+	idx = 0;
+	cli();
+	WaitForBusy(cs);
+	WaitNoBusy(cs);
+	WriteReg(cs, HFCD_DATA_NODEB, cip, bcs->hw.hfc.tx_skb->data[idx++]);
+	while (idx < bcs->hw.hfc.tx_skb->len) {
+		cli();
+		if (!WaitNoBusy(cs))
+			break;
+		WriteReg(cs, HFCD_DATA_NODEB, cip, bcs->hw.hfc.tx_skb->data[idx]);
+		sti();
+		idx++;
+	}
+	if (idx != bcs->hw.hfc.tx_skb->len) {
+		sti();
+		debugl1(cs, "FIFO Send BUSY error");
+		printk(KERN_WARNING "HFC S FIFO channel %d BUSY Error\n", bcs->channel);
+	} else {
+		bcs->tx_cnt -= bcs->hw.hfc.tx_skb->len;
+		if (bcs->st->lli.l1writewakeup &&
+			(PACKET_NOACK != bcs->hw.hfc.tx_skb->pkt_type))
+			bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.hfc.tx_skb->len);
+		dev_kfree_skb(bcs->hw.hfc.tx_skb);
+		bcs->hw.hfc.tx_skb = NULL;
+	}
+	WaitForBusy(cs);
+	cli();
+	WaitNoBusy(cs);
+	ReadReg(cs, HFCD_DATA, HFCB_FIFO | HFCB_F1_INC | HFCB_SEND | HFCB_CHANNEL(bcs->channel));
+	sti();
+	WaitForBusy(cs);
+	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	restore_flags(flags);
+	return;
+}
+
+static void
+hfc_send_data(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	char tmp[32];
+	
+	if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+		hfc_fill_fifo(bcs);
+		test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+	} else {
+		sprintf(tmp,"send_data %d blocked", bcs->channel);
+		debugl1(cs, tmp);
+	}
+}
+
+void
+main_rec_2bds0(struct BCState *bcs)
+{
+	long flags;
+	struct IsdnCardState *cs = bcs->cs;
+	int z1, z2, rcnt;
+	u_char f1, f2, cip;
+	int receive, count = 5;
+	struct sk_buff *skb;
+	char tmp[64];
+
+	save_flags(flags);
+    Begin:
+	count--;
+	cli();
+	if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+		sprintf(tmp,"rec_data %d blocked", bcs->channel);
+		debugl1(cs, tmp);
+		restore_flags(flags);
+		return;
+	}
+	SelFiFo(cs, HFCB_REC | HFCB_CHANNEL(bcs->channel));
+	cip = HFCB_FIFO | HFCB_F1 | HFCB_REC | HFCB_CHANNEL(bcs->channel);
+	WaitNoBusy(cs);
+	f1 = ReadReg(cs, HFCD_DATA, cip);
+	cip = HFCB_FIFO | HFCB_F2 | HFCB_REC | HFCB_CHANNEL(bcs->channel);
+	WaitNoBusy(cs);
+	f2 = ReadReg(cs, HFCD_DATA, cip);
+	sti();
+	if (f1 != f2) {
+		if (cs->debug & L1_DEB_HSCX) {
+			sprintf(tmp, "hfc rec %d f1(%d) f2(%d)",
+				bcs->channel, f1, f2);
+			debugl1(cs, tmp);
+		}
+		cli();
+		z1 = ReadZReg(cs, HFCB_FIFO | HFCB_Z1 | HFCB_REC | HFCB_CHANNEL(bcs->channel));
+		z2 = ReadZReg(cs, HFCB_FIFO | HFCB_Z2 | HFCB_REC | HFCB_CHANNEL(bcs->channel));
+		sti();
+		rcnt = z1 - z2;
+		if (rcnt < 0)
+			rcnt += cs->hw.hfcD.bfifosize;
+		rcnt++;
+		if (cs->debug & L1_DEB_HSCX) {
+			sprintf(tmp, "hfc rec %d z1(%x) z2(%x) cnt(%d)",
+				bcs->channel, z1, z2, rcnt);
+			debugl1(cs, tmp);
+		}
+		if ((skb = hfc_empty_fifo(bcs, rcnt))) {
+			cli();
+			skb_queue_tail(&bcs->rqueue, skb);
+			sti();
+			hfc_sched_event(bcs, B_RCVBUFREADY);
+		}
+		rcnt = f1 -f2;
+		if (rcnt<0)
+			rcnt += 32;
+		if (rcnt>1)
+			receive = 1;
+		else
+			receive = 0;
+	} else
+		receive = 0;
+	test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+	if (count && receive)
+		goto Begin;	
+	restore_flags(flags);
+	return;
+}
+
+void
+mode_2bs0(struct BCState *bcs, int mode, int bc)
+{
+	struct IsdnCardState *cs = bcs->cs;
+
+	if (cs->debug & L1_DEB_HSCX) {
+		char tmp[40];
+		sprintf(tmp, "HFCD bchannel mode %d bchan %d/%d",
+			mode, bc, bcs->channel);
+		debugl1(cs, tmp);
+	}
+	bcs->mode = mode;
+	bcs->channel = bc;
+	switch (mode) {
+		case (L1_MODE_NULL):
+			if (bc) {
+				cs->hw.hfcD.conn |= 0x18;
+				cs->hw.hfcD.sctrl &= ~SCTRL_B2_ENA;
+			} else {
+				cs->hw.hfcD.conn |= 0x3;
+				cs->hw.hfcD.sctrl &= ~SCTRL_B1_ENA;
+			}
+			break;
+		case (L1_MODE_TRANS):
+			if (bc) {
+				cs->hw.hfcD.ctmt |= 2;
+				cs->hw.hfcD.conn &= ~0x18;
+				cs->hw.hfcD.sctrl |= SCTRL_B2_ENA;
+			} else {
+				cs->hw.hfcD.ctmt |= 1;
+				cs->hw.hfcD.conn &= ~0x3;
+				cs->hw.hfcD.sctrl |= SCTRL_B1_ENA;
+			}
+			break;
+		case (L1_MODE_HDLC):
+			if (bc) {
+				cs->hw.hfcD.ctmt &= ~2;
+				cs->hw.hfcD.conn &= ~0x18;
+				cs->hw.hfcD.sctrl |= SCTRL_B2_ENA;
+			} else {
+				cs->hw.hfcD.ctmt &= ~1;
+				cs->hw.hfcD.conn &= ~0x3;
+				cs->hw.hfcD.sctrl |= SCTRL_B1_ENA;
+			}
+			break;
+	}
+	WriteReg(cs, HFCD_DATA, HFCD_SCTRL, cs->hw.hfcD.sctrl);
+	WriteReg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt);
+	WriteReg(cs, HFCD_DATA, HFCD_CONN, cs->hw.hfcD.conn);
+}
+
+static void
+hfc_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	long flags;
+
+	switch (pr) {
+		case (PH_DATA_REQ):
+			save_flags(flags);
+			cli();
+			if (st->l1.bcs->hw.hfc.tx_skb) {
+				skb_queue_tail(&st->l1.bcs->squeue, skb);
+				restore_flags(flags);
+			} else {
+				st->l1.bcs->hw.hfc.tx_skb = skb;
+/*				test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+*/				st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
+				restore_flags(flags);
+			}
+			break;
+		case (PH_PULL_IND):
+			if (st->l1.bcs->hw.hfc.tx_skb) {
+				printk(KERN_WARNING "hfc_l2l1: this shouldn't happen\n");
+				break;
+			}
+			save_flags(flags);
+			cli();
+/*			test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+*/			st->l1.bcs->hw.hfc.tx_skb = skb;
+			st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
+			restore_flags(flags);
+			break;
+		case (PH_PULL_REQ):
+			if (!st->l1.bcs->hw.hfc.tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL_CNF, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+	}
+}
+
+void
+close_2bs0(struct BCState *bcs)
+{
+	struct sk_buff *skb;
+
+	mode_2bs0(bcs, 0, 0);
+	if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+		while ((skb = skb_dequeue(&bcs->rqueue))) {
+			dev_kfree_skb(skb);
+		}
+		while ((skb = skb_dequeue(&bcs->squeue))) {
+			dev_kfree_skb(skb);
+		}
+		if (bcs->hw.hfc.tx_skb) {
+			dev_kfree_skb(bcs->hw.hfc.tx_skb);
+			bcs->hw.hfc.tx_skb = NULL;
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		}
+	}
+}
+
+static int
+open_hfcstate(struct IsdnCardState *cs,
+	      int bc)
+{
+	struct BCState *bcs = cs->bcs + bc;
+
+	if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+		skb_queue_head_init(&bcs->rqueue);
+		skb_queue_head_init(&bcs->squeue);
+	}
+	bcs->hw.hfc.tx_skb = NULL;
+	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	bcs->event = 0;
+	bcs->tx_cnt = 0;
+	return (0);
+}
+
+static void
+hfc_manl1(struct PStack *st, int pr,
+	  void *arg)
+{
+	switch (pr) {
+		case (PH_ACTIVATE_REQ):
+			test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+			mode_2bs0(st->l1.bcs, st->l1.mode, st->l1.bc);
+			st->l1.l1man(st, PH_ACTIVATE_CNF, NULL);
+			break;
+		case (PH_DEACTIVATE_REQ):
+			if (!test_bit(BC_FLG_BUSY, &st->l1.bcs->Flag))
+				mode_2bs0(st->l1.bcs, 0, 0);
+			test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+			break;
+	}
+}
+
+int
+setstack_2b(struct PStack *st, struct BCState *bcs)
+{
+	if (open_hfcstate(st->l1.hardware, bcs->channel))
+		return (-1);
+	st->l1.bcs = bcs;
+	st->l2.l2l1 = hfc_l2l1;
+	st->ma.manl1 = hfc_manl1;
+	setstack_manager(st);
+	bcs->st = st;
+	return (0);
+}
+
+static void
+manl1_msg(struct IsdnCardState *cs, int msg, void *arg) {
+	struct PStack *st;
+
+	st = cs->stlist;
+	while (st) {
+		st->ma.manl1(st, msg, arg);
+		st = st->next;
+	}
+}
+
+static void
+hfcd_bh(struct IsdnCardState *cs)
+{
+/*	struct PStack *stptr;
+*/
+	if (!cs)
+		return;
+#if 0	
+	if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) {
+		if (cs->debug)
+			debugl1(cs, "D-Channel Busy cleared");
+		stptr = cs->stlist;
+		while (stptr != NULL) {
+			stptr->l1.l1l2(stptr, PH_PAUSE_CNF, NULL);
+			stptr = stptr->next;
+		}
+	}
+#endif
+	if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) {
+		switch (cs->ph_state) {
+			case (0):
+				manl1_msg(cs, PH_RESET_IND, NULL);
+				break;
+			case (3):
+				manl1_msg(cs, PH_DEACT_IND, NULL);
+				break;
+			case (8):
+				manl1_msg(cs, PH_RSYNC_IND, NULL);
+				break;
+			case (6):
+				manl1_msg(cs, PH_INFO2_IND, NULL);
+				break;
+			case (7):
+				manl1_msg(cs, PH_I4_P8_IND, NULL);
+				break;
+			default:
+				break;
+		}
+	}
+	if (test_and_clear_bit(D_RCVBUFREADY, &cs->event))
+		DChannel_proc_rcv(cs);
+	if (test_and_clear_bit(D_XMTBUFREADY, &cs->event))
+		DChannel_proc_xmt(cs);
+}
+
+void
+sched_event_D(struct IsdnCardState *cs, int event)
+{
+	test_and_set_bit(event, &cs->event);
+	queue_task(&cs->tqueue, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+}
+
+static
+int receive_dmsg(struct IsdnCardState *cs)
+{
+	struct sk_buff *skb;
+	long flags;
+	int idx;
+	int rcnt, z1, z2;
+	u_char stat, cip, f1, f2;
+	int chksum;
+	int count=5;
+	u_char *ptr;
+	char tmp[64];
+
+	save_flags(flags);
+	cli();
+	if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+		debugl1(cs, "rec_dmsg blocked");
+		restore_flags(flags);
+		return(1);
+	}
+	SelFiFo(cs, 4 | HFCD_REC);
+	cip = HFCD_FIFO | HFCD_F1 | HFCD_REC;
+	WaitNoBusy(cs);
+	f1 = cs->readisac(cs, cip) & 0xf;
+	cip = HFCD_FIFO | HFCD_F2 | HFCD_REC;
+	WaitNoBusy(cs);
+	f2 = cs->readisac(cs, cip) & 0xf;
+	while ((f1 != f2) && count--) {
+		z1 = ReadZReg(cs, HFCD_FIFO | HFCD_Z1 | HFCD_REC);
+		z2 = ReadZReg(cs, HFCD_FIFO | HFCD_Z2 | HFCD_REC);
+		rcnt = z1 - z2;
+		if (rcnt < 0)
+			rcnt += cs->hw.hfcD.dfifosize;
+		rcnt++;
+		if (cs->debug & L1_DEB_ISAC) {
+			sprintf(tmp, "hfcd recd f1(%d) f2(%d) z1(%x) z2(%x) cnt(%d)",
+				f1, f2, z1, z2, rcnt);
+			debugl1(cs, tmp);
+		}
+		sti();
+		idx = 0;
+		cip = HFCD_FIFO | HFCD_FIFO_OUT | HFCD_REC;
+		if (rcnt > MAX_DFRAME_LEN + 3) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "empty_fifo d: incoming packet too large");
+			while (idx < rcnt) {
+				cli();
+				if (!(WaitNoBusy(cs)))
+					break;
+				ReadReg(cs, HFCD_DATA_NODEB, cip);
+				sti();
+				idx++;
+			}
+		} else if (rcnt < 4) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "empty_fifo d: incoming packet too small");
+			cli();
+			while ((idx++ < rcnt) && WaitNoBusy(cs))
+				ReadReg(cs, HFCD_DATA_NODEB, cip);
+		} else if ((skb = dev_alloc_skb(rcnt - 3))) {
+			ptr = skb_put(skb, rcnt - 3);
+			while (idx < (rcnt - 3)) {
+				cli();
+				if (!(WaitNoBusy(cs)))
+					break;
+				*ptr = ReadReg(cs, HFCD_DATA_NODEB, cip);
+				sti();
+				idx++;
+				ptr++;
+			}
+			if (idx != (rcnt - 3)) {
+				sti();
+				debugl1(cs, "RFIFO D BUSY error");
+				printk(KERN_WARNING "HFC DFIFO channel BUSY Error\n");
+				dev_kfree_skb(skb);
+				skb = NULL;
+			} else {
+				cli();
+				WaitNoBusy(cs);
+				chksum = (ReadReg(cs, HFCD_DATA, cip) << 8);
+				WaitNoBusy(cs);
+				chksum += ReadReg(cs, HFCD_DATA, cip);
+				WaitNoBusy(cs);
+				stat = ReadReg(cs, HFCD_DATA, cip);
+				sti();
+				if (cs->debug & L1_DEB_ISAC) {
+					sprintf(tmp, "empty_dfifo chksum %x stat %x",
+						chksum, stat);
+					debugl1(cs, tmp);
+				}
+				if (stat) {
+					debugl1(cs, "FIFO CRC error");
+					dev_kfree_skb(skb);
+					skb = NULL;
+				} else {
+					skb_queue_tail(&cs->rq, skb);
+					sched_event_D(cs, D_RCVBUFREADY);
+				}
+			}
+		} else
+			printk(KERN_WARNING "HFC: D receive out of memory\n");
+		sti();
+		WaitForBusy(cs);
+		cip = HFCD_FIFO | HFCD_F2_INC | HFCD_REC;
+		cli();
+		WaitNoBusy(cs);
+		stat = ReadReg(cs, HFCD_DATA, cip);
+		sti();
+		WaitForBusy(cs);
+		cip = HFCD_FIFO | HFCD_F2 | HFCD_REC;
+		cli();
+		WaitNoBusy(cs);
+		f2 = cs->readisac(cs, cip) & 0xf;
+	}
+	test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+	restore_flags(flags);
+	return(1);
+} 
+
+static void
+hfc_fill_dfifo(struct IsdnCardState *cs)
+{
+	long flags;
+	int idx, fcnt;
+	int count;
+	u_char cip;
+	char tmp[64];
+
+	if (!cs->tx_skb)
+		return;
+	if (cs->tx_skb->len <= 0)
+		return;
+
+	save_flags(flags);
+	cli();
+	SelFiFo(cs, 4 | HFCD_SEND);
+	cip = HFCD_FIFO | HFCD_F1 | HFCD_SEND;
+	WaitNoBusy(cs);
+	cs->hw.hfcD.f1 = ReadReg(cs, HFCD_DATA, cip) & 0xf;
+	WaitNoBusy(cs);
+	cip = HFCD_FIFO | HFCD_F2 | HFCD_SEND;
+	cs->hw.hfcD.f2 = ReadReg(cs, HFCD_DATA, cip) & 0xf;
+	cs->hw.hfcD.send[cs->hw.hfcD.f1] = ReadZReg(cs, HFCD_FIFO | HFCD_Z1 | HFCD_SEND);
+	sti();
+	if (cs->debug & L1_DEB_ISAC) {
+		sprintf(tmp, "hfc_fill_Dfifo f1(%d) f2(%d) z1(%x)",
+			cs->hw.hfcD.f1, cs->hw.hfcD.f2,
+			cs->hw.hfcD.send[cs->hw.hfcD.f1]);
+		debugl1(cs, tmp);
+	}
+	fcnt = cs->hw.hfcD.f1 - cs->hw.hfcD.f2;
+	if (fcnt < 0)
+		fcnt += 16;
+	if (fcnt > 14) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfc_fill_Dfifo more as 14 frames");
+		restore_flags(flags);
+		return;
+	}
+	count = GetFreeFifoBytes_D(cs);
+	if (cs->debug & L1_DEB_ISAC) {
+		sprintf(tmp, "hfc_fill_Dfifo count(%d/%d)",
+			cs->tx_skb->len, count);
+		debugl1(cs, tmp);
+	}
+	if (count < cs->tx_skb->len) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "hfc_fill_Dfifo no fifo mem");
+		restore_flags(flags);
+		return;
+	}
+	cip = HFCD_FIFO | HFCD_FIFO_IN | HFCD_SEND;
+	idx = 0;
+	cli();
+	WaitForBusy(cs);
+	WaitNoBusy(cs);
+	WriteReg(cs, HFCD_DATA_NODEB, cip, cs->tx_skb->data[idx++]);
+	while (idx < cs->tx_skb->len) {
+		cli();
+		if (!(WaitNoBusy(cs)))
+			break;
+		WriteReg(cs, HFCD_DATA_NODEB, cip, cs->tx_skb->data[idx]);
+		sti();
+		idx++;
+	}
+	if (idx != cs->tx_skb->len) {
+		sti();
+		debugl1(cs, "DFIFO Send BUSY error");
+		printk(KERN_WARNING "HFC S DFIFO channel BUSY Error\n");
+	}
+	WaitForBusy(cs);
+	cli();
+	WaitNoBusy(cs);
+	ReadReg(cs, HFCD_DATA, HFCD_FIFO | HFCD_F1_INC | HFCD_SEND);
+	dev_kfree_skb(cs->tx_skb);
+	cs->tx_skb = NULL;
+	sti();
+	WaitForBusy(cs);
+	restore_flags(flags);
+	return;
+}
+
+static 
+struct BCState *Sel_BCS(struct IsdnCardState *cs, int channel)
+{
+	if (cs->bcs[0].mode && (cs->bcs[0].channel == channel))
+		return(&cs->bcs[0]);
+	else if (cs->bcs[1].mode && (cs->bcs[1].channel == channel))
+		return(&cs->bcs[1]);
+	else
+		return(NULL);
+}
+
+void
+hfc2bds0_interrupt(struct IsdnCardState *cs, u_char val)
+{
+       	u_char exval;
+       	struct BCState *bcs;
+	char tmp[32];
+	int count=15;
+	long flags;
+
+	if (cs->debug & L1_DEB_ISAC) {
+		sprintf(tmp, "HFCD irq %x %s", val,
+			test_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags) ?
+			"locked" : "unlocked");
+		debugl1(cs, tmp);
+	}
+	val &= cs->hw.hfcD.int_m1;
+	if (val & 0x40) { /* TE state machine irq */
+		exval = cs->readisac(cs, HFCD_STATES) & 0xf;
+		if (cs->debug & L1_DEB_ISAC) {
+			sprintf(tmp, "ph_state chg %d->%d", cs->ph_state,
+				exval);
+			debugl1(cs, tmp);
+		}
+		cs->ph_state = exval;
+		sched_event_D(cs, D_L1STATECHANGE);
+		val &= ~0x40;
+	}
+	while (val) {
+		save_flags(flags);
+		cli();
+		if (test_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+			cs->hw.hfcD.int_s1 |= val;
+			restore_flags(flags);
+			return;
+		}
+		if (cs->hw.hfcD.int_s1 & 0x18) {
+			exval = val;
+			val =  cs->hw.hfcD.int_s1;
+			cs->hw.hfcD.int_s1 = exval;
+		}	
+		if (val & 0x08) {
+			if (!(bcs=Sel_BCS(cs, 0))) {
+				if (cs->debug)
+					debugl1(cs, "hfcd spurious 0x08 IRQ");
+			} else 
+				main_rec_2bds0(bcs);
+		}
+		if (val & 0x10) {
+			if (!(bcs=Sel_BCS(cs, 1))) {
+				if (cs->debug)
+					debugl1(cs, "hfcd spurious 0x10 IRQ");
+			} else 
+				main_rec_2bds0(bcs);
+		}
+		if (val & 0x01) {
+			if (!(bcs=Sel_BCS(cs, 0))) {
+				if (cs->debug)
+					debugl1(cs, "hfcd spurious 0x01 IRQ");
+			} else {
+				if (bcs->hw.hfc.tx_skb) {
+					if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+						hfc_fill_fifo(bcs);
+						test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+					} else {
+						sprintf(tmp,"fill_data %d blocked", bcs->channel);
+						debugl1(cs, tmp);
+					}
+				} else {
+					if ((bcs->hw.hfc.tx_skb = skb_dequeue(&bcs->squeue))) {
+						if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+							hfc_fill_fifo(bcs);
+							test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+						} else {
+							sprintf(tmp,"fill_data %d blocked", bcs->channel);
+							debugl1(cs, tmp);
+						}
+					} else {
+						hfc_sched_event(bcs, B_XMTBUFREADY);
+					}
+				}
+			}
+		}
+		if (val & 0x02) {
+			if (!(bcs=Sel_BCS(cs, 1))) {
+				if (cs->debug)
+					debugl1(cs, "hfcd spurious 0x02 IRQ");
+			} else {
+				if (bcs->hw.hfc.tx_skb) {
+					if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+						hfc_fill_fifo(bcs);
+						test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+					} else {
+						sprintf(tmp,"fill_data %d blocked", bcs->channel);
+						debugl1(cs, tmp);
+					}
+				} else {
+					if ((bcs->hw.hfc.tx_skb = skb_dequeue(&bcs->squeue))) {
+						if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+							hfc_fill_fifo(bcs);
+							test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+						} else {
+							sprintf(tmp,"fill_data %d blocked", bcs->channel);
+							debugl1(cs, tmp);
+						}
+					} else {
+						hfc_sched_event(bcs, B_XMTBUFREADY);
+					}
+				}
+			}
+		}
+		if (val & 0x20) {	/* receive dframe */
+			receive_dmsg(cs);
+		}
+		if (val & 0x04) {	/* dframe transmitted */
+			if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+				del_timer(&cs->dbusytimer);
+			if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+				sched_event_D(cs, D_CLEARBUSY);
+			if (cs->tx_skb)
+				if (cs->tx_skb->len) {
+					if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+						hfc_fill_dfifo(cs);
+						test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+					} else {
+						debugl1(cs, "hfc_fill_dfifo irq blocked");
+					}
+					goto afterXPR;
+				} else {
+					dev_kfree_skb(cs->tx_skb);
+					cs->tx_cnt = 0;
+					cs->tx_skb = NULL;
+				}
+			if ((cs->tx_skb = skb_dequeue(&cs->sq))) {
+				cs->tx_cnt = 0;
+				if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+					hfc_fill_dfifo(cs);
+					test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+				} else {
+					debugl1(cs, "hfc_fill_dfifo irq blocked");
+				}
+			} else
+				sched_event_D(cs, D_XMTBUFREADY);
+		}
+      afterXPR:
+		if (cs->hw.hfcD.int_s1 && count--) {
+			val = cs->hw.hfcD.int_s1;
+			cs->hw.hfcD.int_s1 = 0;
+			if (cs->debug & L1_DEB_ISAC) {
+				sprintf(tmp, "HFCD irq %x loop %d", val, 15-count);
+				debugl1(cs, tmp);
+			}
+		} else
+			val = 0;
+		restore_flags(flags);
+	}
+}
+
+static void
+HFCD_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
+	struct sk_buff *skb = arg;
+	char str[64];
+	switch (pr) {
+		case (PH_DATA_REQ):
+			if (cs->tx_skb) {
+				skb_queue_tail(&cs->sq, skb);
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "PH_DATA Queued", 0);
+#endif
+			} else {
+				if ((cs->dlogflag) && (!(skb->data[2] & 1))) {	/* I-FRAME */
+					LogFrame(cs, skb->data, skb->len);
+					sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei);
+					dlogframe(cs, skb->data + 4, skb->len - 4,
+						  str);
+				}
+				cs->tx_skb = skb;
+				cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "PH_DATA", 0);
+#endif
+				if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+					hfc_fill_dfifo(cs);
+					test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+				} else
+					debugl1(cs, "hfc_fill_dfifo blocked");
+
+			}
+			break;
+		case (PH_PULL_IND):
+			if (cs->tx_skb) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, " l2l1 tx_skb exist this shouldn't happen");
+				skb_queue_tail(&cs->sq, skb);
+				break;
+			}
+			if ((cs->dlogflag) && (!(skb->data[2] & 1))) {	/* I-FRAME */
+				LogFrame(cs, skb->data, skb->len);
+				sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei);
+				dlogframe(cs, skb->data + 4, skb->len - 4,
+					  str);
+			}
+			cs->tx_skb = skb;
+			cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (cs->debug & L1_DEB_LAPD)
+				Logl2Frame(cs, skb, "PH_DATA_PULLED", 0);
+#endif
+			if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+				hfc_fill_dfifo(cs);
+				test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+			} else
+				debugl1(cs, "hfc_fill_dfifo blocked");
+			break;
+		case (PH_PULL_REQ):
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (cs->debug & L1_DEB_LAPD)
+				debugl1(cs, "-> PH_REQUEST_PULL");
+#endif
+			if (!cs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL_CNF, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+	}
+}
+
+void
+hfcd_l1cmd(struct IsdnCardState *cs, int msg, void *arg)
+{
+	char tmp[32];
+	switch(msg) {
+		case PH_RESET_REQ:
+			cs->writeisac(cs, HFCD_STATES, HFCD_LOAD_STATE | 3); /* HFC ST 3 */
+			udelay(6);
+			cs->writeisac(cs, HFCD_STATES, 3); /* HFC ST 2 */
+			cs->hw.hfcD.mst_m |= HFCD_MASTER;
+			cs->writeisac(cs, HFCD_MST_MODE, cs->hw.hfcD.mst_m);
+			cs->writeisac(cs, HFCD_STATES, HFCD_ACTIVATE | HFCD_DO_ACTION);
+			manl1_msg(cs, PH_POWERUP_CNF, NULL);
+			break;
+		case PH_ENABLE_REQ:
+			cs->writeisac(cs, HFCD_STATES, HFCD_ACTIVATE | HFCD_DO_ACTION);
+			break;
+		case PH_DEACT_ACK:
+			cs->hw.hfcD.mst_m &= ~HFCD_MASTER;
+			cs->writeisac(cs, HFCD_MST_MODE, cs->hw.hfcD.mst_m);
+			break;
+		case PH_INFO3_REQ:
+			cs->hw.hfcD.mst_m |= HFCD_MASTER;
+			cs->writeisac(cs, HFCD_MST_MODE, cs->hw.hfcD.mst_m);
+			break;
+#if 0
+		case PH_TESTLOOP_REQ:
+			u_char val = 0;
+			if (1 & (int) arg)
+				val |= 0x0c;
+			if (2 & (int) arg)
+				val |= 0x3;
+			if (test_bit(HW_IOM1, &cs->HW_Flags)) {
+				/* IOM 1 Mode */
+				if (!val) {
+					cs->writeisac(cs, ISAC_SPCR, 0xa);
+					cs->writeisac(cs, ISAC_ADF1, 0x2);
+				} else {
+					cs->writeisac(cs, ISAC_SPCR, val);
+					cs->writeisac(cs, ISAC_ADF1, 0xa);
+				}
+			} else {
+				/* IOM 2 Mode */
+				cs->writeisac(cs, ISAC_SPCR, val);
+				if (val)
+					cs->writeisac(cs, ISAC_ADF1, 0x8);
+				else
+					cs->writeisac(cs, ISAC_ADF1, 0x0);
+			}
+			break;
+#endif
+		default:
+			if (cs->debug & L1_DEB_WARN) {
+				sprintf(tmp, "hfcd_l1cmd unknown %4x", msg);
+				debugl1(cs, tmp);
+			}
+			break;
+	}
+}
+
+void
+setstack_hfcd(struct PStack *st, struct IsdnCardState *cs)
+{
+	st->l2.l2l1 = HFCD_l2l1;
+}
+
+static void
+hfc_dbusy_timer(struct IsdnCardState *cs)
+{
+#if 0
+	struct PStack *stptr;
+	if (test_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
+		if (cs->debug)
+			debugl1(cs, "D-Channel Busy");
+		test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags);
+		stptr = cs->stlist;
+		
+		while (stptr != NULL) {
+			stptr->l1.l1l2(stptr, PH_PAUSE_IND, NULL);
+			stptr = stptr->next;
+		}
+	}
+#endif
+}
+
+__initfunc(unsigned int
+*init_send_hfcd(int cnt))
+{
+	int i, *send;
+
+	if (!(send = kmalloc(cnt * sizeof(unsigned int), GFP_ATOMIC))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for hfcd.send\n");
+		return(NULL);
+	}
+	for (i = 0; i < cnt; i++)
+		send[i] = 0x1fff;
+	return(send);
+}
+
+__initfunc(void
+init2bds0(struct IsdnCardState *cs))
+{
+	cs->setstack_d = setstack_hfcd;
+	cs->l1cmd = hfcd_l1cmd;
+	cs->dbusytimer.function = (void *) hfc_dbusy_timer;
+	cs->dbusytimer.data = (long) cs;
+	init_timer(&cs->dbusytimer);
+	cs->tqueue.routine = (void *) (void *) hfcd_bh;
+	if (!cs->hw.hfcD.send)
+		cs->hw.hfcD.send = init_send_hfcd(16);
+	if (!cs->bcs[0].hw.hfc.send)
+		cs->bcs[0].hw.hfc.send = init_send_hfcd(32);
+	if (!cs->bcs[1].hw.hfc.send)
+		cs->bcs[1].hw.hfc.send = init_send_hfcd(32);
+	cs->BC_Send_Data = &hfc_send_data;
+	cs->bcs[0].BC_SetStack = setstack_2b;
+	cs->bcs[1].BC_SetStack = setstack_2b;
+	cs->bcs[0].BC_Close = close_2bs0;
+	cs->bcs[1].BC_Close = close_2bs0;
+	mode_2bs0(cs->bcs, 0, 0);
+	mode_2bs0(cs->bcs + 1, 0, 1);
+}
+
+void
+release2bds0(struct IsdnCardState *cs)
+{
+	if (cs->bcs[0].hw.hfc.send) {
+		kfree(cs->bcs[0].hw.hfc.send);
+		cs->bcs[0].hw.hfc.send = NULL;
+	}
+	if (cs->bcs[1].hw.hfc.send) {
+		kfree(cs->bcs[1].hw.hfc.send);
+		cs->bcs[1].hw.hfc.send = NULL;
+	}
+	if (cs->hw.hfcD.send) {
+		kfree(cs->hw.hfcD.send);
+		cs->hw.hfcD.send = NULL;
+	}
+}
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/hfc_2bds0.h linux/drivers/isdn/hisax/hfc_2bds0.h
--- v2.1.91/linux/drivers/isdn/hisax/hfc_2bds0.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/hfc_2bds0.h	Wed Apr  1 16:20:58 1998
@@ -0,0 +1,131 @@
+/* $Id: hfc_2bds0.h,v 1.2 1998/02/02 13:26:15 keil Exp $
+
+ *  specific defines for CCD's HFC 2BDS0
+ *
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ *
+ * $Log: hfc_2bds0.h,v $
+ * Revision 1.2  1998/02/02 13:26:15  keil
+ * New
+ *
+ *
+ *
+ */
+
+#define HFCD_CIRM  	0x18
+#define HFCD_CTMT	0x19
+#define HFCD_INT_M1  	0x1A
+#define HFCD_INT_M2  	0x1B
+#define HFCD_INT_S1  	0x1E
+#define HFCD_STAT  	0x1C
+#define HFCD_STAT_DISB  0x1D
+#define HFCD_STATES  	0x30
+#define HFCD_SCTRL  	0x31
+#define HFCD_TEST  	0x32
+#define HFCD_SQ  	0x34
+#define HFCD_CLKDEL  	0x37
+#define HFCD_MST_MODE	0x2E
+#define HFCD_CONN  	0x2F
+
+#define HFCD_FIFO	0x80
+#define HFCD_Z1		0x10
+#define HFCD_Z2		0x18
+#define HFCD_Z_LOW	0x00
+#define HFCD_Z_HIGH	0x04
+#define HFCD_F1_INC	0x12
+#define HFCD_FIFO_IN	0x16
+#define HFCD_F1		0x1a
+#define HFCD_F2		0x1e
+#define HFCD_F2_INC	0x22
+#define HFCD_FIFO_OUT	0x26
+#define HFCD_REC	0x01
+#define HFCD_SEND	0x00
+
+#define HFCB_FIFO	0x80
+#define HFCB_Z1		0x00
+#define HFCB_Z2		0x08
+#define HFCB_Z_LOW	0x00
+#define HFCB_Z_HIGH	0x04
+#define HFCB_F1_INC	0x28
+#define HFCB_FIFO_IN	0x2c
+#define HFCB_F1		0x30
+#define HFCB_F2		0x34
+#define HFCB_F2_INC	0x38
+#define HFCB_FIFO_OUT	0x3c
+#define HFCB_REC	0x01
+#define HFCB_SEND	0x00
+#define HFCB_B1		0x00
+#define HFCB_B2		0x02
+#define HFCB_CHANNEL(ch) (ch ? HFCB_B2 : HFCB_B1)
+
+#define HFCD_STATUS	0
+#define HFCD_DATA	1
+#define HFCD_DATA_NODEB	2
+
+/* Status (READ) */
+#define HFCD_BUSY	0x01
+#define HFCD_BUSY_NBUSY	0x04
+#define HFCD_TIMER_ELAP	0x10
+#define HFCD_STATINT	0x20
+#define HFCD_FRAMEINT	0x40
+#define HFCD_ANYINT	0x80
+
+/* CTMT (Write) */
+#define HFCD_CLTIMER 0x80
+#define HFCD_TIM25  0x00
+#define HFCD_TIM50  0x08
+#define HFCD_TIM400 0x10
+#define HFCD_TIM800 0x18
+#define HFCD_AUTO_TIMER 0x20
+#define HFCD_TRANSB2 0x02
+#define HFCD_TRANSB1 0x01
+
+/* CIRM (Write) */
+#define HFCD_RESET  	0x08
+#define HFCD_MEM8K	0x10
+#define HFCD_INTA	0x01
+#define HFCD_INTB	0x02
+#define HFCD_INTC	0x03
+#define HFCD_INTD	0x04
+#define HFCD_INTE	0x05
+#define HFCD_INTF	0x06
+
+/* INT_M1;INT_S1 */
+#define HFCD_INTS_B1TRANS	0x01
+#define HFCD_INTS_B2TRANS	0x02
+#define HFCD_INTS_DTRANS	0x04
+#define HFCD_INTS_B1REC		0x08
+#define HFCD_INTS_B2REC		0x10
+#define HFCD_INTS_DREC		0x20
+#define HFCD_INTS_L1STATE	0x40
+#define HFCD_INTS_TIMER		0x80
+
+/* INT_M2 */
+#define HFCD_IRQ_ENABLE		0x08
+
+/* STATES */
+#define HFCD_LOAD_STATE		0x10
+#define HFCD_ACTIVATE		0x20
+#define HFCD_DO_ACTION		0x40
+
+/* HFCD_MST_MODE */
+#define HFCD_MASTER		0x01
+
+/* HFCD_SCTRL */
+#define SCTRL_B1_ENA		0x01
+#define SCTRL_B2_ENA		0x02
+#define SCTRL_LOW_PRIO		0x08
+#define SCTRL_SQ_ENA		0x10
+#define SCTRL_TEST		0x20
+#define SCTRL_NONE_CAP		0x40
+#define SCTRL_PWR_DOWN		0x80
+
+/* HFCD_TEST */
+#define HFCD_AUTO_AWAKE		0x01
+
+extern void main_irq_2bds0(struct BCState *bcs);
+extern void init2bds0(struct IsdnCardState *cs);
+extern void release2bds0(struct IsdnCardState *cs);
+extern void hfc2bds0_interrupt(struct IsdnCardState *cs, u_char val);
+extern void set_cs_func(struct IsdnCardState *cs);
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/hfc_2bs0.c linux/drivers/isdn/hisax/hfc_2bs0.c
--- v2.1.91/linux/drivers/isdn/hisax/hfc_2bs0.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/hfc_2bs0.c	Wed Apr  1 16:20:58 1998
@@ -0,0 +1,615 @@
+/* $Id: hfc_2bs0.c,v 1.4 1998/02/12 23:07:29 keil Exp $
+
+ *  specific routines for CCD's HFC 2BS0
+ *
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ *
+ * $Log: hfc_2bs0.c,v $
+ * Revision 1.4  1998/02/12 23:07:29  keil
+ * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
+ *
+ * Revision 1.3  1997/11/06 17:13:35  keil
+ * New 2.1 init code
+ *
+ * Revision 1.2  1997/10/29 19:04:47  keil
+ * changes for 2.1
+ *
+ * Revision 1.1  1997/09/11 17:31:33  keil
+ * Common part for HFC 2BS0 based cards
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include "hisax.h"
+#include "hfc_2bs0.h"
+#include "isac.h"
+#include "isdnl1.h"
+#include <linux/interrupt.h>
+
+static inline int
+WaitForBusy(struct IsdnCardState *cs)
+{
+	int to = 130;
+	long flags;
+	u_char val;
+
+	save_flags(flags);
+	cli();
+	while (!(cs->BC_Read_Reg(cs, HFC_STATUS, 0) & HFC_BUSY) && to) {
+		val = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2 |
+				      (cs->hw.hfc.cip & 3));
+		udelay(1);
+		to--;
+	}
+	restore_flags(flags);
+	if (!to) {
+		printk(KERN_WARNING "HiSax: waitforBusy timeout\n");
+		return (0);
+	} else
+		return (to);
+}
+
+static inline int
+WaitNoBusy(struct IsdnCardState *cs)
+{
+	int to = 125;
+
+	while ((cs->BC_Read_Reg(cs, HFC_STATUS, 0) & HFC_BUSY) && to) {
+		udelay(1);
+		to--;
+	}
+	if (!to) {
+		printk(KERN_WARNING "HiSax: waitforBusy timeout\n");
+		return (0);
+	} else
+		return (to);
+}
+
+int
+GetFreeFifoBytes(struct BCState *bcs)
+{
+	int s;
+
+	if (bcs->hw.hfc.f1 == bcs->hw.hfc.f2)
+		return (bcs->cs->hw.hfc.fifosize);
+	s = bcs->hw.hfc.send[bcs->hw.hfc.f1] - bcs->hw.hfc.send[bcs->hw.hfc.f2];
+	if (s <= 0)
+		s += bcs->cs->hw.hfc.fifosize;
+	s = bcs->cs->hw.hfc.fifosize - s;
+	return (s);
+}
+
+int
+ReadZReg(struct BCState *bcs, u_char reg)
+{
+	int val;
+
+	WaitNoBusy(bcs->cs);
+	val = 256 * bcs->cs->BC_Read_Reg(bcs->cs, HFC_DATA, reg | HFC_CIP | HFC_Z_HIGH);
+	WaitNoBusy(bcs->cs);
+	val += bcs->cs->BC_Read_Reg(bcs->cs, HFC_DATA, reg | HFC_CIP | HFC_Z_LOW);
+	return (val);
+}
+
+void
+hfc_sched_event(struct BCState *bcs, int event)
+{
+	bcs->event |= 1 << event;
+	queue_task(&bcs->tqueue, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+}
+
+static void
+hfc_clear_fifo(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	long flags;
+	int idx, cnt;
+	int rcnt, z1, z2;
+	u_char cip, f1, f2;
+	char tmp[64];
+
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "hfc_clear_fifo");
+	save_flags(flags);
+	cli();
+	cip = HFC_CIP | HFC_F1 | HFC_REC | HFC_CHANNEL(bcs->channel);
+	if ((cip & 0xc3) != (cs->hw.hfc.cip & 0xc3)) {
+		cs->BC_Write_Reg(cs, HFC_STATUS, cip, cip);
+		WaitForBusy(cs);
+	}
+	WaitNoBusy(cs);
+	f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+	cip = HFC_CIP | HFC_F2 | HFC_REC | HFC_CHANNEL(bcs->channel);
+	WaitNoBusy(cs);
+	f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+	z1 = ReadZReg(bcs, HFC_Z1 | HFC_REC | HFC_CHANNEL(bcs->channel));
+	z2 = ReadZReg(bcs, HFC_Z2 | HFC_REC | HFC_CHANNEL(bcs->channel));
+	cnt = 32;
+	while (((f1 != f2) || (z1 != z2)) && cnt--) {
+		if (cs->debug & L1_DEB_HSCX) {
+			sprintf(tmp, "hfc clear %d f1(%d) f2(%d)",
+				bcs->channel, f1, f2);
+			debugl1(cs, tmp);
+		}
+		rcnt = z1 - z2;
+		if (rcnt < 0)
+			rcnt += cs->hw.hfc.fifosize;
+		if (rcnt)
+			rcnt++;
+		if (cs->debug & L1_DEB_HSCX) {
+			sprintf(tmp, "hfc clear %d z1(%x) z2(%x) cnt(%d)",
+				bcs->channel, z1, z2, rcnt);
+			debugl1(cs, tmp);
+		}
+		cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel);
+		idx = 0;
+		while ((idx < rcnt) && WaitNoBusy(cs)) {
+			cs->BC_Read_Reg(cs, HFC_DATA_NODEB, cip);
+			idx++;
+		}
+		if (f1 != f2) {
+			WaitNoBusy(cs);
+			cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC |
+					HFC_CHANNEL(bcs->channel));
+			WaitForBusy(cs);
+		}
+		cip = HFC_CIP | HFC_F1 | HFC_REC | HFC_CHANNEL(bcs->channel);
+		WaitNoBusy(cs);
+		f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+		cip = HFC_CIP | HFC_F2 | HFC_REC | HFC_CHANNEL(bcs->channel);
+		WaitNoBusy(cs);
+		f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+		z1 = ReadZReg(bcs, HFC_Z1 | HFC_REC | HFC_CHANNEL(bcs->channel));
+		z2 = ReadZReg(bcs, HFC_Z2 | HFC_REC | HFC_CHANNEL(bcs->channel));
+	}
+	restore_flags(flags);
+	return;
+}
+
+
+static struct sk_buff
+*
+hfc_empty_fifo(struct BCState *bcs, int count)
+{
+	u_char *ptr;
+	struct sk_buff *skb;
+	struct IsdnCardState *cs = bcs->cs;
+	int idx;
+	int chksum;
+	u_char stat, cip;
+	char tmp[64];
+
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "hfc_empty_fifo");
+	idx = 0;
+	if (count > HSCX_BUFMAX + 3) {
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "hfc_empty_fifo: incoming packet too large");
+		cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel);
+		while ((idx++ < count) && WaitNoBusy(cs))
+			cs->BC_Read_Reg(cs, HFC_DATA_NODEB, cip);
+		WaitNoBusy(cs);
+		stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC |
+				       HFC_CHANNEL(bcs->channel));
+		WaitForBusy(cs);
+		return (NULL);
+	}
+	if (count < 4) {
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "hfc_empty_fifo: incoming packet too small");
+		cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel);
+		while ((idx++ < count) && WaitNoBusy(cs))
+			cs->BC_Read_Reg(cs, HFC_DATA_NODEB, cip);
+		WaitNoBusy(cs);
+		stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC |
+				       HFC_CHANNEL(bcs->channel));
+		WaitForBusy(cs);
+		return (NULL);
+	}
+	if (!(skb = dev_alloc_skb(count - 3)))
+		printk(KERN_WARNING "HFC: receive out of memory\n");
+	else {
+		ptr = skb_put(skb, count - 3);
+		idx = 0;
+		cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel);
+		while ((idx < count - 3) && WaitNoBusy(cs)) {
+			*ptr++ = cs->BC_Read_Reg(cs, HFC_DATA_NODEB, cip);
+			idx++;
+		}
+		if (idx != count - 3) {
+			debugl1(cs, "RFIFO BUSY error");
+			printk(KERN_WARNING "HFC FIFO channel %d BUSY Error\n", bcs->channel);
+			dev_kfree_skb(skb);
+			WaitNoBusy(cs);
+			stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC |
+					       HFC_CHANNEL(bcs->channel));
+			WaitForBusy(cs);
+			return (NULL);
+		}
+		WaitNoBusy(cs);
+		chksum = (cs->BC_Read_Reg(cs, HFC_DATA, cip) << 8);
+		WaitNoBusy(cs);
+		chksum += cs->BC_Read_Reg(cs, HFC_DATA, cip);
+		WaitNoBusy(cs);
+		stat = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+		if (cs->debug & L1_DEB_HSCX) {
+			sprintf(tmp, "hfc_empty_fifo %d chksum %x stat %x",
+				bcs->channel, chksum, stat);
+			debugl1(cs, tmp);
+		}
+		if (stat) {
+			debugl1(cs, "FIFO CRC error");
+			dev_kfree_skb(skb);
+			skb = NULL;
+		}
+		WaitNoBusy(cs);
+		stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC |
+				       HFC_CHANNEL(bcs->channel));
+		WaitForBusy(cs);
+	}
+	return (skb);
+}
+
+static void
+hfc_fill_fifo(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	long flags;
+	int idx, fcnt;
+	int count;
+	u_char cip;
+	char tmp[64];
+
+	if (!bcs->hw.hfc.tx_skb)
+		return;
+	if (bcs->hw.hfc.tx_skb->len <= 0)
+		return;
+
+	save_flags(flags);
+	cli();
+	cip = HFC_CIP | HFC_F1 | HFC_SEND | HFC_CHANNEL(bcs->channel);
+	if ((cip & 0xc3) != (cs->hw.hfc.cip & 0xc3)) {
+		cs->BC_Write_Reg(cs, HFC_STATUS, cip, cip);
+		WaitForBusy(cs);
+	}
+	WaitNoBusy(cs);
+	bcs->hw.hfc.f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+	cip = HFC_CIP | HFC_F2 | HFC_SEND | HFC_CHANNEL(bcs->channel);
+	WaitNoBusy(cs);
+	bcs->hw.hfc.f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+	bcs->hw.hfc.send[bcs->hw.hfc.f1] = ReadZReg(bcs, HFC_Z1 | HFC_SEND | HFC_CHANNEL(bcs->channel));
+	if (cs->debug & L1_DEB_HSCX) {
+		sprintf(tmp, "hfc_fill_fifo %d f1(%d) f2(%d) z1(%x)",
+			bcs->channel, bcs->hw.hfc.f1, bcs->hw.hfc.f2,
+			bcs->hw.hfc.send[bcs->hw.hfc.f1]);
+		debugl1(cs, tmp);
+	}
+	fcnt = bcs->hw.hfc.f1 - bcs->hw.hfc.f2;
+	if (fcnt < 0)
+		fcnt += 32;
+	if (fcnt > 30) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfc_fill_fifo more as 30 frames");
+		restore_flags(flags);
+		return;
+	}
+	count = GetFreeFifoBytes(bcs);
+	if (cs->debug & L1_DEB_HSCX) {
+		sprintf(tmp, "hfc_fill_fifo %d count(%d/%d)",
+			bcs->channel, bcs->hw.hfc.tx_skb->len,
+			count);
+		debugl1(cs, tmp);
+	}
+	if (count < bcs->hw.hfc.tx_skb->len) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfc_fill_fifo no fifo mem");
+		restore_flags(flags);
+		return;
+	}
+	cip = HFC_CIP | HFC_FIFO_IN | HFC_SEND | HFC_CHANNEL(bcs->channel);
+	idx = 0;
+	while ((idx < bcs->hw.hfc.tx_skb->len) && WaitNoBusy(cs))
+		cs->BC_Write_Reg(cs, HFC_DATA_NODEB, cip, bcs->hw.hfc.tx_skb->data[idx++]);
+	if (idx != bcs->hw.hfc.tx_skb->len) {
+		debugl1(cs, "FIFO Send BUSY error");
+		printk(KERN_WARNING "HFC S FIFO channel %d BUSY Error\n", bcs->channel);
+	} else {
+		count =  bcs->hw.hfc.tx_skb->len;
+		bcs->tx_cnt -= count;
+		if (PACKET_NOACK == bcs->hw.hfc.tx_skb->pkt_type)
+			count = -1;
+		dev_kfree_skb(bcs->hw.hfc.tx_skb);
+		bcs->hw.hfc.tx_skb = NULL;
+		WaitForBusy(cs);
+		WaitNoBusy(cs);
+		cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F1_INC | HFC_SEND | HFC_CHANNEL(bcs->channel));
+		if (bcs->st->lli.l1writewakeup && (count >= 0))
+			bcs->st->lli.l1writewakeup(bcs->st, count);
+		test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	}
+	restore_flags(flags);
+	return;
+}
+
+void
+main_irq_hfc(struct BCState *bcs)
+{
+	long flags;
+	struct IsdnCardState *cs = bcs->cs;
+	int z1, z2, rcnt;
+	u_char f1, f2, cip;
+	int receive, transmit, count = 5;
+	struct sk_buff *skb;
+	char tmp[64];
+
+	save_flags(flags);
+      Begin:
+	cli();
+	count--;
+	cip = HFC_CIP | HFC_F1 | HFC_REC | HFC_CHANNEL(bcs->channel);
+	if ((cip & 0xc3) != (cs->hw.hfc.cip & 0xc3)) {
+		cs->BC_Write_Reg(cs, HFC_STATUS, cip, cip);
+		WaitForBusy(cs);
+	}
+	WaitNoBusy(cs);
+	f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+	cip = HFC_CIP | HFC_F2 | HFC_REC | HFC_CHANNEL(bcs->channel);
+	WaitNoBusy(cs);
+	f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+	if (f1 != f2) {
+		if (cs->debug & L1_DEB_HSCX) {
+			sprintf(tmp, "hfc rec %d f1(%d) f2(%d)",
+				bcs->channel, f1, f2);
+			debugl1(cs, tmp);
+		}
+		WaitForBusy(cs);
+		z1 = ReadZReg(bcs, HFC_Z1 | HFC_REC | HFC_CHANNEL(bcs->channel));
+		z2 = ReadZReg(bcs, HFC_Z2 | HFC_REC | HFC_CHANNEL(bcs->channel));
+		rcnt = z1 - z2;
+		if (rcnt < 0)
+			rcnt += cs->hw.hfc.fifosize;
+		rcnt++;
+		if (cs->debug & L1_DEB_HSCX) {
+			sprintf(tmp, "hfc rec %d z1(%x) z2(%x) cnt(%d)",
+				bcs->channel, z1, z2, rcnt);
+			debugl1(cs, tmp);
+		}
+/*              sti(); */
+		if ((skb = hfc_empty_fifo(bcs, rcnt))) {
+			skb_queue_tail(&bcs->rqueue, skb);
+			hfc_sched_event(bcs, B_RCVBUFREADY);
+		}
+		receive = 1;
+	} else
+		receive = 0;
+	restore_flags(flags);
+	udelay(1);
+	cli();
+	if (bcs->hw.hfc.tx_skb) {
+		transmit = 1;
+		test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+		hfc_fill_fifo(bcs);
+		if (test_bit(BC_FLG_BUSY, &bcs->Flag))
+			transmit = 0;
+	} else {
+		if ((bcs->hw.hfc.tx_skb = skb_dequeue(&bcs->squeue))) {
+			transmit = 1;
+			test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+			hfc_fill_fifo(bcs);
+			if (test_bit(BC_FLG_BUSY, &bcs->Flag))
+				transmit = 0;
+		} else {
+			transmit = 0;
+			hfc_sched_event(bcs, B_XMTBUFREADY);
+		}
+	}
+	restore_flags(flags);
+	if ((receive || transmit) && count)
+		goto Begin;
+	return;
+}
+
+void
+mode_hfc(struct BCState *bcs, int mode, int bc)
+{
+	struct IsdnCardState *cs = bcs->cs;
+
+	if (cs->debug & L1_DEB_HSCX) {
+		char tmp[40];
+		sprintf(tmp, "HFC 2BS0 mode %d bchan %d/%d",
+			mode, bc, bcs->channel);
+		debugl1(cs, tmp);
+	}
+	bcs->mode = mode;
+
+	switch (mode) {
+		case (L1_MODE_NULL):
+			if (bc)
+				cs->hw.hfc.isac_spcr &= ~0x03;
+			else
+				cs->hw.hfc.isac_spcr &= ~0x0c;
+			break;
+		case (L1_MODE_TRANS):
+			if (bc) {
+				cs->hw.hfc.ctmt |= 1;
+				cs->hw.hfc.isac_spcr &= ~0x03;
+				cs->hw.hfc.isac_spcr |= 0x02;
+			} else {
+				cs->hw.hfc.ctmt |= 2;
+				cs->hw.hfc.isac_spcr &= ~0x0c;
+				cs->hw.hfc.isac_spcr |= 0x08;
+			}
+			break;
+		case (L1_MODE_HDLC):
+			if (bc) {
+				cs->hw.hfc.ctmt &= ~1;
+				cs->hw.hfc.isac_spcr &= ~0x03;
+				cs->hw.hfc.isac_spcr |= 0x02;
+			} else {
+				cs->hw.hfc.ctmt &= ~2;
+				cs->hw.hfc.isac_spcr &= ~0x0c;
+				cs->hw.hfc.isac_spcr |= 0x08;
+			}
+			break;
+	}
+	cs->BC_Write_Reg(cs, HFC_STATUS, cs->hw.hfc.ctmt, cs->hw.hfc.ctmt);
+	cs->writeisac(cs, ISAC_SPCR, cs->hw.hfc.isac_spcr);
+	if (mode)
+		hfc_clear_fifo(bcs);
+}
+
+static void
+hfc_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	long flags;
+
+	switch (pr) {
+		case (PH_DATA_REQ):
+			save_flags(flags);
+			cli();
+			if (st->l1.bcs->hw.hfc.tx_skb) {
+				skb_queue_tail(&st->l1.bcs->squeue, skb);
+				restore_flags(flags);
+			} else {
+				st->l1.bcs->hw.hfc.tx_skb = skb;
+				test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+				st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
+				restore_flags(flags);
+			}
+			break;
+		case (PH_PULL_IND):
+			if (st->l1.bcs->hw.hfc.tx_skb) {
+				printk(KERN_WARNING "hfc_l2l1: this shouldn't happen\n");
+				break;
+			}
+			save_flags(flags);
+			cli();
+			test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+			st->l1.bcs->hw.hfc.tx_skb = skb;
+			st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
+			restore_flags(flags);
+			break;
+		case (PH_PULL_REQ):
+			if (!st->l1.bcs->hw.hfc.tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL_CNF, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+	}
+}
+
+void
+close_hfcstate(struct BCState *bcs)
+{
+	struct sk_buff *skb;
+
+	mode_hfc(bcs, 0, 0);
+	if (test_bit(BC_FLG_INIT, &bcs->Flag)) {
+		while ((skb = skb_dequeue(&bcs->rqueue))) {
+			dev_kfree_skb(skb);
+		}
+		while ((skb = skb_dequeue(&bcs->squeue))) {
+			dev_kfree_skb(skb);
+		}
+		if (bcs->hw.hfc.tx_skb) {
+			dev_kfree_skb(bcs->hw.hfc.tx_skb);
+			bcs->hw.hfc.tx_skb = NULL;
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		}
+	}
+	test_and_clear_bit(BC_FLG_INIT, &bcs->Flag);
+}
+
+static int
+open_hfcstate(struct IsdnCardState *cs,
+	      int bc)
+{
+	struct BCState *bcs = cs->bcs + bc;
+
+	if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+		skb_queue_head_init(&bcs->rqueue);
+		skb_queue_head_init(&bcs->squeue);
+	}
+	bcs->hw.hfc.tx_skb = NULL;
+	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	bcs->event = 0;
+	bcs->tx_cnt = 0;
+	return (0);
+}
+
+static void
+hfc_manl1(struct PStack *st, int pr,
+	  void *arg)
+{
+	switch (pr) {
+		case (PH_ACTIVATE_REQ):
+			test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+			mode_hfc(st->l1.bcs, st->l1.mode, st->l1.bc);
+			st->l1.l1man(st, PH_ACTIVATE_CNF, NULL);
+			break;
+		case (PH_DEACTIVATE_REQ):
+			if (!test_bit(BC_FLG_BUSY, &st->l1.bcs->Flag))
+				mode_hfc(st->l1.bcs, 0, 0);
+			test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+			break;
+	}
+}
+
+int
+setstack_hfc(struct PStack *st, struct BCState *bcs)
+{
+	if (open_hfcstate(st->l1.hardware, bcs->channel))
+		return (-1);
+	st->l1.bcs = bcs;
+	st->l2.l2l1 = hfc_l2l1;
+	st->ma.manl1 = hfc_manl1;
+	setstack_manager(st);
+	bcs->st = st;
+	return (0);
+}
+
+__initfunc(void
+init_send(struct BCState *bcs))
+{
+	int i;
+
+	if (!(bcs->hw.hfc.send = kmalloc(32 * sizeof(unsigned int), GFP_ATOMIC))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for hfc.send\n");
+		return;
+	}
+	for (i = 0; i < 32; i++)
+		bcs->hw.hfc.send[i] = 0x1fff;
+}
+
+__initfunc(void
+inithfc(struct IsdnCardState *cs))
+{
+	init_send(&cs->bcs[0]);
+	init_send(&cs->bcs[1]);
+	cs->BC_Send_Data = &hfc_fill_fifo;
+	cs->bcs[0].BC_SetStack = setstack_hfc;
+	cs->bcs[1].BC_SetStack = setstack_hfc;
+	cs->bcs[0].BC_Close = close_hfcstate;
+	cs->bcs[1].BC_Close = close_hfcstate;
+	mode_hfc(cs->bcs, 0, 0);
+	mode_hfc(cs->bcs + 1, 0, 0);
+}
+
+void
+releasehfc(struct IsdnCardState *cs)
+{
+	if (cs->bcs[0].hw.hfc.send) {
+		kfree(cs->bcs[0].hw.hfc.send);
+		cs->bcs[0].hw.hfc.send = NULL;
+	}
+	if (cs->bcs[1].hw.hfc.send) {
+		kfree(cs->bcs[1].hw.hfc.send);
+		cs->bcs[1].hw.hfc.send = NULL;
+	}
+}
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/hfc_2bs0.h linux/drivers/isdn/hisax/hfc_2bs0.h
--- v2.1.91/linux/drivers/isdn/hisax/hfc_2bs0.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/hfc_2bs0.h	Wed Apr  1 16:20:58 1998
@@ -0,0 +1,62 @@
+/* $Id: hfc_2bs0.h,v 1.1 1997/09/11 17:31:34 keil Exp $
+
+ *  specific defines for CCD's HFC 2BS0
+ *
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ *
+ * $Log: hfc_2bs0.h,v $
+ * Revision 1.1  1997/09/11 17:31:34  keil
+ * Common part for HFC 2BS0 based cards
+ *
+ *
+ */
+
+#define HFC_CTMT	0xe0
+#define HFC_CIRM  	0xc0
+#define HFC_CIP		0x80
+#define HFC_Z1		0x00
+#define HFC_Z2		0x08
+#define HFC_Z_LOW	0x00
+#define HFC_Z_HIGH	0x04
+#define HFC_F1_INC	0x28
+#define HFC_FIFO_IN	0x2c
+#define HFC_F1		0x30
+#define HFC_F2		0x34
+#define HFC_F2_INC	0x38
+#define HFC_FIFO_OUT	0x3c
+#define HFC_B1          0x00
+#define HFC_B2		0x02
+#define HFC_REC		0x01
+#define HFC_SEND	0x00
+#define HFC_CHANNEL(ch) (ch ? HFC_B2 : HFC_B1)
+
+#define HFC_STATUS	0
+#define HFC_DATA	1
+#define HFC_DATA_NODEB	2
+
+/* Status (READ) */
+#define HFC_BUSY	0x01
+#define HFC_TIMINT	0x02
+#define HFC_EXTINT	0x04
+
+/* CTMT (Write) */
+#define HFC_CLTIMER 0x10
+#define HFC_TIM50MS 0x08
+#define HFC_TIMIRQE 0x04
+#define HFC_TRANSB2 0x02
+#define HFC_TRANSB1 0x01
+
+/* CIRM (Write) */
+#define HFC_RESET  	0x08
+#define HFC_MEM8K	0x10
+#define HFC_INTA	0x01
+#define HFC_INTB	0x02
+#define HFC_INTC	0x03
+#define HFC_INTD	0x04
+#define HFC_INTE	0x05
+#define HFC_INTF	0x06
+
+extern void main_irq_hfc(struct BCState *bcs);
+extern void inithfc(struct IsdnCardState *cs);
+extern void releasehfc(struct IsdnCardState *cs);
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/hisax.h linux/drivers/isdn/hisax/hisax.h
--- v2.1.91/linux/drivers/isdn/hisax/hisax.h	Mon Nov  3 10:08:48 1997
+++ linux/drivers/isdn/hisax/hisax.h	Wed Apr  1 16:20:58 1998
@@ -1,52 +1,59 @@
-/* $Id: hisax.h,v 1.13 1997/04/06 22:54:12 keil Exp $
+/* $Id: hisax.h,v 2.14 1998/02/11 17:28:04 keil Exp $
 
  *   Basic declarations, defines and prototypes
  *
  * $Log: hisax.h,v $
- * Revision 1.13  1997/04/06 22:54:12  keil
- * Using SKB's
+ * Revision 2.14  1998/02/11 17:28:04  keil
+ * Niccy PnP/PCI support
  *
- * Revision 1.12  1997/03/23 21:45:45  keil
- * Add support for ELSA PCMCIA
+ * Revision 2.13  1998/02/09 18:46:02  keil
+ * Support for Sedlbauer PCMCIA (Marcus Niemann)
  *
- * Revision 1.11  1997/02/11 01:36:02  keil
- * New Param structure
+ * Revision 2.12  1998/02/03 23:31:30  keil
+ * add AMD7930 support
  *
- * Revision 1.10  1997/02/09 00:23:52  keil
- * new interface handling, one interface per card
+ * Revision 2.11  1998/02/02 13:33:00  keil
+ * New card support
  *
- * Revision 1.9  1997/01/27 23:18:44  keil
- * prototype for releasestack_isdnl3
+ * Revision 2.10  1997/11/08 21:37:52  keil
+ * new l1 init;new Compaq card
  *
- * Revision 1.8  1997/01/27 16:02:37  keil
- * new cards, callc timers, HZDELAY macro, HiSax_getrev prototype
+ * Revision 2.9  1997/11/06 17:09:09  keil
+ * New 2.1 init code
  *
- * Revision 1.7  1997/01/21 22:22:14  keil
- * changes for 2.0; Elsa Quickstep support
+ * Revision 2.8  1997/10/29 19:04:13  keil
+ * new L1; changes for 2.1
  *
- * Revision 1.6  1997/01/04 13:48:28  keil
- * primitiv for MDL_REMOVE added
+ * Revision 2.7  1997/10/10 20:56:47  fritz
+ * New HL interface.
  *
- * Revision 1.5  1996/12/08 19:49:19  keil
- * Monitor channel support
+ * Revision 2.6  1997/09/11 17:25:51  keil
+ * Add new cards
  *
- * Revision 1.4  1996/11/18 15:35:39  keil
- * some changes for ELSA cards
+ * Revision 2.5  1997/08/03 14:36:31  keil
+ * Implement RESTART procedure
  *
- * Revision 1.3  1996/11/05 19:37:23  keil
- * using config.h
+ * Revision 2.4  1997/07/31 19:25:20  keil
+ * PTP_DATA_LINK support
  *
- * Revision 1.2  1996/10/27 22:21:52  keil
- * CallFlags for broadcast messages
+ * Revision 2.3  1997/07/31 11:50:17  keil
+ * ONE TEI and FIXED TEI handling
  *
- * Revision 1.1  1996/10/13 20:03:46  keil
- * Initial revision
+ * Revision 2.2  1997/07/30 17:13:02  keil
+ * more changes for 'One TEI per card'
  *
+ * Revision 2.1  1997/07/27 21:45:13  keil
+ * new main structures
  *
+ * Revision 2.0  1997/06/26 11:06:27  keil
+ * New card and L1 interface.
+ * Eicon.Diehl Diva and Dynalink IS64PH support
+ *
+ * old changes removed KKe
  *
  */
-#include <linux/module.h>
 #include <linux/config.h>
+#include <linux/module.h>
 #include <linux/version.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
@@ -64,140 +71,136 @@
 #include <linux/wait.h>
 #include <linux/isdnif.h>
 #include <linux/tty.h>
+#include <linux/init.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;
-};
+#define PH_ACTIVATE_REQ	0x0010
+#define PH_ACTIVATE_CNF	0x0011
+#define PH_ACTIVATE_IND	0x0012
+#define PH_DEACTIVATE_REQ 0x0020
+#define PH_DEACTIVATE_CNF 0x0021
+#define PH_DEACTIVATE_IND 0x0022
+#define PH_DEACT_REQ	0x0024
+#define PH_DEACT_CNF	0x0025
+#define PH_DEACT_IND	0x0026
+#define PH_DEACT_ACK	0x0027
+#define PH_TESTLOOP_REQ	0x0030
+#define PH_PAUSE_CNF	0x0035
+#define PH_PAUSE_IND	0x0036
+#define PH_PULL_REQ	0x0038
+#define PH_PULL_CNF	0x0039
+#define	PH_PULL_IND	0x003A
+#define PH_DATA_REQ	0x0040
+#define PH_DATA_IND	0x0042
+
+#define PH_INFO3_REQ	0x0008
+#define PH_INFO2_IND	0x000A
+#define PH_ENABLE_REQ	0x0004
+#define PH_RSYNC_IND	0x0006
+#define PH_RESET_REQ	0x0000
+#define PH_RESET_IND	0x0002
+#define PH_POWERUP_CNF	0x0003
+#define PH_ACTIV_REQ	0x000C
+#define PH_I4_P8_IND	0x000D
+#define PH_I4_P10_IND	0x000F
+
+#define MDL_ASSIGN_REQ	0x0050
+#define MDL_ASSIGN_IND	0x0052
+#define MDL_REMOVE_REQ	0x0054
+#define MDL_ERROR_REQ	0x0058
+#define MDL_ERROR_IND	0x005A
+#define CARD_AUX_IND	0x005E
+
+#define DL_UNIT_DATA	6
+#define CC_ESTABLISH	7
+#define DL_ESTABLISH	8
+#define DL_DATA		9
+
+#define CC_CONNECT	15
+#define DL_RELEASE	20
+#define DL_FLUSH	21
+
+#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_INFO_SETUP	42
+#define MDL_INFO_CONN	43
+#define MDL_INFO_REL	44
+#define MDL_NOTEIPROC	46
+
+#define LC_ESTABLISH	47
+#define LC_RELEASE	48
+
+#define CC_INFO_CHARGE	52
+
+#define CC_MORE_INFO	53
+#define CC_IGNORE	54
+#define CC_RESTART	55
+
+
+#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
+
+#define CARD_RESET	0x1001
+#define CARD_SETIRQ	0x1002
+#define CARD_INIT	0x1003
+#define CARD_RELEASE	0x1004
+#define CARD_TEST	0x1005
 
 #ifdef __KERNEL__
 
-#undef DEBUG_MAGIC
-
-#define MAX_DFRAME_LEN	3072
+#define MAX_DFRAME_LEN	260
 #define HSCX_BUFMAX	4096
 #define MAX_DATA_SIZE	(HSCX_BUFMAX - 4)
-#define MAX_DATA_MEM    (HSCX_BUFMAX * 2)
+#define MAX_DATA_MEM	(HSCX_BUFMAX + 64)
+#define RAW_BUFMAX	(((HSCX_BUFMAX*6)/5) + 5)
 #define MAX_HEADER_LEN	4
 #define MAX_WINDOW	8
+#define MAX_MON_FRAME	32
+
+/* #define I4L_IRQ_FLAG SA_INTERRUPT */
+#define I4L_IRQ_FLAG    0
 
 /*
  * Statemachine
  */
+
 struct Fsm {
 	int *jumpmatrix;
 	int state_count, event_count;
@@ -226,73 +229,107 @@
 };
 
 struct L3Timer {
-	struct PStack *st;
+	struct l3_process *pc;
 	struct timer_list tl;
 	int event;
 };
 
+#define FLG_L1_ACTIVATING	1
+#define FLG_L1_ACTIVATED	2
+#define FLG_L1_DEACTTIMER	3
+#define FLG_L1_ACTTIMER		4
+#define FLG_L1_T3RUN		5
+#define FLG_L1_PULL_REQ		6
+
 struct Layer1 {
 	void *hardware;
-	int hscx;
+	struct BCState *bcs;
 	struct PStack **stlistp;
-	int act_state;
+	int Flags;
+	struct FsmInst l1m;
+	struct FsmTimer	timer;
 	void (*l1l2) (struct PStack *, int, void *);
 	void (*l1man) (struct PStack *, int, void *);
-	int hscxmode, hscxchannel, requestpull;
+	void (*l1tei) (struct PStack *, int, void *);
+	int mode, bc;
 };
 
+#define GROUP_TEI	127
+#define TEI_SAPI	63
+#define CTRL_SAPI	0
+#define PACKET_NOACK	250
+
+/* Layer2 Flags */
+
+#define FLG_LAPB	0
+#define FLG_LAPD	1
+#define FLG_ORIG	2
+#define FLG_MOD128	3
+#define FLG_PEND_REL	4
+#define FLG_L3_INIT	5 
+#define FLG_T200_RUN	6 
+#define FLG_ACK_PEND	7
+#define FLG_REJEXC	8
+#define FLG_OWN_BUSY	9
+#define FLG_PEER_BUSY	10
+#define FLG_DCHAN_BUSY	11
+
 struct Layer2 {
-	int sap, tei, ces;
-	int extended, laptype;
-	int uihsize, ihsize;
+	int tei;
+	int tei_wanted;
+	int sap;
+	int maxlen;
+	unsigned int flag;
 	int vs, va, vr;
-	struct sk_buff_head i_queue;
-	int window, orig;
-	int rejexp;
-	int debug;
-	struct sk_buff *windowar[MAX_WINDOW];
+	int rc;
+	int window;
 	int sow;
-	struct FsmInst l2m;
+	struct sk_buff *windowar[MAX_WINDOW];
+	struct sk_buff_head i_queue;
+	struct sk_buff_head ui_queue;
 	void (*l2l1) (struct PStack *, int, void *);
-	void (*l2l1discardq) (struct PStack *, int, void *, int);
 	void (*l2man) (struct PStack *, int, void *);
 	void (*l2l3) (struct PStack *, int, void *);
 	void (*l2tei) (struct PStack *, int, void *);
-	struct FsmTimer t200_timer, t203_timer;
-	int t200, n200, t203;
-	int rc, t200_running;
+	struct FsmInst l2m;
+	struct FsmTimer t200, t203;
+	int T200, N200, T203;
+	int debug;
 	char debug_id[32];
 };
 
 struct Layer3 {
-	void (*l3l4) (struct PStack *, int, void *);
+	void (*l3l4) (struct l3_process *, int, void *);
 	void (*l3l2) (struct PStack *, int, void *);
-	int state, callref;
-	struct L3Timer timer;
-	int t303, t304, t305, t308, t310, t313, t318, t319;
-	int n_t303;
+	struct l3_process *proc;
+	struct l3_process *global;
+	int N303;
 	int debug;
-	int channr;
 };
 
-struct Layer4 {
+struct LLInterface {
 	void (*l4l3) (struct PStack *, int, void *);
 	void *userdata;
-	void (*l1writewakeup) (struct PStack *);
-	void (*l2writewakeup) (struct PStack *);
+	void (*l1writewakeup) (struct PStack *, int);
+	void (*l2writewakeup) (struct PStack *, int);
 };
 
+
 struct Management {
+	int	ri;
+	struct FsmInst tei_m;
+	struct FsmTimer t202;
+	int T202, N202, debug;
+	void (*layer) (struct PStack *, int, void *);
 	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
@@ -300,39 +337,114 @@
 	int spv;		/* SPV Flag */
 };
 
+
 struct PStack {
 	struct PStack *next;
 	struct Layer1 l1;
 	struct Layer2 l2;
 	struct Layer3 l3;
-	struct Layer4 l4;
+	struct LLInterface lli; 
 	struct Management ma;
-	struct Param *pa;
 	int protocol;		/* EDSS1 or 1TR6 */
 };
 
-struct HscxState {
-	int inuse, init, active;
-	struct IsdnCardState *sp;
-	int hscx, mode;
-	u_char *rcvbuf;		/* B-Channel receive Buffer */
-	int rcvidx;		/* B-Channel receive Buffer Index */
-	struct sk_buff *tx_skb;	/* B-Channel transmit Buffer */
+struct l3_process {
+	int callref;
+	int state;
+	struct L3Timer timer;
+	int N303;
+	int debug;
+	struct Param para;
+	struct Channel *chan;
+	struct PStack *st;
+	struct l3_process *next;
+};
+
+struct hscx_hw {
+	int rcvidx;
+	int count;              /* Current skb sent count */
+	u_char *rcvbuf;         /* B-Channel receive Buffer */
+	struct sk_buff *tx_skb; /* B-Channel transmit Buffer */
+};
+
+struct hfcB_hw {
+	unsigned int *send;
+	int f1;
+	int f2;
+	struct sk_buff *tx_skb; /* B-Channel transmit Buffer */
+};
+
+struct tiger_hw {
+	struct sk_buff *tx_skb; /* B-Channel transmit Buffer */
+	u_int *send;
+	u_int *s_irq;
+	u_int *s_end;
+	u_int *sendp;
+	u_int *rec;
+	int free;
+	u_char *rcvbuf;
+	u_char *sendbuf;
+	u_char *sp;
+	int sendcnt;
+	u_int s_tot;
+	u_int r_bitcnt;
+	u_int r_tot;
+	u_int r_err;
+	u_int r_fcs;
+	u_char r_state;
+	u_char r_one;
+	u_char r_val;
+	u_char s_state;
+};
+
+struct amd7930_hw {
+	u_char *tx_buff;
+	u_char *rv_buff;
+	int rv_buff_in;
+	int rv_buff_out;
+	struct sk_buff *rv_skb;
+	struct hdlc_state *hdlc_state;
+	struct tq_struct tq_rcv;
+	struct tq_struct tq_xmt;
+	struct sk_buff *tx_skb; /* B-Channel transmit Buffer */
+};
+
+#define BC_FLG_INIT	1
+#define BC_FLG_ACTIV	2
+#define BC_FLG_BUSY	3
+#define BC_FLG_NOFRAME	4
+#define BC_FLG_HALF	5
+#define BC_FLG_EMPTY	6
+
+#define L1_MODE_NULL	0
+#define L1_MODE_TRANS	1
+#define L1_MODE_HDLC	2
+
+struct BCState {
+	int channel;
+	int mode;
+	int Flag;
+	struct IsdnCardState *cs;
 	int tx_cnt;		/* B-Channel transmit counter */
-	int count;		/* Current skb sent count */
 	struct sk_buff_head rqueue;	/* B-Channel receive Queue */
-	struct sk_buff_head squeue;	/* B-Channel receive Queue */
+	struct sk_buff_head squeue;	/* B-Channel send Queue */
 	struct PStack *st;
 	struct tq_struct tqueue;
 	int event;
-#ifdef DEBUG_MAGIC
-	int magic;		/* 301270 */
-#endif
+	int  (*BC_SetStack) (struct PStack *, struct BCState *);
+	void (*BC_Close) (struct BCState *);
+	union {
+		struct hscx_hw hscx;
+		struct hfcB_hw hfc;
+		struct tiger_hw tiger;
+		struct amd7930_hw  amd7930;
+	} hw;
 };
 
 struct LcFsm {
-	struct FsmInst lcfi;
 	int type;
+	int delay;
+	struct FsmInst lcfi;
 	struct Channel *ch;
 	void (*lccall) (struct LcFsm *, int, void *);
 	struct PStack *st;
@@ -343,52 +455,209 @@
 };
 
 struct Channel {
-	struct PStack ds, is;
-	struct IsdnCardState *sp;
-	int hscx;
+	struct PStack *b_st, *d_st;
+	struct IsdnCardState *cs;
+	struct BCState *bcs;
 	int chan;
 	int incoming;
 	struct FsmInst fi;
-	struct LcFsm lc_d, lc_b;
-	struct Param para;
+	struct LcFsm *lc_d;
+	struct LcFsm *lc_b;
 	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;
+	struct l3_process *proc;
+	setup_parm setup;	/* from isdnif.h numbers and Serviceindicator */
 	int Flags;		/* for remembering action done in l4 */
 	int leased;
 };
 
-struct IsdnCardState {
-#ifdef DEBUG_MAGIC
-	int magic;
-#endif
-	unsigned char typ;
-	unsigned char subtyp;
-	int protocol;
-	unsigned int irq;
+struct elsa_hw {
+	unsigned int base;
+	unsigned int cfg;
+	unsigned int ctrl;
+	unsigned int ale;
+	unsigned int isac;
+	unsigned int itac;
+	unsigned int hscx;
+	unsigned int trig;
+	unsigned int timer;
+	unsigned int counter;
+	unsigned int status;
+	struct timer_list tl;
+	u_char ctrl_reg;
+};	
+
+struct teles3_hw {
+	unsigned int cfg_reg;
+	unsigned int isac;
+	unsigned int hscx[2];
+	unsigned int isacfifo;
+	unsigned int hscxfifo[2];
+};	
+
+struct teles0_hw {
 	unsigned int cfg_reg;
 	unsigned int membase;
+};	
+
+struct avm_hw {
+	unsigned int cfg_reg;
 	unsigned int isac;
 	unsigned int hscx[2];
+	unsigned int isacfifo;
+	unsigned int hscxfifo[2];
 	unsigned int counter;
+};	
+
+struct ix1_hw {
+	unsigned int cfg_reg;
+	unsigned int isac_ale;
+	unsigned int isac;
+	unsigned int hscx_ale;
+	unsigned int hscx;
+};
+
+struct diva_hw {
+	unsigned int cfg_reg;
+	unsigned int ctrl;
+	unsigned int isac_adr;
+	unsigned int isac;
+	unsigned int hscx_adr;
+	unsigned int hscx;
+	unsigned int status;
+	struct timer_list tl;
+	u_char ctrl_reg;
+};	
+
+struct asus_hw {
+	unsigned int cfg_reg;
+	unsigned int adr;
+	unsigned int isac;
+	unsigned int hscx;
+	unsigned int u7;
+	unsigned int pots;
+};
+
+
+struct hfc_hw {
+	unsigned int addr;
+	unsigned int fifosize;
+	unsigned char cirm;
+	unsigned char ctmt;
+	unsigned char cip;
+	u_char isac_spcr;
+	struct timer_list timer;
+};
+
+struct sedl_hw {
+	unsigned int cfg_reg;
+	unsigned int adr;
+	unsigned int isac;
+	unsigned int hscx;
+	unsigned int reset_on;
+	unsigned int reset_off;
+};
+
+struct spt_hw {
+	unsigned int cfg_reg;
+	unsigned int isac;
+	unsigned int hscx[2];
+	unsigned char res_irq;
+};	
+
+struct mic_hw {
+	unsigned int cfg_reg;
+	unsigned int adr;
+	unsigned int isac;
+	unsigned int hscx;
+};
+
+struct njet_hw {
+	unsigned int base;
+	unsigned int isac;
+	unsigned int auxa;
+	unsigned char auxd;
+	unsigned char dmactrl;
+	unsigned char ctrl_reg;
+	unsigned char irqmask0;
+	unsigned char irqstat0;
+	unsigned char last_is0;
+};
+
+struct hfcD_hw {
+	unsigned int addr;
+	unsigned int bfifosize;
+	unsigned int dfifosize;
+	unsigned char cirm;
+	unsigned char ctmt;
+	unsigned char cip;
+	unsigned char conn;
+	unsigned char mst_m;
+	unsigned char int_m1;
+	unsigned char int_m2;
+	unsigned char int_s1;
+	unsigned char sctrl;
+	unsigned char stat;
+	unsigned char fifo;
+	unsigned char f1;
+	unsigned char f2;
+	unsigned int *send;
+	struct timer_list timer;
+};
+
+#define HW_IOM1		0
+#define HW_IPAC		1
+#define FLG_TWO_DCHAN	4
+#define FLG_L1_DBUSY	5
+#define FLG_DBUSY_TIMER 6
+#define FLG_LOCK_ATOMIC 7
+#define HW_MON0_RX_END	8
+#define HW_MON1_RX_END	9
+#define HW_MON0_TX_END	10
+#define HW_MON1_TX_END	11
+
+struct IsdnCardState {
+	unsigned char typ;
+	unsigned char subtyp;
+	int protocol;
+	unsigned int irq;
+	int HW_Flags; 
+	int *busy_flag;
+	union {
+		struct elsa_hw elsa;
+		struct teles0_hw teles0;
+		struct teles3_hw teles3;
+		struct avm_hw avm;
+		struct ix1_hw ix1;
+		struct diva_hw diva;
+		struct asus_hw asus;
+		struct hfc_hw hfc;
+		struct sedl_hw sedl;
+		struct spt_hw spt;
+		struct mic_hw mic;
+		struct njet_hw njet;
+		struct hfcD_hw hfcD;
+		struct ix1_hw niccy;
+	} hw;
 	int myid;
 	isdn_if iif;
 	u_char *status_buf;
 	u_char *status_read;
 	u_char *status_write;
 	u_char *status_end;
-	void (*ph_command) (struct IsdnCardState *, unsigned int);
-	void (*modehscx) (struct HscxState *, int, int);
-	void (*hscx_fill_fifo) (struct HscxState *);
-	void (*isac_fill_fifo) (struct IsdnCardState *);
+	u_char (*readisac) (struct IsdnCardState *, u_char);
+	void   (*writeisac) (struct IsdnCardState *, u_char, u_char);
+	void   (*readisacfifo) (struct IsdnCardState *, u_char *, int);
+	void   (*writeisacfifo) (struct IsdnCardState *, u_char *, int);
+	u_char (*BC_Read_Reg) (struct IsdnCardState *, int, u_char);
+	void   (*BC_Write_Reg) (struct IsdnCardState *, int, u_char, u_char);
+	void   (*BC_Send_Data) (struct BCState *);
+	int    (*cardmsg) (struct IsdnCardState *, int, void *);
+	void   (*l1cmd) (struct IsdnCardState *, int, void *);
 	struct Channel channel[2];
+	struct BCState bcs[2];
 	struct PStack *stlist;
 	u_char *rcvbuf;
 	int rcvidx;
@@ -396,16 +665,20 @@
 	int tx_cnt;
 	int event;
 	struct tq_struct tqueue;
-	int ph_active;
+	struct timer_list dbusytimer;
 	struct sk_buff_head rq, sq; /* D-channel queues */
-	int cardnr;
 	int ph_state;
-	struct PStack *teistack;
-	struct HscxState hs[2];
+	int cardnr;
 	int dlogflag;
 	char *dlogspace;
 	int debug;
-	unsigned int CallFlags;
+	u_char *mon_tx;
+	u_char *mon_rx;
+	int mon_txp;
+	int mon_txc;
+	int mon_rxp;
+	u_char mocr;
+	void   (*setstack_d) (struct PStack *, struct IsdnCardState *);
 };
 
 #define  MON0_RX	1
@@ -419,99 +692,246 @@
 #define  ISDN_CTYPE_PNP		4
 #define  ISDN_CTYPE_A1		5
 #define  ISDN_CTYPE_ELSA	6
-#define  ISDN_CTYPE_ELSA_QS1000	7
+#define  ISDN_CTYPE_ELSA_PNP	7
 #define  ISDN_CTYPE_TELESPCMCIA	8
 #define  ISDN_CTYPE_IX1MICROR2	9
+#define  ISDN_CTYPE_ELSA_PCMCIA	10
+#define  ISDN_CTYPE_DIEHLDIVA	11
+#define  ISDN_CTYPE_ASUSCOM	12
+#define  ISDN_CTYPE_TELEINT	13
+#define  ISDN_CTYPE_TELES3C	14
+#define  ISDN_CTYPE_SEDLBAUER	15
+#define  ISDN_CTYPE_SPORTSTER	16
+#define  ISDN_CTYPE_MIC		17
+#define  ISDN_CTYPE_ELSA_PCI	18
+#define  ISDN_CTYPE_COMPAQ_ISA	19
+#define  ISDN_CTYPE_NETJET	20
+#define  ISDN_CTYPE_TELESPCI	21
+#define  ISDN_CTYPE_SEDLBAUER_PCMCIA	22
+#define  ISDN_CTYPE_AMD7930	23
+#define  ISDN_CTYPE_NICCY	24
 
-#define  ISDN_CTYPE_COUNT	9
+#define  ISDN_CTYPE_COUNT	24
+
+#ifdef ISDN_CHIP_ISAC
+#undef ISDN_CHIP_ISAC
+#endif
+
+#define HISAX_INITFUNC(__arginit) __initfunc(__arginit)
+#define HISAX_INITDATA __initdata
 
 #ifdef	CONFIG_HISAX_16_0
 #define  CARD_TELES0 (1<< ISDN_CTYPE_16_0) | (1<< ISDN_CTYPE_8_0)
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
 #else
 #define  CARD_TELES0  0
 #endif
 
 #ifdef	CONFIG_HISAX_16_3
 #define  CARD_TELES3 (1<< ISDN_CTYPE_16_3) | (1<< ISDN_CTYPE_PNP) | \
-		     (1<< ISDN_CTYPE_TELESPCMCIA)
+		     (1<< ISDN_CTYPE_TELESPCMCIA) | (1<< ISDN_CTYPE_COMPAQ_ISA)
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
 #else
 #define  CARD_TELES3  0
 #endif
 
 #ifdef	CONFIG_HISAX_AVM_A1
 #define  CARD_AVM_A1 (1<< ISDN_CTYPE_A1)
+#ifndef ISDN_CHIP_ISAC 
+#define ISDN_CHIP_ISAC 1
+#endif
 #else
 #define  CARD_AVM_A1  0
 #endif
 
-#ifdef	CONFIG_HISAX_ELSA_PCC
-#define  CARD_ELSA (1<< ISDN_CTYPE_ELSA) | (1<< ISDN_CTYPE_ELSA_QS1000)
+#ifdef	CONFIG_HISAX_ELSA
+#define  CARD_ELSA (1<< ISDN_CTYPE_ELSA) | (1<< ISDN_CTYPE_ELSA_PNP) | \
+		   (1<< ISDN_CTYPE_ELSA_PCMCIA) | (1<< ISDN_CTYPE_ELSA_PCI)
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#undef HISAX_INITFUNC
+#define HISAX_INITFUNC(__arginit) __arginit
+#undef HISAX_INITDATA
+#define HISAX_INITDATA
 #else
 #define  CARD_ELSA  0
 #endif
 
-#ifdef	CONFIG_HISAX_ELSA_PCMCIA
-#if CARD_ELSA
-#error "You can't use a ELSA ISA card and a ELSA PCMCIA card with the same driver"
-#else
-#undef CARD_ELSA
-#define CARD_ELSA (1<< ISDN_CTYPE_ELSA_QS1000)
-#endif
-#endif
 
 #ifdef	CONFIG_HISAX_IX1MICROR2
 #define	CARD_IX1MICROR2 (1 << ISDN_CTYPE_IX1MICROR2)
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
 #else
 #define CARD_IX1MICROR2 0
 #endif
 
+#ifdef  CONFIG_HISAX_DIEHLDIVA
+#define CARD_DIEHLDIVA (1 << ISDN_CTYPE_DIEHLDIVA)
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_DIEHLDIVA 0
+#endif
+
+#ifdef  CONFIG_HISAX_ASUSCOM
+#define CARD_ASUSCOM (1 << ISDN_CTYPE_ASUSCOM)
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_ASUSCOM 0
+#endif
+
+#ifdef  CONFIG_HISAX_TELEINT
+#define CARD_TELEINT (1 << ISDN_CTYPE_TELEINT)
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_TELEINT 0
+#endif
+
+#ifdef  CONFIG_HISAX_SEDLBAUER
+#define CARD_SEDLBAUER (1 << ISDN_CTYPE_SEDLBAUER) | (1 << ISDN_CTYPE_SEDLBAUER_PCMCIA)
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_SEDLBAUER 0
+#endif
+
+#ifdef  CONFIG_HISAX_SPORTSTER
+#define CARD_SPORTSTER (1 << ISDN_CTYPE_SPORTSTER)
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_SPORTSTER 0
+#endif
+
+#ifdef  CONFIG_HISAX_MIC
+#define CARD_MIC (1 << ISDN_CTYPE_MIC)
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_MIC 0
+#endif
+
+#ifdef  CONFIG_HISAX_NETJET
+#define CARD_NETJET (1 << ISDN_CTYPE_NETJET)
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_NETJET 0
+#endif
+
+#ifdef	CONFIG_HISAX_TELES3C
+#define  CARD_TELES3C (1<< ISDN_CTYPE_TELES3C)
+#else
+#define  CARD_TELES3C  0
+#endif
+
+#ifdef  CONFIG_HISAX_AMD7930
+#define CARD_AMD7930 (1 << ISDN_CTYPE_AMD7930)
+#else
+#define CARD_AMD7930 0
+#endif
+
+#ifdef	CONFIG_HISAX_NICCY
+#define	CARD_NICCY (1 << ISDN_CTYPE_NICCY)
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_NICCY 0
+#endif
+
+
 #define  SUPORTED_CARDS  (CARD_TELES0 | CARD_TELES3 | CARD_AVM_A1 | CARD_ELSA \
-			 | CARD_IX1MICROR2)
+			 | CARD_IX1MICROR2 | CARD_DIEHLDIVA | CARD_ASUSCOM \
+			 | CARD_TELEINT | CARD_SEDLBAUER | CARD_SPORTSTER \
+			 | CARD_MIC | CARD_NETJET | CARD_TELES3C | CARD_AMD7930 \
+			 | CARD_NICCY)
+
+#define TEI_PER_CARD 0
+
+#ifdef CONFIG_HISAX_1TR6
+#undef TEI_PER_CARD
+#define TEI_PER_CARD 1
+#endif
+
+#ifdef CONFIG_HISAX_EURO
+#undef TEI_PER_CARD
+#define TEI_PER_CARD 1
+#define HISAX_EURO_SENDCOMPLETE 1
+#ifdef	CONFIG_HISAX_ML
+#undef HISAX_EURO_SENDCOMPLETE
+#endif
+#undef HISAX_DE_AOC
+#ifdef CONFIG_DE_AOC
+#define HISAX_DE_AOC 1
+#endif
+#endif
+
+#if TEI_PER_CARD
+#undef TEI_FIXED
+#endif
+
+#undef PTP_DATA_LINK
+
+#ifdef PTP_DATA_LINK
+#undef TEI_FIXED
+#define TEI_FIXED 0
+#define LAYER2_WATCHING
+#endif
 
 struct IsdnCard {
 	int typ;
 	int protocol;		/* EDSS1 or 1TR6 */
-	unsigned int para[3];
-	struct IsdnCardState *sp;
+	unsigned int para[4];
+	struct IsdnCardState *cs;
 };
 
-
-#define LAPD 0
-#define LAPB 1
-
-void l2down(struct PStack *st, u_char pr, struct sk_buff *skb);
-void l2up(struct PStack *st, u_char pr, struct sk_buff *skb);
-void acceptph(struct PStack *st, struct sk_buff *skb);
 void setstack_isdnl2(struct PStack *st, char *debug_id);
-int HiSax_inithardware(void);
+int HiSax_inithardware(int *);
 void HiSax_closehardware(void);
 
-void setstack_HiSax(struct PStack *st, struct IsdnCardState *sp);
-unsigned int randomces(void);
+void setstack_HiSax(struct PStack *st, struct IsdnCardState *cs);
+unsigned int random_ri(void);
 void setstack_isdnl3(struct PStack *st, struct Channel *chanp);
 void HiSax_addlist(struct IsdnCardState *sp, struct PStack *st);
 void releasestack_isdnl2(struct PStack *st);
 void releasestack_isdnl3(struct PStack *st);
 void HiSax_rmlist(struct IsdnCardState *sp, struct PStack *st);
-void newcallref(struct PStack *st);
 
-int setstack_hscx(struct PStack *st, struct HscxState *hs);
 u_char *findie(u_char * p, int size, u_char ie, int wanted_set);
 int getcallref(u_char * p);
+int newcallref(void);
 
 void FsmNew(struct Fsm *fsm, struct FsmNode *fnlist, int fncount);
 void FsmFree(struct Fsm *fsm);
 int FsmEvent(struct FsmInst *fi, int event, void *arg);
 void FsmChangeState(struct FsmInst *fi, int newstate);
 void FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft);
-int FsmAddTimer(struct FsmTimer *ft, int millisec,
-		int event, void *arg, int where);
+int FsmAddTimer(struct FsmTimer *ft, int millisec, int event,
+	void *arg, int where);
+void FsmRestartTimer(struct FsmTimer *ft, int millisec, int event,
+	void *arg, int where);
 void FsmDelTimer(struct FsmTimer *ft, int where);
-int FsmTimerRunning(struct FsmTimer *ft);
 void jiftime(char *s, long mark);
 
 int HiSax_command(isdn_ctrl * ic);
-int HiSax_writebuf_skb(int id, int chan, struct sk_buff *skb);
+int HiSax_writebuf_skb(int id, int chan, int ack, struct sk_buff *skb);
 void HiSax_putstatus(struct IsdnCardState *csta, char *buf);
 void HiSax_reportcard(int cardnr);
 int QuickHex(char *txt, u_char * p, int cnt);
@@ -520,10 +940,12 @@
 void iecpy(u_char * dest, u_char * iestart, int ieoffset);
 void setstack_transl2(struct PStack *st);
 void releasestack_transl2(struct PStack *st);
-void close_hscxstate(struct HscxState *);
 void setstack_tei(struct PStack *st);
-
-#endif				/* __KERNEL__ */
+void setstack_manager(struct PStack *st);
+#ifdef ISDN_CHIP_ISAC
+void setstack_isac(struct PStack *st, struct IsdnCardState *cs);
+#endif	/* ISDN_CHIP_ISAC */
+#endif	/* __KERNEL__ */
 
 #define HZDELAY(jiffs) {int tout = jiffs; while (tout--) udelay(1000000/HZ);}
 
@@ -533,8 +955,12 @@
 void CallcFree(void);
 int CallcNewChan(struct IsdnCardState *csta);
 void CallcFreeChan(struct IsdnCardState *csta);
+void Isdnl1New(void);
+void Isdnl1Free(void);
 void Isdnl2New(void);
 void Isdnl2Free(void);
 void init_tei(struct IsdnCardState *sp, int protocol);
 void release_tei(struct IsdnCardState *sp);
 char *HiSax_getrev(const char *revision);
+void TeiNew(void);
+void TeiFree(void);
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/hscx.c linux/drivers/isdn/hisax/hscx.c
--- v2.1.91/linux/drivers/isdn/hisax/hscx.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/hscx.c	Wed Apr  1 16:20:58 1998
@@ -0,0 +1,280 @@
+/* $Id: hscx.c,v 1.7 1998/02/12 23:07:36 keil Exp $
+
+ * hscx.c   HSCX specific routines
+ *
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ *
+ * $Log: hscx.c,v $
+ * Revision 1.7  1998/02/12 23:07:36  keil
+ * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
+ *
+ * Revision 1.6  1998/02/02 13:41:12  keil
+ * new init
+ *
+ * Revision 1.5  1997/11/06 17:09:34  keil
+ * New 2.1 init code
+ *
+ * Revision 1.4  1997/10/29 19:01:06  keil
+ * changes for 2.1
+ *
+ * Revision 1.3  1997/07/27 21:38:34  keil
+ * new B-channel interface
+ *
+ * Revision 1.2  1997/06/26 11:16:17  keil
+ * first version
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include "hisax.h"
+#include "hscx.h"
+#include "isdnl1.h"
+#include <linux/interrupt.h>
+
+static char *HSCXVer[] HISAX_INITDATA =
+{"A1", "?1", "A2", "?3", "A3", "V2.1", "?6", "?7",
+ "?8", "?9", "?10", "?11", "?12", "?13", "?14", "???"};
+
+HISAX_INITFUNC(int
+HscxVersion(struct IsdnCardState *cs, char *s))
+{
+	int verA, verB;
+
+	verA = cs->BC_Read_Reg(cs, 0, HSCX_VSTR) & 0xf;
+	verB = cs->BC_Read_Reg(cs, 1, HSCX_VSTR) & 0xf;
+	printk(KERN_INFO "%s HSCX version A: %s  B: %s\n", s,
+	       HSCXVer[verA], HSCXVer[verB]);
+	if ((verA == 0) | (verA == 0xf) | (verB == 0) | (verB == 0xf))
+		return (1);
+	else
+		return (0);
+}
+
+void
+modehscx(struct BCState *bcs, int mode, int bc)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int hscx = bcs->channel;
+
+	if (cs->debug & L1_DEB_HSCX) {
+		char tmp[40];
+		sprintf(tmp, "hscx %c mode %d ichan %d",
+			'A' + hscx, mode, bc);
+		debugl1(cs, tmp);
+	}
+	bcs->mode = mode;
+	cs->BC_Write_Reg(cs, hscx, HSCX_CCR1, 0x85);
+	cs->BC_Write_Reg(cs, hscx, HSCX_XAD1, 0xFF);
+	cs->BC_Write_Reg(cs, hscx, HSCX_XAD2, 0xFF);
+	cs->BC_Write_Reg(cs, hscx, HSCX_RAH2, 0xFF);
+	cs->BC_Write_Reg(cs, hscx, HSCX_XBCH, 0x0);
+	cs->BC_Write_Reg(cs, hscx, HSCX_RLCR, 0x0);
+	cs->BC_Write_Reg(cs, hscx, HSCX_CCR2, 0x30);
+	cs->BC_Write_Reg(cs, hscx, HSCX_XCCR, 7);
+	cs->BC_Write_Reg(cs, hscx, HSCX_RCCR, 7);
+
+	/* Switch IOM 1 SSI */
+	if (test_bit(HW_IOM1, &cs->HW_Flags) && (hscx == 0))
+		bc = 1 - bc;
+
+	if (bc == 0) {
+		cs->BC_Write_Reg(cs, hscx, HSCX_TSAX,
+			      test_bit(HW_IOM1, &cs->HW_Flags) ? 0x7 : 0x2f);
+		cs->BC_Write_Reg(cs, hscx, HSCX_TSAR,
+			      test_bit(HW_IOM1, &cs->HW_Flags) ? 0x7 : 0x2f);
+	} else {
+		cs->BC_Write_Reg(cs, hscx, HSCX_TSAX, 0x3);
+		cs->BC_Write_Reg(cs, hscx, HSCX_TSAR, 0x3);
+	}
+	switch (mode) {
+		case (L1_MODE_NULL):
+			cs->BC_Write_Reg(cs, hscx, HSCX_TSAX, 0xff);
+			cs->BC_Write_Reg(cs, hscx, HSCX_TSAR, 0xff);
+			cs->BC_Write_Reg(cs, hscx, HSCX_MODE, 0x84);
+			break;
+		case (L1_MODE_TRANS):
+			cs->BC_Write_Reg(cs, hscx, HSCX_MODE, 0xe4);
+			break;
+		case (L1_MODE_HDLC):
+			cs->BC_Write_Reg(cs, hscx, HSCX_MODE, 0x8c);
+			break;
+	}
+	if (mode)
+		cs->BC_Write_Reg(cs, hscx, HSCX_CMDR, 0x41);
+	cs->BC_Write_Reg(cs, hscx, HSCX_ISTA, 0x00);
+}
+
+void
+hscx_sched_event(struct BCState *bcs, int event)
+{
+	bcs->event |= 1 << event;
+	queue_task(&bcs->tqueue, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+}
+
+static void
+hscx_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	long flags;
+
+	switch (pr) {
+		case (PH_DATA_REQ):
+			save_flags(flags);
+			cli();
+			if (st->l1.bcs->hw.hscx.tx_skb) {
+				skb_queue_tail(&st->l1.bcs->squeue, skb);
+				restore_flags(flags);
+			} else {
+				st->l1.bcs->hw.hscx.tx_skb = skb;
+				test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+				st->l1.bcs->hw.hscx.count = 0;
+				restore_flags(flags);
+				st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
+			}
+			break;
+		case (PH_PULL_IND):
+			if (st->l1.bcs->hw.hscx.tx_skb) {
+				printk(KERN_WARNING "hscx_l2l1: this shouldn't happen\n");
+				break;
+			}
+			test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+			st->l1.bcs->hw.hscx.tx_skb = skb;
+			st->l1.bcs->hw.hscx.count = 0;
+			st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
+			break;
+		case (PH_PULL_REQ):
+			if (!st->l1.bcs->hw.hscx.tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL_CNF, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+	}
+
+}
+
+void
+close_hscxstate(struct BCState *bcs)
+{
+	struct sk_buff *skb;
+
+	modehscx(bcs, 0, 0);
+	if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (bcs->hw.hscx.rcvbuf) {
+			kfree(bcs->hw.hscx.rcvbuf);
+			bcs->hw.hscx.rcvbuf = NULL;
+		}
+		while ((skb = skb_dequeue(&bcs->rqueue))) {
+			dev_kfree_skb(skb);
+		}
+		while ((skb = skb_dequeue(&bcs->squeue))) {
+			dev_kfree_skb(skb);
+		}
+		if (bcs->hw.hscx.tx_skb) {
+			dev_kfree_skb(bcs->hw.hscx.tx_skb);
+			bcs->hw.hscx.tx_skb = NULL;
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		}
+	}
+}
+
+static int
+open_hscxstate(struct IsdnCardState *cs,
+	       int bc)
+{
+	struct BCState *bcs = cs->bcs + bc;
+
+	if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (!(bcs->hw.hscx.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) {
+			printk(KERN_WARNING
+			       "HiSax: No memory for hscx.rcvbuf\n");
+			return (1);
+		}
+		skb_queue_head_init(&bcs->rqueue);
+		skb_queue_head_init(&bcs->squeue);
+	}
+	bcs->hw.hscx.tx_skb = NULL;
+	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	bcs->event = 0;
+	bcs->hw.hscx.rcvidx = 0;
+	bcs->tx_cnt = 0;
+	return (0);
+}
+
+static void
+hscx_manl1(struct PStack *st, int pr,
+	   void *arg)
+{
+	switch (pr) {
+		case (PH_ACTIVATE_REQ):
+			test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+			modehscx(st->l1.bcs, st->l1.mode, st->l1.bc);
+			st->l1.l1man(st, PH_ACTIVATE_CNF, NULL);
+			break;
+		case (PH_DEACTIVATE_REQ):
+			if (!test_bit(BC_FLG_BUSY, &st->l1.bcs->Flag))
+				modehscx(st->l1.bcs, 0, 0);
+			test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+			break;
+	}
+}
+
+int
+setstack_hscx(struct PStack *st, struct BCState *bcs)
+{
+	if (open_hscxstate(st->l1.hardware, bcs->channel))
+		return (-1);
+	st->l1.bcs = bcs;
+	st->l2.l2l1 = hscx_l2l1;
+	st->ma.manl1 = hscx_manl1;
+	setstack_manager(st);
+	bcs->st = st;
+	return (0);
+}
+
+HISAX_INITFUNC(void
+clear_pending_hscx_ints(struct IsdnCardState *cs))
+{
+	int val;
+	char tmp[64];
+
+	val = cs->BC_Read_Reg(cs, 1, HSCX_ISTA);
+	sprintf(tmp, "HSCX B ISTA %x", val);
+	debugl1(cs, tmp);
+	if (val & 0x01) {
+		val = cs->BC_Read_Reg(cs, 1, HSCX_EXIR);
+		sprintf(tmp, "HSCX B EXIR %x", val);
+		debugl1(cs, tmp);
+	} else if (val & 0x02) {
+		val = cs->BC_Read_Reg(cs, 0, HSCX_EXIR);
+		sprintf(tmp, "HSCX A EXIR %x", val);
+		debugl1(cs, tmp);
+	}
+	val = cs->BC_Read_Reg(cs, 0, HSCX_ISTA);
+	sprintf(tmp, "HSCX A ISTA %x", val);
+	debugl1(cs, tmp);
+	val = cs->BC_Read_Reg(cs, 1, HSCX_STAR);
+	sprintf(tmp, "HSCX B STAR %x", val);
+	debugl1(cs, tmp);
+	val = cs->BC_Read_Reg(cs, 0, HSCX_STAR);
+	sprintf(tmp, "HSCX A STAR %x", val);
+	debugl1(cs, tmp);
+	cs->BC_Write_Reg(cs, 0, HSCX_MASK, 0xFF);
+	cs->BC_Write_Reg(cs, 1, HSCX_MASK, 0xFF);
+	cs->BC_Write_Reg(cs, 0, HSCX_MASK, 0);
+	cs->BC_Write_Reg(cs, 1, HSCX_MASK, 0);
+}
+
+HISAX_INITFUNC(void 
+inithscx(struct IsdnCardState *cs))
+{
+	cs->bcs[0].BC_SetStack = setstack_hscx;
+	cs->bcs[1].BC_SetStack = setstack_hscx;
+	cs->bcs[0].BC_Close = close_hscxstate;
+	cs->bcs[1].BC_Close = close_hscxstate;
+	modehscx(cs->bcs, 0, 0);
+	modehscx(cs->bcs + 1, 0, 0);
+}
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/hscx.h linux/drivers/isdn/hisax/hscx.h
--- v2.1.91/linux/drivers/isdn/hisax/hscx.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/hscx.h	Wed Apr  1 16:20:58 1998
@@ -0,0 +1,46 @@
+/* $Id: hscx.h,v 1.3 1997/07/27 21:38:35 keil Exp $
+
+ * hscx.h   HSCX specific defines
+ *
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ *
+ * $Log: hscx.h,v $
+ * Revision 1.3  1997/07/27 21:38:35  keil
+ * new B-channel interface
+ *
+ * Revision 1.2  1997/06/26 11:16:18  keil
+ * first version
+ *
+ *
+ */
+
+/* All Registers original Siemens Spec  */
+
+#define HSCX_ISTA 0x20
+#define HSCX_CCR1 0x2f
+#define HSCX_CCR2 0x2c
+#define HSCX_TSAR 0x31
+#define HSCX_TSAX 0x30
+#define HSCX_XCCR 0x32
+#define HSCX_RCCR 0x33
+#define HSCX_MODE 0x22
+#define HSCX_CMDR 0x21
+#define HSCX_EXIR 0x24
+#define HSCX_XAD1 0x24
+#define HSCX_XAD2 0x25
+#define HSCX_RAH2 0x27
+#define HSCX_RSTA 0x27
+#define HSCX_TIMR 0x23
+#define HSCX_STAR 0x21
+#define HSCX_RBCL 0x25
+#define HSCX_XBCH 0x2d
+#define HSCX_VSTR 0x2e
+#define HSCX_RLCR 0x2e
+#define HSCX_MASK 0x20
+
+extern int HscxVersion(struct IsdnCardState *cs, char *s);
+extern void hscx_sched_event(struct BCState *bcs, int event);
+extern void modehscx(struct BCState *bcs, int mode, int bc);
+extern void clear_pending_hscx_ints(struct IsdnCardState *cs);
+extern void inithscx(struct IsdnCardState *cs);
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/hscx_irq.c linux/drivers/isdn/hisax/hscx_irq.c
--- v2.1.91/linux/drivers/isdn/hisax/hscx_irq.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/hscx_irq.c	Wed Apr  1 16:20:58 1998
@@ -0,0 +1,320 @@
+/* $Id: hscx_irq.c,v 1.7 1998/02/12 23:07:37 keil Exp $
+
+ * hscx_irq.c     low level b-channel stuff for Siemens HSCX
+ *
+ * Author     Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ * This is an include file for fast inline IRQ stuff
+ *
+ * $Log: hscx_irq.c,v $
+ * Revision 1.7  1998/02/12 23:07:37  keil
+ * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
+ *
+ * Revision 1.6  1997/10/29 19:01:07  keil
+ * changes for 2.1
+ *
+ * Revision 1.5  1997/10/01 09:21:35  fritz
+ * Removed old compatibility stuff for 2.0.X kernels.
+ * From now on, this code is for 2.1.X ONLY!
+ * Old stuff is still in the separate branch.
+ *
+ * Revision 1.4  1997/08/15 17:48:02  keil
+ * cosmetic
+ *
+ * Revision 1.3  1997/07/27 21:38:36  keil
+ * new B-channel interface
+ *
+ * Revision 1.2  1997/06/26 11:16:19  keil
+ * first version
+ *
+ *
+ */
+
+
+static inline void
+waitforCEC(struct IsdnCardState *cs, int hscx)
+{
+	int to = 50;
+
+	while ((READHSCX(cs, hscx, HSCX_STAR) & 0x04) && to) {
+		udelay(1);
+		to--;
+	}
+	if (!to)
+		printk(KERN_WARNING "HiSax: waitforCEC timeout\n");
+}
+
+
+static inline void
+waitforXFW(struct IsdnCardState *cs, int hscx)
+{
+	int to = 50;
+
+	while ((!(READHSCX(cs, hscx, HSCX_STAR) & 0x44) == 0x40) && to) {
+		udelay(1);
+		to--;
+	}
+	if (!to)
+		printk(KERN_WARNING "HiSax: waitforXFW timeout\n");
+}
+
+static inline void
+WriteHSCXCMDR(struct IsdnCardState *cs, int hscx, u_char data)
+{
+	long flags;
+
+	save_flags(flags);
+	cli();
+	waitforCEC(cs, hscx);
+	WRITEHSCX(cs, hscx, HSCX_CMDR, data);
+	restore_flags(flags);
+}
+
+
+
+static void
+hscx_empty_fifo(struct BCState *bcs, int count)
+{
+	u_char *ptr;
+	struct IsdnCardState *cs = bcs->cs;
+	long flags;
+
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "hscx_empty_fifo");
+
+	if (bcs->hw.hscx.rcvidx + count > HSCX_BUFMAX) {
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "hscx_empty_fifo: incoming packet too large");
+		WriteHSCXCMDR(cs, bcs->channel, 0x80);
+		bcs->hw.hscx.rcvidx = 0;
+		return;
+	}
+	ptr = bcs->hw.hscx.rcvbuf + bcs->hw.hscx.rcvidx;
+	bcs->hw.hscx.rcvidx += count;
+	save_flags(flags);
+	cli();
+	READHSCXFIFO(cs, bcs->channel, ptr, count);
+	WriteHSCXCMDR(cs, bcs->channel, 0x80);
+	restore_flags(flags);
+	if (cs->debug & L1_DEB_HSCX_FIFO) {
+		char tmp[256];
+		char *t = tmp;
+
+		t += sprintf(t, "hscx_empty_fifo %c cnt %d",
+			     bcs->channel ? 'B' : 'A', count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, tmp);
+	}
+}
+
+static void
+hscx_fill_fifo(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int more, count;
+	int fifo_size = test_bit(HW_IPAC, &cs->HW_Flags)? 64: 32;
+	u_char *ptr;
+	long flags;
+
+
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "hscx_fill_fifo");
+
+	if (!bcs->hw.hscx.tx_skb)
+		return;
+	if (bcs->hw.hscx.tx_skb->len <= 0)
+		return;
+
+	more = (bcs->mode == L1_MODE_TRANS) ? 1 : 0;
+	if (bcs->hw.hscx.tx_skb->len > fifo_size) {
+		more = !0;
+		count = fifo_size;
+	} else
+		count = bcs->hw.hscx.tx_skb->len;
+
+	waitforXFW(cs, bcs->channel);
+	save_flags(flags);
+	cli();
+	ptr = bcs->hw.hscx.tx_skb->data;
+	skb_pull(bcs->hw.hscx.tx_skb, count);
+	bcs->tx_cnt -= count;
+	bcs->hw.hscx.count += count;
+	WRITEHSCXFIFO(cs, bcs->channel, ptr, count);
+	WriteHSCXCMDR(cs, bcs->channel, more ? 0x8 : 0xa);
+	restore_flags(flags);
+	if (cs->debug & L1_DEB_HSCX_FIFO) {
+		char tmp[256];
+		char *t = tmp;
+
+		t += sprintf(t, "hscx_fill_fifo %c cnt %d",
+			     bcs->channel ? 'B' : 'A', count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, tmp);
+	}
+}
+
+static inline void
+hscx_interrupt(struct IsdnCardState *cs, u_char val, u_char hscx)
+{
+	u_char r;
+	struct BCState *bcs = cs->bcs + hscx;
+	struct sk_buff *skb;
+	int fifo_size = test_bit(HW_IPAC, &cs->HW_Flags)? 64: 32;
+	int count;
+	char tmp[32];
+
+	if (!test_bit(BC_FLG_INIT, &bcs->Flag))
+		return;
+
+	if (val & 0x80) {	/* RME */
+		r = READHSCX(cs, hscx, HSCX_RSTA);
+		if ((r & 0xf0) != 0xa0) {
+			if (!(r & 0x80))
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "HSCX invalid frame");
+			if ((r & 0x40) && bcs->mode)
+				if (cs->debug & L1_DEB_WARN) {
+					sprintf(tmp, "HSCX RDO mode=%d",
+						bcs->mode);
+					debugl1(cs, tmp);
+				}
+			if (!(r & 0x20))
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "HSCX CRC error");
+			WriteHSCXCMDR(cs, hscx, 0x80);
+		} else {
+			count = READHSCX(cs, hscx, HSCX_RBCL) & (
+				test_bit(HW_IPAC, &cs->HW_Flags)? 0x3f: 0x1f);
+			if (count == 0)
+				count = fifo_size;
+			hscx_empty_fifo(bcs, count);
+			if ((count = bcs->hw.hscx.rcvidx - 1) > 0) {
+				if (cs->debug & L1_DEB_HSCX_FIFO) {
+					sprintf(tmp, "HX Frame %d", count);
+					debugl1(cs, tmp);
+				}
+				if (!(skb = dev_alloc_skb(count)))
+					printk(KERN_WARNING "HSCX: receive out of memory\n");
+				else {
+					memcpy(skb_put(skb, count), bcs->hw.hscx.rcvbuf, count);
+					skb_queue_tail(&bcs->rqueue, skb);
+				}
+			}
+		}
+		bcs->hw.hscx.rcvidx = 0;
+		hscx_sched_event(bcs, B_RCVBUFREADY);
+	}
+	if (val & 0x40) {	/* RPF */
+		hscx_empty_fifo(bcs, fifo_size);
+		if (bcs->mode == L1_MODE_TRANS) {
+			/* receive audio data */
+			if (!(skb = dev_alloc_skb(fifo_size)))
+				printk(KERN_WARNING "HiSax: receive out of memory\n");
+			else {
+				memcpy(skb_put(skb, fifo_size), bcs->hw.hscx.rcvbuf, fifo_size);
+				skb_queue_tail(&bcs->rqueue, skb);
+			}
+			bcs->hw.hscx.rcvidx = 0;
+			hscx_sched_event(bcs, B_RCVBUFREADY);
+		}
+	}
+	if (val & 0x10) {	/* XPR */
+		if (bcs->hw.hscx.tx_skb)
+			if (bcs->hw.hscx.tx_skb->len) {
+				hscx_fill_fifo(bcs);
+				return;
+			} else {
+				if (bcs->st->lli.l1writewakeup &&
+					(PACKET_NOACK != bcs->hw.hscx.tx_skb->pkt_type))
+					bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.hscx.count);
+				dev_kfree_skb(bcs->hw.hscx.tx_skb);
+				bcs->hw.hscx.count = 0; 
+				bcs->hw.hscx.tx_skb = NULL;
+			}
+		if ((bcs->hw.hscx.tx_skb = skb_dequeue(&bcs->squeue))) {
+			bcs->hw.hscx.count = 0;
+			test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+			hscx_fill_fifo(bcs);
+		} else {
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			hscx_sched_event(bcs, B_XMTBUFREADY);
+		}
+	}
+}
+
+static inline void
+hscx_int_main(struct IsdnCardState *cs, u_char val)
+{
+
+	u_char exval;
+	struct BCState *bcs;
+	char tmp[32];
+
+	if (val & 0x01) {
+		bcs = cs->bcs + 1;
+		exval = READHSCX(cs, 1, HSCX_EXIR);
+		if (exval == 0x40) {
+			if (bcs->mode == 1)
+				hscx_fill_fifo(bcs);
+			else {
+				/* Here we lost an TX interrupt, so
+				   * restart transmitting the whole frame.
+				 */
+				if (bcs->hw.hscx.tx_skb) {
+					skb_push(bcs->hw.hscx.tx_skb, bcs->hw.hscx.count);
+					bcs->tx_cnt += bcs->hw.hscx.count;
+					bcs->hw.hscx.count = 0;
+				}
+				WriteHSCXCMDR(cs, bcs->channel, 0x01);
+				if (cs->debug & L1_DEB_WARN) {
+					sprintf(tmp, "HSCX B EXIR %x Lost TX", exval);
+					debugl1(cs, tmp);
+				}
+			}
+		} else if (cs->debug & L1_DEB_HSCX) {
+			sprintf(tmp, "HSCX B EXIR %x", exval);
+			debugl1(cs, tmp);
+		}
+	}
+	if (val & 0xf8) {
+		if (cs->debug & L1_DEB_HSCX) {
+			sprintf(tmp, "HSCX B interrupt %x", val);
+			debugl1(cs, tmp);
+		}
+		hscx_interrupt(cs, val, 1);
+	}
+	if (val & 0x02) {
+		bcs = cs->bcs;
+		exval = READHSCX(cs, 0, HSCX_EXIR);
+		if (exval == 0x40) {
+			if (bcs->mode == L1_MODE_TRANS)
+				hscx_fill_fifo(bcs);
+			else {
+				/* Here we lost an TX interrupt, so
+				   * restart transmitting the whole frame.
+				 */
+				if (bcs->hw.hscx.tx_skb) {
+					skb_push(bcs->hw.hscx.tx_skb, bcs->hw.hscx.count);
+					bcs->tx_cnt += bcs->hw.hscx.count;
+					bcs->hw.hscx.count = 0;
+				}
+				WriteHSCXCMDR(cs, bcs->channel, 0x01);
+				if (cs->debug & L1_DEB_WARN) {
+					sprintf(tmp, "HSCX A EXIR %x Lost TX", exval);
+					debugl1(cs, tmp);
+				}
+			}
+		} else if (cs->debug & L1_DEB_HSCX) {
+			sprintf(tmp, "HSCX A EXIR %x", exval);
+			debugl1(cs, tmp);
+		}
+	}
+	if (val & 0x04) {
+		exval = READHSCX(cs, 0, HSCX_ISTA);
+		if (cs->debug & L1_DEB_HSCX) {
+			sprintf(tmp, "HSCX A interrupt %x", exval);
+			debugl1(cs, tmp);
+		}
+		hscx_interrupt(cs, exval, 0);
+	}
+}
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/ipac.h linux/drivers/isdn/hisax/ipac.h
--- v2.1.91/linux/drivers/isdn/hisax/ipac.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/ipac.h	Wed Apr  1 16:20:58 1998
@@ -0,0 +1,35 @@
+/* $Id: ipac.h,v 1.2 1997/10/29 18:51:21 keil Exp $
+
+ * ipac.h   IPAC specific defines
+ *
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ *
+ * $Log: ipac.h,v $
+ * Revision 1.2  1997/10/29 18:51:21  keil
+ * New files
+ *
+ * Revision 1.1.2.1  1997/10/17 22:10:48  keil
+ * new files on 2.0
+ *
+ *
+ *
+ */
+
+
+/* All Registers original Siemens Spec  */
+
+#define IPAC_CONF	0xC0
+#define IPAC_MASK	0xC1
+#define IPAC_ISTA	0xC1
+#define IPAC_ID		0xC2
+#define IPAC_ACFG	0xC3
+#define IPAC_AOE	0xC4
+#define IPAC_ARX	0xC5
+#define IPAC_PITA1	0xC6
+#define IPAC_PITA2	0xC7
+#define IPAC_POTA1	0xC8
+#define IPAC_POTA2	0xC9
+#define IPAC_PCFG	0xCA
+#define IPAC_SCFG	0xCB
+#define IPAC_TIMR2	0xCC
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/isac.c linux/drivers/isdn/hisax/isac.c
--- v2.1.91/linux/drivers/isdn/hisax/isac.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/isac.c	Wed Apr  1 16:20:58 1998
@@ -0,0 +1,679 @@
+/* $Id: isac.c,v 1.12 1998/02/12 23:07:40 keil Exp $
+
+ * isac.c   ISAC specific routines
+ *
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ *
+ * $Log: isac.c,v $
+ * Revision 1.12  1998/02/12 23:07:40  keil
+ * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
+ *
+ * Revision 1.11  1998/02/09 10:54:49  keil
+ * fixes for leased mode
+ *
+ * Revision 1.10  1998/02/02 13:37:37  keil
+ * new init
+ *
+ * Revision 1.9  1997/11/06 17:09:07  keil
+ * New 2.1 init code
+ *
+ * Revision 1.8  1997/10/29 19:00:03  keil
+ * new layer1,changes for 2.1
+ *
+ * Revision 1.7  1997/10/01 09:21:37  fritz
+ * Removed old compatibility stuff for 2.0.X kernels.
+ * From now on, this code is for 2.1.X ONLY!
+ * Old stuff is still in the separate branch.
+ *
+ * Revision 1.6  1997/08/15 17:47:08  keil
+ * avoid oops because a uninitialised timer
+ *
+ * Revision 1.5  1997/08/07 17:48:49  keil
+ * fix wrong parenthesis
+ *
+ * Revision 1.4  1997/07/30 17:11:59  keil
+ * fixed Timer3
+ *
+ * Revision 1.3  1997/07/27 21:37:40  keil
+ * T3 implemented; supervisor l1timer; B-channel TEST_LOOP
+ *
+ * Revision 1.2  1997/06/26 11:16:15  keil
+ * first version
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include "hisax.h"
+#include "isac.h"
+#include "isdnl1.h"
+#include <linux/interrupt.h>
+
+#define DBUSY_TIMER_VALUE 80
+#define ARCOFI_USE 1
+
+static char *ISACVer[] HISAX_INITDATA =
+{"2086/2186 V1.1", "2085 B1", "2085 B2",
+ "2085 V2.3"};
+
+void
+ISACVersion(struct IsdnCardState *cs, char *s)
+{
+	int val;
+
+	val = cs->readisac(cs, ISAC_RBCH);
+	printk(KERN_INFO "%s ISAC version : %s\n", s, ISACVer[(val >> 5) & 3]);
+}
+
+static void
+ph_command(struct IsdnCardState *cs, unsigned int command)
+{
+	if (cs->debug & L1_DEB_ISAC) {
+		char tmp[32];
+		sprintf(tmp, "ph_command %x", command);
+		debugl1(cs, tmp);
+	}
+	cs->writeisac(cs, ISAC_CIX0, (command << 2) | 3);
+}
+
+static void
+manl1_msg(struct IsdnCardState *cs, int msg, void *arg) {
+	struct PStack *st;
+
+	st = cs->stlist;
+	while (st) {
+		st->ma.manl1(st, msg, arg);
+		st = st->next;
+	}
+}
+
+static void
+isac_new_ph(struct IsdnCardState *cs)
+{
+	switch (cs->ph_state) {
+		case (ISAC_IND_RS):
+		case (ISAC_IND_EI):
+			ph_command(cs, ISAC_CMD_DUI);
+			manl1_msg(cs, PH_RESET_IND, NULL);
+			break;
+		case (ISAC_IND_DID):
+			manl1_msg(cs, PH_DEACT_CNF, NULL);
+			break;
+		case (ISAC_IND_DR):
+			manl1_msg(cs, PH_DEACT_IND, NULL);
+			break;
+		case (ISAC_IND_PU):
+			manl1_msg(cs, PH_POWERUP_CNF, NULL);
+			break;
+		case (ISAC_IND_RSY):
+			manl1_msg(cs, PH_RSYNC_IND, NULL);
+			break;
+		case (ISAC_IND_ARD):
+			manl1_msg(cs, PH_INFO2_IND, NULL);
+			break;
+		case (ISAC_IND_AI8):
+			manl1_msg(cs, PH_I4_P8_IND, NULL);
+			break;
+		case (ISAC_IND_AI10):
+			manl1_msg(cs, PH_I4_P10_IND, NULL);
+			break;
+		default:
+			break;
+	}
+}
+
+static void
+isac_bh(struct IsdnCardState *cs)
+{
+	struct PStack *stptr;
+	
+	if (!cs)
+		return;
+
+	if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) {
+		if (cs->debug)
+			debugl1(cs, "D-Channel Busy cleared");
+		stptr = cs->stlist;
+		while (stptr != NULL) {
+			stptr->l1.l1l2(stptr, PH_PAUSE_CNF, NULL);
+			stptr = stptr->next;
+		}
+	}
+	if (test_and_clear_bit(D_L1STATECHANGE, &cs->event))
+		isac_new_ph(cs);		
+	if (test_and_clear_bit(D_RCVBUFREADY, &cs->event))
+		DChannel_proc_rcv(cs);
+	if (test_and_clear_bit(D_XMTBUFREADY, &cs->event))
+		DChannel_proc_xmt(cs);
+	if (test_and_clear_bit(D_RX_MON0, &cs->event))
+		test_and_set_bit(HW_MON0_TX_END, &cs->HW_Flags);
+	if (test_and_clear_bit(D_RX_MON1, &cs->event))
+		test_and_set_bit(HW_MON1_TX_END, &cs->HW_Flags);
+	if (test_and_clear_bit(D_TX_MON0, &cs->event))
+		test_and_set_bit(HW_MON0_RX_END, &cs->HW_Flags);
+	if (test_and_clear_bit(D_TX_MON1, &cs->event))
+		test_and_set_bit(HW_MON1_RX_END, &cs->HW_Flags);
+}
+
+void
+isac_empty_fifo(struct IsdnCardState *cs, int count)
+{
+	u_char *ptr;
+	long flags;
+
+	if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO))
+		debugl1(cs, "isac_empty_fifo");
+
+	if ((cs->rcvidx + count) >= MAX_DFRAME_LEN) {
+		if (cs->debug & L1_DEB_WARN) {
+			char tmp[40];
+			sprintf(tmp, "isac_empty_fifo overrun %d",
+				cs->rcvidx + count);
+			debugl1(cs, tmp);
+		}
+		cs->writeisac(cs, ISAC_CMDR, 0x80);
+		cs->rcvidx = 0;
+		return;
+	}
+	ptr = cs->rcvbuf + cs->rcvidx;
+	cs->rcvidx += count;
+	save_flags(flags);
+	cli();
+	cs->readisacfifo(cs, ptr, count);
+	cs->writeisac(cs, ISAC_CMDR, 0x80);
+	restore_flags(flags);
+	if (cs->debug & L1_DEB_ISAC_FIFO) {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "isac_empty_fifo cnt %d", count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, tmp);
+	}
+}
+
+static void
+isac_fill_fifo(struct IsdnCardState *cs)
+{
+	int count, more;
+	u_char *ptr;
+	long flags;
+
+	if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO))
+		debugl1(cs, "isac_fill_fifo");
+
+	if (!cs->tx_skb)
+		return;
+
+	count = cs->tx_skb->len;
+	if (count <= 0)
+		return;
+
+	more = 0;
+	if (count > 32) {
+		more = !0;
+		count = 32;
+	}
+	save_flags(flags);
+	cli();
+	ptr = cs->tx_skb->data;
+	skb_pull(cs->tx_skb, count);
+	cs->tx_cnt += count;
+	cs->writeisacfifo(cs, ptr, count);
+	cs->writeisac(cs, ISAC_CMDR, more ? 0x8 : 0xa);
+	restore_flags(flags);
+	if (test_and_set_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
+		debugl1(cs, "isac_fill_fifo dbusytimer running");
+		del_timer(&cs->dbusytimer);
+	}
+	init_timer(&cs->dbusytimer);
+	cs->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000);
+	add_timer(&cs->dbusytimer);
+	if (cs->debug & L1_DEB_ISAC_FIFO) {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "isac_fill_fifo cnt %d", count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, tmp);
+	}
+}
+
+void
+isac_sched_event(struct IsdnCardState *cs, int event)
+{
+	test_and_set_bit(event, &cs->event);
+	queue_task(&cs->tqueue, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+}
+
+void
+isac_interrupt(struct IsdnCardState *cs, u_char val)
+{
+	u_char exval, v1;
+	struct sk_buff *skb;
+	unsigned int count;
+	long flags;
+	char tmp[32];
+
+	if (cs->debug & L1_DEB_ISAC) {
+		sprintf(tmp, "ISAC interrupt %x", val);
+		debugl1(cs, tmp);
+	}
+	if (val & 0x80) {	/* RME */
+		exval = cs->readisac(cs, ISAC_RSTA);
+		if ((exval & 0x70) != 0x20) {
+			if (exval & 0x40)
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "ISAC RDO");
+			if (!(exval & 0x20))
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "ISAC CRC error");
+			cs->writeisac(cs, ISAC_CMDR, 0x80);
+		} else {
+			count = cs->readisac(cs, ISAC_RBCL) & 0x1f;
+			if (count == 0)
+				count = 32;
+			isac_empty_fifo(cs, count);
+			save_flags(flags);
+			cli();
+			if ((count = cs->rcvidx) > 0) {
+				cs->rcvidx = 0;
+				if (!(skb = alloc_skb(count, GFP_ATOMIC)))
+					printk(KERN_WARNING "HiSax: D receive out of memory\n");
+				else {
+					memcpy(skb_put(skb, count), cs->rcvbuf, count);
+					skb_queue_tail(&cs->rq, skb);
+				}
+			}
+			restore_flags(flags);
+		}
+		cs->rcvidx = 0;
+		isac_sched_event(cs, D_RCVBUFREADY);
+	}
+	if (val & 0x40) {	/* RPF */
+		isac_empty_fifo(cs, 32);
+	}
+	if (val & 0x20) {	/* RSC */
+		/* never */
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "ISAC RSC interrupt");
+	}
+	if (val & 0x10) {	/* XPR */
+		if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+			del_timer(&cs->dbusytimer);
+		if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+			isac_sched_event(cs, D_CLEARBUSY);
+		if (cs->tx_skb)
+			if (cs->tx_skb->len) {
+				isac_fill_fifo(cs);
+				goto afterXPR;
+			} else {
+				dev_kfree_skb(cs->tx_skb);
+				cs->tx_cnt = 0;
+				cs->tx_skb = NULL;
+			}
+		if ((cs->tx_skb = skb_dequeue(&cs->sq))) {
+			cs->tx_cnt = 0;
+			isac_fill_fifo(cs);
+		} else
+			isac_sched_event(cs, D_XMTBUFREADY);
+	}
+      afterXPR:
+	if (val & 0x04) {	/* CISQ */
+		cs->ph_state = (cs->readisac(cs, ISAC_CIX0) >> 2) & 0xf;
+		if (cs->debug & L1_DEB_ISAC) {
+			sprintf(tmp, "ph_state change %x", cs->ph_state);
+			debugl1(cs, tmp);
+		}
+		isac_sched_event(cs, D_L1STATECHANGE);
+	}
+	if (val & 0x02) {	/* SIN */
+		/* never */
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "ISAC SIN interrupt");
+	}
+	if (val & 0x01) {	/* EXI */
+		exval = cs->readisac(cs, ISAC_EXIR);
+		if (cs->debug & L1_DEB_WARN) {
+			sprintf(tmp, "ISAC EXIR %02x", exval);
+			debugl1(cs, tmp);
+		}
+		if (exval & 0x04) {
+			v1 = cs->readisac(cs, ISAC_MOSR);
+			if (cs->debug & L1_DEB_WARN) {
+				sprintf(tmp, "ISAC MOSR %02x", v1);
+				debugl1(cs, tmp);
+			}
+#if ARCOFI_USE
+			if (v1 & 0x08) {
+				if (!cs->mon_rx)
+					if (!(cs->mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) {
+						if (cs->debug & L1_DEB_WARN)
+							debugl1(cs, "ISAC MON RX out of memory!");
+						cs->mocr &= 0xf0;
+						cs->mocr |= 0x0a;
+						cs->writeisac(cs, ISAC_MOCR, cs->mocr);
+						goto afterMONR0;
+					} else
+						cs->mon_rxp = 0;
+				if (cs->mon_rxp >= MAX_MON_FRAME) {
+					cs->mocr &= 0xf0;
+					cs->mocr |= 0x0a;
+					cs->writeisac(cs, ISAC_MOCR, cs->mocr);
+					cs->mon_rxp = 0;
+					if (cs->debug & L1_DEB_WARN)
+						debugl1(cs, "ISAC MON RX overflow!");
+					goto afterMONR0;
+				}
+				cs->mon_rx[cs->mon_rxp++] = cs->readisac(cs, ISAC_MOR0);
+				if (cs->debug & L1_DEB_WARN) {
+					sprintf(tmp, "ISAC MOR0 %02x", cs->mon_rx[cs->mon_rxp -1]);
+					debugl1(cs, tmp);
+				}
+				if (cs->mon_rxp == 1) {
+					cs->mocr |= 0x04;
+					cs->writeisac(cs, ISAC_MOCR, cs->mocr);
+				}
+			}
+		      afterMONR0:
+			if (v1 & 0x80) {
+				if (!cs->mon_rx)
+					if (!(cs->mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) {
+						if (cs->debug & L1_DEB_WARN)
+							debugl1(cs, "ISAC MON RX out of memory!");
+						cs->mocr &= 0x0f;
+						cs->mocr |= 0xa0;
+						cs->writeisac(cs, ISAC_MOCR, cs->mocr);
+						goto afterMONR1;
+					} else
+						cs->mon_rxp = 0;
+				if (cs->mon_rxp >= MAX_MON_FRAME) {
+					cs->mocr &= 0x0f;
+					cs->mocr |= 0xa0;
+					cs->writeisac(cs, ISAC_MOCR, cs->mocr);
+					cs->mon_rxp = 0;
+					if (cs->debug & L1_DEB_WARN)
+						debugl1(cs, "ISAC MON RX overflow!");
+					goto afterMONR1;
+				}
+				cs->mon_rx[cs->mon_rxp++] = cs->readisac(cs, ISAC_MOR1);
+				if (cs->debug & L1_DEB_WARN) {
+					sprintf(tmp, "ISAC MOR1 %02x", cs->mon_rx[cs->mon_rxp -1]);
+					debugl1(cs, tmp);
+				}
+				if (cs->mon_rxp == 1) {
+					cs->mocr |= 0x40;
+					cs->writeisac(cs, ISAC_MOCR, cs->mocr);
+				}
+			}
+		      afterMONR1:
+			if (v1 & 0x04) {
+				cs->mocr &= 0xf0;
+				cs->mocr |= 0x0a;
+				cs->writeisac(cs, ISAC_MOCR, cs->mocr);
+				isac_sched_event(cs, D_RX_MON0);
+			}
+			if (v1 & 0x40) {
+				cs->mocr &= 0x0f;
+				cs->mocr |= 0xa0;
+				cs->writeisac(cs, ISAC_MOCR, cs->mocr);
+				isac_sched_event(cs, D_RX_MON1);
+			}
+			if (v1 & 0x02) {
+				if (!cs->mon_tx) {
+					cs->mocr &= 0xf0;
+					cs->mocr |= 0x0a;
+					cs->writeisac(cs, ISAC_MOCR, cs->mocr);
+					goto AfterMOX0;
+				}
+				if (cs->mon_txp >= cs->mon_txc) {
+					if (cs->mon_txc)
+						isac_sched_event(cs, D_TX_MON0);
+					goto AfterMOX0;
+				}
+				cs->writeisac(cs, ISAC_MOX0,
+					cs->mon_tx[cs->mon_txp++]);
+				if (cs->debug & L1_DEB_WARN) {
+					sprintf(tmp, "ISAC %02x -> MOX0", cs->mon_tx[cs->mon_txp -1]);
+					debugl1(cs, tmp);
+				}
+			}
+		      AfterMOX0:
+			if (v1 & 0x20) {
+				if (!cs->mon_tx) {
+					cs->mocr &= 0x0f;
+					cs->mocr |= 0xa0;
+					cs->writeisac(cs, ISAC_MOCR, cs->mocr);
+					goto AfterMOX1;
+				}
+				if (cs->mon_txp >= cs->mon_txc) {
+					if (cs->mon_txc)
+						isac_sched_event(cs, D_TX_MON1);
+					goto AfterMOX1;
+				}
+				cs->writeisac(cs, ISAC_MOX1,
+					cs->mon_tx[cs->mon_txp++]);
+				if (cs->debug & L1_DEB_WARN) {
+					sprintf(tmp, "ISAC %02x -> MOX1", cs->mon_tx[cs->mon_txp -1]);
+					debugl1(cs, tmp);
+				}
+			}
+		      AfterMOX1:
+#endif
+		}
+	}
+}
+
+static void
+ISAC_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
+	struct sk_buff *skb = arg;
+	char str[64];
+
+	switch (pr) {
+		case (PH_DATA_REQ):
+			if (cs->tx_skb) {
+				skb_queue_tail(&cs->sq, skb);
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "PH_DATA Queued", 0);
+#endif
+			} else {
+				if ((cs->dlogflag) && (!(skb->data[2] & 1))) {	/* I-FRAME */
+					LogFrame(cs, skb->data, skb->len);
+					sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei);
+					dlogframe(cs, skb->data + 4, skb->len - 4,
+						  str);
+				}
+				cs->tx_skb = skb;
+				cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "PH_DATA", 0);
+#endif
+				isac_fill_fifo(cs);
+			}
+			break;
+		case (PH_PULL_IND):
+			if (cs->tx_skb) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, " l2l1 tx_skb exist this shouldn't happen");
+				skb_queue_tail(&cs->sq, skb);
+				break;
+			}
+			if ((cs->dlogflag) && (!(skb->data[2] & 1))) {	/* I-FRAME */
+				LogFrame(cs, skb->data, skb->len);
+				sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei);
+				dlogframe(cs, skb->data + 4, skb->len - 4,
+					  str);
+			}
+			cs->tx_skb = skb;
+			cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (cs->debug & L1_DEB_LAPD)
+				Logl2Frame(cs, skb, "PH_DATA_PULLED", 0);
+#endif
+			isac_fill_fifo(cs);
+			break;
+		case (PH_PULL_REQ):
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (cs->debug & L1_DEB_LAPD)
+				debugl1(cs, "-> PH_REQUEST_PULL");
+#endif
+			if (!cs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL_CNF, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+	}
+}
+
+void
+isac_l1cmd(struct IsdnCardState *cs, int msg, void *arg)
+{
+	u_char val;
+	char tmp[32];
+	
+	switch(msg) {
+		case PH_RESET_REQ:
+			if ((cs->ph_state == ISAC_IND_EI) ||
+				(cs->ph_state == ISAC_IND_DR) ||
+				(cs->ph_state == ISAC_IND_RS))
+			        ph_command(cs, ISAC_CMD_TIM);
+			else
+				ph_command(cs, ISAC_CMD_RS);
+			break;
+		case PH_ENABLE_REQ:
+			ph_command(cs, ISAC_CMD_TIM);
+			break;
+		case PH_INFO3_REQ:
+			ph_command(cs, ISAC_CMD_AR8);
+			break;
+		case PH_TESTLOOP_REQ:
+			val = 0;
+			if (1 & (int) arg)
+				val |= 0x0c;
+			if (2 & (int) arg)
+				val |= 0x3;
+			if (test_bit(HW_IOM1, &cs->HW_Flags)) {
+				/* IOM 1 Mode */
+				if (!val) {
+					cs->writeisac(cs, ISAC_SPCR, 0xa);
+					cs->writeisac(cs, ISAC_ADF1, 0x2);
+				} else {
+					cs->writeisac(cs, ISAC_SPCR, val);
+					cs->writeisac(cs, ISAC_ADF1, 0xa);
+				}
+			} else {
+				/* IOM 2 Mode */
+				cs->writeisac(cs, ISAC_SPCR, val);
+				if (val)
+					cs->writeisac(cs, ISAC_ADF1, 0x8);
+				else
+					cs->writeisac(cs, ISAC_ADF1, 0x0);
+			}
+			break;
+		default:
+			if (cs->debug & L1_DEB_WARN) {
+				sprintf(tmp, "isac_l1cmd unknown %4x", msg);
+				debugl1(cs, tmp);
+			}
+			break;
+	}
+}
+
+void
+setstack_isac(struct PStack *st, struct IsdnCardState *cs)
+{
+	st->l2.l2l1 = ISAC_l2l1;
+}
+
+static void
+dbusy_timer_handler(struct IsdnCardState *cs)
+{
+	struct PStack *stptr;
+
+	if (test_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
+		if (cs->debug)
+			debugl1(cs, "D-Channel Busy");
+		test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags);
+		stptr = cs->stlist;
+		
+		while (stptr != NULL) {
+			stptr->l1.l1l2(stptr, PH_PAUSE_IND, NULL);
+			stptr = stptr->next;
+		}
+	}
+}
+
+HISAX_INITFUNC(void
+initisac(struct IsdnCardState *cs))
+{
+	cs->tqueue.routine = (void *) (void *) isac_bh;
+	cs->l1cmd = isac_l1cmd;
+	cs->setstack_d = setstack_isac;
+	cs->dbusytimer.function = (void *) dbusy_timer_handler;
+	cs->dbusytimer.data = (long) cs;
+	init_timer(&cs->dbusytimer);
+  	cs->writeisac(cs, ISAC_MASK, 0xff);
+  	cs->mocr = 0xaa;
+	if (test_bit(HW_IOM1, &cs->HW_Flags)) {
+		/* IOM 1 Mode */
+		cs->writeisac(cs, ISAC_ADF2, 0x0);
+		cs->writeisac(cs, ISAC_SPCR, 0xa);
+		cs->writeisac(cs, ISAC_ADF1, 0x2);
+		cs->writeisac(cs, ISAC_STCR, 0x70);
+		cs->writeisac(cs, ISAC_MODE, 0xc9);
+	} else {
+		/* IOM 2 Mode */
+		cs->writeisac(cs, ISAC_ADF2, 0x80);
+		cs->writeisac(cs, ISAC_SQXR, 0x2f);
+		cs->writeisac(cs, ISAC_SPCR, 0x00);
+		cs->writeisac(cs, ISAC_STCR, 0x70);
+		cs->writeisac(cs, ISAC_MODE, 0xc9);
+		cs->writeisac(cs, ISAC_TIMR, 0x00);
+		cs->writeisac(cs, ISAC_ADF1, 0x00);
+	}
+	ph_command(cs, ISAC_CMD_RS);
+	cs->writeisac(cs, ISAC_MASK, 0x0);
+}
+
+HISAX_INITFUNC(void
+clear_pending_isac_ints(struct IsdnCardState *cs))
+{
+	int val;
+	char tmp[64];
+
+	val = cs->readisac(cs, ISAC_STAR);
+	sprintf(tmp, "ISAC STAR %x", val);
+	debugl1(cs, tmp);
+	val = cs->readisac(cs, ISAC_MODE);
+	sprintf(tmp, "ISAC MODE %x", val);
+	debugl1(cs, tmp);
+	val = cs->readisac(cs, ISAC_ADF2);
+	sprintf(tmp, "ISAC ADF2 %x", val);
+	debugl1(cs, tmp);
+	val = cs->readisac(cs, ISAC_ISTA);
+	sprintf(tmp, "ISAC ISTA %x", val);
+	debugl1(cs, tmp);
+	if (val & 0x01) {
+		val = cs->readisac(cs, ISAC_EXIR);
+		sprintf(tmp, "ISAC EXIR %x", val);
+		debugl1(cs, tmp);
+	} else if (val & 0x04) {
+		val = cs->readisac(cs, ISAC_CIR0);
+		sprintf(tmp, "ISAC CIR0 %x", val);
+		debugl1(cs, tmp);
+		cs->ph_state = (val >> 2) & 0xf;
+	} else {
+		cs->ph_state = (cs->readisac(cs, ISAC_CIX0) >> 2) & 0xf;
+	}
+	isac_sched_event(cs, D_L1STATECHANGE);
+	cs->writeisac(cs, ISAC_MASK, 0xFF);
+	cs->writeisac(cs, ISAC_MASK, 0);
+	cs->writeisac(cs, ISAC_CMDR, 0x41);
+}
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/isac.h linux/drivers/isdn/hisax/isac.h
--- v2.1.91/linux/drivers/isdn/hisax/isac.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/isac.h	Wed Apr  1 16:20:58 1998
@@ -0,0 +1,74 @@
+/* $Id: isac.h,v 1.4 1997/10/29 19:09:34 keil Exp $
+
+ * isac.h   ISAC specific defines
+ *
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ *
+ * $Log: isac.h,v $
+ * Revision 1.4  1997/10/29 19:09:34  keil
+ * new L1
+ *
+ * Revision 1.3  1997/07/27 21:37:41  keil
+ * T3 implemented; supervisor l1timer; B-channel TEST_LOOP
+ *
+ * Revision 1.2  1997/06/26 11:16:16  keil
+ * first version
+ *
+ *
+ */
+
+
+/* All Registers original Siemens Spec  */
+
+#define ISAC_MASK 0x20
+#define ISAC_ISTA 0x20
+#define ISAC_STAR 0x21
+#define ISAC_CMDR 0x21
+#define ISAC_EXIR 0x24
+#define ISAC_RBCH 0x2a
+#define ISAC_ADF2 0x39
+#define ISAC_SPCR 0x30
+#define ISAC_ADF1 0x38
+#define ISAC_CIR0 0x31
+#define ISAC_CIX0 0x31
+#define ISAC_STCR 0x37
+#define ISAC_MODE 0x22
+#define ISAC_RSTA 0x27
+#define ISAC_RBCL 0x25
+#define ISAC_TIMR 0x23
+#define ISAC_SQXR 0x3b
+#define ISAC_MOSR 0x3a
+#define ISAC_MOCR 0x3a
+#define ISAC_MOR0 0x32
+#define ISAC_MOX0 0x32
+#define ISAC_MOR1 0x34
+#define ISAC_MOX1 0x34
+
+#define ISAC_CMD_TIM	0x0
+#define ISAC_CMD_RS	0x1
+#define ISAC_CMD_SCZ	0x4
+#define ISAC_CMD_SSZ	0x2
+#define ISAC_CMD_AR8	0x8
+#define ISAC_CMD_AR10	0x9
+#define ISAC_CMD_ARL	0xA
+#define ISAC_CMD_DUI	0xF
+
+#define ISAC_IND_RS	0x1
+#define ISAC_IND_PU	0x7
+#define ISAC_IND_DR	0x0
+#define ISAC_IND_SD	0x2
+#define ISAC_IND_DIS	0x3
+#define ISAC_IND_EI	0x6
+#define ISAC_IND_RSY	0x4
+#define ISAC_IND_ARD	0x8
+#define ISAC_IND_TI	0xA
+#define ISAC_IND_ATI	0xB
+#define ISAC_IND_AI8	0xC
+#define ISAC_IND_AI10	0xD
+#define ISAC_IND_DID	0xF
+
+extern void ISACVersion(struct IsdnCardState *cs, char *s);
+extern void initisac(struct IsdnCardState *cs);
+extern void isac_interrupt(struct IsdnCardState *cs, u_char val);
+extern void clear_pending_isac_ints(struct IsdnCardState *cs);
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/isdnl1.c linux/drivers/isdn/hisax/isdnl1.c
--- v2.1.91/linux/drivers/isdn/hisax/isdnl1.c	Thu Feb 12 20:56:06 1998
+++ linux/drivers/isdn/hisax/isdnl1.c	Wed Apr  1 16:20:58 1998
@@ -1,4 +1,4 @@
-/* $Id: isdnl1.c,v 1.15 1997/05/27 15:17:55 fritz Exp $
+/* $Id: isdnl1.c,v 2.18 1998/02/12 23:07:42 keil Exp $
 
  * isdnl1.c     common low level stuff for Siemens Chipsetbased isdn cards
  *              based on the teles driver from Jan den Ouden
@@ -11,131 +11,239 @@
  *
  *
  * $Log: isdnl1.c,v $
- * Revision 1.15  1997/05/27 15:17:55  fritz
- * Added changes for recent 2.1.x kernels:
- *   changed return type of isdn_close
- *   queue_task_* -> queue_task
- *   clear/set_bit -> test_and_... where apropriate.
- *   changed type of hard_header_cache parameter.
+ * Revision 2.18  1998/02/12 23:07:42  keil
+ * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
+ *
+ * Revision 2.17  1998/02/11 17:28:07  keil
+ * Niccy PnP/PCI support
+ *
+ * Revision 2.16  1998/02/09 18:46:08  keil
+ * Support for Sedlbauer PCMCIA (Marcus Niemann)
  *
- * Revision 1.14  1997/04/07 23:00:08  keil
- * GFP_KERNEL ---> GFP_ATOMIC
+ * Revision 2.15  1998/02/09 10:54:51  keil
+ * fixes for leased mode
  *
- * Revision 1.13  1997/04/06 22:55:50  keil
- * Using SKB's
+ * Revision 2.14  1998/02/03 23:31:31  keil
+ * add AMD7930 support
  *
- * Revision 1.12  1997/03/26 13:43:57  keil
- * small cosmetics
+ * Revision 2.13  1998/02/02 13:33:02  keil
+ * New card support
  *
- * Revision 1.11  1997/03/25 23:11:23  keil
- * US NI-1 protocol
+ * Revision 2.12  1998/01/31 21:41:48  keil
+ * changes for newer 2.1 kernels
  *
- * Revision 1.10  1997/03/13 14:45:05  keil
- * using IRQ proof queue_task
+ * Revision 2.11  1997/11/12 15:01:23  keil
+ * COMPAQ_ISA changes
  *
- * Revision 1.9  1997/03/12 21:44:21  keil
- * change Interrupt routine from atomic quick to normal
+ * Revision 2.10  1997/11/08 21:35:48  keil
+ * new l1 init
  *
- * Revision 1.8  1997/02/09 00:24:31  keil
- * new interface handling, one interface per card
+ * Revision 2.9  1997/11/06 17:09:18  keil
+ * New 2.1 init code
  *
- * Revision 1.7  1997/01/27 15:56:03  keil
- * PCMCIA Teles card and ITK ix1 micro added
+ * Revision 2.8  1997/10/29 19:00:05  keil
+ * new layer1,changes for 2.1
  *
- * Revision 1.6  1997/01/21 22:20:00  keil
- * changes for D-channel log; Elsa Quickstep support
+ * Revision 2.7  1997/10/10 20:56:50  fritz
+ * New HL interface.
  *
- * Revision 1.5  1997/01/10 12:51:19  keil
- * cleanup; set newversion
+ * Revision 2.6  1997/09/12 10:05:16  keil
+ * ISDN_CTRL_DEBUG define
  *
- * Revision 1.4  1996/12/08 19:44:53  keil
- * L2FRAME_DEBUG and other changes from Pekka Sarnila
+ * Revision 2.5  1997/09/11 17:24:45  keil
+ * Add new cards
  *
- * Revision 1.3  1996/11/18 15:34:47  keil
- * fix HSCX version code
+ * Revision 2.4  1997/08/15 17:47:09  keil
+ * avoid oops because a uninitialised timer
  *
- * Revision 1.2  1996/10/27 22:16:54  keil
- * ISAC/HSCX version lookup
+ * Revision 2.3  1997/08/01 11:16:40  keil
+ * cosmetics
  *
- * Revision 1.1  1996/10/13 20:04:53  keil
- * Initial revision
+ * Revision 2.2  1997/07/30 17:11:08  keil
+ * L1deactivated exported
  *
+ * Revision 2.1  1997/07/27 21:35:38  keil
+ * new layer1 interface
+ *
+ * Revision 2.0  1997/06/26 11:02:53  keil
+ * New Layer and card interface
+ *
+ * Revision 1.15  1997/05/27 15:17:55  fritz
+ * Added changes for recent 2.1.x kernels:
+ *   changed return type of isdn_close
+ *   queue_task_* -> queue_task
+ *   clear/set_bit -> test_and_... where apropriate.
+ *   changed type of hard_header_cache parameter.
  *
+ * old changes removed KKe
  *
  */
 
-const char *l1_revision = "$Revision: 1.15 $";
+const char *l1_revision = "$Revision: 2.18 $";
 
 #define __NO_VERSION__
 #include <linux/config.h>
 #include "hisax.h"
 #include "isdnl1.h"
+#include <linux/kernel_stat.h>
+#if (LINUX_VERSION_CODE < 0x020150) /* 2.1.80 */
+#define kstat_irqs( PAR ) kstat.interrupts( (PAR) )
+#endif
+
+
 
 #if CARD_TELES0
-#include "teles0.h"
+extern int setup_teles0(struct IsdnCard *card);
 #endif
 
 #if CARD_TELES3
-#include "teles3.h"
+extern int setup_teles3(struct IsdnCard *card);
 #endif
 
 #if CARD_AVM_A1
-#include "avm_a1.h"
+extern int setup_avm_a1(struct IsdnCard *card);
 #endif
 
 #if CARD_ELSA
-#include "elsa.h"
+extern int setup_elsa(struct IsdnCard *card);
 #endif
 
 #if CARD_IX1MICROR2
-#include "ix1_micro.h"
+extern int setup_ix1micro(struct IsdnCard *card);
 #endif
 
-/* #define I4L_IRQ_FLAG SA_INTERRUPT */
-#define I4L_IRQ_FLAG    0
+#if CARD_DIEHLDIVA
+extern	int  setup_diva(struct IsdnCard *card);
+#endif
 
-#define HISAX_STATUS_BUFSIZE 4096
+#if CARD_ASUSCOM
+extern int setup_asuscom(struct IsdnCard *card);
+#endif
 
-#define INCLUDE_INLINE_FUNCS
-#include <linux/tqueue.h>
-#include <linux/interrupt.h>
+#if CARD_TELEINT
+extern int setup_TeleInt(struct IsdnCard *card);
+#endif
 
-const char *CardType[] =
-{"No Card", "Teles 16.0", "Teles 8.0", "Teles 16.3",
- "Creatix/Teles PnP", "AVM A1", "Elsa ML",
-#ifdef CONFIG_HISAX_ELSA_PCMCIA
- "Elsa PCMCIA",
-#else
- "Elsa Quickstep",
+#if CARD_SEDLBAUER
+extern int setup_sedlbauer(struct IsdnCard *card);
 #endif
- "Teles PCMCIA", "ITK ix1-micro Rev.2"};
 
-static char *HSCXVer[] =
-{"A1", "?1", "A2", "?3", "A3", "V2.1", "?6", "?7",
- "?8", "?9", "?10", "?11", "?12", "?13", "?14", "???"};
+#if CARD_SPORTSTER
+extern int setup_sportster(struct IsdnCard *card);
+#endif
 
-static char *ISACVer[] =
-{"2086/2186 V1.1", "2085 B1", "2085 B2",
- "2085 V2.3"};
+#if CARD_MIC
+extern int setup_mic(struct IsdnCard *card);
+#endif
+
+#if CARD_NETJET
+extern int setup_netjet(struct IsdnCard *card);
+#endif
+
+#if CARD_TELES3C
+extern int setup_t163c(struct IsdnCard *card);
+#endif
+
+#if CARD_AMD7930
+extern int setup_amd7930(struct IsdnCard *card);
+#endif
+
+#if CARD_NICCY
+extern int setup_niccy(struct IsdnCard *card);
+#endif
+
+#define HISAX_STATUS_BUFSIZE 4096
+#define ISDN_CTRL_DEBUG 1
+#define INCLUDE_INLINE_FUNCS
+#include <linux/tqueue.h>
+#include <linux/interrupt.h>
+const char *CardType[] =
+{"No Card", "Teles 16.0", "Teles 8.0", "Teles 16.3", "Creatix/Teles PnP",
+ "AVM A1", "Elsa ML", "Elsa Quickstep", "Teles PCMCIA", "ITK ix1-micro Rev.2",
+ "Elsa PCMCIA", "Eicon.Diehl Diva", "ISDNLink", "TeleInt", "Teles 16.3c", 
+ "Sedlbauer Speed Card", "USR Sportster", "ith mic Linux", "Elsa PCI",
+ "Compaq ISA", "NETjet", "Teles PCI", "Sedlbauer Speed Star (PCMCIA)",
+ "AMD 7930", "NICCY"
+};
 
 extern struct IsdnCard cards[];
 extern int nrcards;
 extern char *HiSax_id;
+extern struct IsdnBuffers *tracebuf;
+
+#define TIMER3_VALUE 7
+
+static
+struct Fsm l1fsm =
+{NULL, 0, 0, NULL, NULL};
+
+enum {
+	ST_L1_F2,
+	ST_L1_F3,
+	ST_L1_F4,
+	ST_L1_F5,
+	ST_L1_F6,
+	ST_L1_F7,
+	ST_L1_F8,
+};
+
+#define L1_STATE_COUNT (ST_L1_F8+1)
+
+static char *strL1State[] =
+{
+	"ST_L1_F2",
+	"ST_L1_F3",
+	"ST_L1_F4",
+	"ST_L1_F5",
+	"ST_L1_F6",
+	"ST_L1_F7",
+	"ST_L1_F8",
+};
+
+enum {
+	EV_PH_ACTIVATE,
+	EV_RESET_IND,
+	EV_DEACT_CNF,
+	EV_DEACT_IND,
+	EV_POWER_UP,
+	EV_RSYNC_IND, 
+	EV_INFO2_IND,
+	EV_INFO4_IND,
+	EV_TIMER_DEACT,
+	EV_TIMER_ACT,
+	EV_TIMER3,
+};
+
+#define L1_EVENT_COUNT (EV_TIMER3 + 1)
+
+static char *strL1Event[] =
+{
+	"EV_PH_ACTIVATE",
+	"EV_RESET_IND",
+	"EV_DEACT_CNF",
+	"EV_DEACT_IND",
+	"EV_POWER_UP",
+	"EV_RSYNC_IND", 
+	"EV_INFO2_IND",
+	"EV_INFO4_IND",
+	"EV_TIMER_DEACT",
+	"EV_TIMER_ACT",
+	"EV_TIMER3",
+};
 
 /*
  * Find card with given driverId
  */
 static inline struct IsdnCardState
-*
-hisax_findcard(int driverid)
+*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;
+		if (cards[i].cs)
+			if (cards[i].cs->myid == driverid)
+				return (cards[i].cs);
+	return (NULL);
 }
 
 int
@@ -162,6 +270,7 @@
 	}
 }
 
+#if ISDN_CTRL_DEBUG
 void
 HiSax_putstatus(struct IsdnCardState *csta, char *buf)
 {
@@ -194,6 +303,23 @@
 		csta->iif.statcallb(&ic);
 	}
 }
+#else
+#define KDEBUG_DEF
+#include "../kdebug.h"
+
+static int DbgLineNr=0,DbgSequenzNr=1;
+
+void
+HiSax_putstatus(struct IsdnCardState *csta, char *buf)
+{
+	char tmp[512];
+	
+	if (DbgLineNr==23)
+		DbgLineNr=0;
+	sprintf(tmp, "%5d %s",DbgSequenzNr++,buf);
+	gput_str(tmp,0,DbgLineNr++);
+}	
+#endif
 
 int
 ll_run(struct IsdnCardState *csta)
@@ -238,228 +364,119 @@
 }
 
 void
-debugl1(struct IsdnCardState *sp, char *msg)
+debugl1(struct IsdnCardState *cs, char *msg)
 {
 	char tmp[256], tm[32];
 
 	jiftime(tm, jiffies);
-	sprintf(tmp, "%s Card %d %s\n", tm, sp->cardnr + 1, msg);
-	HiSax_putstatus(sp, tmp);
-}
-
-/*
- * HSCX stuff goes here
- */
-
-
-char *
-HscxVersion(u_char v)
-{
-	return (HSCXVer[v & 0xf]);
-}
-
-void
-hscx_sched_event(struct HscxState *hsp, int event)
-{
-	hsp->event |= 1 << event;
-	queue_task(&hsp->tqueue, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	sprintf(tmp, "%s Card %d %s\n", tm, cs->cardnr + 1, msg);
+	HiSax_putstatus(cs, tmp);
 }
 
-/*
- * ISAC stuff goes here
- */
-
-char *
-ISACVersion(u_char v)
+static void
+l1m_debug(struct FsmInst *fi, char *s)
 {
-	return (ISACVer[(v >> 5) & 3]);
+	struct PStack *st = fi->userdata;
+	
+	debugl1(st->l1.hardware, s);
 }
 
 void
-isac_sched_event(struct IsdnCardState *sp, int event)
-{
-	sp->event |= 1 << event;
-	queue_task(&sp->tqueue, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
-}
-
-int
-act_wanted(struct IsdnCardState *sp)
+L1activated(struct IsdnCardState *cs)
 {
 	struct PStack *st;
 
-	st = sp->stlist;
-	while (st)
-		if (st->l1.act_state)
-			return (!0);
+	st = cs->stlist;
+	while (st) {
+		if (test_and_clear_bit(FLG_L1_ACTIVATING, &st->l1.Flags))
+			st->l1.l1man(st, PH_ACTIVATE_CNF, NULL);
 		else
-			st = st->next;
-	return (0);
-}
-
-void
-isac_new_ph(struct IsdnCardState *sp)
-{
-	int enq;
-
-	enq = act_wanted(sp);
-
-	switch (sp->ph_state) {
-		case (6):
-			sp->ph_active = 0;
-			sp->ph_command(sp, 15);
-			break;
-		case (15):
-			sp->ph_active = 0;
-			if (enq)
-				sp->ph_command(sp, 0);
-			break;
-		case (0):
-			sp->ph_active = 0;
-			if (enq)
-				sp->ph_command(sp, 0);
-#if 0
-			else
-				sp->ph_command(sp, 15);
-#endif
-			break;
-		case (7):
-			sp->ph_active = 0;
-			if (enq)
-				sp->ph_command(sp, 9);
-			break;
-		case (12):
-			sp->ph_command(sp, 8);
-			sp->ph_active = 5;
-			isac_sched_event(sp, ISAC_PHCHANGE);
-			if (!sp->tx_skb)
-				sp->tx_skb = skb_dequeue(&sp->sq);
-			if (sp->tx_skb) {
-				sp->tx_cnt = 0;
-				sp->isac_fill_fifo(sp);
-			}
-			break;
-		case (13):
-			sp->ph_command(sp, 9);
-			sp->ph_active = 5;
-			isac_sched_event(sp, ISAC_PHCHANGE);
-			if (!sp->tx_skb)
-				sp->tx_skb = skb_dequeue(&sp->sq);
-			if (sp->tx_skb) {
-				sp->tx_cnt = 0;
-				sp->isac_fill_fifo(sp);
-			}
-			break;
-		case (4):
-		case (8):
-			sp->ph_active = 0;
-			break;
-		default:
-			sp->ph_active = 0;
-			break;
-	}
-}
-
-static void
-restart_ph(struct IsdnCardState *sp)
-{
-	if (!sp->ph_active) {
-		if ((sp->ph_state == 6) || (sp->ph_state == 0)) {
-			sp->ph_command(sp, 0);
-			sp->ph_active = 2;
-		} else {
-			sp->ph_command(sp, 1);
-			sp->ph_active = 1;
-		}
-	} else if (sp->ph_active == 2) {
-		sp->ph_command(sp, 1);
-		sp->ph_active = 1;
+			st->l1.l1man(st, PH_ACTIVATE_IND, NULL);
+		st = st->next;
 	}
 }
 
-
-static void
-act_ivated(struct IsdnCardState *sp)
+void
+L1deactivated(struct IsdnCardState *cs)
 {
 	struct PStack *st;
 
-	st = sp->stlist;
+	st = cs->stlist;
 	while (st) {
-		if (st->l1.act_state == 1) {
-			st->l1.act_state = 2;
-			st->l1.l1man(st, PH_ACTIVATE, NULL);
-		}
+		if (test_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+			st->l1.l1l2(st, PH_PAUSE_CNF, NULL);
+		st->l1.l1man(st, PH_DEACTIVATE_IND, NULL);
 		st = st->next;
 	}
+	test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags);
 }
 
-static void
-process_new_ph(struct IsdnCardState *sp)
-{
-	if (sp->ph_active == 5)
-		act_ivated(sp);
-}
-
-static void
-process_xmt(struct IsdnCardState *sp)
+void
+DChannel_proc_xmt(struct IsdnCardState *cs)
 {
 	struct PStack *stptr;
 
-	if (sp->tx_skb)
+	if (cs->tx_skb)
 		return;
 
-	stptr = sp->stlist;
+	stptr = cs->stlist;
 	while (stptr != NULL)
-		if (stptr->l1.requestpull) {
-			stptr->l1.requestpull = 0;
-			stptr->l1.l1l2(stptr, PH_PULL_ACK, NULL);
+		if (test_and_clear_bit(FLG_L1_PULL_REQ, &stptr->l1.Flags)) {
+			stptr->l1.l1l2(stptr, PH_PULL_CNF, NULL);
 			break;
 		} else
 			stptr = stptr->next;
 }
 
-static void
-process_rcv(struct IsdnCardState *sp)
+void
+DChannel_proc_rcv(struct IsdnCardState *cs)
 {
 	struct sk_buff *skb, *nskb;
-	struct PStack *stptr;
-	int found, broadc;
+	struct PStack *stptr = cs->stlist;
+	int found, tei, sapi;
 	char tmp[64];
 
-	while ((skb = skb_dequeue(&sp->rq))) {
+	if (stptr)
+		if (test_bit(FLG_L1_ACTTIMER, &stptr->l1.Flags))
+			FsmEvent(&stptr->l1.l1m, EV_TIMER_ACT, NULL);	
+	while ((skb = skb_dequeue(&cs->rq))) {
 #ifdef L2FRAME_DEBUG		/* psa */
-		if (sp->debug & L1_DEB_LAPD)
-			Logl2Frame(sp, skb, "PH_DATA", 1);
+		if (cs->debug & L1_DEB_LAPD)
+			Logl2Frame(cs, skb, "PH_DATA", 1);
 #endif
-		stptr = sp->stlist;
-		broadc = (skb->data[1] >> 1) == 127;
-
-		if (broadc) {
-			if (!(skb->data[0] >> 2)) {	/* sapi 0 */
-				sp->CallFlags = 3;
-				if (sp->dlogflag) {
-					LogFrame(sp, skb->data, skb->len);
-					dlogframe(sp, skb->data + 3, skb->len - 3,
+		stptr = cs->stlist;
+		sapi = skb->data[0] >> 2;
+		tei = skb->data[1] >> 1;
+
+		if (tei == GROUP_TEI) {
+			if (sapi == CTRL_SAPI) {	/* sapi 0 */
+				if (cs->dlogflag) {
+					LogFrame(cs, skb->data, skb->len);
+					dlogframe(cs, skb->data + 3, skb->len - 3,
 						  "Q.931 frame network->user broadcast");
 				}
-			}
-			while (stptr != NULL) {
-				if ((skb->data[0] >> 2) == stptr->l2.sap)
+				while (stptr != NULL) {
 					if ((nskb = skb_clone(skb, GFP_ATOMIC)))
-						stptr->l1.l1l2(stptr, PH_DATA, nskb);
+						stptr->l1.l1l2(stptr, PH_DATA_IND, nskb);
 					else
 						printk(KERN_WARNING "HiSax: isdn broadcast buffer shortage\n");
-				stptr = stptr->next;
+					stptr = stptr->next;
+				}
+			} else if (sapi == TEI_SAPI) {
+				while (stptr != NULL) {
+					if ((nskb = skb_clone(skb, GFP_ATOMIC)))
+						stptr->l1.l1tei(stptr, PH_DATA_IND, nskb);
+					else
+						printk(KERN_WARNING "HiSax: tei broadcast buffer shortage\n");
+					stptr = stptr->next;
+				}
 			}
-			SET_SKB_FREE(skb);
 			dev_kfree_skb(skb);
-		} else {
+		} else if (sapi == CTRL_SAPI) {
 			found = 0;
 			while (stptr != NULL)
-				if (((skb->data[0] >> 2) == stptr->l2.sap) &&
-				((skb->data[1] >> 1) == stptr->l2.tei)) {
-					stptr->l1.l1l2(stptr, PH_DATA, skb);
+				if (tei == stptr->l2.tei) {
+					stptr->l1.l1l2(stptr, PH_DATA_IND, skb);
 					found = !0;
 					break;
 				} else
@@ -474,167 +491,70 @@
 					sprintf(tmp,
 						"Q.931 frame network->user with tei %d (not for us)",
 						skb->data[1] >> 1);
-					LogFrame(sp, skb->data, skb->len);
-					dlogframe(sp, skb->data + 4, skb->len - 4, tmp);
+					LogFrame(cs, skb->data, skb->len);
+					dlogframe(cs, skb->data + 4, skb->len - 4, tmp);
 				}
-				SET_SKB_FREE(skb);
 				dev_kfree_skb(skb);
 			}
 		}
-
 	}
-
-}
-
-static void
-isac_bh(struct IsdnCardState *sp)
-{
-	if (!sp)
-		return;
-
-	if (test_and_clear_bit(ISAC_PHCHANGE, &sp->event))
-		process_new_ph(sp);
-	if (test_and_clear_bit(ISAC_RCVBUFREADY, &sp->event))
-		process_rcv(sp);
-	if (test_and_clear_bit(ISAC_XMTBUFREADY, &sp->event))
-		process_xmt(sp);
 }
 
 static void
-l2l1(struct PStack *st, int pr, void *arg)
+BChannel_proc_xmt(struct BCState *bcs)
 {
-	struct IsdnCardState *sp = (struct IsdnCardState *) st->l1.hardware;
-	struct sk_buff *skb = arg;
-	char str[64];
-
-	switch (pr) {
-		case (PH_DATA):
-			if (sp->tx_skb) {
-				skb_queue_tail(&sp->sq, skb);
-#ifdef L2FRAME_DEBUG		/* psa */
-				if (sp->debug & L1_DEB_LAPD)
-					Logl2Frame(sp, skb, "PH_DATA Queued", 0);
-#endif
-			} else {
-				if ((sp->dlogflag) && (!(skb->data[2] & 1))) {	/* I-FRAME */
-					LogFrame(sp, skb->data, skb->len);
-					sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei);
-					dlogframe(sp, skb->data + st->l2.ihsize, skb->len - st->l2.ihsize,
-						  str);
-				}
-				sp->tx_skb = skb;
-				sp->tx_cnt = 0;
-#ifdef L2FRAME_DEBUG		/* psa */
-				if (sp->debug & L1_DEB_LAPD)
-					Logl2Frame(sp, skb, "PH_DATA", 0);
-#endif
-				sp->isac_fill_fifo(sp);
-			}
-			break;
-		case (PH_DATA_PULLED):
-			if (sp->tx_skb) {
-				if (sp->debug & L1_DEB_WARN)
-					debugl1(sp, " l2l1 tx_skb exist this shouldn't happen");
-				break;
-			}
-			if ((sp->dlogflag) && (!(skb->data[2] & 1))) {	/* I-FRAME */
-				LogFrame(sp, skb->data, skb->len);
-				sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei);
-				dlogframe(sp, skb->data + st->l2.ihsize, skb->len - st->l2.ihsize,
-					  str);
-			}
-			sp->tx_skb = skb;
-			sp->tx_cnt = 0;
-#ifdef L2FRAME_DEBUG		/* psa */
-			if (sp->debug & L1_DEB_LAPD)
-				Logl2Frame(sp, skb, "PH_DATA_PULLED", 0);
-#endif
-			sp->isac_fill_fifo(sp);
-			break;
-		case (PH_REQUEST_PULL):
-#ifdef L2FRAME_DEBUG		/* psa */
-			if (sp->debug & L1_DEB_LAPD)
-				debugl1(sp, "-> PH_REQUEST_PULL");
-#endif
-			if (!sp->tx_skb) {
-				st->l1.requestpull = 0;
-				st->l1.l1l2(st, PH_PULL_ACK, NULL);
-			} else
-				st->l1.requestpull = !0;
-			break;
-	}
-}
-
+	struct PStack *st = bcs->st;
 
-static void
-hscx_process_xmt(struct HscxState *hsp)
-{
-	struct PStack *st = hsp->st;
-
-	if (hsp->tx_skb)
+	if (test_bit(BC_FLG_BUSY, &bcs->Flag))
 		return;
 
-	if (st->l1.requestpull) {
-		st->l1.requestpull = 0;
-		st->l1.l1l2(st, PH_PULL_ACK, NULL);
-	}
-	if (!hsp->active)
-		if ((!hsp->tx_skb) && (!skb_queue_len(&hsp->squeue)))
-			hsp->sp->modehscx(hsp, 0, 0);
+	if (test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags))
+		st->l1.l1l2(st, PH_PULL_CNF, NULL);
+	if (!test_bit(BC_FLG_ACTIV, &bcs->Flag))
+		if (!test_bit(BC_FLG_BUSY, &bcs->Flag) && (!skb_queue_len(&bcs->squeue)))
+			st->ma.manl1(st, PH_DEACTIVATE_CNF, 0);
 }
 
 static void
-hscx_process_rcv(struct HscxState *hsp)
+BChannel_proc_rcv(struct BCState *bcs)
 {
 	struct sk_buff *skb;
 
-#ifdef DEBUG_MAGIC
-	if (hsp->magic != 301270) {
-		printk(KERN_DEBUG "hscx_process_rcv magic not 301270\n");
-		return;
-	}
-#endif
-	while ((skb = skb_dequeue(&hsp->rqueue))) {
-		hsp->st->l1.l1l2(hsp->st, PH_DATA, skb);
+	while ((skb = skb_dequeue(&bcs->rqueue))) {
+		bcs->st->l1.l1l2(bcs->st, PH_DATA_IND, skb);
 	}
 }
 
 static void
-hscx_bh(struct HscxState *hsp)
+BChannel_bh(struct BCState *bcs)
 {
-
-	if (!hsp)
+	if (!bcs)
 		return;
-
-	if (test_and_clear_bit(HSCX_RCVBUFREADY, &hsp->event))
-		hscx_process_rcv(hsp);
-	if (test_and_clear_bit(HSCX_XMTBUFREADY, &hsp->event))
-		hscx_process_xmt(hsp);
-
+	if (test_and_clear_bit(B_RCVBUFREADY, &bcs->event))
+		BChannel_proc_rcv(bcs);
+	if (test_and_clear_bit(B_XMTBUFREADY, &bcs->event))
+		BChannel_proc_xmt(bcs);
 }
 
-/*
- * interrupt stuff ends here
- */
-
 void
-HiSax_addlist(struct IsdnCardState *sp,
+HiSax_addlist(struct IsdnCardState *cs,
 	      struct PStack *st)
 {
-	st->next = sp->stlist;
-	sp->stlist = st;
+	st->next = cs->stlist;
+	cs->stlist = st;
 }
 
 void
-HiSax_rmlist(struct IsdnCardState *sp,
+HiSax_rmlist(struct IsdnCardState *cs,
 	     struct PStack *st)
 {
 	struct PStack *p;
 
-	if (sp->stlist == st)
-		sp->stlist = st->next;
+	FsmDelTimer(&st->l1.timer, 0);
+	if (cs->stlist == st)
+		cs->stlist = st->next;
 	else {
-		p = sp->stlist;
+		p = cs->stlist;
 		while (p)
 			if (p->next == st) {
 				p->next = st->next;
@@ -644,248 +564,117 @@
 	}
 }
 
-static void
-check_ph_act(struct IsdnCardState *sp)
-{
-	struct PStack *st = sp->stlist;
-
-	while (st) {
-		if (st->l1.act_state)
-			return;
-		st = st->next;
-	}
-	if (sp->ph_active == 5)
-		sp->ph_active = 4;
-}
-
-static void
-HiSax_manl1(struct PStack *st, int pr,
-	    void *arg)
-{
-	struct IsdnCardState *sp = (struct IsdnCardState *)
-	st->l1.hardware;
-	long flags;
-	char tmp[32];
-
-	switch (pr) {
-		case (PH_ACTIVATE):
-			if (sp->debug) {
-				sprintf(tmp, "PH_ACT ph_active %d", sp->ph_active);
-				debugl1(sp, tmp);
-			}
-			save_flags(flags);
-			cli();
-			if (sp->ph_active & 4) {
-				sp->ph_active = 5;
-				st->l1.act_state = 2;
-				restore_flags(flags);
-				st->l1.l1man(st, PH_ACTIVATE, NULL);
-			} else {
-				st->l1.act_state = 1;
-				if (sp->ph_active == 0)
-					restart_ph(sp);
-				restore_flags(flags);
-			}
-			break;
-		case (PH_DEACTIVATE):
-			st->l1.act_state = 0;
-			if (sp->debug) {
-				sprintf(tmp, "PH_DEACT ph_active %d", sp->ph_active);
-				debugl1(sp, tmp);
-			}
-			check_ph_act(sp);
-			break;
-	}
-}
-
-static void
-HiSax_l2l1discardq(struct PStack *st, int pr,
-		   void *heldby, int releasetoo)
-{
-	struct IsdnCardState *sp = (struct IsdnCardState *) st->l1.hardware;
-	struct sk_buff *skb;
-
-#ifdef DEBUG_MAGIC
-	if (sp->magic != 301271) {
-		printk(KERN_DEBUG "isac_discardq magic not 301271\n");
-		return;
-	}
-#endif
-
-	while ((skb = skb_dequeue(&sp->sq))) {
-		SET_SKB_FREE(skb);
-		dev_kfree_skb(skb);
-	}
-}
-
-void
-setstack_HiSax(struct PStack *st, struct IsdnCardState *sp)
-{
-	st->l1.hardware = sp;
-	st->protocol = sp->protocol;
-
-	setstack_tei(st);
-
-	st->l1.stlistp = &(sp->stlist);
-	st->l1.act_state = 0;
-	st->l2.l2l1 = l2l1;
-	st->l2.l2l1discardq = HiSax_l2l1discardq;
-	st->ma.manl1 = HiSax_manl1;
-	st->l1.requestpull = 0;
-}
-
 void
-init_hscxstate(struct IsdnCardState *sp,
-	       int hscx)
+init_bcstate(struct IsdnCardState *cs,
+	     int bc)
 {
-	struct HscxState *hsp = sp->hs + hscx;
+	struct BCState *bcs = cs->bcs + bc;
 
-	hsp->sp = sp;
-	hsp->hscx = hscx;
-
-	hsp->tqueue.next = 0;
-	hsp->tqueue.sync = 0;
-	hsp->tqueue.routine = (void *) (void *) hscx_bh;
-	hsp->tqueue.data = hsp;
-
-	hsp->inuse = 0;
-	hsp->init = 0;
-	hsp->active = 0;
-
-#ifdef DEBUG_MAGIC
-	hsp->magic = 301270;
-#endif
-}
-
-int
-get_irq(int cardnr, void *routine)
-{
-	struct IsdnCard *card = cards + cardnr;
-	long flags;
-
-	save_flags(flags);
-	cli();
-	if (request_irq(card->sp->irq, routine,
-			I4L_IRQ_FLAG, "HiSax", card->sp)) {
-		printk(KERN_WARNING "HiSax: couldn't get interrupt %d\n",
-		       card->sp->irq);
-		restore_flags(flags);
-		return (0);
-	}
-	restore_flags(flags);
-	return (1);
-}
-
-static void
-release_irq(int cardnr)
-{
-	struct IsdnCard *card = cards + cardnr;
-
-	free_irq(card->sp->irq, card->sp);
-}
-
-void
-close_hscxstate(struct HscxState *hs)
-{
-	struct sk_buff *skb;
-
-	hs->sp->modehscx(hs, 0, 0);
-	hs->inuse = 0;
-	if (hs->init) {
-		if (hs->rcvbuf) {
-			kfree(hs->rcvbuf);
-			hs->rcvbuf = NULL;
-		}
-		while ((skb = skb_dequeue(&hs->rqueue))) {
-			SET_SKB_FREE(skb);
-			dev_kfree_skb(skb);
-		}
-		while ((skb = skb_dequeue(&hs->squeue))) {
-			SET_SKB_FREE(skb);
-			dev_kfree_skb(skb);
-		}
-		if (hs->tx_skb) {
-			SET_SKB_FREE(hs->tx_skb);
-			dev_kfree_skb(hs->tx_skb);
-			hs->tx_skb = NULL;
-		}
-	}
-	hs->init = 0;
+	bcs->cs = cs;
+	bcs->channel = bc;
+	bcs->tqueue.next = 0;
+	bcs->tqueue.sync = 0;
+	bcs->tqueue.routine = (void *) (void *) BChannel_bh;
+	bcs->tqueue.data = bcs;
+	bcs->BC_SetStack = NULL;
+	bcs->BC_Close = NULL;
+	bcs->Flag = 0;
 }
 
 static void
 closecard(int cardnr)
 {
-	struct IsdnCardState *csta = cards[cardnr].sp;
+	struct IsdnCardState *csta = cards[cardnr].cs;
 	struct sk_buff *skb;
-
-	close_hscxstate(csta->hs + 1);
-	close_hscxstate(csta->hs);
+	
+	if (csta->bcs->BC_Close != NULL) { 
+		csta->bcs->BC_Close(csta->bcs + 1);
+		csta->bcs->BC_Close(csta->bcs);
+	}
 
 	if (csta->rcvbuf) {
 		kfree(csta->rcvbuf);
 		csta->rcvbuf = NULL;
 	}
 	while ((skb = skb_dequeue(&csta->rq))) {
-		SET_SKB_FREE(skb);
 		dev_kfree_skb(skb);
 	}
 	while ((skb = skb_dequeue(&csta->sq))) {
-		SET_SKB_FREE(skb);
 		dev_kfree_skb(skb);
 	}
 	if (csta->tx_skb) {
-		SET_SKB_FREE(csta->tx_skb);
 		dev_kfree_skb(csta->tx_skb);
 		csta->tx_skb = NULL;
 	}
-	switch (csta->typ) {
-#if CARD_TELES0
-		case ISDN_CTYPE_16_0:
-		case ISDN_CTYPE_8_0:
-			release_io_teles0(cards + cardnr);
-			break;
-#endif
-#if CARD_TELES3
-		case ISDN_CTYPE_PNP:
-		case ISDN_CTYPE_16_3:
-		case ISDN_CTYPE_TELESPCMCIA:
-			release_io_teles3(cards + cardnr);
-			break;
-#endif
-#if CARD_AVM_A1
-		case ISDN_CTYPE_A1:
-			release_io_avm_a1(cards + cardnr);
-			break;
-#endif
-#if CARD_ELSA
-		case ISDN_CTYPE_ELSA:
-		case ISDN_CTYPE_ELSA_QS1000:
-			release_io_elsa(cards + cardnr);
-			break;
-#endif
-#if CARD_IX1MICROR2
-		case ISDN_CTYPE_IX1MICROR2:
-			release_io_ix1micro(cards + cardnr);
-			break;
-#endif
-		default:
-			break;
+	if (csta->mon_rx) {
+		kfree(csta->mon_rx);
+		csta->mon_rx = NULL;
+	}
+	if (csta->mon_tx) {
+		kfree(csta->mon_tx);
+		csta->mon_tx = NULL;
 	}
+	csta->cardmsg(csta, CARD_RELEASE, NULL);
+	del_timer(&csta->dbusytimer);
 	ll_unload(csta);
 }
 
-static int
-checkcard(int cardnr, char *id)
+HISAX_INITFUNC(static int init_card(struct IsdnCardState *cs))
+{
+	int irq_cnt, cnt = 3;
+	long flags;
+
+	save_flags(flags);
+	cli();
+	irq_cnt = kstat_irqs(cs->irq);
+	printk(KERN_INFO "%s: IRQ %d count %d\n", CardType[cs->typ], cs->irq,
+		irq_cnt);
+	if (cs->cardmsg(cs, CARD_SETIRQ, NULL)) {
+		printk(KERN_WARNING "HiSax: couldn't get interrupt %d\n",
+			cs->irq);
+		return(1);
+	}
+	while (cnt) {
+		cs->cardmsg(cs, CARD_INIT, NULL);
+		sti();
+		current->state = TASK_INTERRUPTIBLE;
+		/* Timeout 10ms */
+		current->timeout = jiffies + (10 * HZ) / 1000;
+		schedule();
+		restore_flags(flags);
+		printk(KERN_INFO "%s: IRQ %d count %d\n", CardType[cs->typ],
+			cs->irq, kstat_irqs(cs->irq));
+		if (kstat_irqs(cs->irq) == irq_cnt) {
+			printk(KERN_WARNING
+			       "%s: IRQ(%d) getting no interrupts during init %d\n",
+			       CardType[cs->typ], cs->irq, 4 - cnt);
+			if (cnt == 1) {
+				free_irq(cs->irq, cs);
+				return (2);
+			} else {
+				cs->cardmsg(cs, CARD_RESET, NULL);
+				cnt--;
+			}
+		} else {
+			cs->cardmsg(cs, CARD_TEST, NULL);
+			return(0);
+		}
+	}
+	restore_flags(flags);
+	return(3);
+}
+
+HISAX_INITFUNC(static int
+checkcard(int cardnr, char *id, int *busy_flag))
 {
 	long flags;
 	int ret = 0;
 	struct IsdnCard *card = cards + cardnr;
-	struct IsdnCardState *sp;
+	struct IsdnCardState *cs;
 
 	save_flags(flags);
 	cli();
-	if (!(sp = (struct IsdnCardState *)
+	if (!(cs = (struct IsdnCardState *)
 	      kmalloc(sizeof(struct IsdnCardState), GFP_ATOMIC))) {
 		printk(KERN_WARNING
 		       "HiSax: No memory for IsdnCardState(card %d)\n",
@@ -893,10 +682,16 @@
 		restore_flags(flags);
 		return (0);
 	}
-	card->sp = sp;
-	sp->cardnr = cardnr;
-	sp->cfg_reg = 0;
-	sp->protocol = card->protocol;
+	card->cs = cs;
+	cs->cardnr = cardnr;
+	cs->debug = L1_DEB_WARN;
+	cs->HW_Flags = 0;
+	cs->busy_flag = busy_flag;
+#if TEI_PER_CARD
+#else
+	test_and_set_bit(FLG_TWO_DCHAN, &cs->HW_Flags);
+#endif
+	cs->protocol = card->protocol;
 
 	if ((card->typ > 0) && (card->typ < 31)) {
 		if (!((1 << card->typ) & SUPORTED_CARDS)) {
@@ -913,31 +708,34 @@
 		restore_flags(flags);
 		return (0);
 	}
-	if (!(sp->dlogspace = kmalloc(4096, GFP_ATOMIC))) {
+	if (!(cs->dlogspace = kmalloc(4096, GFP_ATOMIC))) {
 		printk(KERN_WARNING
 		       "HiSax: No memory for dlogspace(card %d)\n",
 		       cardnr + 1);
 		restore_flags(flags);
 		return (0);
 	}
-	if (!(sp->status_buf = kmalloc(HISAX_STATUS_BUFSIZE, GFP_ATOMIC))) {
+	if (!(cs->status_buf = kmalloc(HISAX_STATUS_BUFSIZE, GFP_ATOMIC))) {
 		printk(KERN_WARNING
 		       "HiSax: No memory for status_buf(card %d)\n",
 		       cardnr + 1);
-		kfree(sp->dlogspace);
+		kfree(cs->dlogspace);
 		restore_flags(flags);
 		return (0);
 	}
-	sp->status_read = sp->status_buf;
-	sp->status_write = sp->status_buf;
-	sp->status_end = sp->status_buf + HISAX_STATUS_BUFSIZE - 1;
-	sp->typ = card->typ;
-	sp->CallFlags = 0;
-	strcpy(sp->iif.id, id);
-	sp->iif.channels = 2;
-	sp->iif.maxbufsize = MAX_DATA_SIZE;
-	sp->iif.hl_hdrlen = MAX_HEADER_LEN;
-	sp->iif.features =
+	cs->stlist = NULL;
+	cs->dlogflag = 0;
+	cs->mon_tx = NULL;
+	cs->mon_rx = NULL;
+	cs->status_read = cs->status_buf;
+	cs->status_write = cs->status_buf;
+	cs->status_end = cs->status_buf + HISAX_STATUS_BUFSIZE - 1;
+	cs->typ = card->typ;
+	strcpy(cs->iif.id, id);
+	cs->iif.channels = 2;
+	cs->iif.maxbufsize = MAX_DATA_SIZE;
+	cs->iif.hl_hdrlen = MAX_HEADER_LEN;
+	cs->iif.features =
 	    ISDN_FEATURE_L2_X75I |
 	    ISDN_FEATURE_L2_HDLC |
 	    ISDN_FEATURE_L2_TRANS |
@@ -953,21 +751,19 @@
 #endif
 	    0;
 
-	sp->iif.command = HiSax_command;
-	sp->iif.writebuf = NULL;
-	sp->iif.writecmd = NULL;
-	sp->iif.writebuf_skb = HiSax_writebuf_skb;
-	sp->iif.readstat = HiSax_readstatus;
-	register_isdn(&sp->iif);
-	sp->myid = sp->iif.channels;
-	restore_flags(flags);
-	printk(KERN_NOTICE
+	cs->iif.command = HiSax_command;
+	cs->iif.writecmd = NULL;
+	cs->iif.writebuf_skb = HiSax_writebuf_skb;
+	cs->iif.readstat = HiSax_readstatus;
+	register_isdn(&cs->iif);
+	cs->myid = cs->iif.channels;
+	printk(KERN_INFO
 	       "HiSax: Card %d Protocol %s Id=%s (%d)\n", cardnr + 1,
 	       (card->protocol == ISDN_PTYPE_1TR6) ? "1TR6" :
 	       (card->protocol == ISDN_PTYPE_EURO) ? "EDSS1" :
 	       (card->protocol == ISDN_PTYPE_LEASED) ? "LEASED" :
 	       (card->protocol == ISDN_PTYPE_NI1) ? "NI1" :
-	       "NONE", sp->iif.id, sp->myid);
+	       "NONE", cs->iif.id, cs->myid);
 	switch (card->typ) {
 #if CARD_TELES0
 		case ISDN_CTYPE_16_0:
@@ -979,6 +775,7 @@
 		case ISDN_CTYPE_16_3:
 		case ISDN_CTYPE_PNP:
 		case ISDN_CTYPE_TELESPCMCIA:
+		case ISDN_CTYPE_COMPAQ_ISA:
 			ret = setup_teles3(card);
 			break;
 #endif
@@ -989,7 +786,9 @@
 #endif
 #if CARD_ELSA
 		case ISDN_CTYPE_ELSA:
-		case ISDN_CTYPE_ELSA_QS1000:
+		case ISDN_CTYPE_ELSA_PNP:
+		case ISDN_CTYPE_ELSA_PCMCIA:
+		case ISDN_CTYPE_ELSA_PCI:
 			ret = setup_elsa(card);
 			break;
 #endif
@@ -998,90 +797,103 @@
 			ret = setup_ix1micro(card);
 			break;
 #endif
-		default:
-			printk(KERN_WARNING "HiSax: Unknown Card Typ %d\n",
-			       card->typ);
-			ll_unload(sp);
-			return (0);
-	}
-	if (!ret) {
-		ll_unload(sp);
-		return (0);
-	}
-	if (!(sp->rcvbuf = kmalloc(MAX_DFRAME_LEN, GFP_ATOMIC))) {
-		printk(KERN_WARNING
-		       "HiSax: No memory for isac rcvbuf\n");
-		return (1);
-	}
-	sp->rcvidx = 0;
-	sp->tx_skb = NULL;
-	sp->tx_cnt = 0;
-	sp->event = 0;
-	sp->tqueue.next = 0;
-	sp->tqueue.sync = 0;
-	sp->tqueue.routine = (void *) (void *) isac_bh;
-	sp->tqueue.data = sp;
-
-	skb_queue_head_init(&sp->rq);
-	skb_queue_head_init(&sp->sq);
-
-	sp->stlist = NULL;
-	sp->ph_active = 0;
-	sp->dlogflag = 0;
-	sp->debug = L1_DEB_WARN;
-#ifdef DEBUG_MAGIC
-	sp->magic = 301271;
+#if CARD_DIEHLDIVA
+		case ISDN_CTYPE_DIEHLDIVA:
+			ret = setup_diva(card);
+			break;
 #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);
+#if CARD_ASUSCOM
+		case ISDN_CTYPE_ASUSCOM:
+			ret = setup_asuscom(card);
 			break;
 #endif
-#if CARD_TELES3
-		case ISDN_CTYPE_16_3:
-		case ISDN_CTYPE_PNP:
-		case ISDN_CTYPE_TELESPCMCIA:
-			ret = initteles3(sp);
+#if CARD_TELEINT
+		case ISDN_CTYPE_TELEINT:
+			ret = setup_TeleInt(card);
 			break;
 #endif
-#if CARD_AVM_A1
-		case ISDN_CTYPE_A1:
-			ret = initavm_a1(sp);
+#if CARD_SEDLBAUER
+		case ISDN_CTYPE_SEDLBAUER:
+		case ISDN_CTYPE_SEDLBAUER_PCMCIA:
+			ret = setup_sedlbauer(card);
 			break;
 #endif
-#if CARD_ELSA
-		case ISDN_CTYPE_ELSA:
-		case ISDN_CTYPE_ELSA_QS1000:
-			ret = initelsa(sp);
+#if CARD_SPORTSTER
+		case ISDN_CTYPE_SPORTSTER:
+			ret = setup_sportster(card);
 			break;
 #endif
-#if CARD_IX1MICROR2
-		case ISDN_CTYPE_IX1MICROR2:
-			ret = initix1micro(sp);
+#if CARD_MIC
+		case ISDN_CTYPE_MIC:
+			ret = setup_mic(card);
 			break;
 #endif
-		default:
-			ret = 0;
+#if CARD_NETJET
+		case ISDN_CTYPE_NETJET:
+			ret = setup_netjet(card);
+			break;
+#endif
+#if CARD_TELES3C
+		case ISDN_CTYPE_TELES3C:
+			ret = setup_t163c(card);
+			break;
+#endif
+#if CARD_NICCY
+		case ISDN_CTYPE_NICCY:
+			ret = setup_niccy(card);
+			break;
+#endif
+#if CARD_AMD7930
+		case ISDN_CTYPE_AMD7930:
+			ret = setup_amd7930(card);
 			break;
+#endif
+		default:
+			printk(KERN_WARNING "HiSax: Unknown Card Typ %d\n",
+			       card->typ);
+			ll_unload(cs);
+			restore_flags(flags);
+			return (0);
 	}
 	if (!ret) {
+		ll_unload(cs);
+		restore_flags(flags);
+		return (0);
+	}
+	if (!(cs->rcvbuf = kmalloc(MAX_DFRAME_LEN, GFP_ATOMIC))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for isac rcvbuf\n");
+		return (1);
+	}
+	cs->rcvidx = 0;
+	cs->tx_skb = NULL;
+	cs->tx_cnt = 0;
+	cs->event = 0;
+	cs->tqueue.next = 0;
+	cs->tqueue.sync = 0;
+	cs->tqueue.data = cs;
+
+	skb_queue_head_init(&cs->rq);
+	skb_queue_head_init(&cs->sq);
+
+	init_bcstate(cs, 0);
+	init_bcstate(cs, 1);
+	ret = init_card(cs);
+	if (ret) {
 		closecard(cardnr);
+		restore_flags(flags);
 		return (0);
 	}
-	init_tei(sp, sp->protocol);
-	CallcNewChan(sp);
-	ll_run(sp);
+	init_tei(cs, cs->protocol);
+	CallcNewChan(cs);
+	ll_run(cs);
+	cs->l1cmd(cs, PH_RESET_REQ, NULL);
+	restore_flags(flags);
 	return (1);
 }
 
-void
-HiSax_shiftcards(int idx)
+HISAX_INITFUNC(void
+HiSax_shiftcards(int idx))
 {
 	int i;
 
@@ -1089,8 +901,8 @@
 		memcpy(&cards[i], &cards[i + 1], sizeof(cards[i]));
 }
 
-int
-HiSax_inithardware(void)
+HISAX_INITFUNC(int
+HiSax_inithardware(int *busy_flag))
 {
 	int foundcards = 0;
 	int i = 0;
@@ -1120,15 +932,15 @@
 			else
 				sprintf(ids, "%s%d", id, i);
 		}
-		if (checkcard(i, ids)) {
+		if (checkcard(i, ids, busy_flag)) {
 			foundcards++;
 			i++;
 		} else {
 			printk(KERN_WARNING "HiSax: Card %s not installed !\n",
 			       CardType[cards[i].typ]);
-			if (cards[i].sp)
-				kfree((void *) cards[i].sp);
-			cards[i].sp = NULL;
+			if (cards[i].cs)
+				kfree((void *) cards[i].cs);
+			cards[i].cs = NULL;
 			HiSax_shiftcards(i);
 		}
 	}
@@ -1144,159 +956,66 @@
 	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);
+		if (cards[i].cs) {
+			ll_stop(cards[i].cs);
+			release_tei(cards[i].cs);
 			closecard(i);
-			kfree((void *) cards[i].sp);
-			cards[i].sp = NULL;
+			free_irq(cards[i].cs->irq, cards[i].cs);
+			kfree((void *) cards[i].cs);
+			cards[i].cs = NULL;
 		}
+	Isdnl1Free();
+	TeiFree();
 	Isdnl2Free();
 	CallcFree();
 	restore_flags(flags);
 }
 
-static void
-hscx_l2l1(struct PStack *st, int pr, void *arg)
-{
-	struct sk_buff *skb = arg;
-	struct IsdnCardState *sp = (struct IsdnCardState *) st->l1.hardware;
-	struct HscxState *hsp = sp->hs + st->l1.hscx;
-	long flags;
-
-	switch (pr) {
-		case (PH_DATA):
-			save_flags(flags);
-			cli();
-			if (hsp->tx_skb) {
-				skb_queue_tail(&hsp->squeue, skb);
-				restore_flags(flags);
-			} else {
-				restore_flags(flags);
-				hsp->tx_skb = skb;
-				hsp->count = 0;
-				sp->hscx_fill_fifo(hsp);
-			}
-			break;
-		case (PH_DATA_PULLED):
-			if (hsp->tx_skb) {
-				printk(KERN_WARNING "hscx_l2l1: this shouldn't happen\n");
-				break;
-			}
-			hsp->tx_skb = skb;
-			hsp->count = 0;
-			sp->hscx_fill_fifo(hsp);
-			break;
-		case (PH_REQUEST_PULL):
-			if (!hsp->tx_skb) {
-				st->l1.requestpull = 0;
-				st->l1.l1l2(st, PH_PULL_ACK, NULL);
-			} else
-				st->l1.requestpull = !0;
-			break;
-	}
-
-}
-extern struct IsdnBuffers *tracebuf;
-
-static void
-hscx_l2l1discardq(struct PStack *st, int pr, void *heldby,
-		  int releasetoo)
-{
-	struct IsdnCardState *sp = (struct IsdnCardState *)
-	st->l1.hardware;
-	struct HscxState *hsp = sp->hs + st->l1.hscx;
-	struct sk_buff *skb;
-
-#ifdef DEBUG_MAGIC
-	if (hsp->magic != 301270) {
-		printk(KERN_DEBUG "hscx_discardq magic not 301270\n");
-		return;
-	}
-#endif
-
-	while ((skb = skb_dequeue(&hsp->squeue))) {
-		SET_SKB_FREE(skb);
-		dev_kfree_skb(skb);
-	}
-}
-
-static int
-open_hscxstate(struct IsdnCardState *sp,
-	       int hscx)
-{
-	struct HscxState *hsp = sp->hs + hscx;
-
-	if (!hsp->init) {
-		if (!(hsp->rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) {
-			printk(KERN_WARNING
-			       "HiSax: No memory for hscx_rcvbuf\n");
-			return (1);
-		}
-		skb_queue_head_init(&hsp->rqueue);
-		skb_queue_head_init(&hsp->squeue);
-	}
-	hsp->init = !0;
-
-	hsp->tx_skb = NULL;
-	hsp->event = 0;
-	hsp->rcvidx = 0;
-	hsp->tx_cnt = 0;
-	return (0);
-}
-
-static void
-hscx_manl1(struct PStack *st, int pr,
-	   void *arg)
-{
-	struct IsdnCardState *sp = (struct IsdnCardState *) st->l1.hardware;
-	struct HscxState *hsp = sp->hs + st->l1.hscx;
-
-	switch (pr) {
-		case (PH_ACTIVATE):
-			hsp->active = !0;
-			sp->modehscx(hsp, st->l1.hscxmode, st->l1.hscxchannel);
-			st->l1.l1man(st, PH_ACTIVATE, NULL);
-			break;
-		case (PH_DEACTIVATE):
-			if (!hsp->tx_skb)
-				sp->modehscx(hsp, 0, 0);
-
-			hsp->active = 0;
-			break;
-	}
-}
-
-int
-setstack_hscx(struct PStack *st, struct HscxState *hs)
-{
-	if (open_hscxstate(st->l1.hardware, hs->hscx))
-		return (-1);
-
-	st->l1.hscx = hs->hscx;
-	st->l2.l2l1 = hscx_l2l1;
-	st->ma.manl1 = hscx_manl1;
-	st->l2.l2l1discardq = hscx_l2l1discardq;
-
-	st->l1.act_state = 0;
-	st->l1.requestpull = 0;
-
-	hs->st = st;
-	return (0);
-}
-
 void
 HiSax_reportcard(int cardnr)
 {
-	struct IsdnCardState *sp = cards[cardnr].sp;
+	struct IsdnCardState *cs = cards[cardnr].cs;
+	struct PStack *stptr;
+	struct l3_process *pc;
+	int j, i = 1;
 
 	printk(KERN_DEBUG "HiSax: reportcard No %d\n", cardnr + 1);
-	printk(KERN_DEBUG "HiSax: Type %s\n", CardType[sp->typ]);
-	printk(KERN_DEBUG "HiSax: debuglevel %x\n", sp->debug);
+	printk(KERN_DEBUG "HiSax: Type %s\n", CardType[cs->typ]);
+	printk(KERN_DEBUG "HiSax: debuglevel %x\n", cs->debug);
 	printk(KERN_DEBUG "HiSax: HiSax_reportcard address 0x%lX\n",
 	       (ulong) & HiSax_reportcard);
+	printk(KERN_DEBUG "HiSax: cs 0x%lX\n", (ulong) cs);
+	printk(KERN_DEBUG "HiSax: cs stl 0x%lX\n", (ulong) & (cs->stlist));
+	stptr = cs->stlist;
+	while (stptr != NULL) {
+		printk(KERN_DEBUG "HiSax: dst%d 0x%lX\n", i, (ulong) stptr);
+		printk(KERN_DEBUG "HiSax: dst%d stp 0x%lX\n", i, (ulong) stptr->l1.stlistp);
+		printk(KERN_DEBUG "HiSax:   tei %d sapi %d\n",
+		       stptr->l2.tei, stptr->l2.sap);
+		printk(KERN_DEBUG "HiSax:      man 0x%lX\n", (ulong) stptr->ma.layer);
+		pc = stptr->l3.proc;
+		while (pc) {
+			printk(KERN_DEBUG "HiSax: l3proc %x 0x%lX\n", pc->callref,
+			       (ulong) pc);
+			printk(KERN_DEBUG "HiSax:    state %d  st 0x%lX chan 0x%lX\n",
+			    pc->state, (ulong) pc->st, (ulong) pc->chan);
+			pc = pc->next;
+		}
+		stptr = stptr->next;
+		i++;
+	}
+	for (j = 0; j < 2; j++) {
+		printk(KERN_DEBUG "HiSax: ch%d 0x%lX\n", j,
+		       (ulong) & cs->channel[j]);
+		stptr = cs->channel[j].b_st;
+		i = 1;
+		while (stptr != NULL) {
+			printk(KERN_DEBUG "HiSax:  b_st%d 0x%lX\n", i, (ulong) stptr);
+			printk(KERN_DEBUG "HiSax:    man 0x%lX\n", (ulong) stptr->ma.layer);
+			stptr = stptr->next;
+			i++;
+		}
+	}
 }
 
 #ifdef L2FRAME_DEBUG		/* psa */
@@ -1366,7 +1085,7 @@
 }
 
 void
-Logl2Frame(struct IsdnCardState *sp, struct sk_buff *skb, char *buf, int dir)
+Logl2Frame(struct IsdnCardState *cs, struct sk_buff *skb, char *buf, int dir)
 {
 	char tmp[132];
 	u_char *ptr;
@@ -1374,13 +1093,293 @@
 	ptr = skb->data;
 
 	if (ptr[0] & 1 || !(ptr[1] & 1))
-		debugl1(sp, "Addres not LAPD");
+		debugl1(cs, "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);
+		debugl1(cs, tmp);
 	}
 }
-
 #endif
+
+static void
+l1_reset(struct FsmInst *fi, int event, void *arg)
+{
+	FsmChangeState(fi, ST_L1_F3);
+}
+
+static void
+l1_deact_cnf(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct IsdnCardState *cs = st->l1.hardware;
+
+	FsmChangeState(fi, ST_L1_F3);
+	if (test_bit(FLG_L1_ACTIVATING, &st->l1.Flags))
+		cs->l1cmd(cs, PH_ENABLE_REQ, NULL);
+}
+
+static void
+l1_deact_req(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	FsmChangeState(fi, ST_L1_F3);
+	if (!test_bit(FLG_L1_T3RUN, &st->l1.Flags)) {
+		FsmDelTimer(&st->l1.timer, 1);
+		FsmAddTimer(&st->l1.timer, 550, EV_TIMER_DEACT, NULL, 2);
+		test_and_set_bit(FLG_L1_DEACTTIMER, &st->l1.Flags);
+	}
+}
+
+static void
+l1_power_up(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct IsdnCardState *cs = st->l1.hardware;
+
+	if (test_bit(FLG_L1_ACTIVATING, &st->l1.Flags)) {
+		FsmChangeState(fi, ST_L1_F4);
+		cs->l1cmd(cs, PH_INFO3_REQ, NULL);
+		FsmDelTimer(&st->l1.timer, 1);
+		FsmAddTimer(&st->l1.timer, TIMER3_VALUE * HZ, EV_TIMER3, NULL, 2);
+		test_and_set_bit(FLG_L1_T3RUN, &st->l1.Flags);
+	} else
+		FsmChangeState(fi, ST_L1_F3);
+}
+
+static void
+l1_go_F5(struct FsmInst *fi, int event, void *arg)
+{
+	FsmChangeState(fi, ST_L1_F5);
+}
+
+static void
+l1_go_F8(struct FsmInst *fi, int event, void *arg)
+{
+	FsmChangeState(fi, ST_L1_F8);
+}
+
+static void
+l1_info2_ind(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct IsdnCardState *cs = st->l1.hardware;
+
+	FsmChangeState(fi, ST_L1_F6);
+	cs->l1cmd(cs, PH_INFO3_REQ, NULL);
+}
+
+static void
+l1_info4_ind(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct IsdnCardState *cs = st->l1.hardware;
+
+	FsmChangeState(fi, ST_L1_F7);
+	cs->l1cmd(cs, PH_INFO3_REQ, NULL);
+	if (test_and_clear_bit(FLG_L1_DEACTTIMER, &st->l1.Flags))
+		FsmDelTimer(&st->l1.timer, 4);
+	if (!test_bit(FLG_L1_ACTIVATED, &st->l1.Flags)) {
+		if (test_and_clear_bit(FLG_L1_T3RUN, &st->l1.Flags))
+			FsmDelTimer(&st->l1.timer, 3);
+		FsmAddTimer(&st->l1.timer, 110, EV_TIMER_ACT, NULL, 2);
+		test_and_set_bit(FLG_L1_ACTTIMER, &st->l1.Flags);
+	}
+}
+
+static void
+l1_timer3(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct IsdnCardState *cs = st->l1.hardware;
+	
+	test_and_clear_bit(FLG_L1_T3RUN, &st->l1.Flags);	
+        if (test_and_clear_bit(FLG_L1_ACTIVATING, &st->l1.Flags))
+        	L1deactivated(cs);
+        if (st->l1.l1m.state != ST_L1_F6)
+        	FsmChangeState(fi, ST_L1_F3);
+}
+
+static void
+l1_timer_act(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct IsdnCardState *cs = st->l1.hardware;
+	
+	test_and_clear_bit(FLG_L1_ACTTIMER, &st->l1.Flags);
+	test_and_set_bit(FLG_L1_ACTIVATED, &st->l1.Flags);
+	L1activated(cs);
+}
+
+static void
+l1_timer_deact(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct IsdnCardState *cs = st->l1.hardware;
+	
+	test_and_clear_bit(FLG_L1_DEACTTIMER, &st->l1.Flags);
+	test_and_clear_bit(FLG_L1_ACTIVATED, &st->l1.Flags);
+	L1deactivated(cs);
+	cs->l1cmd(cs, PH_DEACT_ACK, NULL);
+}
+
+static void
+l1_activate(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct IsdnCardState *cs = st->l1.hardware;
+                
+	cs->l1cmd(cs, PH_RESET_REQ, NULL);
+}
+
+static struct FsmNode L1FnList[] HISAX_INITDATA =
+{
+	{ST_L1_F3, EV_PH_ACTIVATE, l1_activate},
+	{ST_L1_F3, EV_RESET_IND, l1_reset},
+	{ST_L1_F4, EV_RESET_IND, l1_reset},
+	{ST_L1_F5, EV_RESET_IND, l1_reset},
+	{ST_L1_F6, EV_RESET_IND, l1_reset},
+	{ST_L1_F7, EV_RESET_IND, l1_reset},
+	{ST_L1_F8, EV_RESET_IND, l1_reset},
+	{ST_L1_F3, EV_DEACT_CNF, l1_deact_cnf},
+	{ST_L1_F4, EV_DEACT_CNF, l1_deact_cnf},
+	{ST_L1_F5, EV_DEACT_CNF, l1_deact_cnf},
+	{ST_L1_F6, EV_DEACT_CNF, l1_deact_cnf},
+	{ST_L1_F7, EV_DEACT_CNF, l1_deact_cnf},
+	{ST_L1_F8, EV_DEACT_CNF, l1_deact_cnf},
+	{ST_L1_F6, EV_DEACT_IND, l1_deact_req},
+	{ST_L1_F7, EV_DEACT_IND, l1_deact_req},
+	{ST_L1_F8, EV_DEACT_IND, l1_deact_req},
+	{ST_L1_F3, EV_POWER_UP, l1_power_up},
+	{ST_L1_F4, EV_RSYNC_IND, l1_go_F5},
+	{ST_L1_F6, EV_RSYNC_IND, l1_go_F8},
+	{ST_L1_F7, EV_RSYNC_IND, l1_go_F8},
+	{ST_L1_F3, EV_INFO2_IND, l1_info2_ind},
+	{ST_L1_F4, EV_INFO2_IND, l1_info2_ind},
+	{ST_L1_F5, EV_INFO2_IND, l1_info2_ind},
+	{ST_L1_F7, EV_INFO2_IND, l1_info2_ind},
+	{ST_L1_F8, EV_INFO2_IND, l1_info2_ind},
+	{ST_L1_F3, EV_INFO4_IND, l1_info4_ind},
+	{ST_L1_F4, EV_INFO4_IND, l1_info4_ind},
+	{ST_L1_F5, EV_INFO4_IND, l1_info4_ind},
+	{ST_L1_F6, EV_INFO4_IND, l1_info4_ind},
+	{ST_L1_F8, EV_INFO4_IND, l1_info4_ind},
+	{ST_L1_F3, EV_TIMER3, l1_timer3},
+	{ST_L1_F4, EV_TIMER3, l1_timer3},
+	{ST_L1_F5, EV_TIMER3, l1_timer3},
+	{ST_L1_F6, EV_TIMER3, l1_timer3},
+	{ST_L1_F8, EV_TIMER3, l1_timer3},
+	{ST_L1_F7, EV_TIMER_ACT, l1_timer_act},
+	{ST_L1_F3, EV_TIMER_DEACT, l1_timer_deact},
+	{ST_L1_F4, EV_TIMER_DEACT, l1_timer_deact},
+	{ST_L1_F5, EV_TIMER_DEACT, l1_timer_deact},
+	{ST_L1_F6, EV_TIMER_DEACT, l1_timer_deact},
+	{ST_L1_F7, EV_TIMER_DEACT, l1_timer_deact},
+	{ST_L1_F8, EV_TIMER_DEACT, l1_timer_deact},
+};
+
+#define L1_FN_COUNT (sizeof(L1FnList)/sizeof(struct FsmNode))
+
+HISAX_INITFUNC(void Isdnl1New(void))
+{
+	l1fsm.state_count = L1_STATE_COUNT;
+	l1fsm.event_count = L1_EVENT_COUNT;
+	l1fsm.strEvent = strL1Event;
+	l1fsm.strState = strL1State;
+	FsmNew(&l1fsm, L1FnList, L1_FN_COUNT);
+}
+
+void Isdnl1Free(void)
+{
+	FsmFree(&l1fsm);
+}
+
+static void
+dch_manl1(struct PStack *st, int pr,
+	   void *arg)
+{
+	struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
+	char tmp[32];
+
+	switch (pr) {
+		case PH_ACTIVATE_REQ:
+			if (cs->debug) {
+				sprintf(tmp, "PH_ACTIVATE_REQ %s",
+					strL1State[st->l1.l1m.state]);
+				debugl1(cs, tmp);
+			}
+			if (test_bit(FLG_L1_ACTIVATED, &st->l1.Flags))
+				st->l1.l1man(st, PH_ACTIVATE_CNF, NULL);
+			else {
+				test_and_set_bit(FLG_L1_ACTIVATING, &st->l1.Flags);
+				FsmEvent(&st->l1.l1m, EV_PH_ACTIVATE, arg);
+			}
+			break;
+		case PH_DEACTIVATE_REQ:
+			if (cs->debug) {
+				sprintf(tmp, "PH_DEACTIVATE_REQ %s",
+					strL1State[st->l1.l1m.state]);
+				debugl1(cs, tmp);
+			}
+			break;
+		case PH_TESTLOOP_REQ:
+			if (1 & (int) arg)
+				debugl1(cs, "PH_TEST_LOOP B1");
+			if (2 & (int) arg)
+				debugl1(cs, "PH_TEST_LOOP B2");
+			if (!(3 & (int) arg))
+				debugl1(cs, "PH_TEST_LOOP DISABLED");
+			cs->l1cmd(cs, PH_TESTLOOP_REQ, arg);
+			break;
+		case PH_RESET_IND:
+			FsmEvent(&st->l1.l1m, EV_RESET_IND, arg);
+			break;
+		case PH_DEACT_CNF:
+			FsmEvent(&st->l1.l1m, EV_DEACT_CNF, arg);
+			break;
+		case PH_DEACT_IND:
+			FsmEvent(&st->l1.l1m, EV_DEACT_IND, arg);
+			break;
+		case PH_POWERUP_CNF:
+			FsmEvent(&st->l1.l1m, EV_POWER_UP, arg);
+			break;
+		case PH_RSYNC_IND:
+			FsmEvent(&st->l1.l1m, EV_RSYNC_IND, arg);
+			break;
+		case PH_INFO2_IND:
+			FsmEvent(&st->l1.l1m, EV_INFO2_IND, arg);
+			break;
+		case PH_I4_P8_IND:
+		case PH_I4_P10_IND:
+			FsmEvent(&st->l1.l1m, EV_INFO4_IND, arg);
+			break;
+		default:
+			if (cs->debug) {
+				sprintf(tmp, "dch_manl1 msg %04X unhandled", pr);
+				debugl1(cs, tmp);
+			}
+			break;
+	}
+}
+
+void
+setstack_HiSax(struct PStack *st, struct IsdnCardState *cs)
+{
+	st->l1.hardware = cs;
+	st->protocol = cs->protocol;
+	st->l1.l1m.fsm = &l1fsm;
+	st->l1.l1m.state = ST_L1_F3;
+	st->l1.l1m.debug = cs->debug;
+	st->l1.l1m.userdata = st;
+	st->l1.l1m.userint = 0;
+	st->l1.l1m.printdebug = l1m_debug;
+	FsmInitTimer(&st->l1.l1m, &st->l1.timer);
+	setstack_tei(st);
+	setstack_manager(st);
+	st->l1.stlistp = &(cs->stlist);
+	st->ma.manl1 = dch_manl1;
+	st->l1.Flags = 0;
+	cs->setstack_d(st, cs);
+}
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/isdnl1.h linux/drivers/isdn/hisax/isdnl1.h
--- v2.1.91/linux/drivers/isdn/hisax/isdnl1.h	Sun Jan  4 10:55:08 1998
+++ linux/drivers/isdn/hisax/isdnl1.h	Wed Apr  1 16:20:58 1998
@@ -1,18 +1,23 @@
-/* $Id: isdnl1.h,v 1.4 1997/04/06 22:55:52 keil Exp $
- *
+/* $Id: isdnl1.h,v 2.5 1998/02/02 13:36:58 keil Exp $
+
  * $Log: isdnl1.h,v $
- * Revision 1.4  1997/04/06 22:55:52  keil
- * Using SKB's
+ * Revision 2.5  1998/02/02 13:36:58  keil
+ * more debug
+ *
+ * Revision 2.4  1997/11/08 21:35:49  keil
+ * new l1 init
  *
- * Revision 1.3  1996/12/08 19:41:55  keil
- * L2FRAME_DEBUG
+ * Revision 2.3  1997/10/29 19:07:53  keil
+ * changes for 2.1
  *
- * Revision 1.2  1996/10/27 22:26:27  keil
- * ISAC/HSCX version functions
+ * Revision 2.2  1997/07/30 17:11:09  keil
+ * L1deactivated exported
  *
- * Revision 1.1  1996/10/13 20:03:47  keil
- * Initial revision
+ * Revision 2.1  1997/07/27 21:43:58  keil
+ * new l1 interface
  *
+ * Revision 2.0  1997/06/26 11:02:55  keil
+ * New Layer and card interface
  *
  *
  */
@@ -29,22 +34,25 @@
 #define	L1_DEB_HSCX		0x10
 #define	L1_DEB_HSCX_FIFO	0x20
 #define	L1_DEB_LAPD	        0x40
+#define	L1_DEB_IPAC	        0x80
+#define	L1_DEB_RECEIVE_FRAME    0x100
 
+#define D_RCVBUFREADY	0
+#define D_XMTBUFREADY	1
+#define D_L1STATECHANGE	2
+#define D_CLEARBUSY	3
+#define D_RX_MON0	4
+#define D_RX_MON1	5
+#define D_TX_MON0	6
+#define D_TX_MON1	7
 
-#define ISAC_RCVBUFREADY 0
-#define ISAC_XMTBUFREADY 1
-#define ISAC_PHCHANGE    2
-
-#define HSCX_RCVBUFREADY 0
-#define HSCX_XMTBUFREADY 1
+#define B_RCVBUFREADY 0
+#define B_XMTBUFREADY 1
 
 extern void debugl1(struct IsdnCardState *sp, char *msg);
-extern char *HscxVersion(u_char v);
-extern char *ISACVersion(u_char v);
-extern void hscx_sched_event(struct HscxState *hsp, int event);
-extern void isac_sched_event(struct IsdnCardState *sp, int event);
-extern void isac_new_ph(struct IsdnCardState *sp);
-extern int get_irq(int cardnr, void *routine);
+extern void DChannel_proc_xmt(struct IsdnCardState *cs);
+extern void DChannel_proc_rcv(struct IsdnCardState *cs);
+
 
 #ifdef L2FRAME_DEBUG
 extern void Logl2Frame(struct IsdnCardState *sp, struct sk_buff *skb, char *buf, int dir);
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/isdnl2.c linux/drivers/isdn/hisax/isdnl2.c
--- v2.1.91/linux/drivers/isdn/hisax/isdnl2.c	Thu Feb 12 20:56:06 1998
+++ linux/drivers/isdn/hisax/isdnl2.c	Wed Apr  1 16:20:58 1998
@@ -1,4 +1,4 @@
-/* $Id: isdnl2.c,v 1.10 1997/05/06 09:38:13 keil Exp $
+/* $Id: isdnl2.c,v 2.7 1998/02/12 23:07:47 keil Exp $
 
  * Author       Karsten Keil (keil@temic-ech.spacenet.de)
  *              based on the teles driver from Jan den Ouden
@@ -7,56 +7,51 @@
  *              Fritz Elfert
  *
  * $Log: isdnl2.c,v $
- * Revision 1.10  1997/05/06 09:38:13  keil
- * Bugfixes: - clear ack queue entries after resend
- *           - acknowlege each frame to linklevel
- *           - UA for SABM is Response, not command
- *           - only RR was send as supervisor frame (X.75 hangs after a
- *             sequence error)
+ * Revision 2.7  1998/02/12 23:07:47  keil
+ * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
  *
- * Revision 1.9  1997/04/07 23:02:11  keil
- * missing braces
+ * Revision 2.6  1998/02/02 13:36:15  keil
+ * bugfix X.75 win calculation
  *
- * Revision 1.8  1997/04/06 22:59:59  keil
- * Using SKB's; changing function names; some minor changes
+ * Revision 2.5  1997/11/06 17:09:22  keil
+ * New 2.1 init code
  *
- * Revision 1.7  1997/02/09 00:25:44  keil
- * new interface handling, one interface per card
+ * Revision 2.4  1997/10/29 19:02:01  keil
+ * new LL interface
  *
- * Revision 1.6  1997/01/21 22:23:42  keil
- * D-channel log changed
+ * Revision 2.3  1997/10/01 09:21:39  fritz
+ * Removed old compatibility stuff for 2.0.X kernels.
+ * From now on, this code is for 2.1.X ONLY!
+ * Old stuff is still in the separate branch.
  *
- * Revision 1.5  1997/01/04 13:47:06  keil
- * handling of MDL_REMOVE added (Thanks to Sim Yskes)
+ * Revision 2.2  1997/07/31 11:49:05  keil
+ * Eroor handling for no TEI assign
  *
- * Revision 1.4  1996/12/08 19:51:51  keil
- * many fixes from Pekka Sarnila
+ * Revision 2.1  1997/07/27 21:34:38  keil
+ * cosmetics
  *
- * 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
+ * Revision 2.0  1997/06/26 11:07:29  keil
+ * New q.921 and X.75 Layer2
  *
  *
+ *  Old log removed KKe
  *
  */
 #define __NO_VERSION__
 #include "hisax.h"
 #include "isdnl2.h"
 
-const char *l2_revision = "$Revision: 1.10 $";
+const char *l2_revision = "$Revision: 2.7 $";
 
 static void l2m_debug(struct FsmInst *fi, char *s);
 
+static
 struct Fsm l2fsm =
 {NULL, 0, 0, NULL, NULL};
 
 enum {
 	ST_L2_1,
+	ST_L2_2,
 	ST_L2_3,
 	ST_L2_4,
 	ST_L2_5,
@@ -70,6 +65,7 @@
 static char *strL2State[] =
 {
 	"ST_L2_1",
+	"ST_L2_2",
 	"ST_L2_3",
 	"ST_L2_4",
 	"ST_L2_5",
@@ -81,53 +77,53 @@
 enum {
 	EV_L2_UI,
 	EV_L2_SABMX,
-	EV_L2_UA,
 	EV_L2_DISC,
-	EV_L2_I,
-	EV_L2_RR,
-	EV_L2_REJ,
+	EV_L2_DM,
+	EV_L2_UA,
 	EV_L2_FRMR,
+	EV_L2_SUPER,
+	EV_L2_I,
 	EV_L2_DL_DATA,
+	EV_L2_ACK_PULL,
+	EV_L2_DL_UNIT_DATA,
 	EV_L2_DL_ESTABLISH,
+	EV_L2_DL_RELEASE,
 	EV_L2_MDL_ASSIGN,
 	EV_L2_MDL_REMOVE,
-	EV_L2_DL_UNIT_DATA,
-	EV_L2_DL_RELEASE,
+	EV_L2_MDL_ERROR,
 	EV_L2_MDL_NOTEIPROC,
+	EV_L1_DEACTIVATE,
 	EV_L2_T200,
-	EV_L2_ACK_PULL,
 	EV_L2_T203,
-	EV_L2_RNR,
 };
 
-#define L2_EVENT_COUNT (EV_L2_RNR+1)
+#define L2_EVENT_COUNT (EV_L2_T203+1)
 
 static char *strL2Event[] =
 {
 	"EV_L2_UI",
 	"EV_L2_SABMX",
-	"EV_L2_UA",
 	"EV_L2_DISC",
-	"EV_L2_I",
-	"EV_L2_RR",
-	"EV_L2_REJ",
+	"EV_L2_DM",
+	"EV_L2_UA",
 	"EV_L2_FRMR",
+	"EV_L2_SUPER",
+	"EV_L2_I",
 	"EV_L2_DL_DATA",
+	"EV_L2_ACK_PULL",
+	"EV_L2_DL_UNIT_DATA",
 	"EV_L2_DL_ESTABLISH",
+	"EV_L2_DL_RELEASE",
 	"EV_L2_MDL_ASSIGN",
 	"EV_L2_MDL_REMOVE",
-	"EV_L2_DL_UNIT_DATA",
-	"EV_L2_DL_RELEASE",
+	"EV_L2_MDL_ERROR",
 	"EV_L2_MDL_NOTEIPROC",
+	"EV_L1_DEACTIVATE",
 	"EV_L2_T200",
-	"EV_L2_ACK_PULL",
 	"EV_L2_T203",
-	"EV_L2_RNR",
 };
 
-int errcount = 0;
-
-static int l2addrsize(struct Layer2 *tsp);
+static int l2addrsize(struct Layer2 *l2);
 
 static void
 InitWin(struct Layer2 *l2)
@@ -143,11 +139,9 @@
 {
 	int i, cnt = 0;
 
-
 	for (i = 0; i < MAX_WINDOW; i++) {
 		if (l2->windowar[i]) {
 			cnt++;
-			SET_SKB_FREE(l2->windowar[i]);
 			dev_kfree_skb(l2->windowar[i]);
 			l2->windowar[i] = NULL;
 		}
@@ -156,13 +150,15 @@
 		printk(KERN_WARNING "isdl2 freed %d skbuffs in release\n", cnt);
 }
 
-static int
+inline int
 cansend(struct PStack *st)
 {
 	int p1;
 
-	p1 = (st->l2.va + st->l2.window) % (st->l2.extended ? 128 : 8);
-	return (st->l2.vs != p1);
+	p1 = st->l2.vs - st->l2.va;
+	if (p1 < 0)
+		p1 += (test_bit(FLG_MOD128, &st->l2.flag) ? 128 : 8);
+	return ((p1 < st->l2.window) && !test_bit(FLG_PEER_BUSY, &st->l2.flag));
 }
 
 static void
@@ -171,40 +167,54 @@
 	struct sk_buff *skb;
 
 	while ((skb = skb_dequeue(&st->l2.i_queue))) {
-		SET_SKB_FREE(skb);
 		dev_kfree_skb(skb);
 	}
 }
 
-int
-l2headersize(struct Layer2 *tsp, int ui)
+static void
+discard_ui_queue(struct PStack *st)
+{
+	struct sk_buff *skb;
+
+	while ((skb = skb_dequeue(&st->l2.ui_queue))) {
+		dev_kfree_skb(skb);
+	}
+}
+
+inline void
+clear_exception(struct Layer2 *l2)
+{
+	test_and_clear_bit(FLG_ACK_PEND, &l2->flag);
+	test_and_clear_bit(FLG_REJEXC, &l2->flag);
+	test_and_clear_bit(FLG_OWN_BUSY, &l2->flag);
+	test_and_clear_bit(FLG_PEER_BUSY, &l2->flag);
+}
+
+inline int
+l2headersize(struct Layer2 *l2, int ui)
 {
-	return ((tsp->extended && (!ui) ? 2 : 1) + (tsp->laptype == LAPD ? 2 : 1));
+	return (((test_bit(FLG_MOD128, &l2->flag) && (!ui)) ? 2 : 1) +
+		(test_bit(FLG_LAPD, &l2->flag) ? 2 : 1));
 }
 
-int
-l2addrsize(struct Layer2 *tsp)
+inline int
+l2addrsize(struct Layer2 *l2)
 {
-	return (tsp->laptype == LAPD ? 2 : 1);
+	return (test_bit(FLG_LAPD, &l2->flag) ? 2 : 1);
 }
 
 static int
-sethdraddr(struct Layer2 *tsp,
-	   u_char * header, int rsp)
+sethdraddr(struct Layer2 *l2, u_char * header, int rsp)
 {
 	u_char *ptr = header;
-	int crbit;
+	int crbit = rsp;
 
-	if (tsp->laptype == LAPD) {
-		crbit = rsp;
-		if (!tsp->orig)
-			crbit = !crbit;
-		*ptr++ = (tsp->sap << 2) | (crbit ? 2 : 0);
-		*ptr++ = (tsp->tei << 1) | 1;
+	if (test_bit(FLG_LAPD, &l2->flag)) {
+		*ptr++ = (l2->sap << 2) | (rsp ? 2 : 0);
+		*ptr++ = (l2->tei << 1) | 1;
 		return (2);
 	} else {
-		crbit = rsp;
-		if (tsp->orig)
+		if (test_bit(FLG_ORIG, &l2->flag))
 			crbit = !crbit;
 		if (crbit)
 			*ptr++ = 1;
@@ -218,79 +228,107 @@
 enqueue_ui(struct PStack *st,
 	   struct sk_buff *skb)
 {
-	st->l2.l2l1(st, PH_DATA, skb);
+	st->l2.l2l1(st, PH_DATA_REQ, skb);
 }
 
 static void
 enqueue_super(struct PStack *st,
 	      struct sk_buff *skb)
 {
-	st->l2.l2l1(st, PH_DATA, skb);
+	st->l2.l2l1(st, PH_DATA_REQ, skb);
 }
 
-static int
-legalnr(struct PStack *st, int nr)
+inline int
+IsUI(u_char * data, int ext)
 {
-	struct Layer2 *l2 = &st->l2;
-	int lnr, lvs;
+	return ((data[0] & 0xef) == UI);
+}
 
-	lvs = (l2->vs >= l2->va) ? l2->vs : (l2->vs + l2->extended ? 128 : 8);
-	lnr = (nr >= l2->va) ? nr : (nr + l2->extended ? 128 : 8);
-	return (lnr <= lvs);
+inline int
+IsUA(u_char * data, int ext)
+{
+	return ((data[0] & 0xef) == UA);
 }
 
-static void
-setva(struct PStack *st, int nr)
+inline int
+IsDM(u_char * data, int ext)
 {
-	struct Layer2 *l2 = &st->l2;
+	return ((data[0] & 0xef) == DM);
+}
 
-	if (l2->va != nr) {
-		while (l2->va != nr) {
-			l2->va = (l2->va + 1) % (l2->extended ? 128 : 8);
-			SET_SKB_FREE(l2->windowar[l2->sow]);
-			dev_kfree_skb(l2->windowar[l2->sow]);
-			l2->windowar[l2->sow] = NULL;
-			l2->sow = (l2->sow + 1) % l2->window;
-			if (st->l4.l2writewakeup)
-				st->l4.l2writewakeup(st);
-		}
-	}
+inline int
+IsDISC(u_char * data, int ext)
+{
+	return ((data[0] & 0xef) == DISC);
 }
 
-static void
-l2s1(struct FsmInst *fi, int event, void *arg)
+inline int
+IsRR(u_char * data, int ext)
 {
-	struct PStack *st = fi->userdata;
+	if (ext)
+		return (data[0] == RR);
+	else
+		return ((data[0] & 0xf) == 1);
+}
+
+inline int
+IsSABMX(u_char * data, int ext)
+{
+	u_char d = data[0] & ~0x10;
 
-	st->l2.l2tei(st, MDL_ASSIGN, (void *) st->l2.ces);
-	FsmChangeState(fi, ST_L2_3);
+	return (ext ? d == SABME : d == SABM);
 }
 
-static void
-l2_send_ui(struct FsmInst *fi, int event, void *arg)
+inline int
+IsREJ(u_char * data, int ext)
 {
-	struct PStack *st = fi->userdata;
-	struct sk_buff *skb = arg;
-	u_char header[MAX_HEADER_LEN];
-	int i;
+	return (ext ? data[0] == REJ : (data[0] & 0xf) == REJ);
+}
 
-	i = sethdraddr(&(st->l2), header, CMD);
-	header[i++] = UI;
-	memcpy(skb_push(skb, i), header, i);
-	enqueue_ui(st, skb);
+inline int
+IsFRMR(u_char * data, int ext)
+{
+	return ((data[0] & 0xef) == FRMR);
+}
+
+inline int
+IsRNR(u_char * data, int ext)
+{
+	return (ext ? data[0] == RNR : (data[0] & 0xf) == RNR);
+}
+
+static int
+legalnr(struct PStack *st, int nr)
+{
+	struct Layer2 *l2 = &st->l2;
+	int lnr, lvs;
+
+	lvs = (l2->vs >= l2->va) ? l2->vs :
+	    (l2->vs + (test_bit(FLG_MOD128, &l2->flag) ? 128 : 8));
+	lnr = (nr >= l2->va) ? nr : (test_bit(FLG_MOD128, &l2->flag) ? 128 : 8);
+	return (lnr <= lvs);
 }
 
 static void
-l2_receive_ui(struct FsmInst *fi, int event, void *arg)
+setva(struct PStack *st, int nr)
 {
-	struct PStack *st = fi->userdata;
-	struct sk_buff *skb = arg;
+	struct Layer2 *l2 = &st->l2;
+	int len;
 
-	skb_pull(skb, l2headersize(&st->l2, 1));
-	st->l2.l2l3(st, DL_UNIT_DATA, skb);
+	while (l2->va != nr) {
+		l2->va = (l2->va + 1) % (test_bit(FLG_MOD128, &l2->flag) ? 128 : 8);
+		len = l2->windowar[l2->sow]->len;
+		if (PACKET_NOACK == l2->windowar[l2->sow]->pkt_type)
+			len = -1;
+		dev_kfree_skb(l2->windowar[l2->sow]);
+		l2->windowar[l2->sow] = NULL;
+		l2->sow = (l2->sow + 1) % l2->window;
+		if (st->lli.l2writewakeup && (len >=0))
+			st->lli.l2writewakeup(st, len);
+	}
 }
 
-inline void
+static void
 send_uframe(struct PStack *st, u_char cmd, u_char cr)
 {
 	struct sk_buff *skb;
@@ -307,53 +345,156 @@
 	enqueue_super(st, skb);
 }
 
+inline u_char
+get_PollFlag(struct PStack * st, struct sk_buff * skb)
+{
+	return (skb->data[l2addrsize(&(st->l2))] & 0x10);
+}
+
+inline void
+FreeSkb(struct sk_buff *skb)
+{
+	dev_kfree_skb(skb);
+}
+
+
+inline u_char
+get_PollFlagFree(struct PStack *st, struct sk_buff *skb)
+{
+	u_char PF;
+
+	PF = get_PollFlag(st, skb);
+	FreeSkb(skb);
+	return (PF);
+}
+
 static void
 establishlink(struct FsmInst *fi)
 {
 	struct PStack *st = fi->userdata;
 	u_char cmd;
 
-	FsmChangeState(fi, ST_L2_5);
+	clear_exception(&st->l2);
 	st->l2.rc = 0;
+	cmd = (test_bit(FLG_MOD128, &st->l2.flag) ? SABME : SABM) | 0x10;
+	send_uframe(st, cmd, CMD);
+	FsmDelTimer(&st->l2.t203, 1);
+	FsmRestartTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 1);
+	test_and_set_bit(FLG_T200_RUN, &st->l2.flag);
+	FsmChangeState(fi, ST_L2_5);
+}
 
-	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");
+static void
+l2_mdl_error(struct FsmInst *fi, int event, void *arg)
+{
+	struct sk_buff *skb = arg;
+	struct PStack *st = fi->userdata;
 
+	switch (event) {
+		case EV_L2_UA:
+			if (get_PollFlagFree(st, skb))
+				st->ma.layer(st, MDL_ERROR_IND, (void *) 'C');
+			else
+				st->ma.layer(st, MDL_ERROR_IND, (void *) 'D');
+			break;
+		case EV_L2_DM:
+			if (get_PollFlagFree(st, skb))
+				st->ma.layer(st, MDL_ERROR_IND, (void *) 'B');
+			else {
+				st->ma.layer(st, MDL_ERROR_IND, (void *) 'E');
+				establishlink(fi);
+				test_and_clear_bit(FLG_L3_INIT, &st->l2.flag);
+			}
+			break;
+	}
+}
 
-	cmd = (st->l2.extended ? SABME : SABM) | 0x10; 
-	send_uframe(st, cmd, CMD);
+static void
+l2_dl_establish(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	int state = fi->state;
+
+	FsmChangeState(fi, ST_L2_3); 
+	if (state == ST_L2_1)
+		st->l2.l2tei(st, MDL_ASSIGN_IND, NULL);
 }
 
 static void
-l2_establish(struct FsmInst *fi, int event, void *arg)
+l2_send_ui(struct PStack *st)
 {
-	establishlink(fi);
+	struct sk_buff *skb;
+	u_char header[MAX_HEADER_LEN];
+	int i;
+
+	i = sethdraddr(&(st->l2), header, CMD);
+	header[i++] = UI;
+	while ((skb = skb_dequeue(&st->l2.ui_queue))) {
+		memcpy(skb_push(skb, i), header, i);
+		enqueue_ui(st, skb);
+	}
 }
 
 static void
-l2_send_disconn(struct FsmInst *fi, int event, void *arg)
+l2_put_ui(struct FsmInst *fi, int event, void *arg)
 {
 	struct PStack *st = fi->userdata;
-	struct Channel *chanp = st->l4.userdata;
-
-	FsmChangeState(fi, ST_L2_6);
+	struct sk_buff *skb = arg;
 
-	FsmDelTimer(&st->l2.t203_timer, 1);
-	if (st->l2.t200_running) {
-		FsmDelTimer(&st->l2.t200_timer, 2);
-		st->l2.t200_running = 0;
+	skb_queue_tail(&st->l2.ui_queue, skb);
+	if (fi->state == ST_L2_1) {
+		FsmChangeState(fi, ST_L2_2);
+		st->l2.l2tei(st, MDL_ASSIGN_IND, NULL);
 	}
-	st->l2.rc = 0;
-	if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 2))
-		if (st->l2.l2m.debug)
-			l2m_debug(&st->l2.l2m, "FAT 2");
+	if (fi->state > ST_L2_3)
+		l2_send_ui(st);
+}
 
+static void
+l2_got_ui(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
 
-	if (!((chanp->impair == 2) && (st->l2.laptype == LAPB)))
-		send_uframe(st, DISC | 0x10, CMD);
+	skb_pull(skb, l2headersize(&st->l2, 1));
+	if (skb->len > st->l2.maxlen) { 
+		st->ma.layer(st, MDL_ERROR_IND, (void *) 'O');
+		FreeSkb(skb);
+	} else
+		st->l2.l2l3(st, DL_UNIT_DATA, skb);
+}
+
+static void
+l2_establish(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	if (fi->state != ST_L2_4)
+		discard_i_queue(st);
+	if (fi->state != ST_L2_5)
+		establishlink(fi);
+	test_and_set_bit(FLG_L3_INIT, &st->l2.flag);
+}
+
+static void
+l2_dl_release(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
 
+	if (fi->state == ST_L2_4) {
+		st->l2.l2man(st, DL_RELEASE, NULL);
+		return;
+	} else if (fi->state == ST_L2_5) {
+		test_and_set_bit(FLG_PEND_REL, &st->l2.flag);
+		return;
+	}
 	discard_i_queue(st);
+	FsmChangeState(fi, ST_L2_6);
+	st->l2.rc = 0;
+	send_uframe(st, DISC | 0x10, CMD);
+	FsmDelTimer(&st->l2.t203, 1);
+	FsmRestartTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 2);
+	test_and_set_bit(FLG_T200_RUN, &st->l2.flag);
 }
 
 static void
@@ -361,46 +502,59 @@
 {
 	struct PStack *st = fi->userdata;
 	struct sk_buff *skb = arg;
-	int est = 1, state;
+	int est = 1, state, rsp;
 	u_char PollFlag;
 
 	state = fi->state;
-
-	skb_pull(skb, l2addrsize(&(st->l2)));
-	PollFlag = *skb->data & 0x10;
-	SET_SKB_FREE(skb);
-	dev_kfree_skb(skb);
-
-	if (ST_L2_4 != state)
+	rsp = *skb->data & 0x2;
+	if (test_bit(FLG_ORIG, &st->l2.flag))
+		rsp = !rsp;
+	if (rsp) {
+		st->ma.layer(st, MDL_ERROR_IND, (void *) 'L');
+		FreeSkb(skb);
+		if ((state == ST_L2_7) || (state == ST_L2_8))
+			establishlink(fi);
+		return;
+	}	
+	if (skb->len != (l2addrsize(&st->l2) + 1)) {
+		st->ma.layer(st, MDL_ERROR_IND, (void *) 'N');
+		FreeSkb(skb);
+		if ((state == ST_L2_7) || (state == ST_L2_8))
+			establishlink(fi);
+		return;
+	}
+	PollFlag = get_PollFlagFree(st, skb);
+	if (ST_L2_6 == state) {
+		send_uframe(st, DM | PollFlag, RSP);
+		return;
+	} else
+		send_uframe(st, UA | PollFlag, RSP);
+	if (ST_L2_5 == state)
+		return;
+	if (ST_L2_4 != state) {
+		st->ma.layer(st, MDL_ERROR_IND, (void *) 'F');
 		if (st->l2.vs != st->l2.va) {
 			discard_i_queue(st);
 			est = 1;
 		} else
 			est = 0;
-
+	}
+	clear_exception(&st->l2);
 	st->l2.vs = 0;
 	st->l2.va = 0;
 	st->l2.vr = 0;
 	st->l2.sow = 0;
-	if (ST_L2_7 != state)
-		FsmChangeState(fi, ST_L2_7);
-
-	send_uframe(st, UA | PollFlag, RSP);
-
-	if (st->l2.t200_running) {
-		FsmDelTimer(&st->l2.t200_timer, 15);
-		st->l2.t200_running = 0;
-	}
-	if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 3))
-		if (st->l2.l2m.debug)
-			l2m_debug(&st->l2.l2m, "FAT 3");
+	FsmChangeState(fi, ST_L2_7);
+	if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag))
+		FsmDelTimer(&st->l2.t200, 2);
+	FsmAddTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 3);
 
 	if (est)
 		st->l2.l2man(st, DL_ESTABLISH, NULL);
 
 	if (ST_L2_8 == state)
 		if (skb_queue_len(&st->l2.i_queue) && cansend(st))
-			st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+			st->l2.l2l1(st, PH_PULL_REQ, NULL);
 }
 
 static void
@@ -408,87 +562,152 @@
 {
 	struct PStack *st = fi->userdata;
 	struct sk_buff *skb = arg;
-	struct Channel *chanp = st->l4.userdata;
-	u_char PollFlag;
+	u_char PollFlag, cmd = UA;
+	int state, rel = 1, cst = 1, rsp;
 
-	skb_pull(skb, l2addrsize(&(st->l2)));
-	PollFlag = *skb->data & 0x10;
-	SET_SKB_FREE(skb);
-	dev_kfree_skb(skb);
-
-	FsmChangeState(fi, ST_L2_4);
+	state = fi->state;
+	rsp = *skb->data & 0x2;
+	if (test_bit(FLG_ORIG, &st->l2.flag))
+		rsp = !rsp;
 
-	FsmDelTimer(&st->l2.t203_timer, 3);
-	if (st->l2.t200_running) {
-		FsmDelTimer(&st->l2.t200_timer, 4);
-		st->l2.t200_running = 0;
+	if (rsp) {
+		st->ma.layer(st, MDL_ERROR_IND, (void *) 'L');
+		FreeSkb(skb);
+		if ((state == ST_L2_7) || (state == ST_L2_8))
+			establishlink(fi);
+		return;
+	}	
+	if (skb->len != (l2addrsize(&st->l2) + 1)) {
+		st->ma.layer(st, MDL_ERROR_IND, (void *) 'N');
+		FreeSkb(skb);
+		if ((state == ST_L2_7) || (state == ST_L2_8))
+			establishlink(fi);
+		return;
 	}
-	if (!((chanp->impair == 1) && (st->l2.laptype == LAPB)))
-		send_uframe(st, UA | PollFlag, RSP);
-
-	st->l2.l2man(st, DL_RELEASE, NULL);
+	PollFlag = get_PollFlagFree(st, skb);
+	if ((state == ST_L2_4) || (state == ST_L2_5)) {
+		rel = 0;
+		cst = 0;
+		cmd = DM;
+	} else if (state == ST_L2_6) {
+		rel = 0;
+		cst = 0;
+	}
+	if (cst) {
+		FsmChangeState(fi, ST_L2_4);
+		FsmDelTimer(&st->l2.t203, 3);
+		if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) 
+			FsmDelTimer(&st->l2.t200, 2);
+	}
+	send_uframe(st, cmd | PollFlag, RSP);
+	if (rel)
+		st->l2.l2man(st, DL_RELEASE, NULL);
 }
 
+
 static void
-l2_got_st4_disc(struct FsmInst *fi, int event, void *arg)
+l2_got_ua(struct FsmInst *fi, int event, void *arg)
 {
 	struct PStack *st = fi->userdata;
 	struct sk_buff *skb = arg;
-	struct Channel *chanp = st->l4.userdata;
-	u_char PollFlag;
+	u_char PollFlag, est = 1;
+	int state,rsp;
 
-	skb_pull(skb, l2addrsize(&(st->l2)));
-	PollFlag = *skb->data & 0x10;
-	SET_SKB_FREE(skb);
-	dev_kfree_skb(skb);
+	state = fi->state;
+	rsp = *skb->data & 0x2;
+	if (test_bit(FLG_ORIG, &st->l2.flag))
+		rsp = !rsp;
 
-	if (!((chanp->impair == 1) && (st->l2.laptype == LAPB)))
-		send_uframe(st, DM | (PollFlag ? 0x10 : 0x0), RSP);
+	if (!rsp) {
+		st->ma.layer(st, MDL_ERROR_IND, (void *) 'L');
+		FreeSkb(skb);
+		if ((state == ST_L2_7) || (state == ST_L2_8))
+			establishlink(fi);
+		return;
+	}	
+	if (skb->len != (l2addrsize(&st->l2) + 1)) {
+		st->ma.layer(st, MDL_ERROR_IND, (void *) 'N');
+		FreeSkb(skb);
+		if ((fi->state == ST_L2_7) || (fi->state == ST_L2_8))
+			establishlink(fi);
+		return;
+	}
+	PollFlag = get_PollFlag(st, skb);
+	if (!PollFlag) {
+		l2_mdl_error(fi, event, arg);
+		return;
+	}
+	FreeSkb(skb);
 
+	if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag))
+		FsmDelTimer(&st->l2.t200, 2);
+	if (fi->state == ST_L2_5) {
+		if (test_and_clear_bit(FLG_PEND_REL, &st->l2.flag)) {
+			discard_i_queue(st);
+			st->l2.rc = 0;
+			send_uframe(st, DISC | 0x10, CMD);
+			FsmChangeState(fi, ST_L2_6);
+			FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 4);
+			test_and_set_bit(FLG_T200_RUN, &st->l2.flag);
+		} else {
+			if (!test_and_clear_bit(FLG_L3_INIT, &st->l2.flag))
+				if (st->l2.vs != st->l2.va)
+					discard_i_queue(st);
+				else
+					est = 0;
+			st->l2.vs = 0;
+			st->l2.va = 0;
+			st->l2.vr = 0;
+			st->l2.sow = 0;
+			FsmChangeState(fi, ST_L2_7);
+			FsmAddTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 4);
+			if (est)
+				st->l2.l2man(st, DL_ESTABLISH, NULL);
+		}
+	} else {		/* ST_L2_6 */
+		st->l2.l2man(st, DL_RELEASE, NULL);
+		FsmChangeState(fi, ST_L2_4);
+	}
 }
 
 static void
-l2_got_ua_establish(struct FsmInst *fi, int event, void *arg)
+l2_got_dm(struct FsmInst *fi, int event, void *arg)
 {
 	struct PStack *st = fi->userdata;
 	struct sk_buff *skb = arg;
-	u_char f;
+	u_char PollFlag;
+	int state,rsp;
 
-	skb_pull(skb, l2addrsize(&(st->l2)));
-	f = *skb->data & 0x10;
-	SET_SKB_FREE(skb);
-	dev_kfree_skb(skb);
-	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");
+	state = fi->state;
+	rsp = *skb->data & 0x2;
+	if (test_bit(FLG_ORIG, &st->l2.flag))
+		rsp = !rsp;
 
-		st->l2.l2man(st, DL_ESTABLISH, NULL);
+	if (!rsp) {
+		st->ma.layer(st, MDL_ERROR_IND, (void *) 'L');
+		FreeSkb(skb);
+		if ((state == ST_L2_7) || (state == ST_L2_8))
+			establishlink(fi);
+		return;
+	}	
+	if (skb->len != (l2addrsize(&st->l2) + 1)) {
+		st->ma.layer(st, MDL_ERROR_IND, (void *) 'N');
+		FreeSkb(skb);
+		if ((fi->state == ST_L2_7) || (fi->state == ST_L2_8))
+			establishlink(fi);
+		return;
 	}
-}
-
-static void
-l2_got_ua_disconn(struct FsmInst *fi, int event, void *arg)
-{
-	struct PStack *st = fi->userdata;
-	struct sk_buff *skb = arg;
-	u_char f;
-
-	skb_pull(skb, l2addrsize(&(st->l2)));
-	f = *skb->data & 0x10;
-	SET_SKB_FREE(skb);
-	dev_kfree_skb(skb);
-	if (f) {
-		FsmDelTimer(&st->l2.t200_timer, 6);
-		FsmChangeState(fi, ST_L2_4);
+	PollFlag = get_PollFlagFree(st, skb);
+	if (!PollFlag) {
+		establishlink(fi);
+		test_and_clear_bit(FLG_L3_INIT, &st->l2.flag);
+	} else {
+		if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag))
+			FsmDelTimer(&st->l2.t200, 2);
+	 	if (fi->state == ST_L2_5 && !test_bit(FLG_L3_INIT, &st->l2.flag))
+			discard_i_queue(st);
 		st->l2.l2man(st, DL_RELEASE, NULL);
+		FsmChangeState(fi, ST_L2_4);
 	}
 }
 
@@ -502,7 +721,7 @@
 
 	l2 = &st->l2;
 	i = sethdraddr(l2, tmp, cr);
-	if (l2->extended) {
+	if (test_bit(FLG_MOD128, &l2->flag)) {
 		tmp[i++] = typ;
 		tmp[i++] = (l2->vr << 1) | (pf ? 1 : 0);
 	} else
@@ -516,77 +735,133 @@
 }
 
 inline void
-enquiry_response(struct PStack *st, u_char typ, u_char final)
+enquiry_response(struct PStack *st)
 {
-	enquiry_cr(st, typ, RSP, final);
+	if (test_bit(FLG_OWN_BUSY, &st->l2.flag))
+		enquiry_cr(st, RNR, RSP, 1);
+	else
+		enquiry_cr(st, RR, RSP, 1);
+	test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag);
 }
 
 inline void
-enquiry_command(struct PStack *st, u_char typ, u_char poll)
+transmit_enquiry(struct PStack *st)
 {
-	enquiry_cr(st, typ, CMD, poll);
+	if (test_bit(FLG_OWN_BUSY, &st->l2.flag))
+		enquiry_cr(st, RNR, CMD, 1);
+	else
+		enquiry_cr(st, RR, CMD, 1);
+	test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag);
+	FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 12);
+	test_and_set_bit(FLG_T200_RUN, &st->l2.flag);
 }
 
+
 static void
 nrerrorrecovery(struct FsmInst *fi)
 {
-	/* should log error here */
+	struct PStack *st = fi->userdata;
+
+	st->ma.layer(st, MDL_ERROR_IND, (void *) 'J');
 	establishlink(fi);
 }
 
 static void
-l2_got_st7_RR(struct FsmInst *fi, int event, void *arg)
+invoke_retransmission(struct PStack *st, int nr)
+{
+	struct Layer2 *l2 = &st->l2;
+	int p1;
+	long flags;
+
+	if (l2->vs != nr) {
+		save_flags(flags);
+		cli();
+		while (l2->vs != nr) {
+			l2->vs = l2->vs - 1;
+			if (l2->vs < 0)
+				l2->vs += (test_bit(FLG_MOD128, &l2->flag) ? 128 : 8);
+			p1 = l2->vs - l2->va;
+			if (p1 < 0)
+				p1 += (test_bit(FLG_MOD128, &l2->flag) ? 128 : 8);
+			p1 = (p1 + l2->sow) % l2->window;
+			skb_queue_head(&l2->i_queue, l2->windowar[p1]);
+			l2->windowar[p1] = NULL;
+		}
+		restore_flags(flags);
+		st->l2.l2l1(st, PH_PULL_REQ, NULL);
+	}
+}
+
+static void
+l2_got_st7_super(struct FsmInst *fi, int event, void *arg)
 {
 	struct PStack *st = fi->userdata;
-	struct Channel *chanp = st->l4.userdata;
 	struct sk_buff *skb = arg;
-	int PollFlag, seq, rsp;
-	struct Layer2 *l2;
+	int PollFlag, nr, rsp, typ = RR;
+	struct Layer2 *l2 = &st->l2;
 
-	l2 = &st->l2;
-	if (l2->laptype == LAPD)
-		rsp = *skb->data & 0x2;
-	else
-		rsp = *skb->data == 0x3;
-	if (l2->orig)
+	rsp = *skb->data & 0x2;
+	if (test_bit(FLG_ORIG, &l2->flag))
 		rsp = !rsp;
 
 	skb_pull(skb, l2addrsize(l2));
-	if (l2->extended) {
-		PollFlag = (skb->data[1] & 0x1) == 0x1;
-		seq = skb->data[1] >> 1;
+	if (IsRNR(skb->data, test_bit(FLG_MOD128, &l2->flag))) {
+		test_and_set_bit(FLG_PEER_BUSY, &l2->flag);
+		typ = RNR;
+	} else
+		test_and_clear_bit(FLG_PEER_BUSY, &l2->flag);
+	if (IsREJ(skb->data, test_bit(FLG_MOD128, &l2->flag)))
+		typ = REJ;
+	if (test_bit(FLG_MOD128, &l2->flag)) {
+		if (skb->len == 2) {
+			PollFlag = (skb->data[1] & 0x1) == 0x1;
+			nr = skb->data[1] >> 1;
+		} else {
+			st->ma.layer(st, MDL_ERROR_IND, (void *) 'N');
+			FreeSkb(skb);
+			establishlink(fi);
+			return;
+		}
 	} else {
-		PollFlag = (skb->data[0] & 0x10);
-		seq = (skb->data[0] >> 5) & 0x7;
+		if (skb->len == 1) {
+			PollFlag = (skb->data[0] & 0x10);
+			nr = (skb->data[0] >> 5) & 0x7;
+		} else {
+			st->ma.layer(st, MDL_ERROR_IND, (void *) 'N');
+			FreeSkb(skb);
+			establishlink(fi);
+			return;
+		}
 	}
-	SET_SKB_FREE(skb);
-	dev_kfree_skb(skb);
+	FreeSkb(skb);
 
-	if (!((chanp->impair == 4) && (st->l2.laptype == LAPB)))
-		if ((!rsp) && PollFlag)
-			enquiry_response(st, RR, PollFlag);
-
-	if (legalnr(st, seq)) {
-		if (seq == l2->vs) {
-			setva(st, seq);
-			FsmDelTimer(&l2->t200_timer, 7);
-			l2->t200_running = 0;
-			FsmDelTimer(&l2->t203_timer, 8);
-			if (FsmAddTimer(&l2->t203_timer, l2->t203, EV_L2_T203, NULL, 5))
-				if (l2->l2m.debug)
-					l2m_debug(&st->l2.l2m, "FAT 5");
-
-			if (skb_queue_len(&st->l2.i_queue))
-				st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
-		} else if (l2->va != seq) {
-			setva(st, seq);
-			FsmDelTimer(&st->l2.t200_timer, 9);
-			if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 6))
-				if (st->l2.l2m.debug)
-					l2m_debug(&st->l2.l2m, "FAT 6");
-			if (skb_queue_len(&st->l2.i_queue))
-				st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+	if ((!rsp) && PollFlag)
+		enquiry_response(st);
+	if (rsp && PollFlag)
+		st->ma.layer(st, MDL_ERROR_IND, (void *) 'A');
+	if (legalnr(st, nr)) {
+		if (typ == REJ) {
+			setva(st, nr);
+			invoke_retransmission(st, nr);
+			if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag))
+				FsmDelTimer(&st->l2.t200, 9);
+			if (FsmAddTimer(&st->l2.t203, st->l2.T203,
+					EV_L2_T203, NULL, 6))
+				l2m_debug(&st->l2.l2m, "Restart T203 ST7 REJ");
+		} else if ((nr == l2->vs) && (typ == RR)) {
+			setva(st, nr);
+			if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag))
+				FsmDelTimer(&st->l2.t200, 9);
+			FsmRestartTimer(&st->l2.t203, st->l2.T203,
+					EV_L2_T203, NULL, 7);
+		} else if ((l2->va != nr) || (typ == RNR)) {
+			setva(st, nr);
+			FsmDelTimer(&st->l2.t203, 9);
+			FsmRestartTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 6);
+			test_and_set_bit(FLG_T200_RUN, &st->l2.flag);
 		}
+		if (skb_queue_len(&st->l2.i_queue) && (typ == RR))
+			st->l2.l2l1(st, PH_PULL_REQ, NULL);
 	} else
 		nrerrorrecovery(fi);
 
@@ -602,122 +877,121 @@
 	struct PStack *st = fi->userdata;
 	struct sk_buff *skb = arg;
 
-	skb_queue_tail(&st->l2.i_queue, skb);
-	st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+	if (test_bit(FLG_LAPB, &st->l2.flag))
+		st->l1.bcs->tx_cnt += skb->len + l2headersize(&st->l2, 0);
+	if (!((fi->state == ST_L2_5) && test_bit(FLG_L3_INIT, &st->l2.flag)))
+		skb_queue_tail(&st->l2.i_queue, skb);
+	if (fi->state == ST_L2_7)
+		st->l2.l2l1(st, PH_PULL_REQ, NULL);
 }
 
-static int
-icommandreceived(struct FsmInst *fi, int event, void *arg, int *nr)
+static void
+l2_got_iframe(struct FsmInst *fi, int event, void *arg)
 {
 	struct PStack *st = fi->userdata;
-	struct Channel *chanp = st->l4.userdata;
 	struct sk_buff *skb = arg;
 	struct IsdnCardState *sp = st->l1.hardware;
 	struct Layer2 *l2 = &(st->l2);
-	int i, p, seq, wasok;
+	int PollFlag, ns, nr, i, hs, rsp;
 	char str[64];
 
+	rsp = *skb->data & 0x2;
+	if (test_bit(FLG_ORIG, &l2->flag))
+		rsp = !rsp;
+
+	if (rsp) {
+		st->ma.layer(st, MDL_ERROR_IND, (void *) 'L');
+		FreeSkb(skb);
+		establishlink(fi);
+		return;
+	}	
 	i = l2addrsize(l2);
-	if (l2->extended) {
-		p = (skb->data[i + 1] & 0x1) == 0x1;
-		seq = skb->data[i] >> 1;
-		*nr = (skb->data[i + 1] >> 1) & 0x7f;
+	if (test_bit(FLG_MOD128, &l2->flag)) {
+		if (skb->len <= (i + 1)) {
+			st->ma.layer(st, MDL_ERROR_IND, (void *) 'N');
+			FreeSkb(skb);
+			establishlink(fi);
+			return;
+		} else if ((skb->len - i - 1) > l2->maxlen) { 
+			st->ma.layer(st, MDL_ERROR_IND, (void *) 'O');
+			FreeSkb(skb);
+			establishlink(fi);
+			return;
+		}
+		PollFlag = ((skb->data[i + 1] & 0x1) == 0x1);
+		ns = skb->data[i] >> 1;
+		nr = (skb->data[i + 1] >> 1) & 0x7f;
 	} else {
-		p = (skb->data[i] & 0x10);
-		seq = (skb->data[i] >> 1) & 0x7;
-		*nr = (skb->data[i] >> 5) & 0x7;
-	}
-
-	if (l2->vr == seq) {
-		wasok = !0;
-
-		l2->vr = (l2->vr + 1) % (l2->extended ? 128 : 8);
-		l2->rejexp = 0;
-
-		if (st->l2.laptype == LAPD)
+		if (skb->len <= i) {
+			st->ma.layer(st, MDL_ERROR_IND, (void *) 'N');
+			FreeSkb(skb);
+			establishlink(fi);
+			return;
+		} else if ((skb->len - i) > l2->maxlen) { 
+			st->ma.layer(st, MDL_ERROR_IND, (void *) 'O');
+			FreeSkb(skb);
+			establishlink(fi);
+			return;
+		}
+		PollFlag = (skb->data[i] & 0x10);
+		ns = (skb->data[i] >> 1) & 0x7;
+		nr = (skb->data[i] >> 5) & 0x7;
+	}
+	if (test_bit(FLG_OWN_BUSY, &l2->flag)) {
+		FreeSkb(skb);
+		enquiry_response(st);
+	} else if (l2->vr == ns) {
+		l2->vr = (l2->vr + 1) % (test_bit(FLG_MOD128, &l2->flag) ? 128 : 8);
+		test_and_clear_bit(FLG_REJEXC, &l2->flag);
+		if (test_bit(FLG_LAPD, &l2->flag))
 			if (sp->dlogflag) {
+				hs = l2headersize(l2, 0);
 				LogFrame(st->l1.hardware, skb->data, skb->len);
 				sprintf(str, "Q.931 frame network->user tei %d", st->l2.tei);
-				dlogframe(st->l1.hardware, skb->data + l2->ihsize,
-					  skb->len - l2->ihsize, str);
+				dlogframe(st->l1.hardware, skb->data + hs,
+					  skb->len - hs, str);
 			}
-		if (!((chanp->impair == 3) && (st->l2.laptype == LAPB)))
-			if (p || (!skb_queue_len(&st->l2.i_queue)))
-				enquiry_response(st, RR, p);
+		if (PollFlag)
+			enquiry_response(st);
+		else
+			test_and_set_bit(FLG_ACK_PEND, &l2->flag);
 		skb_pull(skb, l2headersize(l2, 0));
+		st->l2.l2l3(st, DL_DATA, skb);
 	} else {
 		/* n(s)!=v(r) */
-		wasok = 0;
-		SET_SKB_FREE(skb);
-		dev_kfree_skb(skb);
-		if (st->l2.rejexp) {
-			if (p)
-				if (!((chanp->impair == 3) && (st->l2.laptype == LAPB)))
-					enquiry_response(st, RR, p);
+		FreeSkb(skb);
+		if (test_and_set_bit(FLG_REJEXC, &l2->flag)) {
+			if (PollFlag)
+				enquiry_response(st);
 		} else {
-			st->l2.rejexp = !0;
-			enquiry_command(st, REJ, 1);
+			enquiry_cr(st, REJ, RSP, PollFlag);
+			test_and_clear_bit(FLG_ACK_PEND, &l2->flag);
 		}
 	}
-	return wasok;
-}
-
-static void
-l2_got_st7_data(struct FsmInst *fi, int event, void *arg)
-{
-	struct PStack *st = fi->userdata;
-	struct sk_buff *skb = arg;
-	int nr, wasok;
-
-	wasok = icommandreceived(fi, event, arg, &nr);
-
-	if (legalnr(st, nr)) {
-		if (nr == st->l2.vs) {
-			setva(st, nr);
-			FsmDelTimer(&st->l2.t200_timer, 10);
-			st->l2.t200_running = 0;
-			FsmDelTimer(&st->l2.t203_timer, 11);
-			if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 7))
-				if (st->l2.l2m.debug)
-					l2m_debug(&st->l2.l2m, "FAT 5");
-
-			if (skb_queue_len(&st->l2.i_queue))
-				st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
-		} else if (nr != st->l2.va) {
-			setva(st, nr);
-			FsmDelTimer(&st->l2.t200_timer, 12);
-			if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 8))
-				if (st->l2.l2m.debug)
-					l2m_debug(&st->l2.l2m, "FAT 6");
-
-			if (skb_queue_len(&st->l2.i_queue))
-				st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
-		}
-	} else
-		nrerrorrecovery(fi);
-
-	if (wasok)
-		st->l2.l2l3(st, DL_DATA, skb);
-}
-
-static void
-l2_got_st8_data(struct FsmInst *fi, int event, void *arg)
-{
-	struct PStack *st = fi->userdata;
-	struct sk_buff *skb = arg;
-	int nr, wasok;
-
-	wasok = icommandreceived(fi, event, arg, &nr);
 
 	if (legalnr(st, nr)) {
 		setva(st, nr);
-		if (skb_queue_len(&st->l2.i_queue))
-			st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
-	} else
+		if (!test_bit(FLG_PEER_BUSY, &st->l2.flag) && (fi->state == ST_L2_7)) {
+			if (nr == st->l2.vs) {
+				if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag))
+					FsmDelTimer(&st->l2.t200, 10);
+				FsmRestartTimer(&st->l2.t203, st->l2.T203,
+						EV_L2_T203, NULL, 7);
+			} else if (nr != st->l2.va) {
+				FsmRestartTimer(&st->l2.t200, st->l2.T200, EV_L2_T200,
+						NULL, 8);
+				test_and_set_bit(FLG_T200_RUN, &st->l2.flag);
+			}
+		}
+	} else {
 		nrerrorrecovery(fi);
+		return;
+	}
 
-	if (wasok)
-		st->l2.l2l3(st, DL_DATA, skb);
+	if (skb_queue_len(&st->l2.i_queue) && (fi->state == ST_L2_7))
+		st->l2.l2l1(st, PH_PULL_REQ, NULL);
+	if (test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag))
+		enquiry_cr(st, RR, RSP, 0);
 }
 
 static void
@@ -726,69 +1000,14 @@
 	struct PStack *st = fi->userdata;
 
 	st->l2.tei = (int) arg;
-	establishlink(fi);
-}
-
-static void
-invoke_retransmission(struct PStack *st, int nr)
-{
-	struct Layer2 *l2 = &st->l2;
-	int p1;
-
-	if (l2->vs != nr) {
-		while (l2->vs != nr) {
-
-			l2->vs = l2->vs - 1;
-			if (l2->vs < 0)
-				l2->vs += l2->extended ? 128 : 8;
-
-			p1 = l2->vs - l2->va;
-			if (p1 < 0)
-				p1 += l2->extended ? 128 : 8;
-			p1 = (p1 + l2->sow) % l2->window;
-
-			skb_queue_head(&l2->i_queue, l2->windowar[p1]);
-			l2->windowar[p1] = NULL;
-		}
-		st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
-	}
-}
-
-static void
-l2_got_st7_rej(struct FsmInst *fi, int event, void *arg)
-{
-	struct PStack *st = fi->userdata;
-	struct sk_buff *skb = arg;
-	int PollFlag, seq, rsp;
-	struct Layer2 *l2;
-
-	l2 = &st->l2;
-	if (l2->laptype == LAPD)
-		rsp = *skb->data & 0x2;
-	else
-		rsp = *skb->data == 0x3;
-	if (l2->orig)
-		rsp = !rsp;
-
-	skb_pull(skb, l2addrsize(l2));
-	if (l2->extended) {
-		PollFlag = (skb->data[1] & 0x1) == 0x1;
-		seq = skb->data[1] >> 1;
-	} else {
-		PollFlag = (skb->data[0] & 0x10);
-		seq = (skb->data[0] >> 5) & 0x7;
-	}
-	SET_SKB_FREE(skb);
-	dev_kfree_skb(skb);
-
-	if ((!rsp) && PollFlag)
-		enquiry_response(st, RR, PollFlag);
 
-	if (!legalnr(st, seq))
-		return;
-
-	setva(st, seq);
-	invoke_retransmission(st, seq);
+	if (fi->state == ST_L2_3) {
+		establishlink(fi);
+		test_and_set_bit(FLG_L3_INIT, &st->l2.flag);
+	} else
+		FsmChangeState(fi, ST_L2_4);
+	if (skb_queue_len(&st->l2.ui_queue))
+		l2_send_ui(st);
 }
 
 static void
@@ -801,21 +1020,21 @@
 l2_st5_tout_200(struct FsmInst *fi, int event, void *arg)
 {
 	struct PStack *st = fi->userdata;
-	u_char cmd;
 
-	if (st->l2.rc == st->l2.n200) {
+	if (test_bit(FLG_LAPD, &st->l2.flag) &&
+		test_bit(FLG_DCHAN_BUSY, &st->l2.flag)) {
+		FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9);
+	} else if (st->l2.rc == st->l2.N200) {
 		FsmChangeState(fi, ST_L2_4);
-		st->l2.l2tei(st, MDL_VERIFY, (void *) st->l2.tei);
+		test_and_clear_bit(FLG_T200_RUN, &st->l2.flag);
+		discard_i_queue(st);
+		st->ma.layer(st, MDL_ERROR_IND, (void *) 'G');
 		st->l2.l2man(st, DL_RELEASE, NULL);
 	} else {
 		st->l2.rc++;
-
-		if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 9))
-			if (st->l2.l2m.debug)
-				l2m_debug(&st->l2.l2m, "FAT 7");
-
-		cmd = (st->l2.extended ? SABME : SABM) | 0x10; 
-		send_uframe(st, cmd, CMD);
+		FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9);
+		send_uframe(st, (test_bit(FLG_MOD128, &st->l2.flag) ? SABME : SABM)
+			    | 0x10, CMD);
 	}
 }
 
@@ -823,30 +1042,65 @@
 l2_st6_tout_200(struct FsmInst *fi, int event, void *arg)
 {
 	struct PStack *st = fi->userdata;
-	struct Channel *chanp = st->l4.userdata;
 
-	if (st->l2.rc == st->l2.n200) {
+	if (test_bit(FLG_LAPD, &st->l2.flag) &&
+		test_bit(FLG_DCHAN_BUSY, &st->l2.flag)) {
+		FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9);
+	} else if (st->l2.rc == st->l2.N200) {
 		FsmChangeState(fi, ST_L2_4);
+		st->ma.layer(st, MDL_ERROR_IND, (void *) 'H');
 		st->l2.l2man(st, DL_RELEASE, NULL);
 	} else {
 		st->l2.rc++;
+		FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200,
+			    NULL, 9);
+		send_uframe(st, DISC | 0x10, CMD);
+	}
+}
 
-		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");
+static void
+l2_st78_tout_200(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
 
+	if (test_bit(FLG_LAPD, &st->l2.flag) &&
+		test_bit(FLG_DCHAN_BUSY, &st->l2.flag)) {
+		FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9);
+		return;
+	}
+	test_and_clear_bit(FLG_T200_RUN, &st->l2.flag);
+	if (fi->state == ST_L2_7) {
+		st->l2.rc = 0;
+		FsmChangeState(fi, ST_L2_8);
+	}
+	if (st->l2.rc == st->l2.N200) {
+		establishlink(fi);
+	} else {
+		transmit_enquiry(st);
+		st->l2.rc++;
+	}
+}
 
-		if (!((chanp->impair == 2) && (st->l2.laptype == LAPB)))
-			send_uframe(st, DISC | 0x10, CMD);
+static void
+l2_st7_tout_203(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
 
+	if (test_bit(FLG_LAPD, &st->l2.flag) &&
+		test_bit(FLG_DCHAN_BUSY, &st->l2.flag)) {
+		FsmAddTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 9);
+		return;
 	}
+	FsmChangeState(fi, ST_L2_8);
+	transmit_enquiry(st);
+	st->l2.rc = 0;
 }
 
 static void
 l2_pull_iqueue(struct FsmInst *fi, int event, void *arg)
 {
 	struct PStack *st = fi->userdata;
-	struct sk_buff *skb;
+	struct sk_buff *skb, *oskb;
 	struct Layer2 *l2 = &st->l2;
 	u_char header[MAX_HEADER_LEN];
 	int p1, i;
@@ -860,19 +1114,18 @@
 
 	p1 = l2->vs - l2->va;
 	if (p1 < 0)
-		p1 += l2->extended ? 128 : 8;
+		p1 += test_bit(FLG_MOD128, &l2->flag) ? 128 : 8;
 	p1 = (p1 + l2->sow) % l2->window;
 	if (l2->windowar[p1]) {
 		printk(KERN_WARNING "isdnl2 try overwrite ack queue entry %d\n",
 		       p1);
-		SET_SKB_FREE(l2->windowar[p1]);
 		dev_kfree_skb(l2->windowar[p1]);
 	}
 	l2->windowar[p1] = skb_clone(skb, GFP_ATOMIC);
 
 	i = sethdraddr(&st->l2, header, CMD);
 
-	if (l2->extended) {
+	if (test_bit(FLG_MOD128, &l2->flag)) {
 		header[i++] = l2->vs << 1;
 		header[i++] = l2->vr << 1;
 		l2->vs = (l2->vs + 1) % 128;
@@ -880,88 +1133,86 @@
 		header[i++] = (l2->vr << 5) | (l2->vs << 1);
 		l2->vs = (l2->vs + 1) % 8;
 	}
-
-	memcpy(skb_push(skb, i), header, i);
-	st->l2.l2l1(st, PH_DATA_PULLED, skb);
-	if (!st->l2.t200_running) {
-		FsmDelTimer(&st->l2.t203_timer, 13);
-		if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 11))
-			if (st->l2.l2m.debug)
-				l2m_debug(&st->l2.l2m, "FAT 9");
-
-		st->l2.t200_running = !0;
+	p1 = skb->data - skb->head;
+	if (p1 >= i)
+		memcpy(skb_push(skb, i), header, i);
+	else {
+		printk(KERN_WARNING
+		"isdl2 pull_iqueue skb header(%d/%d) too short\n", i, p1);
+		oskb = skb;
+		skb = alloc_skb(oskb->len + i, GFP_ATOMIC);
+		memcpy(skb_put(skb, i), header, i);
+		memcpy(skb_put(skb, oskb->len), oskb->data, oskb->len);
+		FreeSkb(oskb);
+	}
+	st->l2.l2l1(st, PH_PULL_IND, skb);
+	test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag);
+	if (!test_and_set_bit(FLG_T200_RUN, &st->l2.flag)) {
+		FsmDelTimer(&st->l2.t203, 13);
+		FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 11);
 	}
 	if (skb_queue_len(&l2->i_queue) && cansend(st))
-		st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+		st->l2.l2l1(st, PH_PULL_REQ, NULL);
 }
 
 static void
-transmit_enquiry(struct PStack *st)
-{
-
-	enquiry_command(st, RR, 1);
-	if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 12))
-		if (st->l2.l2m.debug)
-			l2m_debug(&st->l2.l2m, "FAT 10");
-
-	st->l2.t200_running = !0;
-}
-
-static void
-l2_st7_tout_200(struct FsmInst *fi, int event, void *arg)
-{
-	struct PStack *st = fi->userdata;
-
-	st->l2.t200_running = 0;
-
-	st->l2.rc = 1;
-	FsmChangeState(fi, ST_L2_8);
-	transmit_enquiry(st);
-}
-
-static void
-l2_got_st8_rr_rej(struct FsmInst *fi, int event, void *arg)
+l2_got_st8_super(struct FsmInst *fi, int event, void *arg)
 {
 	struct PStack *st = fi->userdata;
 	struct sk_buff *skb = arg;
-	int PollFlag, seq, rsp;
-	struct Layer2 *l2;
+	int PollFlag, nr, rsp, rnr = 0;
+	struct Layer2 *l2 = &st->l2;
 
-	l2 = &st->l2;
-	if (l2->laptype == LAPD)
-		rsp = *skb->data & 0x2;
-	else
-		rsp = *skb->data == 0x3;
-	if (l2->orig)
+	rsp = *skb->data & 0x2;
+	if (test_bit(FLG_ORIG, &l2->flag))
 		rsp = !rsp;
-
 	skb_pull(skb, l2addrsize(l2));
-	if (l2->extended) {
-		PollFlag = (skb->data[1] & 0x1) == 0x1;
-		seq = skb->data[1] >> 1;
+
+	if (IsRNR(skb->data, test_bit(FLG_MOD128, &l2->flag))) {
+		test_and_set_bit(FLG_PEER_BUSY, &l2->flag);
+		rnr = 1;
+	} else
+		test_and_clear_bit(FLG_PEER_BUSY, &l2->flag);
+	if (test_bit(FLG_MOD128, &l2->flag)) {
+		if (skb->len == 2) {
+			PollFlag = (skb->data[1] & 0x1) == 0x1;
+			nr = skb->data[1] >> 1;
+		} else {
+			st->ma.layer(st, MDL_ERROR_IND, (void *) 'N');
+			FreeSkb(skb);
+			establishlink(fi);
+			return;
+		}
 	} else {
-		PollFlag = (skb->data[0] & 0x10);
-		seq = (skb->data[0] >> 5) & 0x7;
+		if (skb->len == 1) {
+			PollFlag = (skb->data[0] & 0x10);
+			nr = (skb->data[0] >> 5) & 0x7;
+		} else {
+			st->ma.layer(st, MDL_ERROR_IND, (void *) 'N');
+			FreeSkb(skb);
+			establishlink(fi);
+			return;
+		}
 	}
-	SET_SKB_FREE(skb);
-	dev_kfree_skb(skb);
+	FreeSkb(skb);
 
 	if (rsp && PollFlag) {
-		if (legalnr(st, seq)) {
+		if (legalnr(st, nr)) {
+			setva(st, nr);
+			if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag))
+				FsmDelTimer(&st->l2.t200, 7);
+			FsmDelTimer(&l2->t203, 8);
+			if (rnr) {
+				FsmRestartTimer(&l2->t200, l2->T200,
+						EV_L2_T200, NULL, 14);
+				test_and_set_bit(FLG_T200_RUN, &st->l2.flag);
+			} else
+				FsmAddTimer(&l2->t203, l2->T203,
+					    EV_L2_T203, NULL, 5);
+			invoke_retransmission(st, nr);
 			FsmChangeState(fi, ST_L2_7);
-			setva(st, seq);
-			if (st->l2.t200_running) {
-				FsmDelTimer(&st->l2.t200_timer, 14);
-				st->l2.t200_running = 0;
-			}
-			if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 13))
-				if (st->l2.l2m.debug)
-					l2m_debug(&st->l2.l2m, "FAT 11");
-
-			invoke_retransmission(st, seq);
-
 			if (skb_queue_len(&l2->i_queue) && cansend(st))
-				st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+				st->l2.l2l1(st, PH_PULL_REQ, NULL);
 			else if (fi->userint & LC_FLUSH_WAIT) {
 				fi->userint &= ~LC_FLUSH_WAIT;
 				st->l2.l2man(st, DL_FLUSH, NULL);
@@ -969,57 +1220,46 @@
 		}
 	} else {
 		if (!rsp && PollFlag)
-			enquiry_response(st, RR, PollFlag);
-		if (legalnr(st, seq)) {
-			setva(st, seq);
+			enquiry_response(st);
+		if (legalnr(st, nr)) {
+			setva(st, nr);
 		}
 	}
 }
 
 static void
-l2_st7_tout_203(struct FsmInst *fi, int event, void *arg)
-{
-	struct PStack *st = fi->userdata;
-
-	st->l2.rc = 0;
-	FsmChangeState(fi, ST_L2_8);
-	transmit_enquiry(st);
-}
-
-static void
-l2_st8_tout_200(struct FsmInst *fi, int event, void *arg)
-{
-	struct PStack *st = fi->userdata;
-
-	if (st->l2.rc == st->l2.n200) {
-		establishlink(fi);
-	} else {
-		st->l2.rc++;
-		transmit_enquiry(st);
-	}
-}
-
-static void
 l2_got_FRMR(struct FsmInst *fi, int event, void *arg)
 {
 	struct PStack *st = fi->userdata;
 	struct sk_buff *skb = arg;
 	char tmp[64];
 
-	skb_pull(skb, l2addrsize(&st->l2));
-	if (st->l2.l2m.debug) {
-		if (st->l2.extended)
+	skb_pull(skb, l2addrsize(&st->l2) + 1);
+	if (test_bit(FLG_MOD128, &st->l2.flag)) {
+		if (skb->len < 5)
+			st->ma.layer(st, MDL_ERROR_IND, (void *) 'N');
+		else {
 			sprintf(tmp, "FRMR information %2x %2x %2x %2x %2x",
 				skb->data[0], skb->data[1], skb->data[2],
 				skb->data[3], skb->data[4]);
-		else
+			l2m_debug(&st->l2.l2m, tmp);
+		}
+	} else {
+		if (skb->len < 3)
+			st->ma.layer(st, MDL_ERROR_IND, (void *) 'N');
+		else {
 			sprintf(tmp, "FRMR information %2x %2x %2x",
 				skb->data[0], skb->data[1], skb->data[2]);
-
-		l2m_debug(&st->l2.l2m, tmp);
+			l2m_debug(&st->l2.l2m, tmp);
+		}
 	}
-	SET_SKB_FREE(skb);
-	dev_kfree_skb(skb);
+	if (!(skb->data[0] & 1) || ((skb->data[0] & 3) == 1) ||		/* I or S */
+	    (IsUA(skb->data, 0) && (fi->state == ST_L2_7))) {
+		st->ma.layer(st, MDL_ERROR_IND, (void *) 'K');
+		establishlink(fi);
+		test_and_clear_bit(FLG_L3_INIT, &st->l2.flag);
+	}
+	FreeSkb(skb);
 }
 
 static void
@@ -1027,135 +1267,126 @@
 {
 	struct PStack *st = fi->userdata;
 
-/*TODO
-   if( DL_RELEASE.req outstanding ) {
-   ... issue DL_RELEASE.confirm
-   } else {
-   if( fi->state != ST_L2_4 ) {
-   ... issue DL_RELEASE.indication
-   }
-   }
-   TODO */
-	discard_i_queue(st);	/* There is no UI queue in layer 2 */
-	st->l2.tei = 255;
-	if (st->l2.t200_running) {
-		FsmDelTimer(&st->l2.t200_timer, 18);
-		st->l2.t200_running = 0;
-	}
-	FsmDelTimer(&st->l2.t203_timer, 19);
-	st->l2.l2man(st, DL_RELEASE, NULL);	/* TEMP */
+	discard_i_queue(st);
+	discard_ui_queue(st);
+	st->l2.tei = -1;
+	if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag))
+		FsmDelTimer(&st->l2.t200, 18);
+	FsmDelTimer(&st->l2.t203, 19);
+	if (fi->state != ST_L2_4)
+		st->l2.l2man(st, DL_RELEASE, NULL);
 	FsmChangeState(fi, ST_L2_1);
 }
 
-inline int
-IsUI(u_char * data, int ext)
-{
-	return ((data[0] & 0xef) == UI);
-}
-
-inline int
-IsUA(u_char * data, int ext)
-{
-	return ((data[0] & 0xef) == UA);
-}
-
-inline int
-IsDISC(u_char * data, int ext)
-{
-	return ((data[0] & 0xef) == DISC);
-}
-
-inline int
-IsRR(u_char * data, int ext)
-{
-	if (ext)
-		return (data[0] == RR);
-	else
-		return ((data[0] & 0xf) == 1);
-}
-
-inline int
-IsI(u_char * data, int ext)
-{
-	return ((data[0] & 0x1) == 0x0);
-}
-
-inline int
-IsSABMX(u_char * data, int ext)
+static void
+l2_persistant_da(struct FsmInst *fi, int event, void *arg)
 {
-	u_char d = data[0] & ~0x10;
+	struct PStack *st = fi->userdata;
 	
-	return (ext ? d == SABME : d == SABM);
-}
-
-inline int
-IsREJ(u_char * data, int ext)
-{
-	return (ext ? data[0] == REJ : (data[0] & 0xf) == 0x9);
-}
-
-inline int
-IsFRMR(u_char * data, int ext)
-{
-	return ((data[0] & 0xef) == FRMR);
-}
-
-inline int
-IsRNR(u_char * data, int ext)
-{
-	if (ext)
-		return (data[0] == RNR);
-	else
-		return ((data[0] & 0xf) == 5);
+	discard_i_queue(st);
+	discard_ui_queue(st);
+	if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag))
+		FsmDelTimer(&st->l2.t200, 18);
+	FsmDelTimer(&st->l2.t203, 19);
+	test_and_clear_bit(FLG_PEND_REL, &st->l2.flag);
+	clear_exception(&st->l2);
+	switch (fi->state) {
+		case ST_L2_3:
+			st->l2.l2man(st, DL_RELEASE, NULL);
+		case ST_L2_2:
+			FsmChangeState(fi, ST_L2_1);
+			break;
+		case ST_L2_5:
+		case ST_L2_6:
+		case ST_L2_7:
+		case ST_L2_8:
+			st->l2.l2man(st, DL_RELEASE, NULL);
+			FsmChangeState(fi, ST_L2_4);
+			break;
+	}
 }
 
-static struct FsmNode L2FnList[] =
+static struct FsmNode L2FnList[] HISAX_INITDATA =
 {
-	{ST_L2_1, EV_L2_DL_ESTABLISH, l2s1},
 	{ST_L2_1, EV_L2_MDL_NOTEIPROC, l2_no_tei},
-	{ST_L2_3, EV_L2_MDL_ASSIGN, l2_got_tei},
-	{ST_L2_4, EV_L2_DL_UNIT_DATA, l2_send_ui},
+	{ST_L2_1, EV_L2_DL_ESTABLISH, l2_dl_establish},
+	{ST_L2_2, EV_L2_DL_ESTABLISH, l2_dl_establish},
 	{ST_L2_4, EV_L2_DL_ESTABLISH, l2_establish},
-	{ST_L2_7, EV_L2_DL_UNIT_DATA, l2_send_ui},
+	{ST_L2_5, EV_L2_DL_ESTABLISH, l2_establish},
+	{ST_L2_7, EV_L2_DL_ESTABLISH, l2_establish},
+	{ST_L2_8, EV_L2_DL_ESTABLISH, l2_establish},
+	{ST_L2_4, EV_L2_DL_RELEASE, l2_dl_release},
+	{ST_L2_5, EV_L2_DL_RELEASE, l2_dl_release},
+	{ST_L2_7, EV_L2_DL_RELEASE, l2_dl_release},
+	{ST_L2_8, EV_L2_DL_RELEASE, l2_dl_release},
+	{ST_L2_5, EV_L2_DL_DATA, l2_feed_iqueue},
 	{ST_L2_7, EV_L2_DL_DATA, l2_feed_iqueue},
-	{ST_L2_7, EV_L2_DL_RELEASE, l2_send_disconn},
-	{ST_L2_7, EV_L2_ACK_PULL, l2_pull_iqueue},
 	{ST_L2_8, EV_L2_DL_DATA, l2_feed_iqueue},
-	{ST_L2_8, EV_L2_DL_RELEASE, l2_send_disconn},
-
-	{ST_L2_1, EV_L2_UI, l2_receive_ui},
-	{ST_L2_4, EV_L2_UI, l2_receive_ui},
+	{ST_L2_1, EV_L2_DL_UNIT_DATA, l2_put_ui},
+	{ST_L2_2, EV_L2_DL_UNIT_DATA, l2_put_ui},
+	{ST_L2_3, EV_L2_DL_UNIT_DATA, l2_put_ui},
+	{ST_L2_4, EV_L2_DL_UNIT_DATA, l2_put_ui},
+	{ST_L2_5, EV_L2_DL_UNIT_DATA, l2_put_ui},
+	{ST_L2_6, EV_L2_DL_UNIT_DATA, l2_put_ui},
+	{ST_L2_7, EV_L2_DL_UNIT_DATA, l2_put_ui},
+	{ST_L2_8, EV_L2_DL_UNIT_DATA, l2_put_ui},
+	{ST_L2_1, EV_L2_MDL_ASSIGN, l2_got_tei},
+	{ST_L2_2, EV_L2_MDL_ASSIGN, l2_got_tei},
+	{ST_L2_3, EV_L2_MDL_ASSIGN, l2_got_tei},
+	{ST_L2_2, EV_L2_MDL_ERROR, l2_tei_remove},
+	{ST_L2_3, EV_L2_MDL_ERROR, l2_tei_remove},
+	{ST_L2_4, EV_L2_MDL_REMOVE, l2_tei_remove},
+	{ST_L2_5, EV_L2_MDL_REMOVE, l2_tei_remove},
+	{ST_L2_6, EV_L2_MDL_REMOVE, l2_tei_remove},
+	{ST_L2_7, EV_L2_MDL_REMOVE, l2_tei_remove},
+	{ST_L2_8, EV_L2_MDL_REMOVE, l2_tei_remove},
 	{ST_L2_4, EV_L2_SABMX, l2_got_SABMX},
-	{ST_L2_4, EV_L2_DISC, l2_got_st4_disc},
-	{ST_L2_5, EV_L2_UA, l2_got_ua_establish},
-	{ST_L2_6, EV_L2_UA, l2_got_ua_disconn},
-	{ST_L2_7, EV_L2_UI, l2_receive_ui},
-	{ST_L2_7, EV_L2_DISC, l2_got_disconn},
-	{ST_L2_7, EV_L2_I, l2_got_st7_data},
-	{ST_L2_7, EV_L2_RR, l2_got_st7_RR},
-	{ST_L2_7, EV_L2_REJ, l2_got_st7_rej},
+	{ST_L2_5, EV_L2_SABMX, l2_got_SABMX},
+	{ST_L2_6, EV_L2_SABMX, l2_got_SABMX},
 	{ST_L2_7, EV_L2_SABMX, l2_got_SABMX},
-	{ST_L2_7, EV_L2_FRMR, l2_got_FRMR},
-	{ST_L2_8, EV_L2_RR, l2_got_st8_rr_rej},
-	{ST_L2_8, EV_L2_REJ, l2_got_st8_rr_rej},
 	{ST_L2_8, EV_L2_SABMX, l2_got_SABMX},
+	{ST_L2_4, EV_L2_DISC, l2_got_disconn},
+	{ST_L2_5, EV_L2_DISC, l2_got_disconn},
+	{ST_L2_6, EV_L2_DISC, l2_got_disconn},
+	{ST_L2_7, EV_L2_DISC, l2_got_disconn},
 	{ST_L2_8, EV_L2_DISC, l2_got_disconn},
+	{ST_L2_4, EV_L2_UA, l2_mdl_error},
+	{ST_L2_5, EV_L2_UA, l2_got_ua},
+	{ST_L2_6, EV_L2_UA, l2_got_ua},
+	{ST_L2_7, EV_L2_UA, l2_mdl_error},
+	{ST_L2_8, EV_L2_UA, l2_mdl_error},
+	{ST_L2_4, EV_L2_DM, l2_got_dm},
+	{ST_L2_5, EV_L2_DM, l2_got_dm},
+	{ST_L2_6, EV_L2_DM, l2_got_dm},
+	{ST_L2_7, EV_L2_DM, l2_mdl_error},
+	{ST_L2_8, EV_L2_DM, l2_mdl_error},
+	{ST_L2_1, EV_L2_UI, l2_got_ui},
+	{ST_L2_2, EV_L2_UI, l2_got_ui},
+	{ST_L2_3, EV_L2_UI, l2_got_ui},
+	{ST_L2_4, EV_L2_UI, l2_got_ui},
+	{ST_L2_5, EV_L2_UI, l2_got_ui},
+	{ST_L2_6, EV_L2_UI, l2_got_ui},
+	{ST_L2_7, EV_L2_UI, l2_got_ui},
+	{ST_L2_8, EV_L2_UI, l2_got_ui},
+	{ST_L2_7, EV_L2_FRMR, l2_got_FRMR},
 	{ST_L2_8, EV_L2_FRMR, l2_got_FRMR},
-	{ST_L2_8, EV_L2_I, l2_got_st8_data},
-
+	{ST_L2_7, EV_L2_SUPER, l2_got_st7_super},
+	{ST_L2_8, EV_L2_SUPER, l2_got_st8_super},
+	{ST_L2_7, EV_L2_I, l2_got_iframe},
+	{ST_L2_8, EV_L2_I, l2_got_iframe},
 	{ST_L2_5, EV_L2_T200, l2_st5_tout_200},
 	{ST_L2_6, EV_L2_T200, l2_st6_tout_200},
-	{ST_L2_7, EV_L2_T200, l2_st7_tout_200},
+	{ST_L2_7, EV_L2_T200, l2_st78_tout_200},
+	{ST_L2_8, EV_L2_T200, l2_st78_tout_200},
 	{ST_L2_7, EV_L2_T203, l2_st7_tout_203},
-	{ST_L2_8, EV_L2_T200, l2_st8_tout_200},
-
-	{ST_L2_1, EV_L2_MDL_REMOVE, l2_tei_remove},
-	{ST_L2_3, EV_L2_MDL_REMOVE, l2_tei_remove},
-	{ST_L2_4, EV_L2_MDL_REMOVE, l2_tei_remove},
-	{ST_L2_5, EV_L2_MDL_REMOVE, l2_tei_remove},
-	{ST_L2_6, EV_L2_MDL_REMOVE, l2_tei_remove},
-	{ST_L2_7, EV_L2_MDL_REMOVE, l2_tei_remove},
-	{ST_L2_8, EV_L2_MDL_REMOVE, l2_tei_remove},
+	{ST_L2_7, EV_L2_ACK_PULL, l2_pull_iqueue},
+	{ST_L2_2, EV_L1_DEACTIVATE, l2_persistant_da},
+	{ST_L2_3, EV_L1_DEACTIVATE, l2_persistant_da},
+	{ST_L2_4, EV_L1_DEACTIVATE, l2_persistant_da},
+	{ST_L2_5, EV_L1_DEACTIVATE, l2_persistant_da},
+	{ST_L2_6, EV_L1_DEACTIVATE, l2_persistant_da},
+	{ST_L2_7, EV_L1_DEACTIVATE, l2_persistant_da},
+	{ST_L2_8, EV_L1_DEACTIVATE, l2_persistant_da},
 };
 
 #define L2_FN_COUNT (sizeof(L2FnList)/sizeof(struct FsmNode))
@@ -1165,40 +1396,53 @@
 {
 	struct sk_buff *skb = arg;
 	u_char *datap;
-	int ret = !0;
+	int ret = 1, len;
 
 	switch (pr) {
-		case (PH_DATA):
+		case (PH_DATA_IND):
 			datap = skb->data;
-			datap += l2addrsize(&st->l2);
-
-			if (IsI(datap, st->l2.extended))
+			len = l2addrsize(&st->l2);
+			if (skb->len > len)
+				datap += len;
+			else {
+				st->ma.layer(st, MDL_ERROR_IND, (void *) 'N');
+				FreeSkb(skb);
+				return;
+			}
+			if (!(*datap & 1))	/* I-Frame */
 				ret = FsmEvent(&st->l2.l2m, EV_L2_I, skb);
-			else if (IsRR(datap, st->l2.extended))
-				ret = FsmEvent(&st->l2.l2m, EV_L2_RR, skb);
-			else if (IsUI(datap, st->l2.extended))
+			else if ((*datap & 3) == 1)	/* S-Frame */
+				ret = FsmEvent(&st->l2.l2m, EV_L2_SUPER, skb);
+			else if (IsUI(datap, test_bit(FLG_MOD128, &st->l2.flag)))
 				ret = FsmEvent(&st->l2.l2m, EV_L2_UI, skb);
-			else if (IsSABMX(datap, st->l2.extended))
+			else if (IsSABMX(datap, test_bit(FLG_MOD128, &st->l2.flag)))
 				ret = FsmEvent(&st->l2.l2m, EV_L2_SABMX, skb);
-			else if (IsUA(datap, st->l2.extended))
+			else if (IsUA(datap, test_bit(FLG_MOD128, &st->l2.flag)))
 				ret = FsmEvent(&st->l2.l2m, EV_L2_UA, skb);
-			else if (IsDISC(datap, st->l2.extended))
+			else if (IsDISC(datap, test_bit(FLG_MOD128, &st->l2.flag)))
 				ret = FsmEvent(&st->l2.l2m, EV_L2_DISC, skb);
-			else if (IsREJ(datap, st->l2.extended))
-				ret = FsmEvent(&st->l2.l2m, EV_L2_REJ, skb);
-			else if (IsFRMR(datap, st->l2.extended))
+			else if (IsDM(datap, test_bit(FLG_MOD128, &st->l2.flag)))
+				ret = FsmEvent(&st->l2.l2m, EV_L2_DM, skb);
+			else if (IsFRMR(datap, test_bit(FLG_MOD128, &st->l2.flag)))
 				ret = FsmEvent(&st->l2.l2m, EV_L2_FRMR, skb);
-			else if (IsRNR(datap, st->l2.extended))
-				ret = FsmEvent(&st->l2.l2m, EV_L2_RNR, skb);
-
+			else {
+				ret = 0;
+				st->ma.layer(st, MDL_ERROR_IND, (void *) 'L');
+				FreeSkb(skb);
+			}
 			if (ret) {
-				SET_SKB_FREE(skb);
-				dev_kfree_skb(skb);
+				FreeSkb(skb);
 			}
 			break;
-		case (PH_PULL_ACK):
+		case (PH_PULL_CNF):
 			FsmEvent(&st->l2.l2m, EV_L2_ACK_PULL, arg);
 			break;
+		case (PH_PAUSE_IND):
+			test_and_set_bit(FLG_DCHAN_BUSY, &st->l2.flag);
+			break;
+		case (PH_PAUSE_CNF):
+			test_and_clear_bit(FLG_DCHAN_BUSY, &st->l2.flag);
+			break;
 	}
 }
 
@@ -1208,13 +1452,11 @@
 	switch (pr) {
 		case (DL_DATA):
 			if (FsmEvent(&st->l2.l2m, EV_L2_DL_DATA, arg)) {
-				SET_SKB_FREE(((struct sk_buff *) arg));
 				dev_kfree_skb((struct sk_buff *) arg);
 			}
 			break;
 		case (DL_UNIT_DATA):
 			if (FsmEvent(&st->l2.l2m, EV_L2_DL_UNIT_DATA, arg)) {
-				SET_SKB_FREE(((struct sk_buff *) arg));
 				dev_kfree_skb((struct sk_buff *) arg);
 			}
 			break;
@@ -1237,28 +1479,28 @@
 		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):
+		case (PH_DEACTIVATE_IND):
+			FsmEvent(&st->l2.l2m, EV_L1_DEACTIVATE, arg);
+			break;
+		case (MDL_ASSIGN_REQ):
 			FsmEvent(&st->l2.l2m, EV_L2_MDL_ASSIGN, arg);
 			break;
-		case (MDL_REMOVE):
+		case (MDL_REMOVE_REQ):
 			FsmEvent(&st->l2.l2m, EV_L2_MDL_REMOVE, arg);
 			break;
+		case (MDL_ERROR_REQ):
+			FsmEvent(&st->l2.l2m, EV_L2_MDL_ERROR, arg);
+			break;
 	}
 }
 
 void
 releasestack_isdnl2(struct PStack *st)
 {
-	FsmDelTimer(&st->l2.t200_timer, 15);
-	FsmDelTimer(&st->l2.t203_timer, 16);
+	FsmDelTimer(&st->l2.t200, 15);
+	FsmDelTimer(&st->l2.t203, 16);
 	discard_i_queue(st);
+	discard_ui_queue(st);
 	ReleaseWin(&st->l2);
 }
 
@@ -1279,13 +1521,10 @@
 	st->l1.l1l2 = isdnl2_l1l2;
 	st->l3.l3l2 = isdnl2_l3l2;
 	st->ma.manl2 = isdnl2_manl2;
-	st->ma.teil2 = isdnl2_teil2;
 
-	st->l2.uihsize = l2headersize(&st->l2, !0);
-	st->l2.ihsize = l2headersize(&st->l2, 0);
 	skb_queue_head_init(&st->l2.i_queue);
+	skb_queue_head_init(&st->l2.ui_queue);
 	InitWin(&st->l2);
-	st->l2.rejexp = 0;
 	st->l2.debug = 0;
 
 	st->l2.l2m.fsm = &l2fsm;
@@ -1296,9 +1535,8 @@
 	st->l2.l2m.printdebug = l2m_debug;
 	strcpy(st->l2.debug_id, debug_id);
 
-	FsmInitTimer(&st->l2.l2m, &st->l2.t200_timer);
-	FsmInitTimer(&st->l2.l2m, &st->l2.t203_timer);
-	st->l2.t200_running = 0;
+	FsmInitTimer(&st->l2.l2m, &st->l2.t200);
+	FsmInitTimer(&st->l2.l2m, &st->l2.t203);
 }
 
 void
@@ -1311,8 +1549,8 @@
 {
 }
 
-void
-Isdnl2New(void)
+HISAX_INITFUNC(void
+Isdnl2New(void))
 {
 	l2fsm.state_count = L2_STATE_COUNT;
 	l2fsm.event_count = L2_EVENT_COUNT;
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/isdnl3.c linux/drivers/isdn/hisax/isdnl3.c
--- v2.1.91/linux/drivers/isdn/hisax/isdnl3.c	Thu Feb 12 20:56:06 1998
+++ linux/drivers/isdn/hisax/isdnl3.c	Wed Apr  1 16:20:59 1998
@@ -1,4 +1,4 @@
-/* $Id: isdnl3.c,v 1.10 1997/04/06 22:54:16 keil Exp $
+/* $Id: isdnl3.c,v 2.5 1998/02/12 23:07:52 keil Exp $
 
  * Author       Karsten Keil (keil@temic-ech.spacenet.de)
  *              based on the teles driver from Jan den Ouden
@@ -7,38 +7,39 @@
  *              Fritz Elfert
  *
  * $Log: isdnl3.c,v $
- * Revision 1.10  1997/04/06 22:54:16  keil
- * Using SKB's
- *
- * Revision 1.9  1997/03/25 23:11:25  keil
- * US NI-1 protocol
+ * Revision 2.5  1998/02/12 23:07:52  keil
+ * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
  *
- * Revision 1.8  1997/03/21 18:53:44  keil
- * Report no protocol error to syslog too
+ * Revision 2.4  1997/11/06 17:09:25  keil
+ * New 2.1 init code
  *
- * Revision 1.7  1997/03/17 18:34:38  keil
- * fixed oops if no protocol selected during config
+ * Revision 2.3  1997/10/29 19:07:53  keil
+ * changes for 2.1
  *
- * Revision 1.6  1997/02/16 01:04:08  fritz
- * Bugfix: Changed timer handling caused hang with 2.1.X
+ * Revision 2.2  1997/10/01 09:21:41  fritz
+ * Removed old compatibility stuff for 2.0.X kernels.
+ * From now on, this code is for 2.1.X ONLY!
+ * Old stuff is still in the separate branch.
  *
- * Revision 1.5  1997/02/09 00:26:27  keil
- * new interface handling, one interface per card
- * leased line changes
+ * Revision 2.1  1997/08/03 14:36:32  keil
+ * Implement RESTART procedure
  *
- * Revision 1.4  1997/01/27 23:17:44  keil
- * delete timers while unloading
+ * Revision 2.0  1997/07/27 21:15:42  keil
+ * New Callref based layer3
  *
- * Revision 1.3  1997/01/21 22:31:12  keil
- * new statemachine; L3 timers
+ * Revision 1.11  1997/06/26 11:11:44  keil
+ * SET_SKBFREE now on creation of a SKB
  *
- * Revision 1.2  1996/11/05 19:42:04  keil
- * using config.h
+ * Revision 1.10  1997/04/06 22:54:16  keil
+ * Using SKB's
  *
- * Revision 1.1  1996/10/13 20:04:54  keil
- * Initial revision
+ * Revision 1.9  1997/03/25 23:11:25  keil
+ * US NI-1 protocol
  *
+ * Revision 1.8  1997/03/21 18:53:44  keil
+ * Report no protocol error to syslog too
  *
+ * Remove old logs /KKe
  *
  */
 #define __NO_VERSION__
@@ -46,7 +47,72 @@
 #include "isdnl3.h"
 #include <linux/config.h>
 
-const char *l3_revision = "$Revision: 1.10 $";
+const char *l3_revision = "$Revision: 2.5 $";
+
+u_char *
+findie(u_char * p, int size, u_char ie, int wanted_set)
+{
+	int l, codeset, maincodeset;
+	u_char *pend = p + size;
+
+	/* skip protocol discriminator, callref and message type */
+	p++;
+	l = (*p++) & 0xf;
+	p += l;
+	p++;
+	codeset = 0;
+	maincodeset = 0;
+	/* while there are bytes left... */
+	while (p < pend) {
+		if ((*p & 0xf0) == 0x90) {
+			codeset = *p & 0x07;
+			if (!(*p & 0x08))
+				maincodeset = codeset;
+		}
+		if (*p & 0x80)
+			p++;
+		else {
+			if (codeset == wanted_set) {
+				if (*p == ie)
+					return (p);
+				if (*p > ie)
+					return (NULL);
+			}
+			p++;
+			l = *p++;
+			p += l;
+			codeset = maincodeset;
+		}
+	}
+	return (NULL);
+}
+
+int
+getcallref(u_char * p)
+{
+	int l, m = 1, cr = 0;
+	p++;			/* prot discr */
+	l = 0xf & *p++;		/* callref length */
+	if (!l)			/* dummy CallRef */
+		return(-1);
+	while (l--) {
+		cr += m * (*p++);
+		m *= 8;
+	}
+	return (cr);
+}
+
+static int OrigCallRef = 0;
+
+int
+newcallref(void)
+{
+	if (OrigCallRef == 127)
+		OrigCallRef = 1;
+	else
+		OrigCallRef++;
+	return (OrigCallRef);
+}
 
 void
 l3_debug(struct PStack *st, char *s)
@@ -54,34 +120,33 @@
 	char str[256], tm[32];
 
 	jiftime(tm, jiffies);
-	sprintf(str, "%s Channel %d l3 %s\n", tm, st->l3.channr, s);
+	sprintf(str, "%s l3 %s\n", tm, s);
 	HiSax_putstatus(st->l1.hardware, str);
 }
 
-
-
 void
-newl3state(struct PStack *st, int state)
+newl3state(struct l3_process *pc, 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);
+	if (pc->debug & L3_DEB_STATE) {
+		sprintf(tmp, "newstate cr %d %d --> %d", pc->callref,
+			pc->state, state);
+		l3_debug(pc->st, tmp);
 	}
-	st->l3.state = state;
+	pc->state = state;
 }
 
 static void
 L3ExpireTimer(struct L3Timer *t)
 {
-	t->st->l4.l4l3(t->st, t->event, NULL);
+	t->pc->st->lli.l4l3(t->pc->st, t->event, t->pc);
 }
 
 void
-L3InitTimer(struct PStack *st, struct L3Timer *t)
+L3InitTimer(struct l3_process *pc, struct L3Timer *t)
 {
-	t->st = st;
+	t->pc = pc;
 	t->tl.function = (void *) L3ExpireTimer;
 	t->tl.data = (long) t;
 	init_timer(&t->tl);
@@ -109,9 +174,9 @@
 }
 
 void
-StopAllL3Timer(struct PStack *st)
+StopAllL3Timer(struct l3_process *pc)
 {
-	L3DelTimer(&st->l3.timer);
+	L3DelTimer(&pc->timer);
 }
 
 struct sk_buff *
@@ -132,9 +197,8 @@
 {
 	struct sk_buff *skb = arg;
 
-	l3_debug(st, "no protocol");
+	HiSax_putstatus(st->l1.hardware, "L3 no D protocol\n");
 	if (skb) {
-		SET_SKB_FREE(skb);
 		dev_kfree_skb(skb);
 	}
 }
@@ -151,14 +215,78 @@
 extern void setstack_1tr6(struct PStack *st);
 #endif
 
+struct l3_process
+*getl3proc(struct PStack *st, int cr)
+{
+	struct l3_process *p = st->l3.proc;
+
+	while (p)
+		if (p->callref == cr)
+			return (p);
+		else
+			p = p->next;
+	return (NULL);
+}
+
+struct l3_process
+*new_l3_process(struct PStack *st, int cr)
+{
+	struct l3_process *p, *np;
+
+	if (!(p = kmalloc(sizeof(struct l3_process), GFP_ATOMIC))) {
+		printk(KERN_ERR "HiSax can't get memory for cr %d\n", cr);
+		return (NULL);
+	}
+	if (!st->l3.proc)
+		st->l3.proc = p;
+	else {
+		np = st->l3.proc;
+		while (np->next)
+			np = np->next;
+		np->next = p;
+	}
+	p->next = NULL;
+	p->debug = L3_DEB_WARN;
+	p->callref = cr;
+	p->state = 0;
+	p->chan = NULL;
+	p->st = st;
+	p->N303 = st->l3.N303;
+	L3InitTimer(p, &p->timer);
+	return (p);
+};
+
+void
+release_l3_process(struct l3_process *p)
+{
+	struct l3_process *np, *pp = NULL;
+
+	if (!p)
+		return;
+	np = p->st->l3.proc;
+	while (np) {
+		if (np == p) {
+			StopAllL3Timer(p);
+			if (pp)
+				pp->next = np->next;
+			else
+				p->st->l3.proc = np->next;
+			kfree(p);
+			return;
+		}
+		pp = np;
+		np = np->next;
+	}
+	printk(KERN_ERR "HiSax internal L3 error CR not in list\n");
+};
+
 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);
+	st->l3.proc   = NULL;
+	st->l3.global = NULL;
 
 #ifdef	CONFIG_HISAX_EURO
 	if (st->protocol == ISDN_PTYPE_EURO) {
@@ -176,11 +304,11 @@
 	} else
 #endif
 	if (st->protocol == ISDN_PTYPE_LEASED) {
-		st->l4.l4l3 = no_l3_proto;
+		st->lli.l4l3 = no_l3_proto;
 		st->l2.l2l3 = no_l3_proto;
-		printk(KERN_NOTICE "HiSax: Leased line mode\n");
+		printk(KERN_INFO "HiSax: Leased line mode\n");
 	} else {
-		st->l4.l4l3 = no_l3_proto;
+		st->lli.l4l3 = no_l3_proto;
 		st->l2.l2l3 = no_l3_proto;
 		sprintf(tmp, "protocol %s not supported",
 			(st->protocol == ISDN_PTYPE_1TR6) ? "1tr6" :
@@ -188,15 +316,18 @@
 			(st->protocol == ISDN_PTYPE_NI1) ? "ni1" :
 			"unknown");
 		printk(KERN_WARNING "HiSax: %s\n", tmp);
-		l3_debug(st, tmp);
 		st->protocol = -1;
 	}
-	st->l3.state = 0;
-	st->l3.callref = 0;
 }
 
 void
 releasestack_isdnl3(struct PStack *st)
 {
-	StopAllL3Timer(st);
+	while (st->l3.proc)
+		release_l3_process(st->l3.proc);
+	if (st->l3.global) {
+		StopAllL3Timer(st->l3.global);
+		kfree(st->l3.global);
+		st->l3.global = NULL;
+	}
 }
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/isdnl3.h linux/drivers/isdn/hisax/isdnl3.h
--- v2.1.91/linux/drivers/isdn/hisax/isdnl3.h	Thu May 29 21:53:06 1997
+++ linux/drivers/isdn/hisax/isdnl3.h	Wed Apr  1 16:20:59 1998
@@ -1,6 +1,12 @@
-/* $Id: isdnl3.h,v 1.3 1997/04/06 22:54:17 keil Exp $
- *
+/* $Id: isdnl3.h,v 2.0 1997/07/27 21:15:42 keil Exp $
+
  * $Log: isdnl3.h,v $
+ * Revision 2.0  1997/07/27 21:15:42  keil
+ * New Callref based layer3
+ *
+ * Revision 1.4  1997/06/26 11:20:57  keil
+ * ?
+ *
  * Revision 1.3  1997/04/06 22:54:17  keil
  * Using SKB's
  *
@@ -24,15 +30,18 @@
 #define	L3_DEB_CHARGE	0x08
 
 struct stateentry {
-	int	state;
-	u_char	primitive;
-	void	(*rout) (struct PStack *, u_char, void *);
+	int state;
+	u_char primitive;
+	void (*rout) (struct l3_process *, u_char, void *);
 };
 
 extern void l3_debug(struct PStack *st, char *s);
-extern void newl3state(struct PStack *st, int state);
-extern void L3InitTimer(struct PStack *st, struct L3Timer *t);
+extern void newl3state(struct l3_process *pc, int state);
+extern void L3InitTimer(struct l3_process *pc, struct L3Timer *t);
 extern void L3DelTimer(struct L3Timer *t);
-extern int  L3AddTimer(struct L3Timer *t, int millisec, int event);
-extern void StopAllL3Timer(struct PStack *st);
+extern int L3AddTimer(struct L3Timer *t, int millisec, int event);
+extern void StopAllL3Timer(struct l3_process *pc);
 extern struct sk_buff *l3_alloc_skb(int len);
+extern struct l3_process *new_l3_process(struct PStack *st, int cr);
+extern void release_l3_process(struct l3_process *p);
+extern struct l3_process *getl3proc(struct PStack *st, int cr);
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/ix1_micro.c linux/drivers/isdn/hisax/ix1_micro.c
--- v2.1.91/linux/drivers/isdn/hisax/ix1_micro.c	Thu Feb 12 20:56:06 1998
+++ linux/drivers/isdn/hisax/ix1_micro.c	Wed Apr  1 16:21:01 1998
@@ -1,4 +1,4 @@
-/* $Id: ix1_micro.c,v 1.3 1997/04/13 19:54:02 keil Exp $
+/* $Id: ix1_micro.c,v 2.6 1998/02/11 17:28:09 keil Exp $
 
  * ix1_micro.c  low level stuff for ITK ix1-micro Rev.2 isdn cards
  *              derived from the original file teles3.c from Karsten Keil
@@ -11,6 +11,27 @@
  *              Beat Doebeli
  *
  * $Log: ix1_micro.c,v $
+ * Revision 2.6  1998/02/11 17:28:09  keil
+ * Niccy PnP/PCI support
+ *
+ * Revision 2.5  1998/02/02 13:29:42  keil
+ * fast io
+ *
+ * Revision 2.4  1997/11/08 21:35:50  keil
+ * new l1 init
+ *
+ * Revision 2.3  1997/11/06 17:09:35  keil
+ * New 2.1 init code
+ *
+ * Revision 2.2  1997/10/29 18:55:51  keil
+ * changes for 2.1.60 (irq2dev_map)
+ *
+ * Revision 2.1  1997/07/27 21:47:09  keil
+ * new interface structures
+ *
+ * Revision 2.0  1997/06/26 11:02:50  keil
+ * New Layer and card interface
+ *
  * Revision 1.3  1997/04/13 19:54:02  keil
  * Change in IRQ check delay for SMP
  *
@@ -54,17 +75,16 @@
 
 
 #define __NO_VERSION__
-#include "siemens.h"
 #include "hisax.h"
-#include "teles3.h"
+#include "isac.h"
+#include "hscx.h"
 #include "isdnl1.h"
-#include <linux/kernel_stat.h>
 
 extern const char *CardType[];
-const char *ix1_revision = "$Revision: 1.3 $";
+const char *ix1_revision = "$Revision: 2.6 $";
 
-#define byteout(addr,val) outb_p(val,addr)
-#define bytein(addr) inb_p(addr)
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
 
 #define SPECIAL_PORT_OFFSET 3
 
@@ -73,865 +93,250 @@
 #define HSCX_COMMAND_OFFSET 2
 #define HSCX_DATA_OFFSET 1
 
-#define ISAC_FIFOSIZE 16
-#define HSCX_FIFOSIZE 16
-
 #define TIMEOUT 50
 
 static inline u_char
-IsacReadReg(unsigned int adr, u_char off)
-{
-	byteout(adr + ISAC_COMMAND_OFFSET, off + 0x20);
-	return bytein(adr + ISAC_DATA_OFFSET);
-}
-
-static inline void
-IsacWriteReg(unsigned int adr, u_char off, u_char data)
-{
-	byteout(adr + ISAC_COMMAND_OFFSET, off + 0x20);
-	byteout(adr + ISAC_DATA_OFFSET, data);
-}
-
-#define HSCX_OFFSET(WhichHscx,offset) \
-( (WhichHscx) ? (offset+0x60) : (offset+0x20) )
-
-static inline u_char
-HscxReadReg(unsigned int adr, int WhichHscx, u_char off)
+readreg(unsigned int ale, unsigned int adr, u_char off)
 {
-	byteout(adr + HSCX_COMMAND_OFFSET, HSCX_OFFSET(WhichHscx, off));
-	return bytein(adr + HSCX_DATA_OFFSET);
-}
-
-static inline void
-HscxWriteReg(unsigned int adr, int WhichHscx, u_char off, u_char data)
-{
-	byteout(adr + HSCX_COMMAND_OFFSET, HSCX_OFFSET(WhichHscx, off));
-	byteout(adr + HSCX_DATA_OFFSET, data);
-}
-
-
-static inline void
-IsacReadFifo(unsigned int adr, u_char * data, int size)
-{
-	byteout(adr + ISAC_COMMAND_OFFSET, 0);
-	while (size--)
-		*data++ = bytein(adr + ISAC_DATA_OFFSET);
-}
-
-static void
-IsacWriteFifo(unsigned int adr, u_char * data, int size)
-{
-	byteout(adr + ISAC_COMMAND_OFFSET, 0);
-	while (size--) {
-		byteout(adr + ISAC_DATA_OFFSET, *data);
-		data++;
-	}
-}
-
-static inline void
-HscxReadFifo(unsigned int adr, int WhichHscx, u_char * data, int size)
-{
-	byteout(adr + HSCX_COMMAND_OFFSET, (WhichHscx) ? 0x40 : 0x00);
-	while (size--)
-		*data++ = bytein(adr + HSCX_DATA_OFFSET);
-}
+	register u_char ret;
+	long flags;
 
-static void
-HscxWriteFifo(unsigned int adr, int WhichHscx, u_char * data, int size)
-{
-	byteout(adr + HSCX_COMMAND_OFFSET, (WhichHscx) ? 0x40 : 0x00);
-	while (size--) {
-		byteout(adr + HSCX_DATA_OFFSET, *data);
-		data++;
-	}
+	save_flags(flags);
+	cli();
+	byteout(ale, off);
+	ret = bytein(adr);
+	restore_flags(flags);
+	return (ret);
 }
 
 static inline void
-waitforCEC(int adr, int WhichHscx)
+readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
 {
-	int to = TIMEOUT;
+	/* fifo read without cli because it's allready done  */
 
-	while ((HscxReadReg(adr, WhichHscx, HSCX_STAR) & 0x04) && to) {
-		udelay(1);
-		to--;
-	}
-	if (!to)
-		printk(KERN_WARNING "ix1-Micro: waitforCEC timeout\n");
+	byteout(ale, off);
+	insb(adr, data, size);
 }
 
 
 static inline void
-waitforXFW(int adr, int WhichHscx)
-{
-	int to = TIMEOUT;
-
-	while ((!(HscxReadReg(adr, WhichHscx, HSCX_STAR) & 0x44) == 0x40) && to) {
-		udelay(1);
-		to--;
-	}
-	if (!to)
-		printk(KERN_WARNING "ix1-Micro: waitforXFW timeout\n");
-}
-
-static inline void
-writehscxCMDR(int adr, int WhichHscx, u_char data)
+writereg(unsigned int ale, unsigned int adr, u_char off, u_char data)
 {
 	long flags;
 
 	save_flags(flags);
 	cli();
-	waitforCEC(adr, WhichHscx);
-	HscxWriteReg(adr, WhichHscx, HSCX_CMDR, data);
+	byteout(ale, off);
+	byteout(adr, data);
 	restore_flags(flags);
 }
 
-/*
- * fast interrupt here
- */
-
-static void
-hscxreport(struct IsdnCardState *sp, int hscx)
-{
-	printk(KERN_DEBUG "HSCX %d\n", hscx);
-	printk(KERN_DEBUG "ISTA %x\n", HscxReadReg(sp->hscx[hscx], hscx, HSCX_ISTA));
-	printk(KERN_DEBUG "STAR %x\n", HscxReadReg(sp->hscx[hscx], hscx, HSCX_STAR));
-	printk(KERN_DEBUG "EXIR %x\n", HscxReadReg(sp->hscx[hscx], hscx, HSCX_EXIR));
-}
-
-void
-ix1micro_report(struct IsdnCardState *sp)
+static inline void
+writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
 {
-	printk(KERN_DEBUG "ISAC\n");
-	printk(KERN_DEBUG "ISTA %x\n", IsacReadReg(sp->isac, ISAC_ISTA));
-	printk(KERN_DEBUG "STAR %x\n", IsacReadReg(sp->isac, ISAC_STAR));
-	printk(KERN_DEBUG "EXIR %x\n", IsacReadReg(sp->isac, ISAC_EXIR));
-	hscxreport(sp, 0);
-	hscxreport(sp, 1);
+	/* fifo write without cli because it's allready done  */
+	byteout(ale, off);
+	outsb(adr, data, size);
 }
 
-/*
- * HSCX stuff goes here
- */
+/* Interface functions */
 
-static void
-hscx_empty_fifo(struct HscxState *hsp, int count)
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
 {
-	u_char *ptr;
-	struct IsdnCardState *sp = hsp->sp;
-	long flags;
-
-	if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO))
-		debugl1(sp, "hscx_empty_fifo");
-
-	if (hsp->rcvidx + count > HSCX_BUFMAX) {
-		if (sp->debug & L1_DEB_WARN)
-			debugl1(sp, "hscx_empty_fifo: incoming packet too large");
-		writehscxCMDR(sp->hscx[hsp->hscx], hsp->hscx, 0x80);
-		hsp->rcvidx = 0;
-		return;
-	}
-	ptr = hsp->rcvbuf + hsp->rcvidx;
-	hsp->rcvidx += count;
-	save_flags(flags);
-	cli();
-	HscxReadFifo(sp->hscx[hsp->hscx], hsp->hscx, ptr, count);
-	writehscxCMDR(sp->hscx[hsp->hscx], hsp->hscx, 0x80);
-	restore_flags(flags);
-	if (sp->debug & L1_DEB_HSCX_FIFO) {
-		char tmp[128];
-		char *t = tmp;
-
-		t += sprintf(t, "hscx_empty_fifo %c cnt %d",
-			     hsp->hscx ? 'B' : 'A', count);
-		QuickHex(t, ptr, count);
-		debugl1(sp, tmp);
-	}
+	return (readreg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, offset));
 }
 
 static void
-hscx_fill_fifo(struct HscxState *hsp)
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
 {
-	struct IsdnCardState *sp = hsp->sp;
-	int more, count;
-	u_char *ptr;
-	long flags;
-
-	if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO))
-		debugl1(sp, "hscx_fill_fifo");
-
-	if (!hsp->tx_skb)
-		return;
-	if (hsp->tx_skb->len <= 0)
-		return;
-
-	more = (hsp->mode == 1) ? 1 : 0;
-	if (hsp->tx_skb->len > 32) {
-		more = !0;
-		count = 32;
-	} else
-		count = hsp->tx_skb->len;
-
-	waitforXFW(sp->hscx[hsp->hscx], hsp->hscx);
-	save_flags(flags);
-	cli();
-	ptr = hsp->tx_skb->data;
-	skb_pull(hsp->tx_skb, count);
-	hsp->tx_cnt -= count;
-	hsp->count += count;
-	HscxWriteFifo(sp->hscx[hsp->hscx], hsp->hscx, ptr, count);
-	writehscxCMDR(sp->hscx[hsp->hscx], hsp->hscx, more ? 0x8 : 0xa);
-	restore_flags(flags);
-	if (sp->debug & L1_DEB_HSCX_FIFO) {
-		char tmp[128];
-		char *t = tmp;
-
-		t += sprintf(t, "hscx_fill_fifo %c cnt %d",
-			     hsp->hscx ? 'B' : 'A', count);
-		QuickHex(t, ptr, count);
-		debugl1(sp, tmp);
-	}
+	writereg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, offset, value);
 }
 
-static inline void
-hscx_interrupt(struct IsdnCardState *sp, u_char val, u_char hscx)
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
 {
-	u_char r;
-	struct HscxState *hsp = sp->hs + hscx;
-	struct sk_buff *skb;
-	int count;
-	char tmp[32];
-
-	if (!hsp->init)
-		return;
-
-	if (val & 0x80) {	/* RME */
-
-		r = HscxReadReg(sp->hscx[hsp->hscx], hscx, HSCX_RSTA);
-		if ((r & 0xf0) != 0xa0) {
-			if (!r & 0x80)
-				if (sp->debug & L1_DEB_WARN)
-					debugl1(sp, "HSCX invalid frame");
-			if ((r & 0x40) && hsp->mode)
-				if (sp->debug & L1_DEB_WARN) {
-					sprintf(tmp, "HSCX RDO mode=%d",
-						hsp->mode);
-					debugl1(sp, tmp);
-				}
-			if (!r & 0x20)
-				if (sp->debug & L1_DEB_WARN)
-					debugl1(sp, "HSCX CRC error");
-			writehscxCMDR(sp->hscx[hsp->hscx], hsp->hscx, 0x80);
-		} else {
-			count = HscxReadReg(sp->hscx[hsp->hscx], hscx, HSCX_RBCL) & 0x1f;
-			if (count == 0)
-				count = 32;
-			hscx_empty_fifo(hsp, count);
-			if ((count = hsp->rcvidx - 1) > 0) {
-				if (sp->debug & L1_DEB_HSCX_FIFO) {
-					sprintf(tmp, "HX Frame %d", count);
-					debugl1(sp, tmp);
-				}
-				if (!(skb = dev_alloc_skb(count)))
-					printk(KERN_WARNING "IX1: receive out of memory\n");
-				else {
-					memcpy(skb_put(skb, count), hsp->rcvbuf, count);
-					skb_queue_tail(&hsp->rqueue, skb);
-				}
-			}
-		}
-		hsp->rcvidx = 0;
-		hscx_sched_event(hsp, HSCX_RCVBUFREADY);
-	}
-	if (val & 0x40) {	/* RPF */
-		hscx_empty_fifo(hsp, 32);
-		if (hsp->mode == 1) {
-			/* receive audio data */
-			if (!(skb = dev_alloc_skb(32)))
-				printk(KERN_WARNING "IX1: receive out of memory\n");
-			else {
-				memcpy(skb_put(skb, 32), hsp->rcvbuf, 32);
-				skb_queue_tail(&hsp->rqueue, skb);
-			}
-			hsp->rcvidx = 0;
-			hscx_sched_event(hsp, HSCX_RCVBUFREADY);
-		}
-	}
-	if (val & 0x10) {	/* XPR */
-		if (hsp->tx_skb)
-			if (hsp->tx_skb->len) {
-				hscx_fill_fifo(hsp);
-				return;
-			} else {
-				SET_SKB_FREE(hsp->tx_skb);
-				dev_kfree_skb(hsp->tx_skb);
-				hsp->count = 0;
-				if (hsp->st->l4.l1writewakeup)
-					hsp->st->l4.l1writewakeup(hsp->st);
-				hsp->tx_skb = NULL;
-			}
-		if ((hsp->tx_skb = skb_dequeue(&hsp->squeue))) {
-			hsp->count = 0;
-			hscx_fill_fifo(hsp);
-		} else
-			hscx_sched_event(hsp, HSCX_XMTBUFREADY);
-	}
+	readfifo(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, 0, data, size);
 }
 
-/*
- * ISAC stuff goes here
- */
-
 static void
-isac_empty_fifo(struct IsdnCardState *sp, int count)
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
 {
-	u_char *ptr;
-	long flags;
-
-	if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO))
-		if (sp->debug & L1_DEB_ISAC)
-			debugl1(sp, "isac_empty_fifo");
-
-	if ((sp->rcvidx + count) >= MAX_DFRAME_LEN) {
-		if (sp->debug & L1_DEB_WARN) {
-			char tmp[40];
-			sprintf(tmp, "isac_empty_fifo overrun %d",
-				sp->rcvidx + count);
-			debugl1(sp, tmp);
-		}
-		IsacWriteReg(sp->isac, ISAC_CMDR, 0x80);
-		sp->rcvidx = 0;
-		return;
-	}
-	ptr = sp->rcvbuf + sp->rcvidx;
-	sp->rcvidx += count;
-	save_flags(flags);
-	cli();
-	IsacReadFifo(sp->isac, ptr, count);
-	IsacWriteReg(sp->isac, ISAC_CMDR, 0x80);
-	restore_flags(flags);
-	if (sp->debug & L1_DEB_ISAC_FIFO) {
-		char tmp[128];
-		char *t = tmp;
-
-		t += sprintf(t, "isac_empty_fifo cnt %d", count);
-		QuickHex(t, ptr, count);
-		debugl1(sp, tmp);
-	}
+	writefifo(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, 0, data, size);
 }
 
-static void
-isac_fill_fifo(struct IsdnCardState *sp)
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
 {
-	int count, more;
-	u_char *ptr;
-	long flags;
-
-	if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO))
-		debugl1(sp, "isac_fill_fifo");
-
-	if (!sp->tx_skb)
-		return;
-
-	count = sp->tx_skb->len;
-	if (count <= 0)
-		return;
-
-	more = 0;
-	if (count > 32) {
-		more = !0;
-		count = 32;
-	}
-	save_flags(flags);
-	cli();
-	ptr = sp->tx_skb->data;
-	skb_pull(sp->tx_skb, count);
-	sp->tx_cnt += count;
-	IsacWriteFifo(sp->isac, ptr, count);
-	IsacWriteReg(sp->isac, ISAC_CMDR, more ? 0x8 : 0xa);
-	restore_flags(flags);
-	if (sp->debug & L1_DEB_ISAC_FIFO) {
-		char tmp[128];
-		char *t = tmp;
-
-		t += sprintf(t, "isac_fill_fifo cnt %d", count);
-		QuickHex(t, ptr, count);
-		debugl1(sp, tmp);
-	}
+	return (readreg(cs->hw.ix1.hscx_ale,
+			cs->hw.ix1.hscx, offset + (hscx ? 0x40 : 0)));
 }
 
 static void
-ph_command(struct IsdnCardState *sp, unsigned int command)
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
 {
-	if (sp->debug & L1_DEB_ISAC) {
-		char tmp[32];
-		sprintf(tmp, "ph_command %d", command);
-		debugl1(sp, tmp);
-	}
-	IsacWriteReg(sp->isac, ISAC_CIX0, (command << 2) | 3);
+	writereg(cs->hw.ix1.hscx_ale,
+		 cs->hw.ix1.hscx, offset + (hscx ? 0x40 : 0), value);
 }
 
+#define READHSCX(cs, nr, reg) readreg(cs->hw.ix1.hscx_ale, \
+		cs->hw.ix1.hscx, reg + (nr ? 0x40 : 0))
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.ix1.hscx_ale, \
+		cs->hw.ix1.hscx, reg + (nr ? 0x40 : 0), data)
 
-static inline void
-isac_interrupt(struct IsdnCardState *sp, u_char val)
-{
-	u_char exval;
-	struct sk_buff *skb;
-	unsigned int count;
-	char tmp[32];
-
-	if (sp->debug & L1_DEB_ISAC) {
-		sprintf(tmp, "ISAC interrupt %x", val);
-		debugl1(sp, tmp);
-	}
-	if (val & 0x80) {	/* RME */
-		exval = IsacReadReg(sp->isac, ISAC_RSTA);
-		if ((exval & 0x70) != 0x20) {
-			if (exval & 0x40)
-				if (sp->debug & L1_DEB_WARN)
-					debugl1(sp, "ISAC RDO");
-			if (!exval & 0x20)
-				if (sp->debug & L1_DEB_WARN)
-					debugl1(sp, "ISAC CRC error");
-			IsacWriteReg(sp->isac, ISAC_CMDR, 0x80);
-		} else {
-			count = IsacReadReg(sp->isac, ISAC_RBCL) & 0x1f;
-			if (count == 0)
-				count = 32;
-			isac_empty_fifo(sp, count);
-			if ((count = sp->rcvidx) > 0) {
-				sp->rcvidx = 0;
-				if (!(skb = alloc_skb(count, GFP_ATOMIC)))
-					printk(KERN_WARNING "IX1: D receive out of memory\n");
-				else {
-					memcpy(skb_put(skb, count), sp->rcvbuf, count);
-					skb_queue_tail(&sp->rq, skb);
-				}
-			}
-		}
-		sp->rcvidx = 0;
-		isac_sched_event(sp, ISAC_RCVBUFREADY);
-	}
-	if (val & 0x40) {	/* RPF */
-		isac_empty_fifo(sp, 32);
-	}
-	if (val & 0x20) {	/* RSC */
-		/* never */
-		if (sp->debug & L1_DEB_WARN)
-			debugl1(sp, "ISAC RSC interrupt");
-	}
-	if (val & 0x10) {	/* XPR */
-		if (sp->tx_skb)
-			if (sp->tx_skb->len) {
-				isac_fill_fifo(sp);
-				goto afterXPR;
-			} else {
-				SET_SKB_FREE(sp->tx_skb);
-				dev_kfree_skb(sp->tx_skb);
-				sp->tx_cnt = 0;
-				sp->tx_skb = NULL;
-			}
-		if ((sp->tx_skb = skb_dequeue(&sp->sq))) {
-			sp->tx_cnt = 0;
-			isac_fill_fifo(sp);
-		} else
-			isac_sched_event(sp, ISAC_XMTBUFREADY);
-	}
-      afterXPR:
-	if (val & 0x04) {	/* CISQ */
-		sp->ph_state = (IsacReadReg(sp->isac, ISAC_CIX0) >> 2)
-		    & 0xf;
-		if (sp->debug & L1_DEB_ISAC) {
-			sprintf(tmp, "l1state %d", sp->ph_state);
-			debugl1(sp, tmp);
-		}
-		isac_new_ph(sp);
-	}
-	if (val & 0x02) {	/* SIN */
-		/* never */
-		if (sp->debug & L1_DEB_WARN)
-			debugl1(sp, "ISAC SIN interrupt");
-	}
-	if (val & 0x01) {	/* EXI */
-		exval = IsacReadReg(sp->isac, ISAC_EXIR);
-		if (sp->debug & L1_DEB_WARN) {
-			sprintf(tmp, "ISAC EXIR %02x", exval);
-			debugl1(sp, tmp);
-		}
-	}
-}
+#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.ix1.hscx_ale, \
+		cs->hw.ix1.hscx, (nr ? 0x40 : 0), ptr, cnt)
 
-static inline void
-hscx_int_main(struct IsdnCardState *sp, u_char val)
-{
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.ix1.hscx_ale, \
+		cs->hw.ix1.hscx, (nr ? 0x40 : 0), ptr, cnt)
 
-	u_char exval;
-	struct HscxState *hsp;
-	char tmp[32];
-
-
-	if (val & 0x01) {
-		hsp = sp->hs + 1;
-		exval = HscxReadReg(sp->hscx[1], 1, HSCX_EXIR);
-		if (exval == 0x40) {
-			if (hsp->mode == 1)
-				hscx_fill_fifo(hsp);
-			else {
-				/* Here we lost an TX interrupt, so
-				   * restart transmitting the whole frame.
-				 */
-				if (hsp->tx_skb) {
-					skb_push(hsp->tx_skb, hsp->count);
-					hsp->tx_cnt += hsp->count;
-					hsp->count = 0;
-				}
-				writehscxCMDR(sp->hscx[hsp->hscx], hsp->hscx, 0x01);
-				if (sp->debug & L1_DEB_WARN) {
-					sprintf(tmp, "HSCX B EXIR %x Lost TX", exval);
-					debugl1(sp, tmp);
-				}
-			}
-		} else if (sp->debug & L1_DEB_HSCX) {
-			sprintf(tmp, "HSCX B EXIR %x", exval);
-			debugl1(sp, tmp);
-		}
-	}
-	if (val & 0xf8) {
-		if (sp->debug & L1_DEB_HSCX) {
-			sprintf(tmp, "HSCX B interrupt %x", val);
-			debugl1(sp, tmp);
-		}
-		hscx_interrupt(sp, val, 1);
-	}
-	if (val & 0x02) {
-		hsp = sp->hs;
-		exval = HscxReadReg(sp->hscx[0], 0, HSCX_EXIR);
-		if (exval == 0x40) {
-			if (hsp->mode == 1)
-				hscx_fill_fifo(hsp);
-			else {
-				/* Here we lost an TX interrupt, so
-				   * restart transmitting the whole frame.
-				 */
-				if (hsp->tx_skb) {
-					skb_push(hsp->tx_skb, hsp->count);
-					hsp->tx_cnt += hsp->count;
-					hsp->count = 0;
-				}
-				writehscxCMDR(sp->hscx[hsp->hscx], hsp->hscx, 0x01);
-				if (sp->debug & L1_DEB_WARN) {
-					sprintf(tmp, "HSCX A EXIR %x Lost TX", exval);
-					debugl1(sp, tmp);
-				}
-			}
-		} else if (sp->debug & L1_DEB_HSCX) {
-			sprintf(tmp, "HSCX A EXIR %x", exval);
-			debugl1(sp, tmp);
-		}
-	}
-	if (val & 0x04) {
-		exval = HscxReadReg(sp->hscx[0], 0, HSCX_ISTA);
-		if (sp->debug & L1_DEB_HSCX) {
-			sprintf(tmp, "HSCX A interrupt %x", exval);
-			debugl1(sp, tmp);
-		}
-		hscx_interrupt(sp, exval, 0);
-	}
-}
+#include "hscx_irq.c"
 
 static void
 ix1micro_interrupt(int intno, void *dev_id, struct pt_regs *regs)
 {
-	struct IsdnCardState *sp;
+	struct IsdnCardState *cs = dev_id;
 	u_char val, stat = 0;
 
-	sp = (struct IsdnCardState *) dev_id;
-
-	if (!sp) {
-		printk(KERN_WARNING "Teles: Spurious interrupt!\n");
+	if (!cs) {
+		printk(KERN_WARNING "IX1: Spurious interrupt!\n");
 		return;
 	}
-	val = HscxReadReg(sp->hscx[1], 1, HSCX_ISTA);
+	val = readreg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_ISTA + 0x40);
       Start_HSCX:
 	if (val) {
-		hscx_int_main(sp, val);
+		hscx_int_main(cs, val);
 		stat |= 1;
 	}
-	val = IsacReadReg(sp->isac, ISAC_ISTA);
+	val = readreg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_ISTA);
       Start_ISAC:
 	if (val) {
-		isac_interrupt(sp, val);
+		isac_interrupt(cs, val);
 		stat |= 2;
 	}
-	val = HscxReadReg(sp->hscx[1], 1, HSCX_ISTA);
+	val = readreg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_ISTA + 0x40);
 	if (val) {
-		if (sp->debug & L1_DEB_HSCX)
-			debugl1(sp, "HSCX IntStat after IntRoutine");
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX IntStat after IntRoutine");
 		goto Start_HSCX;
 	}
-	val = IsacReadReg(sp->isac, ISAC_ISTA);
+	val = readreg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_ISTA);
 	if (val) {
-		if (sp->debug & L1_DEB_ISAC)
-			debugl1(sp, "ISAC IntStat after IntRoutine");
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
 		goto Start_ISAC;
 	}
 	if (stat & 1) {
-		HscxWriteReg(sp->hscx[0], 0, HSCX_MASK, 0xFF);
-		HscxWriteReg(sp->hscx[1], 1, HSCX_MASK, 0xFF);
-		HscxWriteReg(sp->hscx[0], 0, HSCX_MASK, 0x0);
-		HscxWriteReg(sp->hscx[1], 1, HSCX_MASK, 0x0);
+		writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK, 0xFF);
+		writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK + 0x40, 0xFF);
+		writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK, 0);
+		writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK + 0x40, 0);
 	}
 	if (stat & 2) {
-		IsacWriteReg(sp->isac, ISAC_MASK, 0xFF);
-		IsacWriteReg(sp->isac, ISAC_MASK, 0x0);
+		writereg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_MASK, 0xFF);
+		writereg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_MASK, 0);
 	}
 }
 
-
-static void
-initisac(struct IsdnCardState *sp)
+void
+release_io_ix1micro(struct IsdnCardState *cs)
 {
-	unsigned int adr = sp->isac;
-
-	/* 16.3 IOM 2 Mode */
-	IsacWriteReg(adr, ISAC_MASK, 0xff);
-	IsacWriteReg(adr, ISAC_ADF2, 0x80);
-	IsacWriteReg(adr, ISAC_SQXR, 0x2f);
-	IsacWriteReg(adr, ISAC_SPCR, 0x0);
-	IsacWriteReg(adr, ISAC_ADF1, 0x2);
-	IsacWriteReg(adr, ISAC_STCR, 0x70);
-	IsacWriteReg(adr, ISAC_MODE, 0xc9);
-	IsacWriteReg(adr, ISAC_TIMR, 0x0);
-	IsacWriteReg(adr, ISAC_ADF1, 0x0);
-	IsacWriteReg(adr, ISAC_CMDR, 0x41);
-	IsacWriteReg(adr, ISAC_CIX0, (1 << 2) | 3);
-	IsacWriteReg(adr, ISAC_MASK, 0xff);
-	IsacWriteReg(adr, ISAC_MASK, 0x0);
+	if (cs->hw.ix1.cfg_reg)
+		release_region(cs->hw.ix1.cfg_reg, 4);
 }
 
 static void
-modehscx(struct HscxState *hs, int mode, int ichan)
+ix1_reset(struct IsdnCardState *cs)
 {
-	struct IsdnCardState *sp = hs->sp;
-	int hscx = hs->hscx;
+	long flags;
+	int cnt;
 
-	if (sp->debug & L1_DEB_HSCX) {
-		char tmp[40];
-		sprintf(tmp, "hscx %c mode %d ichan %d",
-			'A' + hscx, mode, ichan);
-		debugl1(sp, tmp);
-	}
-	hs->mode = mode;
-	HscxWriteReg(sp->hscx[hscx], hscx, HSCX_CCR1, 0x85);
-	HscxWriteReg(sp->hscx[hscx], hscx, HSCX_XAD1, 0xFF);
-	HscxWriteReg(sp->hscx[hscx], hscx, HSCX_XAD2, 0xFF);
-	HscxWriteReg(sp->hscx[hscx], hscx, HSCX_RAH2, 0xFF);
-	HscxWriteReg(sp->hscx[hscx], hscx, HSCX_XBCH, 0x0);
-	HscxWriteReg(sp->hscx[hscx], hscx, HSCX_RLCR, 0x0);
-
-	switch (mode) {
-		case 0:
-			HscxWriteReg(sp->hscx[hscx], hscx, HSCX_CCR2, 0x30);
-			HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAX, 0xff);
-			HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAR, 0xff);
-			HscxWriteReg(sp->hscx[hscx], hscx, HSCX_XCCR, 7);
-			HscxWriteReg(sp->hscx[hscx], hscx, HSCX_RCCR, 7);
-			HscxWriteReg(sp->hscx[hscx], hscx, HSCX_MODE, 0x84);
-			break;
-		case 1:
-			if (ichan == 0) {
-				HscxWriteReg(sp->hscx[hscx], hscx, HSCX_CCR2, 0x30);
-				HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAX, 0x2f);
-				HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAR, 0x2f);
-				HscxWriteReg(sp->hscx[hscx], hscx, HSCX_XCCR, 7);
-				HscxWriteReg(sp->hscx[hscx], hscx, HSCX_RCCR, 7);
-			} else {
-				HscxWriteReg(sp->hscx[hscx], hscx, HSCX_CCR2, 0x30);
-				HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAX, 0x3);
-				HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAR, 0x3);
-				HscxWriteReg(sp->hscx[hscx], hscx, HSCX_XCCR, 7);
-				HscxWriteReg(sp->hscx[hscx], hscx, HSCX_RCCR, 7);
-			}
-			HscxWriteReg(sp->hscx[hscx], hscx, HSCX_MODE, 0xe4);
-			HscxWriteReg(sp->hscx[hscx], hscx, HSCX_CMDR, 0x41);
-			break;
-		case 2:
-			if (ichan == 0) {
-				HscxWriteReg(sp->hscx[hscx], hscx, HSCX_CCR2, 0x30);
-				HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAX, 0x2f);
-				HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAR, 0x2f);
-				HscxWriteReg(sp->hscx[hscx], hscx, HSCX_XCCR, 7);
-				HscxWriteReg(sp->hscx[hscx], hscx, HSCX_RCCR, 7);
-			} else {
-				HscxWriteReg(sp->hscx[hscx], hscx, HSCX_CCR2, 0x30);
-				HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAX, 0x3);
-				HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAR, 0x3);
-				HscxWriteReg(sp->hscx[hscx], hscx, HSCX_XCCR, 7);
-				HscxWriteReg(sp->hscx[hscx], hscx, HSCX_RCCR, 7);
-			}
-			HscxWriteReg(sp->hscx[hscx], hscx, HSCX_MODE, 0x8c);
-			HscxWriteReg(sp->hscx[hscx], hscx, HSCX_CMDR, 0x41);
-			break;
+	/* reset isac */
+	save_flags(flags);
+	cnt = 3 * (HZ / 10) + 1;
+	sti();
+	while (cnt--) {
+		byteout(cs->hw.ix1.cfg_reg + SPECIAL_PORT_OFFSET, 1);
+		HZDELAY(1);	/* wait >=10 ms */
 	}
-	HscxWriteReg(sp->hscx[hscx], hscx, HSCX_ISTA, 0x00);
-}
-
-void
-release_io_ix1micro(struct IsdnCard *card)
-{
-	if (card->sp->cfg_reg)
-		release_region(card->sp->cfg_reg, 4);
+	byteout(cs->hw.ix1.cfg_reg + SPECIAL_PORT_OFFSET, 0);
+	restore_flags(flags);
 }
 
-static void
-clear_pending_ints(struct IsdnCardState *sp)
+static int
+ix1_card_msg(struct IsdnCardState *cs, int mt, void *arg)
 {
-	int val;
-	char tmp[64];
-
-	val = HscxReadReg(sp->hscx[1], 1, HSCX_ISTA);
-	sprintf(tmp, "HSCX B ISTA %x", val);
-	debugl1(sp, tmp);
-	if (val & 0x01) {
-		val = HscxReadReg(sp->hscx[1], 1, HSCX_EXIR);
-		sprintf(tmp, "HSCX B EXIR %x", val);
-		debugl1(sp, tmp);
-	} else if (val & 0x02) {
-		val = HscxReadReg(sp->hscx[0], 0, HSCX_EXIR);
-		sprintf(tmp, "HSCX A EXIR %x", val);
-		debugl1(sp, tmp);
-	}
-	val = HscxReadReg(sp->hscx[0], 0, HSCX_ISTA);
-	sprintf(tmp, "HSCX A ISTA %x", val);
-	debugl1(sp, tmp);
-	val = HscxReadReg(sp->hscx[1], 1, HSCX_STAR);
-	sprintf(tmp, "HSCX B STAR %x", val);
-	debugl1(sp, tmp);
-	val = HscxReadReg(sp->hscx[0], 0, HSCX_STAR);
-	sprintf(tmp, "HSCX A STAR %x", val);
-	debugl1(sp, tmp);
-	val = IsacReadReg(sp->isac, ISAC_STAR);
-	sprintf(tmp, "ISAC STAR %x", val);
-	debugl1(sp, tmp);
-	val = IsacReadReg(sp->isac, ISAC_MODE);
-	sprintf(tmp, "ISAC MODE %x", val);
-	debugl1(sp, tmp);
-	val = IsacReadReg(sp->isac, ISAC_ADF2);
-	sprintf(tmp, "ISAC ADF2 %x", val);
-	debugl1(sp, tmp);
-	val = IsacReadReg(sp->isac, ISAC_ISTA);
-	sprintf(tmp, "ISAC ISTA %x", val);
-	debugl1(sp, tmp);
-	if (val & 0x01) {
-		val = IsacReadReg(sp->isac, ISAC_EXIR);
-		sprintf(tmp, "ISAC EXIR %x", val);
-		debugl1(sp, tmp);
-	} else if (val & 0x04) {
-		val = IsacReadReg(sp->isac, ISAC_CIR0);
-		sprintf(tmp, "ISAC CIR0 %x", val);
-		debugl1(sp, tmp);
-	}
-	IsacWriteReg(sp->isac, ISAC_MASK, 0);
-	IsacWriteReg(sp->isac, ISAC_CMDR, 0x41);
-}
-
-int
-initix1micro(struct IsdnCardState *sp)
-{
-	int ret;
-	int loop = 0;
-	char tmp[40];
-
-	sp->counter = kstat_irqs(sp->irq);
-	sprintf(tmp, "IRQ %d count %d", sp->irq, sp->counter);
-	debugl1(sp, tmp);
-	clear_pending_ints(sp);
-	ret = get_irq(sp->cardnr, &ix1micro_interrupt);
-	if (ret) {
-		initisac(sp);
-		sp->modehscx(sp->hs, 0, 0);
-		sp->modehscx(sp->hs + 1, 0, 0);
-		while (loop++ < 10) {
-			/* At least 1-3 irqs must happen
-			 * (one from HSCX A, one from HSCX B, 3rd from ISAC)
-			 */
-			if (kstat_irqs(sp->irq) > sp->counter)
-				break;
-			current->state = TASK_INTERRUPTIBLE;
-			current->timeout = jiffies + 1;
-			schedule();
-		}
-		sprintf(tmp, "IRQ %d count %d", sp->irq,
-			kstat_irqs(sp->irq));
-		debugl1(sp, tmp);
-		if (kstat_irqs(sp->irq) == sp->counter) {
-			printk(KERN_WARNING
-			       "ix1-Micro: IRQ(%d) getting no interrupts during init\n",
-			       sp->irq);
-			free_irq(sp->irq, sp);
-			return (0);
-		}
+	switch (mt) {
+		case CARD_RESET:
+			ix1_reset(cs);
+			return(0);
+		case CARD_RELEASE:
+			release_io_ix1micro(cs);
+			return(0);
+		case CARD_SETIRQ:
+			return(request_irq(cs->irq, &ix1micro_interrupt,
+					I4L_IRQ_FLAG, "HiSax", cs));
+		case CARD_INIT:
+			clear_pending_isac_ints(cs);
+			clear_pending_hscx_ints(cs);
+			initisac(cs);
+			inithscx(cs);
+			return(0);
+		case CARD_TEST:
+			return(0);
 	}
-	return (ret);
+	return(0);
 }
 
-int
-setup_ix1micro(struct IsdnCard *card)
+
+__initfunc(int
+setup_ix1micro(struct IsdnCard *card))
 {
-	u_char val, verA, verB;
-	struct IsdnCardState *sp = card->sp;
-	long flags;
+	struct IsdnCardState *cs = card->cs;
 	char tmp[64];
 
 	strcpy(tmp, ix1_revision);
-	printk(KERN_NOTICE "HiSax: ITK IX1 driver Rev. %s\n", HiSax_getrev(tmp));
-	if (sp->typ != ISDN_CTYPE_IX1MICROR2)
+	printk(KERN_INFO "HiSax: ITK IX1 driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_IX1MICROR2)
 		return (0);
 
 	/* IO-Ports */
-	sp->isac = sp->hscx[0] = sp->hscx[1] = sp->cfg_reg = card->para[1];
-	sp->irq = card->para[0];
-	if (sp->cfg_reg) {
-		if (check_region((sp->cfg_reg), 4)) {
+	cs->hw.ix1.isac_ale = card->para[1] + ISAC_COMMAND_OFFSET;
+	cs->hw.ix1.hscx_ale = card->para[1] + HSCX_COMMAND_OFFSET;
+	cs->hw.ix1.isac = card->para[1] + ISAC_DATA_OFFSET;
+	cs->hw.ix1.hscx = card->para[1] + HSCX_DATA_OFFSET;
+	cs->hw.ix1.cfg_reg = card->para[1];
+	cs->irq = card->para[0];
+	if (cs->hw.ix1.cfg_reg) {
+		if (check_region((cs->hw.ix1.cfg_reg), 4)) {
 			printk(KERN_WARNING
 			  "HiSax: %s config port %x-%x already in use\n",
 			       CardType[card->typ],
-			       sp->cfg_reg,
-			       sp->cfg_reg + 4);
+			       cs->hw.ix1.cfg_reg,
+			       cs->hw.ix1.cfg_reg + 4);
 			return (0);
 		} else
-			request_region(sp->cfg_reg, 4, "ix1micro cfg");
+			request_region(cs->hw.ix1.cfg_reg, 4, "ix1micro cfg");
 	}
-	/* reset isac */
-	save_flags(flags);
-	val = 3 * (HZ / 10) + 1;
-	sti();
-	while (val--) {
-		byteout(sp->cfg_reg + SPECIAL_PORT_OFFSET, 1);
-		HZDELAY(1);	/* wait >=10 ms */
-	}
-	byteout(sp->cfg_reg + SPECIAL_PORT_OFFSET, 0);
-	restore_flags(flags);
-
-	printk(KERN_NOTICE
-	       "HiSax: %s config irq:%d io:0x%x\n",
-	       CardType[sp->typ], sp->irq,
-	       sp->cfg_reg);
-	verA = HscxReadReg(sp->hscx[0], 0, HSCX_VSTR) & 0xf;
-	verB = HscxReadReg(sp->hscx[1], 1, HSCX_VSTR) & 0xf;
-	printk(KERN_INFO "ix1-Micro: HSCX version A: %s  B: %s\n",
-	       HscxVersion(verA), HscxVersion(verB));
-	val = IsacReadReg(sp->isac, ISAC_RBCH);
-	printk(KERN_INFO "ix1-Micro: ISAC %s\n",
-	       ISACVersion(val));
-	if ((verA == 0) | (verA == 0xf) | (verB == 0) | (verB == 0xf)) {
+	printk(KERN_INFO
+	       "HiSax: %s config irq:%d io:0x%X\n",
+	       CardType[cs->typ], cs->irq,
+	       cs->hw.ix1.cfg_reg);
+	ix1_reset(cs);
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &ix1_card_msg;
+	ISACVersion(cs, "ix1-Micro:");
+	if (HscxVersion(cs, "ix1-Micro:")) {
 		printk(KERN_WARNING
 		    "ix1-Micro: wrong HSCX versions check IO address\n");
-		release_io_ix1micro(card);
+		release_io_ix1micro(cs);
 		return (0);
 	}
-	sp->modehscx = &modehscx;
-	sp->ph_command = &ph_command;
-	sp->hscx_fill_fifo = &hscx_fill_fifo;
-	sp->isac_fill_fifo = &isac_fill_fifo;
 	return (1);
 }
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/ix1_micro.h linux/drivers/isdn/hisax/ix1_micro.h
--- v2.1.91/linux/drivers/isdn/hisax/ix1_micro.h	Thu Feb 27 10:57:29 1997
+++ linux/drivers/isdn/hisax/ix1_micro.h	Wed Dec 31 16:00:00 1969
@@ -1,50 +0,0 @@
-/* $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.91/linux/drivers/isdn/hisax/l3_1tr6.c linux/drivers/isdn/hisax/l3_1tr6.c
--- v2.1.91/linux/drivers/isdn/hisax/l3_1tr6.c	Thu Feb 12 20:56:06 1998
+++ linux/drivers/isdn/hisax/l3_1tr6.c	Wed Apr  1 16:21:02 1998
@@ -1,4 +1,4 @@
-/* $Id: l3_1tr6.c,v 1.11 1997/04/06 22:54:18 keil Exp $
+/* $Id: l3_1tr6.c,v 2.4 1998/02/12 23:07:57 keil Exp $
 
  *  German 1TR6 D-channel protocol
  *
@@ -6,39 +6,28 @@
  *
  *
  * $Log: l3_1tr6.c,v $
- * Revision 1.11  1997/04/06 22:54:18  keil
- * Using SKB's
- *
- * Revision 1.10  1997/03/13 20:37:58  keil
- * channel request added
- *
- * Revision 1.9  1997/02/11 01:37:40  keil
- * Changed setup-interface (incoming and outgoing)
- *
- * Revision 1.8  1997/01/27 23:20:21  keil
- * report revision only ones
- *
- * Revision 1.7  1997/01/21 22:30:07  keil
- * new statemachine; L3 timers
+ * Revision 2.4  1998/02/12 23:07:57  keil
+ * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
  *
- * Revision 1.6  1996/12/14 21:07:20  keil
- * additional states for CC_REJECT
+ * Revision 2.3  1997/11/06 17:12:24  keil
+ * KERN_NOTICE --> KERN_INFO
  *
- * Revision 1.5  1996/12/08 19:55:17  keil
- * change CC_REJECT_REQ routine
+ * Revision 2.2  1997/10/29 19:03:00  keil
+ * changes for 2.1
  *
- * Revision 1.4  1996/10/30 10:18:01  keil
- * bugfixes in debugging output
+ * Revision 2.1  1997/08/03 15:28:09  keil
+ * release L3 empty processes
  *
- * Revision 1.3  1996/10/27 22:15:37  keil
- * bugfix reject handling
+ * Revision 2.0  1997/07/27 21:15:45  keil
+ * New Callref based layer3
  *
- * Revision 1.2  1996/10/13 23:08:56  keil
- * added missing state for callback reject
+ * Revision 1.12  1997/06/26 11:11:45  keil
+ * SET_SKBFREE now on creation of a SKB
  *
- * Revision 1.1  1996/10/13 20:04:55  keil
- * Initial revision
+ * Revision 1.11  1997/04/06 22:54:18  keil
+ * Using SKB's
  *
+ * Old Log removed /KKe
  *
  */
 
@@ -49,16 +38,16 @@
 #include <linux/ctype.h>
 
 extern char *HiSax_getrev(const char *revision);
-const char *l3_1tr6_revision = "$Revision: 1.11 $";
+const char *l3_1tr6_revision = "$Revision: 2.4 $";
 
 #define MsgHead(ptr, cref, mty, dis) \
 	*ptr++ = dis; \
 	*ptr++ = 0x1; \
-	*ptr++ = cref; \
+	*ptr++ = cref ^ 0x80; \
 	*ptr++ = mty
 
 static void
-l3_1TR6_message(struct PStack *st, u_char mt, u_char pd)
+l3_1TR6_message(struct l3_process *pc, u_char mt, u_char pd)
 {
 	struct sk_buff *skb;
 	u_char *p;
@@ -66,12 +55,71 @@
 	if (!(skb = l3_alloc_skb(4)))
 		return;
 	p = skb_put(skb, 4);
-	MsgHead(p, st->l3.callref, mt, pd);
-	st->l3.l3l2(st, DL_DATA, skb);
+	MsgHead(p, pc->callref, mt, pd);
+	pc->st->l3.l3l2(pc->st, DL_DATA, skb);
+}
+
+static int
+l31tr6_check_messagetype_validity(int mt, int pd) {
+/* verify if a message type exists */
+
+	if (pd == PROTO_DIS_N0)
+		switch(mt) {
+		   case MT_N0_REG_IND:
+		   case MT_N0_CANC_IND:
+		   case MT_N0_FAC_STA:
+		   case MT_N0_STA_ACK:
+		   case MT_N0_STA_REJ:
+		   case MT_N0_FAC_INF:
+		   case MT_N0_INF_ACK:
+		   case MT_N0_INF_REJ:
+		   case MT_N0_CLOSE:
+		   case MT_N0_CLO_ACK:
+			return(1);
+		   default:
+			return(0);
+		}
+	else if (pd == PROTO_DIS_N1)
+		switch(mt) {
+		   case MT_N1_ESC:
+		   case MT_N1_ALERT:
+		   case MT_N1_CALL_SENT:
+		   case MT_N1_CONN:
+		   case MT_N1_CONN_ACK:
+		   case MT_N1_SETUP:
+		   case MT_N1_SETUP_ACK:
+		   case MT_N1_RES:
+		   case MT_N1_RES_ACK:
+		   case MT_N1_RES_REJ:
+		   case MT_N1_SUSP:
+		   case MT_N1_SUSP_ACK:
+		   case MT_N1_SUSP_REJ:
+		   case MT_N1_USER_INFO:
+		   case MT_N1_DET:
+		   case MT_N1_DISC:
+		   case MT_N1_REL:
+		   case MT_N1_REL_ACK:
+		   case MT_N1_CANC_ACK:
+		   case MT_N1_CANC_REJ:
+		   case MT_N1_CON_CON:
+		   case MT_N1_FAC:
+		   case MT_N1_FAC_ACK:
+		   case MT_N1_FAC_CAN:
+		   case MT_N1_FAC_REG:
+		   case MT_N1_FAC_REJ:
+		   case MT_N1_INFO:
+		   case MT_N1_REG_ACK:
+		   case MT_N1_REG_REJ:
+		   case MT_N1_STAT:
+		   	return (1);
+		   default:
+		   	return(0);
+		}
+	return(0);
 }
 
 static void
-l3_1tr6_setup_req(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_setup_req(struct l3_process *pc, u_char pr, void *arg)
 {
 	struct sk_buff *skb;
 	u_char tmp[128];
@@ -81,16 +129,13 @@
 	u_char channel = 0;
 	int l;
 
-
-	st->l3.callref = st->pa->callref;
-	MsgHead(p, st->l3.callref, MT_N1_SETUP, PROTO_DIS_N1);
-
-	teln = st->pa->setup.phone;
-	st->pa->spv = 0;
+	MsgHead(p, pc->callref, MT_N1_SETUP, PROTO_DIS_N1);
+	teln = pc->para.setup.phone;
+	pc->para.spv = 0;
 	if (!isdigit(*teln)) {
 		switch (0x5f & *teln) {
 			case 'S':
-				st->pa->spv = 1;
+				pc->para.spv = 1;
 				break;
 			case 'C':
 				channel = 0x08;
@@ -103,8 +148,8 @@
 					channel |= 0x02;
 				break;
 			default:
-				if (st->l3.debug & L3_DEB_WARN)
-					l3_debug(st, "Wrong MSN Code");
+				if (pc->st->l3.debug & L3_DEB_WARN)
+					l3_debug(pc->st, "Wrong MSN Code");
 				break;
 		}
 		teln++;
@@ -114,22 +159,22 @@
 		*p++ = 1;
 		*p++ = channel;
 	}
-	if (st->pa->spv) {	/* SPV ? */
+	if (pc->para.spv) {	/* SPV ? */
 		/* NSF SPV */
 		*p++ = WE0_netSpecFac;
 		*p++ = 4;	/* Laenge */
 		*p++ = 0;
 		*p++ = FAC_SPV;	/* SPV */
-		*p++ = st->pa->setup.si1;	/* 0 for all Services */
-		*p++ = st->pa->setup.si2;	/* 0 for all Services */
+		*p++ = pc->para.setup.si1;	/* 0 for all Services */
+		*p++ = pc->para.setup.si2;	/* 0 for all Services */
 		*p++ = WE0_netSpecFac;
 		*p++ = 4;	/* Laenge */
 		*p++ = 0;
 		*p++ = FAC_Activate;	/* aktiviere SPV (default) */
-		*p++ = st->pa->setup.si1;	/* 0 for all Services */
-		*p++ = st->pa->setup.si2;	/* 0 for all Services */
+		*p++ = pc->para.setup.si1;	/* 0 for all Services */
+		*p++ = pc->para.setup.si2;	/* 0 for all Services */
 	}
-	eaz = st->pa->setup.eazmsn;
+	eaz = pc->para.setup.eazmsn;
 	if (*eaz) {
 		*p++ = WE0_origAddr;
 		*p++ = strlen(eaz) + 1;
@@ -149,22 +194,21 @@
 	/* Codesatz 6 fuer Service */
 	*p++ = WE6_serviceInd;
 	*p++ = 2;		/* len=2 info,info2 */
-	*p++ = st->pa->setup.si1;
-	*p++ = st->pa->setup.si2;
+	*p++ = pc->para.setup.si1;
+	*p++ = pc->para.setup.si2;
 
 	l = p - tmp;
 	if (!(skb = l3_alloc_skb(l)))
 		return;
 	memcpy(skb_put(skb, l), tmp, l);
-	L3DelTimer(&st->l3.timer);
-	L3AddTimer(&st->l3.timer, st->l3.t303, CC_T303);
-	newl3state(st, 1);
-	st->l3.l3l2(st, DL_DATA, skb);
-
+	L3DelTimer(&pc->timer);
+	L3AddTimer(&pc->timer, T303, CC_T303);
+	newl3state(pc, 1);
+	pc->st->l3.l3l2(pc->st, DL_DATA, skb);
 }
 
 static void
-l3_1tr6_setup(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_setup(struct l3_process *pc, u_char pr, void *arg)
 {
 	u_char *p;
 	int bcfound = 0;
@@ -172,110 +216,105 @@
 	struct sk_buff *skb = arg;
 
 	p = skb->data;
-	st->pa->callref = getcallref(p);
-	st->l3.callref = 0x80 + st->pa->callref;
 
 	/* Channel Identification */
 	p = skb->data;
 	if ((p = findie(p, skb->len, WE0_chanID, 0))) {
-		st->pa->bchannel = p[2] & 0x3;
+		pc->para.bchannel = p[2] & 0x3;
 		bcfound++;
-	} else if (st->l3.debug & L3_DEB_WARN)
-		l3_debug(st, "setup without bchannel");
+	} else if (pc->st->l3.debug & L3_DEB_WARN)
+		l3_debug(pc->st, "setup without bchannel");
 
 	p = skb->data;
 	if ((p = findie(p, skb->len, WE6_serviceInd, 6))) {
-		st->pa->setup.si1 = p[2];
-		st->pa->setup.si2 = p[3];
-	} else if (st->l3.debug & L3_DEB_WARN)
-		l3_debug(st, "setup without service indicator");
+		pc->para.setup.si1 = p[2];
+		pc->para.setup.si2 = p[3];
+	} else if (pc->st->l3.debug & L3_DEB_WARN)
+		l3_debug(pc->st, "setup without service indicator");
 
 	p = skb->data;
 	if ((p = findie(p, skb->len, WE0_destAddr, 0)))
-		iecpy(st->pa->setup.eazmsn, p, 1);
+		iecpy(pc->para.setup.eazmsn, p, 1);
 	else
-		st->pa->setup.eazmsn[0] = 0;
+		pc->para.setup.eazmsn[0] = 0;
 
 	p = skb->data;
 	if ((p = findie(p, skb->len, WE0_origAddr, 0))) {
-		iecpy(st->pa->setup.phone, p, 1);
+		iecpy(pc->para.setup.phone, p, 1);
 	} else
-		st->pa->setup.phone[0] = 0;
+		pc->para.setup.phone[0] = 0;
 
 	p = skb->data;
-	st->pa->spv = 0;
+	pc->para.spv = 0;
 	if ((p = findie(p, skb->len, WE0_netSpecFac, 0))) {
 		if ((FAC_SPV == p[3]) || (FAC_Activate == p[3]))
-			st->pa->spv = 1;
+			pc->para.spv = 1;
 	}
-	SET_SKB_FREE(skb);
 	dev_kfree_skb(skb);
 
 	/* Signal all services, linklevel takes care of Service-Indicator */
 	if (bcfound) {
-		if ((st->pa->setup.si1 != 7) && (st->l3.debug & L3_DEB_WARN)) {
+		if ((pc->para.setup.si1 != 7) && (pc->st->l3.debug & L3_DEB_WARN)) {
 			sprintf(tmp, "non-digital call: %s -> %s",
-				st->pa->setup.phone,
-				st->pa->setup.eazmsn);
-			l3_debug(st, tmp);
+				pc->para.setup.phone,
+				pc->para.setup.eazmsn);
+			l3_debug(pc->st, tmp);
 		}
-		newl3state(st, 6);
-		st->l3.l3l4(st, CC_SETUP_IND, NULL);
-	}
+		newl3state(pc, 6);
+		pc->st->l3.l3l4(pc, CC_SETUP_IND, NULL);
+	} else
+		release_l3_process(pc);
 }
 
 static void
-l3_1tr6_setup_ack(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_setup_ack(struct l3_process *pc, u_char pr, void *arg)
 {
 	u_char *p;
 	struct sk_buff *skb = arg;
 
-	L3DelTimer(&st->l3.timer);
+	L3DelTimer(&pc->timer);
 	p = skb->data;
-	newl3state(st, 2);
+	newl3state(pc, 2);
 	if ((p = findie(p, skb->len, WE0_chanID, 0))) {
-		st->pa->bchannel = p[2] & 0x3;
-	} else if (st->l3.debug & L3_DEB_WARN)
-		l3_debug(st, "setup answer without bchannel");
-	SET_SKB_FREE(skb);
+		pc->para.bchannel = p[2] & 0x3;
+	} else if (pc->st->l3.debug & L3_DEB_WARN)
+		l3_debug(pc->st, "setup answer without bchannel");
 	dev_kfree_skb(skb);
-	L3AddTimer(&st->l3.timer, st->l3.t304, CC_T304);
-	st->l3.l3l4(st, CC_MORE_INFO, NULL);
+	L3AddTimer(&pc->timer, T304, CC_T304);
+	pc->st->l3.l3l4(pc, CC_MORE_INFO, NULL);
 }
 
 static void
-l3_1tr6_call_sent(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_call_sent(struct l3_process *pc, u_char pr, void *arg)
 {
 	u_char *p;
 	struct sk_buff *skb = arg;
 
-	L3DelTimer(&st->l3.timer);
+	L3DelTimer(&pc->timer);
 	p = skb->data;
 	if ((p = findie(p, skb->len, WE0_chanID, 0))) {
-		st->pa->bchannel = p[2] & 0x3;
-	} else if (st->l3.debug & L3_DEB_WARN)
-		l3_debug(st, "setup answer without bchannel");
-	SET_SKB_FREE(skb);
+		pc->para.bchannel = p[2] & 0x3;
+	} else if (pc->st->l3.debug & L3_DEB_WARN)
+		l3_debug(pc->st, "setup answer without bchannel");
 	dev_kfree_skb(skb);
-	L3AddTimer(&st->l3.timer, st->l3.t310, CC_T310);
-	newl3state(st, 3);
-	st->l3.l3l4(st, CC_PROCEEDING_IND, NULL);
+	L3AddTimer(&pc->timer, T310, CC_T310);
+	newl3state(pc, 3);
+	pc->st->l3.l3l4(pc, CC_PROCEEDING_IND, NULL);
 }
 
 static void
-l3_1tr6_alert(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_alert(struct l3_process *pc, u_char pr, void *arg)
 {
 	struct sk_buff *skb = arg;
 
-	SET_SKB_FREE(skb);
 	dev_kfree_skb(skb);
-	L3DelTimer(&st->l3.timer);	/* T304 */
-	newl3state(st, 4);
-	st->l3.l3l4(st, CC_ALERTING_IND, NULL);
+	L3DelTimer(&pc->timer);	/* T304 */
+	newl3state(pc, 4);
+	pc->st->l3.l3l4(pc, CC_ALERTING_IND, NULL);
 }
 
 static void
-l3_1tr6_info(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_info(struct l3_process *pc, u_char pr, void *arg)
 {
 	u_char *p;
 	int i, tmpcharge = 0;
@@ -289,45 +328,42 @@
 			tmpcharge *= 10;
 			tmpcharge += a_charge[i] & 0xf;
 		}
-		if (tmpcharge > st->pa->chargeinfo) {
-			st->pa->chargeinfo = tmpcharge;
-			st->l3.l3l4(st, CC_INFO_CHARGE, NULL);
+		if (tmpcharge > pc->para.chargeinfo) {
+			pc->para.chargeinfo = tmpcharge;
+			pc->st->l3.l3l4(pc, CC_INFO_CHARGE, NULL);
 		}
-		if (st->l3.debug & L3_DEB_CHARGE) {
-			sprintf(tmp, "charging info %d", st->pa->chargeinfo);
-			l3_debug(st, tmp);
+		if (pc->st->l3.debug & L3_DEB_CHARGE) {
+			sprintf(tmp, "charging info %d", pc->para.chargeinfo);
+			l3_debug(pc->st, tmp);
 		}
-	} else if (st->l3.debug & L3_DEB_CHARGE)
-		l3_debug(st, "charging info not found");
-	SET_SKB_FREE(skb);
+	} else if (pc->st->l3.debug & L3_DEB_CHARGE)
+		l3_debug(pc->st, "charging info not found");
 	dev_kfree_skb(skb);
 
 }
 
 static void
-l3_1tr6_info_s2(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_info_s2(struct l3_process *pc, u_char pr, void *arg)
 {
 	struct sk_buff *skb = arg;
 
-	SET_SKB_FREE(skb);
 	dev_kfree_skb(skb);
 }
 
 static void
-l3_1tr6_connect(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_connect(struct l3_process *pc, u_char pr, void *arg)
 {
 	struct sk_buff *skb = arg;
 
-	L3DelTimer(&st->l3.timer);	/* T310 */
-	newl3state(st, 10);
-	SET_SKB_FREE(skb);
+	L3DelTimer(&pc->timer);	/* T310 */
+	newl3state(pc, 10);
 	dev_kfree_skb(skb);
-	st->pa->chargeinfo = 0;
-	st->l3.l3l4(st, CC_SETUP_CNF, NULL);
+	pc->para.chargeinfo = 0;
+	pc->st->l3.l3l4(pc, CC_SETUP_CNF, NULL);
 }
 
 static void
-l3_1tr6_rel(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_rel(struct l3_process *pc, u_char pr, void *arg)
 {
 	struct sk_buff *skb = arg;
 	u_char *p;
@@ -335,47 +371,47 @@
 	p = skb->data;
 	if ((p = findie(p, skb->len, WE0_cause, 0))) {
 		if (p[1] > 0) {
-			st->pa->cause = p[2];
+			pc->para.cause = p[2];
 			if (p[1] > 1)
-				st->pa->loc = p[3];
+				pc->para.loc = p[3];
 			else
-				st->pa->loc = 0;
+				pc->para.loc = 0;
 		} else {
-			st->pa->cause = 0;
-			st->pa->loc = 0;
+			pc->para.cause = 0;
+			pc->para.loc = 0;
 		}
 	} else
-		st->pa->cause = -1;
-	SET_SKB_FREE(skb);
+		pc->para.cause = -1;
 	dev_kfree_skb(skb);
-	StopAllL3Timer(st);
-	newl3state(st, 0);
-	l3_1TR6_message(st, MT_N1_REL_ACK, PROTO_DIS_N1);
-	st->l3.l3l4(st, CC_RELEASE_IND, NULL);
+	StopAllL3Timer(pc);
+	newl3state(pc, 0);
+	l3_1TR6_message(pc, MT_N1_REL_ACK, PROTO_DIS_N1);
+	pc->st->l3.l3l4(pc, CC_RELEASE_IND, NULL);
+	release_l3_process(pc);
 }
 
 static void
-l3_1tr6_rel_ack(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_rel_ack(struct l3_process *pc, u_char pr, void *arg)
 {
 	struct sk_buff *skb = arg;
 
-	SET_SKB_FREE(skb);
 	dev_kfree_skb(skb);
-	StopAllL3Timer(st);
-	newl3state(st, 0);
-	st->pa->cause = -1;
-	st->l3.l3l4(st, CC_RELEASE_CNF, NULL);
+	StopAllL3Timer(pc);
+	newl3state(pc, 0);
+	pc->para.cause = -1;
+	pc->st->l3.l3l4(pc, CC_RELEASE_CNF, NULL);
+	release_l3_process(pc);
 }
 
 static void
-l3_1tr6_disc(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_disc(struct l3_process *pc, u_char pr, void *arg)
 {
 	struct sk_buff *skb = arg;
 	u_char *p;
 	int i, tmpcharge = 0;
 	char a_charge[8], tmp[32];
 
-	StopAllL3Timer(st);
+	StopAllL3Timer(pc);
 	p = skb->data;
 	if ((p = findie(p, skb->len, WE6_chargingInfo, 6))) {
 		iecpy(a_charge, p, 1);
@@ -383,104 +419,102 @@
 			tmpcharge *= 10;
 			tmpcharge += a_charge[i] & 0xf;
 		}
-		if (tmpcharge > st->pa->chargeinfo) {
-			st->pa->chargeinfo = tmpcharge;
-			st->l3.l3l4(st, CC_INFO_CHARGE, NULL);
+		if (tmpcharge > pc->para.chargeinfo) {
+			pc->para.chargeinfo = tmpcharge;
+			pc->st->l3.l3l4(pc, CC_INFO_CHARGE, NULL);
+		}
+		if (pc->st->l3.debug & L3_DEB_CHARGE) {
+			sprintf(tmp, "charging info %d", pc->para.chargeinfo);
+			l3_debug(pc->st, tmp);
 		}
-		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");
+	} else if (pc->st->l3.debug & L3_DEB_CHARGE)
+		l3_debug(pc->st, "charging info not found");
 
 
 	p = skb->data;
 	if ((p = findie(p, skb->len, WE0_cause, 0))) {
 		if (p[1] > 0) {
-			st->pa->cause = p[2];
+			pc->para.cause = p[2];
 			if (p[1] > 1)
-				st->pa->loc = p[3];
+				pc->para.loc = p[3];
 			else
-				st->pa->loc = 0;
+				pc->para.loc = 0;
 		} else {
-			st->pa->cause = 0;
-			st->pa->loc = 0;
+			pc->para.cause = 0;
+			pc->para.loc = 0;
 		}
 	} else {
-		if (st->l3.debug & L3_DEB_WARN)
-			l3_debug(st, "cause not found");
-		st->pa->cause = -1;
+		if (pc->st->l3.debug & L3_DEB_WARN)
+			l3_debug(pc->st, "cause not found");
+		pc->para.cause = -1;
 	}
-	SET_SKB_FREE(skb);
 	dev_kfree_skb(skb);
-	newl3state(st, 12);
-	st->l3.l3l4(st, CC_DISCONNECT_IND, NULL);
+	newl3state(pc, 12);
+	pc->st->l3.l3l4(pc, CC_DISCONNECT_IND, NULL);
 }
 
 
 static void
-l3_1tr6_connect_ack(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_connect_ack(struct l3_process *pc, u_char pr, void *arg)
 {
 	struct sk_buff *skb = arg;
 
-	SET_SKB_FREE(skb);
 	dev_kfree_skb(skb);
-	newl3state(st, 10);
-	st->pa->chargeinfo = 0;
-	L3DelTimer(&st->l3.timer);
-	st->l3.l3l4(st, CC_SETUP_COMPLETE_IND, NULL);
+	newl3state(pc, 10);
+	pc->para.chargeinfo = 0;
+	L3DelTimer(&pc->timer);
+	pc->st->l3.l3l4(pc, CC_SETUP_COMPLETE_IND, NULL);
 }
 
 static void
-l3_1tr6_alert_req(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_alert_req(struct l3_process *pc, u_char pr, void *arg)
 {
-	newl3state(st, 7);
-	l3_1TR6_message(st, MT_N1_ALERT, PROTO_DIS_N1);
+	newl3state(pc, 7);
+	l3_1TR6_message(pc, MT_N1_ALERT, PROTO_DIS_N1);
 }
 
 static void
-l3_1tr6_setup_rsp(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_setup_rsp(struct l3_process *pc, u_char pr, void *arg)
 {
 	struct sk_buff *skb;
 	u_char tmp[24];
 	u_char *p = tmp;
 	int l;
 
-	MsgHead(p, st->l3.callref, MT_N1_CONN, PROTO_DIS_N1);
-	if (st->pa->spv) {	/* SPV ? */
+	MsgHead(p, pc->callref, MT_N1_CONN, PROTO_DIS_N1);
+	if (pc->para.spv) {	/* SPV ? */
 		/* NSF SPV */
 		*p++ = WE0_netSpecFac;
 		*p++ = 4;	/* Laenge */
 		*p++ = 0;
 		*p++ = FAC_SPV;	/* SPV */
-		*p++ = st->pa->setup.si1;
-		*p++ = st->pa->setup.si2;
+		*p++ = pc->para.setup.si1;
+		*p++ = pc->para.setup.si2;
 		*p++ = WE0_netSpecFac;
 		*p++ = 4;	/* Laenge */
 		*p++ = 0;
 		*p++ = FAC_Activate;	/* aktiviere SPV */
-		*p++ = st->pa->setup.si1;
-		*p++ = st->pa->setup.si2;
+		*p++ = pc->para.setup.si1;
+		*p++ = pc->para.setup.si2;
 	}
-	newl3state(st, 8);
+	newl3state(pc, 8);
 	l = p - tmp;
 	if (!(skb = l3_alloc_skb(l)))
 		return;
 	memcpy(skb_put(skb, l), tmp, l);
-	st->l3.l3l2(st, DL_DATA, skb);
-	L3DelTimer(&st->l3.timer);
-	L3AddTimer(&st->l3.timer, st->l3.t313, CC_T313);
+	pc->st->l3.l3l2(pc->st, DL_DATA, skb);
+	L3DelTimer(&pc->timer);
+	L3AddTimer(&pc->timer, T313, CC_T313);
 }
 
 static void
-l3_1tr6_reset(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_reset(struct l3_process *pc, u_char pr, void *arg)
 {
-	newl3state(st, 0);
+	release_l3_process(pc);
 }
 
 static void
-l3_1tr6_disconnect_req(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_disconnect_req(struct l3_process *pc, u_char pr, void *arg)
 {
 	struct sk_buff *skb;
 	u_char tmp[16];
@@ -489,8 +523,8 @@
 	u_char cause = 0x10;
 	u_char clen = 1;
 
-	if (st->pa->cause > 0)
-		cause = st->pa->cause;
+	if (pc->para.cause > 0)
+		cause = pc->para.cause;
 	/* Map DSS1 causes */
 	switch (cause & 0x7f) {
 		case 0x10:
@@ -500,57 +534,55 @@
 			cause = CAUSE_CallRejected;
 			break;
 	}
-	StopAllL3Timer(st);
-	MsgHead(p, st->l3.callref, MT_N1_DISC, PROTO_DIS_N1);
+	StopAllL3Timer(pc);
+	MsgHead(p, pc->callref, MT_N1_DISC, PROTO_DIS_N1);
 	*p++ = WE0_cause;
 	*p++ = clen;		/* Laenge */
 	if (clen)
 		*p++ = cause | 0x80;
-	newl3state(st, 11);
+	newl3state(pc, 11);
 	l = p - tmp;
 	if (!(skb = l3_alloc_skb(l)))
 		return;
 	memcpy(skb_put(skb, l), tmp, l);
-	st->l3.l3l2(st, DL_DATA, skb);
-	L3AddTimer(&st->l3.timer, st->l3.t305, CC_T305);
+	pc->st->l3.l3l2(pc->st, DL_DATA, skb);
+	L3AddTimer(&pc->timer, T305, CC_T305);
 }
 
 static void
-l3_1tr6_release_req(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_release_req(struct l3_process *pc, u_char pr, void *arg)
 {
-	StopAllL3Timer(st);
-	newl3state(st, 19);
-	l3_1TR6_message(st, MT_N1_REL, PROTO_DIS_N1);
-	L3AddTimer(&st->l3.timer, st->l3.t308, CC_T308_1);
+	StopAllL3Timer(pc);
+	newl3state(pc, 19);
+	l3_1TR6_message(pc, MT_N1_REL, PROTO_DIS_N1);
+	L3AddTimer(&pc->timer, T308, CC_T308_1);
 }
 
 static void
-l3_1tr6_t303(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_t303(struct l3_process *pc, u_char pr, void *arg)
 {
-	if (st->l3.n_t303 > 0) {
-		st->l3.n_t303--;
-		L3DelTimer(&st->l3.timer);
-		l3_1tr6_setup_req(st, pr, arg);
+	if (pc->N303 > 0) {
+		pc->N303--;
+		L3DelTimer(&pc->timer);
+		l3_1tr6_setup_req(pc, pr, arg);
 	} else {
-		L3DelTimer(&st->l3.timer);
-		st->l3.l3l4(st, CC_NOSETUP_RSP_ERR, NULL);
-		st->l3.n_t303 = 1;
-		newl3state(st, 0);
+		L3DelTimer(&pc->timer);
+		pc->st->l3.l3l4(pc, CC_NOSETUP_RSP_ERR, NULL);
+		release_l3_process(pc);
 	}
 }
 
 static void
-l3_1tr6_t304(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_t304(struct l3_process *pc, u_char pr, void *arg)
 {
-	L3DelTimer(&st->l3.timer);
-	st->pa->cause = 0xE6;
-	l3_1tr6_disconnect_req(st, pr, NULL);
-	st->l3.l3l4(st, CC_SETUP_ERR, NULL);
-
+	L3DelTimer(&pc->timer);
+	pc->para.cause = 0xE6;
+	l3_1tr6_disconnect_req(pc, pr, NULL);
+	pc->st->l3.l3l4(pc, CC_SETUP_ERR, NULL);
 }
 
 static void
-l3_1tr6_t305(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_t305(struct l3_process *pc, u_char pr, void *arg)
 {
 	struct sk_buff *skb;
 	u_char tmp[16];
@@ -559,9 +591,9 @@
 	u_char cause = 0x90;
 	u_char clen = 1;
 
-	L3DelTimer(&st->l3.timer);
-	if (st->pa->cause > 0)
-		cause = st->pa->cause;
+	L3DelTimer(&pc->timer);
+	if (pc->para.cause > 0)
+		cause = pc->para.cause;
 	/* Map DSS1 causes */
 	switch (cause & 0x7f) {
 		case 0x10:
@@ -571,53 +603,53 @@
 			cause = CAUSE_CallRejected;
 			break;
 	}
-	MsgHead(p, st->l3.callref, MT_N1_REL, PROTO_DIS_N1);
+	MsgHead(p, pc->callref, MT_N1_REL, PROTO_DIS_N1);
 	*p++ = WE0_cause;
 	*p++ = clen;		/* Laenge */
 	if (clen)
 		*p++ = cause;
-	newl3state(st, 19);
+	newl3state(pc, 19);
 	l = p - tmp;
 	if (!(skb = l3_alloc_skb(l)))
 		return;
 	memcpy(skb_put(skb, l), tmp, l);
-	st->l3.l3l2(st, DL_DATA, skb);
-	L3AddTimer(&st->l3.timer, st->l3.t308, CC_T308_1);
+	pc->st->l3.l3l2(pc->st, DL_DATA, skb);
+	L3AddTimer(&pc->timer, T308, CC_T308_1);
 }
 
 static void
-l3_1tr6_t310(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_t310(struct l3_process *pc, u_char pr, void *arg)
 {
-	L3DelTimer(&st->l3.timer);
-	st->pa->cause = 0xE6;
-	l3_1tr6_disconnect_req(st, pr, NULL);
-	st->l3.l3l4(st, CC_SETUP_ERR, NULL);
+	L3DelTimer(&pc->timer);
+	pc->para.cause = 0xE6;
+	l3_1tr6_disconnect_req(pc, pr, NULL);
+	pc->st->l3.l3l4(pc, CC_SETUP_ERR, NULL);
 }
 
 static void
-l3_1tr6_t313(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_t313(struct l3_process *pc, u_char pr, void *arg)
 {
-	L3DelTimer(&st->l3.timer);
-	st->pa->cause = 0xE6;
-	l3_1tr6_disconnect_req(st, pr, NULL);
-	st->l3.l3l4(st, CC_CONNECT_ERR, NULL);
+	L3DelTimer(&pc->timer);
+	pc->para.cause = 0xE6;
+	l3_1tr6_disconnect_req(pc, pr, NULL);
+	pc->st->l3.l3l4(pc, CC_CONNECT_ERR, NULL);
 }
 
 static void
-l3_1tr6_t308_1(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_t308_1(struct l3_process *pc, u_char pr, void *arg)
 {
-	L3DelTimer(&st->l3.timer);
-	l3_1TR6_message(st, MT_N1_REL, PROTO_DIS_N1);
-	L3AddTimer(&st->l3.timer, st->l3.t308, CC_T308_2);
-	newl3state(st, 19);
+	L3DelTimer(&pc->timer);
+	l3_1TR6_message(pc, MT_N1_REL, PROTO_DIS_N1);
+	L3AddTimer(&pc->timer, T308, CC_T308_2);
+	newl3state(pc, 19);
 }
 
 static void
-l3_1tr6_t308_2(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_t308_2(struct l3_process *pc, u_char pr, void *arg)
 {
-	L3DelTimer(&st->l3.timer);
-	st->l3.l3l4(st, CC_RELEASE_ERR, NULL);
-	newl3state(st, 0);
+	L3DelTimer(&pc->timer);
+	pc->st->l3.l3l4(pc, CC_RELEASE_ERR, NULL);
+	release_l3_process(pc);
 }
 /* *INDENT-OFF* */
 static struct stateentry downstl[] =
@@ -688,49 +720,79 @@
 
 
 
+
 static int datastln1_len = sizeof(datastln1) /
 sizeof(struct stateentry);
 
 static void
 up1tr6(struct PStack *st, int pr, void *arg)
 {
-	int i, mt;
+	int i, mt, cr;
+	struct l3_process *proc;
 	struct sk_buff *skb = arg;
 	char tmp[80];
 
+	if (skb->len < 4) {
+		if (st->l3.debug & L3_DEB_PROTERR) {
+			sprintf(tmp, "up1tr6 len only %d", skb->len);
+			l3_debug(st, tmp);
+		}
+		dev_kfree_skb(skb);
+		return;
+	}
 	if ((skb->data[0] & 0xfe) != PROTO_DIS_N0) {
 		if (st->l3.debug & L3_DEB_PROTERR) {
-			sprintf(tmp, "up1tr6%sunexpected discriminator %x message len %d state %d",
+			sprintf(tmp, "up1tr6%sunexpected discriminator %x message len %d",
 				(pr == DL_DATA) ? " " : "(broadcast) ",
-				skb->data[0], skb->len, st->l3.state);
+				skb->data[0], skb->len);
+			l3_debug(st, tmp);
+		}
+		dev_kfree_skb(skb);
+		return;
+	}
+	if (skb->data[1] != 1) {
+		if (st->l3.debug & L3_DEB_PROTERR) {
+			sprintf(tmp, "up1tr6 CR len not 1");
 			l3_debug(st, tmp);
 		}
-		SET_SKB_FREE(skb);
 		dev_kfree_skb(skb);
 		return;
 	}
-	mt = skb->data[skb->data[1] + 2];
+	cr = skb->data[2];
+	mt = skb->data[3];
 	if (skb->data[0] == PROTO_DIS_N0) {
-		SET_SKB_FREE(skb);
 		dev_kfree_skb(skb);
 		if (st->l3.debug & L3_DEB_STATE) {
-			sprintf(tmp, "up1tr6%s N0 state %d mt %x unhandled",
-				(pr == DL_DATA) ? " " : "(broadcast) ",
-				st->l3.state, mt);
+			sprintf(tmp, "up1tr6%s N0 mt %x unhandled",
+			     (pr == DL_DATA) ? " " : "(broadcast) ", mt);
 			l3_debug(st, tmp);
 		}
 	} else if (skb->data[0] == PROTO_DIS_N1) {
+		if (!(proc = getl3proc(st, cr))) {
+			if ((mt == MT_N1_SETUP) && (cr < 128)) {
+				if (!(proc = new_l3_process(st, cr))) {
+					if (st->l3.debug & L3_DEB_PROTERR) {
+						sprintf(tmp, "up1tr6 no roc mem");
+						l3_debug(st, tmp);
+					}
+					dev_kfree_skb(skb);
+					return;
+				}
+			} else {
+				dev_kfree_skb(skb);
+				return;
+			}
+		}
 		for (i = 0; i < datastln1_len; i++)
 			if ((mt == datastln1[i].primitive) &&
-			    ((1 << st->l3.state) & datastln1[i].state))
+			    ((1 << proc->state) & datastln1[i].state))
 				break;
 		if (i == datastln1_len) {
-			SET_SKB_FREE(skb);
 			dev_kfree_skb(skb);
 			if (st->l3.debug & L3_DEB_STATE) {
 				sprintf(tmp, "up1tr6%sstate %d mt %x unhandled",
 				  (pr == DL_DATA) ? " " : "(broadcast) ",
-					st->l3.state, mt);
+					proc->state, mt);
 				l3_debug(st, tmp);
 			}
 			return;
@@ -738,10 +800,10 @@
 			if (st->l3.debug & L3_DEB_STATE) {
 				sprintf(tmp, "up1tr6%sstate %d mt %x",
 				  (pr == DL_DATA) ? " " : "(broadcast) ",
-					st->l3.state, mt);
+					proc->state, mt);
 				l3_debug(st, tmp);
 			}
-			datastln1[i].rout(st, pr, skb);
+			datastln1[i].rout(proc, pr, skb);
 		}
 	}
 }
@@ -749,26 +811,44 @@
 static void
 down1tr6(struct PStack *st, int pr, void *arg)
 {
-	int i;
+	int i, cr;
+	struct l3_process *proc;
+	struct Channel *chan;
 	char tmp[80];
 
+	if (CC_SETUP_REQ == pr) {
+		chan = arg;
+		cr = newcallref();
+		cr |= 0x80;
+		if (!(proc = new_l3_process(st, cr))) {
+			return;
+		} else {
+			proc->chan = chan;
+			chan->proc = proc;
+			proc->para.setup = chan->setup;
+			proc->callref = cr;
+		}
+	} else {
+		proc = arg;
+	}
+
 	for (i = 0; i < downstl_len; i++)
 		if ((pr == downstl[i].primitive) &&
-		    ((1 << st->l3.state) & downstl[i].state))
+		    ((1 << proc->state) & downstl[i].state))
 			break;
 	if (i == downstl_len) {
 		if (st->l3.debug & L3_DEB_STATE) {
 			sprintf(tmp, "down1tr6 state %d prim %d unhandled",
-				st->l3.state, pr);
+				proc->state, pr);
 			l3_debug(st, tmp);
 		}
 	} else {
 		if (st->l3.debug & L3_DEB_STATE) {
 			sprintf(tmp, "down1tr6 state %d prim %d",
-				st->l3.state, pr);
+				proc->state, pr);
 			l3_debug(st, tmp);
 		}
-		downstl[i].rout(st, pr, arg);
+		downstl[i].rout(proc, pr, arg);
 	}
 }
 
@@ -777,20 +857,10 @@
 {
 	char tmp[64];
 
-	st->l4.l4l3 = down1tr6;
+	st->lli.l4l3 = down1tr6;
 	st->l2.l2l3 = up1tr6;
-	st->l3.t303 = 4000;
-	st->l3.t304 = 20000;
-	st->l3.t305 = 4000;
-	st->l3.t308 = 4000;
-	st->l3.t310 = 120000;
-	st->l3.t313 = 4000;
-	st->l3.t318 = 4000;
-	st->l3.t319 = 4000;
-	st->l3.n_t303 = 0;
-
-	if (st->l3.channr & 1) {
-		strcpy(tmp, l3_1tr6_revision);
-		printk(KERN_NOTICE "HiSax: 1TR6 Rev. %s\n", HiSax_getrev(tmp));
-	}
+	st->l3.N303 = 0;
+
+	strcpy(tmp, l3_1tr6_revision);
+	printk(KERN_INFO "HiSax: 1TR6 Rev. %s\n", HiSax_getrev(tmp));
 }
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/l3_1tr6.h linux/drivers/isdn/hisax/l3_1tr6.h
--- v2.1.91/linux/drivers/isdn/hisax/l3_1tr6.h	Thu Feb 27 10:57:29 1997
+++ linux/drivers/isdn/hisax/l3_1tr6.h	Wed Apr  1 16:21:02 1998
@@ -1,13 +1,14 @@
-/* $Id: l3_1tr6.h,v 1.1 1996/10/13 20:03:48 keil Exp $
+/* $Id: l3_1tr6.h,v 2.0 1997/07/27 21:15:47 keil Exp $
  *
  *  German 1TR6 D-channel protocol defines
  *
  * $Log: l3_1tr6.h,v $
+ * Revision 2.0  1997/07/27 21:15:47  keil
+ * New Callref based layer3
+ *
  * Revision 1.1  1996/10/13 20:03:48  keil
  * Initial revision
  *
- *
- *
  */
 #ifndef l3_1tr6
 #define l3_1tr6
@@ -29,7 +30,6 @@
 #define MT_N0_CLOSE   0x75
 #define MT_N0_CLO_ACK 0x77
 
-
 /*
  * MsgType N1
  */
@@ -65,8 +65,6 @@
 #define MT_N1_REG_REJ 0x6F
 #define MT_N1_STAT 0x63
 
-
-
 /*
  * W Elemente
  */
@@ -156,5 +154,13 @@
 #define CAUSE_RemoteUserResumed 0x73
 #define CAUSE_UserInfoDiscarded 0x7F
 
+#define T303	4000
+#define T304	20000
+#define T305	4000
+#define T308	4000
+#define T310	120000
+#define T313	4000
+#define T318	4000
+#define T319	4000
 
 #endif
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/l3dss1.c linux/drivers/isdn/hisax/l3dss1.c
--- v2.1.91/linux/drivers/isdn/hisax/l3dss1.c	Thu Feb 12 20:56:06 1998
+++ linux/drivers/isdn/hisax/l3dss1.c	Wed Apr  1 16:21:02 1998
@@ -1,4 +1,4 @@
-/* $Id: l3dss1.c,v 1.15 1997/04/17 11:50:48 keil Exp $
+/* $Id: l3dss1.c,v 2.7 1998/02/12 23:08:01 keil Exp $
 
  * EURO/DSS1 D-channel protocol
  *
@@ -9,71 +9,272 @@
  *              Fritz Elfert
  *
  * $Log: l3dss1.c,v $
- * Revision 1.15  1997/04/17 11:50:48  keil
- * pa->loc was undefined, if it was not send by the exchange
- *
- * Revision 1.14  1997/04/06 22:54:20  keil
- * Using SKB's
- *
- * Revision 1.13  1997/03/13 20:37:28  keil
- * CLIR and channel request added
- *
- * Revision 1.12  1997/02/17 00:34:26  keil
- * Bugfix: Wrong cause delivered
- *
- * Revision 1.11  1997/02/16 12:12:47  fritz
- * Bugfix: SI2 was nont initialized on incoming calls.
- *
- * Revision 1.10  1997/02/11 01:37:24  keil
- * Changed setup-interface (incoming and outgoing)
+ * Revision 2.7  1998/02/12 23:08:01  keil
+ * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
  *
- * Revision 1.9  1997/01/27 23:20:52  keil
- * report revision only ones
+ * Revision 2.6  1998/02/03 23:26:35  keil
+ * V110 extensions from Thomas Pfeiffer
  *
- * Revision 1.8  1997/01/21 22:29:41  keil
- * new statemachine; L3 timers
+ * Revision 2.5  1998/02/02 13:34:28  keil
+ * Support australian Microlink net and german AOCD
  *
- * Revision 1.7  1996/12/14 21:06:59  keil
- * additional states for CC_REJECT
+ * Revision 2.4  1997/11/06 17:12:25  keil
+ * KERN_NOTICE --> KERN_INFO
  *
- * Revision 1.6  1996/12/08 22:59:16  keil
- * fixed calling party number without octet 3a
+ * Revision 2.3  1997/10/29 19:03:01  keil
+ * changes for 2.1
  *
- * Revision 1.5  1996/12/08 19:53:31  keil
- * fixes from Pekka Sarnila
+ * Revision 2.2  1997/08/07 17:44:36  keil
+ * Fix RESTART
  *
- * Revision 1.4  1996/11/05 19:44:36  keil
- * some fixes from Henner Eisen
+ * Revision 2.1  1997/08/03 14:36:33  keil
+ * Implement RESTART procedure
  *
- * Revision 1.3  1996/10/30 10:18:01  keil
- * bugfixes in debugging output
+ * Revision 2.0  1997/07/27 21:15:43  keil
+ * New Callref based layer3
  *
- * 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
+ * Revision 1.17  1997/06/26 11:11:46  keil
+ * SET_SKBFREE now on creation of a SKB
  *
+ * Revision 1.15  1997/04/17 11:50:48  keil
+ * pa->loc was undefined, if it was not send by the exchange
  *
+ * Old log removed /KKe
  *
  */
 
 #define __NO_VERSION__
 #include "hisax.h"
 #include "isdnl3.h"
+#include "l3dss1.h"
 #include <linux/ctype.h>
 
 extern char *HiSax_getrev(const char *revision);
-const char *dss1_revision = "$Revision: 1.15 $";
+const char *dss1_revision = "$Revision: 2.7 $";
+
+#define EXT_BEARER_CAPS 1
 
 #define	MsgHead(ptr, cref, mty) \
 	*ptr++ = 0x8; \
 	*ptr++ = 0x1; \
-	*ptr++ = cref; \
+	*ptr++ = cref^0x80; \
 	*ptr++ = mty
 
+
+#ifdef HISAX_DE_AOC
 static void
-l3dss1_message(struct PStack *st, u_char mt)
+l3dss1_parse_facility(struct l3_process *pc, u_char *p)
+{
+	int qd_len = 0;
+	char tmp[32];
+
+	p++;
+	qd_len = *p++;
+	if (qd_len == 0) {
+		l3_debug(pc->st, "qd_len == 0");
+		return;
+	}
+	if((*p & 0x1F) != 0x11) {	/* Service discriminator, supplementary service */
+		l3_debug(pc->st, "supplementary service != 0x11");
+		return;
+	}
+	while(qd_len > 0 && !(*p & 0x80)) {	/* extension ? */
+		p++; qd_len--;
+	} 
+	if(qd_len < 2) {
+		l3_debug(pc->st, "qd_len < 2");
+		return;
+	}
+	p++; qd_len--;
+	if((*p & 0xE0) != 0xA0) {	/* class and form */
+		l3_debug(pc->st, "class and form != 0xA0");
+		return;
+	}
+	switch(*p & 0x1F) {		/* component tag */
+	    case 1: /* invoke */
+		{
+		    unsigned char nlen, ilen;
+		    int ident;
+    
+		    p++; qd_len--;
+		    if(qd_len < 1) {
+			    l3_debug(pc->st, "qd_len < 1");
+			    break;
+		    }
+		    if(*p & 0x80) { /* length format */
+			    l3_debug(pc->st, "*p & 0x80 length format");
+			    break;
+		    }
+		    nlen = *p++; qd_len--;
+		    if(qd_len < nlen) {
+			    l3_debug(pc->st, "qd_len < nlen");
+			    return;
+		    }
+		    qd_len -= nlen;
+    
+		    if(nlen < 2) {
+			    l3_debug(pc->st, "nlen < 2");
+			    return;
+		    }
+		    if(*p != 0x02) {	/* invoke identifier tag */
+			    l3_debug(pc->st, "invoke identifier tag !=0x02");
+			    return;
+		    }
+		    p++; nlen--;
+		    if(*p & 0x80) { /* length format */
+			    l3_debug(pc->st, "*p & 0x80 length format 2");
+			    break;
+		    }
+		    ilen = *p++; nlen--;
+		    if(ilen > nlen || ilen == 0) {
+			    l3_debug(pc->st, "ilen > nlen || ilen == 0");
+			    return;
+		    }
+		    nlen -= ilen;
+		    ident = 0;
+		    while(ilen > 0) {
+			    ident = (ident << 8) | (*p++ & 0xFF);	/* invoke identifier */
+			    ilen--;
+		    }
+    
+		    if(nlen < 2) {
+			    l3_debug(pc->st, "nlen < 2 22");
+			    return;
+		    }
+		    if(*p != 0x02)	{	/* operation value */ 
+			    l3_debug(pc->st, "operation value !=0x02");
+			    return;
+		    }
+		    p++; nlen--;
+		    ilen = *p++; nlen--;
+		    if(ilen > nlen || ilen == 0) {
+			    l3_debug(pc->st, "ilen > nlen || ilen == 0 22");
+			    return;
+		    }
+		    nlen -= ilen;
+		    ident = 0;
+		    while(ilen > 0) {
+			    ident = (ident << 8) | (*p++ & 0xFF);
+			    ilen--;
+		    }
+    
+    #define FOO1(s,a,b) \
+	    while(nlen > 1) {		\
+		    int ilen = p[1];	\
+		    if(nlen < ilen+2) {	\
+			    l3_debug(pc->st, "FOO1  nlen < ilen+2"); \
+			    return;		\
+		    }			\
+		    nlen -= ilen+2;		\
+		    if((*p & 0xFF) == (a)) {	\
+			    int nlen = ilen;	\
+			    p += 2;		\
+			    b;		\
+		    } else {		\
+			    p += ilen+2;	\
+		    }			\
+	    }
+			    
+		    switch(ident) {
+		    default:
+			    break;
+		    case 0x22: /* during */
+			    FOO1("1A",0x30,FOO1("1C",0xA1,FOO1("1D",0x30,FOO1("1E",0x02,({
+				    ident = 0;
+				    while(ilen > 0) {
+					    ident = (ident<<8) | *p++;
+					    ilen--;
+				    }
+				    if (ident > pc->para.chargeinfo) {
+					    pc->para.chargeinfo = ident;
+					    pc->st->l3.l3l4(pc, CC_INFO_CHARGE, NULL);
+				    }
+				    if (pc->st->l3.debug & L3_DEB_CHARGE) {
+					    if (*(p+2) == 0) {
+						    sprintf(tmp, "charging info during %d", pc->para.chargeinfo);
+						    l3_debug(pc->st, tmp);
+					    }
+					    else {
+					    sprintf(tmp, "charging info final %d", pc->para.chargeinfo);
+					    l3_debug(pc->st, tmp);
+					    }
+				    }
+			    })))))
+			    break;
+		    case 0x24: /* final */
+			    FOO1("2A",0x30,FOO1("2B",0x30,FOO1("2C",0xA1,FOO1("2D",0x30,FOO1("2E",0x02,({
+				    ident = 0;
+				    while(ilen > 0) {
+					    ident = (ident<<8) | *p++;
+					    ilen--;
+				    }
+				    if (ident > pc->para.chargeinfo) {
+					    pc->para.chargeinfo = ident;
+					    pc->st->l3.l3l4(pc, CC_INFO_CHARGE, NULL);
+				    }
+				    if (pc->st->l3.debug & L3_DEB_CHARGE) {
+					    sprintf(tmp, "charging info final %d", pc->para.chargeinfo);
+					    l3_debug(pc->st, tmp);
+				    }
+			    }))))))
+		    break;
+		    }
+    #undef FOO1
+    
+		}
+	    break;
+	    case 2: /* return result */
+		    l3_debug(pc->st, "return result break");
+		    break;
+	    case 3: /* return error */
+		    l3_debug(pc->st, "return error break");
+		    break;
+	    default:
+		    l3_debug(pc->st, "default break");
+		    break;
+	}
+}
+#endif	
+
+static int 
+l3dss1_check_messagetype_validity(int mt) {
+/* verify if a message type exists */
+	switch(mt) {
+		case MT_ALERTING:
+		case MT_CALL_PROCEEDING:
+		case MT_CONNECT:
+		case MT_CONNECT_ACKNOWLEDGE:
+		case MT_PROGRESS:
+		case MT_SETUP:
+		case MT_SETUP_ACKNOWLEDGE:
+		case MT_RESUME:
+		case MT_RESUME_ACKNOWLEDGE:
+		case MT_RESUME_REJECT:
+		case MT_SUSPEND:
+		case MT_SUSPEND_ACKNOWLEDGE:
+		case MT_SUSPEND_REJECT:
+		case MT_USER_INFORMATION:
+		case MT_DISCONNECT:
+		case MT_RELEASE:
+		case MT_RELEASE_COMPLETE:
+		case MT_RESTART:
+		case MT_RESTART_ACKNOWLEDGE:
+		case MT_SEGMENT:
+		case MT_CONGESTION_CONTROL:
+		case MT_INFORMATION:
+		case MT_FACILITY:
+		case MT_NOTIFY:
+		case MT_STATUS:
+		case MT_STATUS_ENQUIRY:
+			return(1);
+		default:
+			return(0);
+	}
+	return(0);
+}
+
+static void
+l3dss1_message(struct l3_process *pc, u_char mt)
 {
 	struct sk_buff *skb;
 	u_char *p;
@@ -81,63 +282,226 @@
 	if (!(skb = l3_alloc_skb(4)))
 		return;
 	p = skb_put(skb, 4);
-	MsgHead(p, st->l3.callref, mt);
-	st->l3.l3l2(st, DL_DATA, skb);
+	MsgHead(p, pc->callref, mt);
+	pc->st->l3.l3l2(pc->st, DL_DATA, skb);
 }
 
 static void
-l3dss1_release_req(struct PStack *st, u_char pr, void *arg)
+l3dss1_release_req(struct l3_process *pc, u_char pr, void *arg)
 {
-	StopAllL3Timer(st);
-	newl3state(st, 19);
-	l3dss1_message(st, MT_RELEASE);
-	L3AddTimer(&st->l3.timer, st->l3.t308, CC_T308_1);
+	StopAllL3Timer(pc);
+	newl3state(pc, 19);
+	l3dss1_message(pc, MT_RELEASE);
+	L3AddTimer(&pc->timer, T308, CC_T308_1);
 }
 
 static void
-l3dss1_release_cmpl(struct PStack *st, u_char pr, void *arg)
+l3dss1_release_cmpl(struct l3_process *pc, u_char pr, void *arg)
 {
 	u_char *p;
 	struct sk_buff *skb = arg;
 	int cause = -1;
 
 	p = skb->data;
-	st->pa->loc = 0;
+	pc->para.loc = 0;
 	if ((p = findie(p, skb->len, IE_CAUSE, 0))) {
 		p++;
 		if (*p++ == 2)
-			st->pa->loc = *p++;
+			pc->para.loc = *p++;
 		cause = *p & 0x7f;
 	}
-	SET_SKB_FREE(skb);
 	dev_kfree_skb(skb);
-	StopAllL3Timer(st);
-	st->pa->cause = cause;
-	newl3state(st, 0);
-	st->l3.l3l4(st, CC_RELEASE_CNF, NULL);
+	StopAllL3Timer(pc);
+	pc->para.cause = cause;
+	newl3state(pc, 0);
+	pc->st->l3.l3l4(pc, CC_RELEASE_CNF, NULL);
+	release_l3_process(pc);
+}
+
+#ifdef EXT_BEARER_CAPS
+
+u_char *EncodeASyncParams(u_char *p, u_char si2)
+{ // 7c 06 88  90 21 42 00 bb
+
+  p[0] = p[1] = 0; p[2] = 0x80;
+  if (si2 & 32) // 7 data bits
+    p[2] += 16;
+  else          // 8 data bits
+    p[2] +=24;
+
+  if (si2 & 16) // 2 stop bits
+    p[2] += 96;
+  else          // 1 stop bit
+    p[2] = 32;
+
+  if (si2 & 8)  // even parity
+    p[2] += 2;
+  else          // no parity
+    p[2] += 3;
+
+  switch (si2 & 0x07)
+  {
+    case 0:     p[0] = 66;      // 1200 bit/s
+                break;
+    case 1:     p[0] = 88;      // 1200/75 bit/s
+                break;
+    case 2:     p[0] = 87;      // 75/1200 bit/s
+                break;
+    case 3:     p[0] = 67;      // 2400 bit/s
+                break;
+    case 4:     p[0] = 69;      // 4800 bit/s
+                break;
+    case 5:     p[0] = 72;      // 9600 bit/s
+                break;
+    case 6:     p[0] = 73;      // 14400 bit/s
+                break;
+    case 7:     p[0] = 75;      // 19200 bit/s
+                break;
+  }
+  return p+3;
+}
+
+u_char EncodeSyncParams(u_char si2, u_char ai)
+{
+
+  switch (si2)
+  {
+    case 0:     return ai + 2;  // 1200 bit/s
+    case 1:     return ai + 24; // 1200/75 bit/s
+    case 2:     return ai + 23; // 75/1200 bit/s
+    case 3:     return ai + 3;  // 2400 bit/s
+    case 4:     return ai + 5;  // 4800 bit/s
+    case 5:     return ai + 8;  // 9600 bit/s
+    case 6:     return ai + 9;  // 14400 bit/s
+    case 7:     return ai + 11; // 19200 bit/s
+    case 8:     return ai + 14; // 48000 bit/s
+    case 9:     return ai + 15; // 56000 bit/s
+    case 15:    return ai + 40; // negotiate bit/s
+    default:    break;
+  }
+  return ai;
+}
+
+
+static u_char DecodeASyncParams(u_char si2, u_char *p)
+{ u_char info;
+
+  switch (p[5])
+  {
+    case 66: // 1200 bit/s
+             break; // si2 bleibt gleich
+    case 88: // 1200/75 bit/s
+             si2 += 1;
+             break;
+    case 87: // 75/1200 bit/s
+             si2 += 2;
+             break;
+    case 67: // 2400 bit/s
+             si2 += 3;
+             break;
+    case 69: // 4800 bit/s
+             si2 += 4;
+             break;
+    case 72: // 9600 bit/s
+             si2 += 5;
+             break;
+    case 73: // 14400 bit/s
+             si2 += 6;
+             break;
+    case 75: // 19200 bit/s
+             si2 += 7;
+             break;
+  }
+
+  info = p[7] & 0x7f;
+  if ((info & 16) && (!(info & 8)))   // 7 data bits
+    si2 += 32;                        // else 8 data bits
+  if ((info & 96) == 96)              // 2 stop bits
+    si2 += 16;                        // else 1 stop bit
+  if ((info & 2) && (!(info & 1)))    // even parity
+    si2 += 8;                         // else no parity
+
+  return si2;
+}
+
+
+static u_char DecodeSyncParams(u_char si2, u_char info)
+{
+  info &= 0x7f;
+  switch (info)
+  {
+    case 40: // bit/s aushandeln  --- hat nicht geklappt, ai wird 165 statt 175!
+      return si2 + 15;
+    case 15: // 56000 bit/s --- hat nicht geklappt, ai wird 0 statt 169 !
+      return si2 + 9;
+    case 14: // 48000 bit/s
+      return si2 + 8;
+    case 11: // 19200 bit/s
+      return si2 + 7;
+    case 9:  // 14400 bit/s
+      return si2 + 6;
+    case 8:  // 9600  bit/s
+      return si2 + 5;
+    case 5:  // 4800  bit/s
+      return si2 + 4;
+    case 3:  // 2400  bit/s
+      return si2 + 3;
+    case 23: // 75/1200 bit/s
+      return si2 + 2;
+    case 24: // 1200/75 bit/s
+      return si2 + 1;
+    default: // 1200 bit/s
+      return si2;
+  }
+}
+
+static u_char DecodeSI2(struct sk_buff *skb)
+{ u_char *p; //, *pend=skb->data + skb->len;
+
+        if ((p = findie(skb->data, skb->len, 0x7c, 0)))
+        {
+          switch (p[4] & 0x0f)
+          {
+            case 0x01:  if (p[1] == 0x04) // sync. Bitratenadaption
+                          return DecodeSyncParams(160, p[5]); // V.110/X.30
+                        else if (p[1] == 0x06) // async. Bitratenadaption
+                          return DecodeASyncParams(192, p);   // V.110/X.30
+                        break;
+            case 0x08:  // if (p[5] == 0x02) // sync. Bitratenadaption
+                          return DecodeSyncParams(176, p[5]); // V.120
+                        break;
+          }
+        }
+        return 0;
 }
 
+#endif
+
+
 static void
-l3dss1_setup_req(struct PStack *st, u_char pr,
+l3dss1_setup_req(struct l3_process *pc, u_char pr,
 		 void *arg)
 {
 	struct sk_buff *skb;
 	u_char tmp[128];
 	u_char *p = tmp;
 	u_char channel = 0;
-	u_char screen = 0;
+	u_char screen = 0x80;
 	u_char *teln;
 	u_char *msn;
+	u_char *sub;
+	u_char *sp;
 	int l;
 
-	st->l3.callref = st->pa->callref;
-	MsgHead(p, st->l3.callref, MT_SETUP);
+	MsgHead(p, pc->callref, MT_SETUP);
 
 	/*
 	 * Set Bearer Capability, Map info from 1TR6-convention to EDSS1
 	 */
+#ifdef HISAX_EURO_SENDCOMPLETE
 	*p++ = 0xa1;		/* complete indicator */
-	switch (st->pa->setup.si1) {
+#endif
+	switch (pc->para.setup.si1) {
 		case 1:	/* Telephony                               */
 			*p++ = 0x4;	/* BC-IE-code                              */
 			*p++ = 0x3;	/* Length                                  */
@@ -157,7 +521,7 @@
 	/*
 	 * What about info2? Mapping to High-Layer-Compatibility?
 	 */
-	teln = st->pa->setup.phone;
+	teln = pc->para.setup.phone;
 	if (*teln) {
 		/* parse number for special things */
 		if (!isdigit(*teln)) {
@@ -179,19 +543,28 @@
 					screen = 0x80;
 					break;
 				default:
-					if (st->l3.debug & L3_DEB_WARN)
-						l3_debug(st, "Wrong MSN Code");
+					if (pc->debug & L3_DEB_WARN)
+						l3_debug(pc->st, "Wrong MSN Code");
 					break;
 			}
 			teln++;
 		}
 	}
 	if (channel) {
-		*p++ = 0x18;	/* channel indicator */
+		*p++ = IE_CHANNEL_ID;
 		*p++ = 1;
 		*p++ = channel;
 	}
-	msn = st->pa->setup.eazmsn;
+	msn = pc->para.setup.eazmsn;
+	sub = NULL;
+	sp = msn;
+	while (*sp) { 
+		if ('.' == *sp) {
+			sub = sp;
+			*sp = 0;
+		} else 
+			sp++;
+	}
 	if (*msn) {
 		*p++ = 0x6c;
 		*p++ = strlen(msn) + (screen ? 2 : 1);
@@ -204,241 +577,372 @@
 		while (*msn)
 			*p++ = *msn++ & 0x7f;
 	}
+	if (sub) {
+		*sub++ = '.';
+		*p++ = 0x6d; /* Calling party subaddress */
+ 		*p++ = strlen(sub) + 2;
+		*p++ = 0x80;	/* NSAP coded */
+		*p++ = 0x50;	/* local IDI format */
+ 		while (*sub)
+			*p++ = *sub++ & 0x7f;
+	}
+	sub = NULL;
+	sp = teln;
+	while (*sp) { 
+		if ('.' == *sp) {
+			sub = sp;
+			*sp = 0;
+		} else 
+			sp++;
+	}
 	*p++ = 0x70;
 	*p++ = strlen(teln) + 1;
 	/* Classify as AnyPref. */
 	*p++ = 0x81;		/* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
-
 	while (*teln)
 		*p++ = *teln++ & 0x7f;
 
+	if (sub) {
+		*sub++ = '.';
+		*p++ = 0x71; /* Called party subaddress */
+ 		*p++ = strlen(sub) + 2;
+		*p++ = 0x80;	/* NSAP coded */
+		*p++ = 0x50;	/* local IDI format */
+ 		while (*sub)
+			*p++ = *sub++ & 0x7f;
+	}
+
+#ifdef EXT_BEARER_CAPS
+        if ((pc->para.setup.si2 >= 160) && (pc->para.setup.si2 <= 175))
+        { // sync. Bitratenadaption, V.110/X.30
+          *p++ = 0x7c; *p++ = 0x04; *p++ = 0x88; *p++ = 0x90; *p++ = 0x21;
+          *p++ = EncodeSyncParams(pc->para.setup.si2 - 160, 0x80);
+        }
+        else if ((pc->para.setup.si2 >= 176) && (pc->para.setup.si2 <= 191))
+        { // sync. Bitratenadaption, V.120
+          *p++ = 0x7c; *p++ = 0x05; *p++ = 0x88; *p++ = 0x90; *p++ = 0x28;
+          *p++ = EncodeSyncParams(pc->para.setup.si2 - 176, 0);
+          *p++ = 0x82;
+        }
+        else if (pc->para.setup.si2 >= 192)
+        { // async. Bitratenadaption, V.110/X.30
+          *p++ = 0x7c; *p++ = 0x06; *p++ = 0x88; *p++ = 0x90; *p++ = 0x21;
+          p = EncodeASyncParams(p, pc->para.setup.si2 - 192);
+        }
+#endif
 	l = p - tmp;
 	if (!(skb = l3_alloc_skb(l)))
 		return;
 	memcpy(skb_put(skb, l), tmp, l);
-	L3DelTimer(&st->l3.timer);
-	L3AddTimer(&st->l3.timer, st->l3.t303, CC_T303);
-	newl3state(st, 1);
-	st->l3.l3l2(st, DL_DATA, skb);
-
+	L3DelTimer(&pc->timer);
+	L3AddTimer(&pc->timer, T303, CC_T303);
+	newl3state(pc, 1);
+	pc->st->l3.l3l2(pc->st, DL_DATA, skb);
 }
 
 static void
-l3dss1_call_proc(struct PStack *st, u_char pr, void *arg)
+l3dss1_call_proc(struct l3_process *pc, u_char pr, void *arg)
 {
 	u_char *p;
 	struct sk_buff *skb = arg;
 
-	L3DelTimer(&st->l3.timer);
+	L3DelTimer(&pc->timer);
 	p = skb->data;
-	if ((p = findie(p, skb->len, 0x18, 0))) {
-		st->pa->bchannel = p[2] & 0x3;
-		if ((!st->pa->bchannel) && (st->l3.debug & L3_DEB_WARN))
-			l3_debug(st, "setup answer without bchannel");
-	} else if (st->l3.debug & L3_DEB_WARN)
-		l3_debug(st, "setup answer without bchannel");
-	SET_SKB_FREE(skb);
+	if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) {
+		pc->para.bchannel = p[2] & 0x3;
+		if ((!pc->para.bchannel) && (pc->debug & L3_DEB_WARN))
+			l3_debug(pc->st, "setup answer without bchannel");
+	} else if (pc->debug & L3_DEB_WARN)
+		l3_debug(pc->st, "setup answer without bchannel");
 	dev_kfree_skb(skb);
-	newl3state(st, 3);
-	L3AddTimer(&st->l3.timer, st->l3.t310, CC_T310);
-	st->l3.l3l4(st, CC_PROCEEDING_IND, NULL);
+	newl3state(pc, 3);
+	L3AddTimer(&pc->timer, T310, CC_T310);
+	pc->st->l3.l3l4(pc, CC_PROCEEDING_IND, NULL);
 }
 
 static void
-l3dss1_setup_ack(struct PStack *st, u_char pr, void *arg)
+l3dss1_setup_ack(struct l3_process *pc, u_char pr, void *arg)
 {
 	u_char *p;
 	struct sk_buff *skb = arg;
 
-	L3DelTimer(&st->l3.timer);
+	L3DelTimer(&pc->timer);
 	p = skb->data;
-	if ((p = findie(p, skb->len, 0x18, 0))) {
-		st->pa->bchannel = p[2] & 0x3;
-		if ((!st->pa->bchannel) && (st->l3.debug & L3_DEB_WARN))
-			l3_debug(st, "setup answer without bchannel");
-	} else if (st->l3.debug & L3_DEB_WARN)
-		l3_debug(st, "setup answer without bchannel");
-	SET_SKB_FREE(skb);
+	if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) {
+		pc->para.bchannel = p[2] & 0x3;
+		if ((!pc->para.bchannel) && (pc->debug & L3_DEB_WARN))
+			l3_debug(pc->st, "setup answer without bchannel");
+	} else if (pc->debug & L3_DEB_WARN)
+		l3_debug(pc->st, "setup answer without bchannel");
 	dev_kfree_skb(skb);
-	newl3state(st, 2);
-	L3AddTimer(&st->l3.timer, st->l3.t304, CC_T304);
-	st->l3.l3l4(st, CC_MORE_INFO, NULL);
+	newl3state(pc, 2);
+	L3AddTimer(&pc->timer, T304, CC_T304);
+	pc->st->l3.l3l4(pc, CC_MORE_INFO, NULL);
 }
 
 static void
-l3dss1_disconnect(struct PStack *st, u_char pr, void *arg)
+l3dss1_disconnect(struct l3_process *pc, u_char pr, void *arg)
 {
 	u_char *p;
 	struct sk_buff *skb = arg;
 	int cause = -1;
 
-	StopAllL3Timer(st);
+	StopAllL3Timer(pc);
 	p = skb->data;
-	st->pa->loc = 0;
+	pc->para.loc = 0;
 	if ((p = findie(p, skb->len, IE_CAUSE, 0))) {
 		p++;
 		if (*p++ == 2)
-			st->pa->loc = *p++;
+			pc->para.loc = *p++;
 		cause = *p & 0x7f;
 	}
-	SET_SKB_FREE(skb);
 	dev_kfree_skb(skb);
-	newl3state(st, 12);
-	st->pa->cause = cause;
-	st->l3.l3l4(st, CC_DISCONNECT_IND, NULL);
+	newl3state(pc, 12);
+	pc->para.cause = cause;
+	pc->st->l3.l3l4(pc, CC_DISCONNECT_IND, NULL);
 }
 
 static void
-l3dss1_connect(struct PStack *st, u_char pr, void *arg)
+l3dss1_connect(struct l3_process *pc, u_char pr, void *arg)
 {
 	struct sk_buff *skb = arg;
 
-	SET_SKB_FREE(skb);
 	dev_kfree_skb(skb);
-	L3DelTimer(&st->l3.timer);	/* T310 */
-	newl3state(st, 10);
-	st->l3.l3l4(st, CC_SETUP_CNF, NULL);
+	L3DelTimer(&pc->timer);	/* T310 */
+	newl3state(pc, 10);
+	pc->para.chargeinfo = 0;
+	pc->st->l3.l3l4(pc, CC_SETUP_CNF, NULL);
 }
 
 static void
-l3dss1_alerting(struct PStack *st, u_char pr, void *arg)
+l3dss1_alerting(struct l3_process *pc, u_char pr, void *arg)
 {
 	struct sk_buff *skb = arg;
 
-	SET_SKB_FREE(skb);
 	dev_kfree_skb(skb);
-	L3DelTimer(&st->l3.timer);	/* T304 */
-	newl3state(st, 4);
-	st->l3.l3l4(st, CC_ALERTING_IND, NULL);
+	L3DelTimer(&pc->timer);	/* T304 */
+	newl3state(pc, 4);
+	pc->st->l3.l3l4(pc, CC_ALERTING_IND, NULL);
 }
 
 static void
-l3dss1_setup(struct PStack *st, u_char pr, void *arg)
+l3dss1_msg_without_setup(struct l3_process *pc, u_char pr, void *arg)
 {
-	u_char *p;
+  /* This routine is called if here was no SETUP made (checks in dss1up and in
+   * l3dss1_setup) and a RELEASE_COMPLETE have to be sent with an error code
+   * It is called after it is veryfied that Layer2 is up.
+   * The cause value is allready in pc->para.cause
+   * MT_STATUS_ENQUIRE in the NULL state is handled too
+   */
+	u_char tmp[16];
+	u_char *p=tmp;
+	int l;
+	struct sk_buff *skb;
+
+	switch (pc->para.cause) {
+	  case  81: /* 0x51 invalid callreference */
+	  case  96: /* 0x60 mandory IE missing */
+	  case 101: /* 0x65 incompatible Callstate */
+	  	MsgHead(p, pc->callref, MT_RELEASE_COMPLETE);
+		*p++ = IE_CAUSE;
+		*p++ = 0x2;
+		*p++ = 0x80;
+		*p++ = pc->para.cause | 0x80;
+		break;
+	  default:
+	  	printk(KERN_ERR "HiSax internal error l3dss1_msg_without_setup\n");
+	  	return;
+	}	
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	pc->st->l3.l3l2(pc->st, DL_DATA, skb);
+	release_l3_process(pc);
+}
+
+static void
+l3dss1_setup(struct l3_process *pc, u_char pr, void *arg)
+{
+        u_char *p, *ptmp[8];
+	int i;
 	int bcfound = 0;
 	char tmp[80];
 	struct sk_buff *skb = arg;
 
+	/* ETS 300-104 1.3.4 and 1.3.5
+	 * we need to detect unknown inform. element from 0 to 7
+	 */	
 	p = skb->data;
-	st->pa->callref = getcallref(p);
-	st->l3.callref = 0x80 + st->pa->callref;
+	for(i = 0; i < 8; i++)
+		ptmp[i] = skb->data;
+	if (findie(ptmp[1], skb->len, 0x01, 0)
+	    || findie(ptmp[2], skb->len, 0x02, 0)
+	    || findie(ptmp[3], skb->len, 0x03, 0)
+	    || findie(ptmp[5], skb->len, 0x05, 0)
+	    || findie(ptmp[6], skb->len, 0x06, 0)
+	    || findie(ptmp[7], skb->len, 0x07, 0)) {
+	  	/* if ie is < 8 and not 0 nor 4, send RELEASE_COMPLETE 
+	  	 * cause 0x60
+	  	 */
+	  	pc->para.cause = 0x60;
+		dev_kfree_skb(skb);
+		if (pc->state == 0)
+			pc->st->l3.l3l4(pc, CC_ESTABLISH, NULL);
+		else
+			l3dss1_msg_without_setup(pc, pr, NULL);
+		return;
+	}
 
 	/*
 	 * Channel Identification
 	 */
 	p = skb->data;
-	if ((p = findie(p, skb->len, 0x18, 0))) {
-		st->pa->bchannel = p[2] & 0x3;
-		if (st->pa->bchannel)
+	if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) {
+		pc->para.bchannel = p[2] & 0x3;
+		if (pc->para.bchannel)
 			bcfound++;
-		else if (st->l3.debug & L3_DEB_WARN)
-			l3_debug(st, "setup without bchannel");
-	} else if (st->l3.debug & L3_DEB_WARN)
-		l3_debug(st, "setup without bchannel");
+		else if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "setup without bchannel");
+	} else if (pc->debug & L3_DEB_WARN)
+		l3_debug(pc->st, "setup without bchannel");
 
 	/*
 	   * Bearer Capabilities
 	 */
 	p = skb->data;
 	if ((p = findie(p, skb->len, 0x04, 0))) {
-		st->pa->setup.si2 = 0;
+		pc->para.setup.si2 = 0;
 		switch (p[2] & 0x1f) {
 			case 0x00:
 				/* Speech */
 			case 0x10:
 				/* 3.1 Khz audio */
-				st->pa->setup.si1 = 1;
+				pc->para.setup.si1 = 1;
 				break;
 			case 0x08:
 				/* Unrestricted digital information */
-				st->pa->setup.si1 = 7;
+				pc->para.setup.si1 = 7;
+/* JIM, 05.11.97 I wanna set service indicator 2 */
+#ifdef EXT_BEARER_CAPS
+                                pc->para.setup.si2 = DecodeSI2(skb);
+                                printk(KERN_DEBUG "HiSax: SI=%d, AI=%d\n",
+                                       pc->para.setup.si1, pc->para.setup.si2);
+#endif
 				break;
 			case 0x09:
 				/* Restricted digital information */
-				st->pa->setup.si1 = 2;
+				pc->para.setup.si1 = 2;
 				break;
 			case 0x11:
 				/* Unrestr. digital information  with tones/announcements */
-				st->pa->setup.si1 = 3;
+				pc->para.setup.si1 = 3;
 				break;
 			case 0x18:
 				/* Video */
-				st->pa->setup.si1 = 4;
+				pc->para.setup.si1 = 4;
 				break;
 			default:
-				st->pa->setup.si1 = 0;
+				pc->para.setup.si1 = 0;
 		}
-	} else if (st->l3.debug & L3_DEB_WARN)
-		l3_debug(st, "setup without bearer capabilities");
+	} else {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "setup without bearer capabilities");
+		/* ETS 300-104 1.3.3 */
+	  	pc->para.cause = 0x60;
+		dev_kfree_skb(skb);
+		if (pc->state == 0)
+			pc->st->l3.l3l4(pc, CC_ESTABLISH, NULL);
+		else
+			l3dss1_msg_without_setup(pc, pr, NULL);
+		return;
+	}
 
 	p = skb->data;
 	if ((p = findie(p, skb->len, 0x70, 0)))
-		iecpy(st->pa->setup.eazmsn, p, 1);
+		iecpy(pc->para.setup.eazmsn, p, 1);
 	else
-		st->pa->setup.eazmsn[0] = 0;
+		pc->para.setup.eazmsn[0] = 0;
 
 	p = skb->data;
+	if ((p = findie(p, skb->len, 0x71, 0))) {
+		/* Called party subaddress */
+		if ((p[1]>=2) && (p[2]==0x80) && (p[3]==0x50)) {
+			tmp[0]='.';
+			iecpy(&tmp[1], p, 2);
+			strcat(pc->para.setup.eazmsn, tmp);
+		} else if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "wrong called subaddress");
+	}
+	p = skb->data;
 	if ((p = findie(p, skb->len, 0x6c, 0))) {
-		st->pa->setup.plan = p[2];
+		pc->para.setup.plan = p[2];
 		if (p[2] & 0x80) {
-			iecpy(st->pa->setup.phone, p, 1);
-			st->pa->setup.screen = 0;
+			iecpy(pc->para.setup.phone, p, 1);
+			pc->para.setup.screen = 0;
 		} else {
-			iecpy(st->pa->setup.phone, p, 2);
-			st->pa->setup.screen = p[3];
+			iecpy(pc->para.setup.phone, p, 2);
+			pc->para.setup.screen = p[3];
 		}
 	} else {
-		st->pa->setup.phone[0] = 0;
-		st->pa->setup.plan = 0;
-		st->pa->setup.screen = 0;
+		pc->para.setup.phone[0] = 0;
+		pc->para.setup.plan = 0;
+		pc->para.setup.screen = 0;
 	}
-	SET_SKB_FREE(skb);
+	p = skb->data;
+	if ((p = findie(p, skb->len, 0x6d, 0))) {
+		/* Calling party subaddress */
+		if ((p[1]>=2) && (p[2]==0x80) && (p[3]==0x50)) {
+			tmp[0]='.';
+			iecpy(&tmp[1], p, 2);
+			strcat(pc->para.setup.phone, tmp);
+		} else if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "wrong calling subaddress");
+	}
+
 	dev_kfree_skb(skb);
 
 	if (bcfound) {
-		if ((st->pa->setup.si1 != 7) && (st->l3.debug & L3_DEB_WARN)) {
+		if ((pc->para.setup.si1 != 7) && (pc->debug & L3_DEB_WARN)) {
 			sprintf(tmp, "non-digital call: %s -> %s",
-				st->pa->setup.phone,
-				st->pa->setup.eazmsn);
-			l3_debug(st, tmp);
+				pc->para.setup.phone, pc->para.setup.eazmsn);
+			l3_debug(pc->st, tmp);
 		}
-		newl3state(st, 6);
-		st->l3.l3l4(st, CC_SETUP_IND, NULL);
-	}
+		newl3state(pc, 6);
+		pc->st->l3.l3l4(pc, CC_SETUP_IND, NULL);
+	} else
+		release_l3_process(pc);
 }
 
 static void
-l3dss1_reset(struct PStack *st, u_char pr, void *arg)
+l3dss1_reset(struct l3_process *pc, u_char pr, void *arg)
 {
-	StopAllL3Timer(st);
-	newl3state(st, 0);
+	release_l3_process(pc);
 }
 
 static void
-l3dss1_setup_rsp(struct PStack *st, u_char pr,
+l3dss1_setup_rsp(struct l3_process *pc, u_char pr,
 		 void *arg)
 {
-	newl3state(st, 8);
-	l3dss1_message(st, MT_CONNECT);
-	L3DelTimer(&st->l3.timer);
-	L3AddTimer(&st->l3.timer, st->l3.t313, CC_T313);
+	newl3state(pc, 8);
+	l3dss1_message(pc, MT_CONNECT);
+	L3DelTimer(&pc->timer);
+	L3AddTimer(&pc->timer, T313, CC_T313);
 }
 
 static void
-l3dss1_connect_ack(struct PStack *st, u_char pr, void *arg)
+l3dss1_connect_ack(struct l3_process *pc, u_char pr, void *arg)
 {
 	struct sk_buff *skb = arg;
 
-	SET_SKB_FREE(skb);
 	dev_kfree_skb(skb);
-	newl3state(st, 10);
-	L3DelTimer(&st->l3.timer);
-	st->l3.l3l4(st, CC_SETUP_COMPLETE_IND, NULL);
+	newl3state(pc, 10);
+	L3DelTimer(&pc->timer);
+	pc->st->l3.l3l4(pc, CC_SETUP_COMPLETE_IND, NULL);
 }
 
 static void
-l3dss1_disconnect_req(struct PStack *st, u_char pr, void *arg)
+l3dss1_disconnect_req(struct l3_process *pc, u_char pr, void *arg)
 {
 	struct sk_buff *skb;
 	u_char tmp[16];
@@ -446,12 +950,12 @@
 	int l;
 	u_char cause = 0x10;
 
-	if (st->pa->cause > 0)
-		cause = st->pa->cause;
+	if (pc->para.cause > 0)
+		cause = pc->para.cause;
 
-	StopAllL3Timer(st);
+	StopAllL3Timer(pc);
 
-	MsgHead(p, st->l3.callref, MT_DISCONNECT);
+	MsgHead(p, pc->callref, MT_DISCONNECT);
 
 	*p++ = IE_CAUSE;
 	*p++ = 0x2;
@@ -462,13 +966,13 @@
 	if (!(skb = l3_alloc_skb(l)))
 		return;
 	memcpy(skb_put(skb, l), tmp, l);
-	newl3state(st, 11);
-	st->l3.l3l2(st, DL_DATA, skb);
-	L3AddTimer(&st->l3.timer, st->l3.t305, CC_T305);
+	newl3state(pc, 11);
+	pc->st->l3.l3l2(pc->st, DL_DATA, skb);
+	L3AddTimer(&pc->timer, T305, CC_T305);
 }
 
 static void
-l3dss1_reject_req(struct PStack *st, u_char pr, void *arg)
+l3dss1_reject_req(struct l3_process *pc, u_char pr, void *arg)
 {
 	struct sk_buff *skb;
 	u_char tmp[16];
@@ -476,10 +980,10 @@
 	int l;
 	u_char cause = 0x95;
 
-	if (st->pa->cause > 0)
-		cause = st->pa->cause;
+	if (pc->para.cause > 0)
+		cause = pc->para.cause;
 
-	MsgHead(p, st->l3.callref, MT_RELEASE_COMPLETE);
+	MsgHead(p, pc->callref, MT_RELEASE_COMPLETE);
 
 	*p++ = IE_CAUSE;
 	*p++ = 0x2;
@@ -490,13 +994,14 @@
 	if (!(skb = l3_alloc_skb(l)))
 		return;
 	memcpy(skb_put(skb, l), tmp, l);
-	newl3state(st, 0);
-	st->l3.l3l2(st, DL_DATA, skb);
-	st->l3.l3l4(st, CC_RELEASE_IND, NULL);
+	pc->st->l3.l3l2(pc->st, DL_DATA, skb);
+	pc->st->l3.l3l4(pc, CC_RELEASE_IND, NULL);
+	newl3state(pc, 0);
+	release_l3_process(pc);
 }
 
 static void
-l3dss1_release(struct PStack *st, u_char pr, void *arg)
+l3dss1_release(struct l3_process *pc, u_char pr, void *arg)
 {
 	u_char *p;
 	struct sk_buff *skb = arg;
@@ -506,38 +1011,45 @@
 	if ((p = findie(p, skb->len, IE_CAUSE, 0))) {
 		p++;
 		if (*p++ == 2)
-			st->pa->loc = *p++;
+			pc->para.loc = *p++;
 		cause = *p & 0x7f;
 	}
-	SET_SKB_FREE(skb);
+	p = skb->data;
+	if ((p = findie(p, skb->len, IE_FACILITY, 0))) {
+#ifdef HISAX_DE_AOC
+	    l3dss1_parse_facility(pc,p);
+#else
+		p = NULL;
+#endif
+	}
 	dev_kfree_skb(skb);
-	StopAllL3Timer(st);
-	st->pa->cause = cause;
-	newl3state(st, 0);
-	l3dss1_message(st, MT_RELEASE_COMPLETE);
-	st->l3.l3l4(st, CC_RELEASE_IND, NULL);
+	StopAllL3Timer(pc);
+	pc->para.cause = cause;
+	l3dss1_message(pc, MT_RELEASE_COMPLETE);
+	pc->st->l3.l3l4(pc, CC_RELEASE_IND, NULL);
+	newl3state(pc, 0);
+	release_l3_process(pc);
 }
 
 static void
-l3dss1_alert_req(struct PStack *st, u_char pr,
+l3dss1_alert_req(struct l3_process *pc, u_char pr,
 		 void *arg)
 {
-	newl3state(st, 7);
-	l3dss1_message(st, MT_ALERTING);
+	newl3state(pc, 7);
+	l3dss1_message(pc, MT_ALERTING);
 }
 
 static void
-l3dss1_status_enq(struct PStack *st, u_char pr, void *arg)
+l3dss1_status_enq(struct l3_process *pc, u_char pr, void *arg)
 {
 	u_char tmp[16];
 	u_char *p = tmp;
 	int l;
 	struct sk_buff *skb = arg;
 
-	SET_SKB_FREE(skb);
 	dev_kfree_skb(skb);
 
-	MsgHead(p, st->l3.callref, MT_STATUS);
+	MsgHead(p, pc->callref, MT_STATUS);
 
 	*p++ = IE_CAUSE;
 	*p++ = 0x2;
@@ -546,42 +1058,96 @@
 
 	*p++ = 0x14;		/* CallState */
 	*p++ = 0x1;
-	*p++ = st->l3.state & 0x3f;
+	*p++ = pc->state & 0x3f;
 
 	l = p - tmp;
 	if (!(skb = l3_alloc_skb(l)))
 		return;
 	memcpy(skb_put(skb, l), tmp, l);
-	st->l3.l3l2(st, DL_DATA, skb);
+	pc->st->l3.l3l2(pc->st, DL_DATA, skb);
 }
 
 static void
-l3dss1_t303(struct PStack *st, u_char pr, void *arg)
+l3dss1_status_req(struct l3_process *pc, u_char pr, void *arg)
 {
-	if (st->l3.n_t303 > 0) {
-		st->l3.n_t303--;
-		L3DelTimer(&st->l3.timer);
-		l3dss1_setup_req(st, pr, arg);
+  /* ETS 300-104 7.4.1, 8.4.1, 10.3.1, 11.4.1, 12.4.1, 13.4.1, 14.4.1...
+     if setup has been made and a non expected message type is received, we must send MT_STATUS cause 0x62  */
+        u_char tmp[16];
+	u_char *p = tmp;
+	int l;
+	struct sk_buff *skb = arg;
+
+	dev_kfree_skb(skb);
+
+	MsgHead(p, pc->callref, MT_STATUS);
+
+	*p++ = IE_CAUSE;
+	*p++ = 0x2;
+	*p++ = 0x80;
+	*p++ = 0x62 | 0x80;		/* status sending */
+
+	*p++ = 0x14;		/* CallState */
+	*p++ = 0x1;
+	*p++ = pc->state & 0x3f;
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	pc->st->l3.l3l2(pc->st, DL_DATA, skb);
+}
+
+static void
+l3dss1_release_ind(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char *p;
+	struct sk_buff *skb = arg;
+	int callState = 0;
+	p = skb->data;
+
+	if ((p = findie(p, skb->len, IE_CALL_STATE, 0))) {
+		p++;
+		if (1== *p++)
+			callState = *p;
+	}
+	if(callState == 0) {
+		/* ETS 300-104 7.6.1, 8.6.1, 10.6.1... and 16.1
+		 * set down layer 3 without sending any message
+		 */
+		pc->st->l3.l3l4(pc, CC_RELEASE_IND, NULL);
+		newl3state(pc, 0);
+		release_l3_process(pc);
 	} else {
-		newl3state(st, 0);
-		L3DelTimer(&st->l3.timer);
-		st->l3.l3l4(st, CC_NOSETUP_RSP_ERR, NULL);
-		st->l3.n_t303 = 1;
+		pc->st->l3.l3l4(pc, CC_IGNORE, NULL);
 	}
 }
 
 static void
-l3dss1_t304(struct PStack *st, u_char pr, void *arg)
+l3dss1_t303(struct l3_process *pc, u_char pr, void *arg)
 {
-	L3DelTimer(&st->l3.timer);
-	st->pa->cause = 0xE6;
-	l3dss1_disconnect_req(st, pr, NULL);
-	st->l3.l3l4(st, CC_SETUP_ERR, NULL);
+	if (pc->N303 > 0) {
+		pc->N303--;
+		L3DelTimer(&pc->timer);
+		l3dss1_setup_req(pc, pr, arg);
+	} else {
+		L3DelTimer(&pc->timer);
+		pc->st->l3.l3l4(pc, CC_NOSETUP_RSP_ERR, NULL);
+		release_l3_process(pc);
+	}
+}
+
+static void
+l3dss1_t304(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.cause = 0xE6;
+	l3dss1_disconnect_req(pc, pr, NULL);
+	pc->st->l3.l3l4(pc, CC_SETUP_ERR, NULL);
 
 }
 
 static void
-l3dss1_t305(struct PStack *st, u_char pr, void *arg)
+l3dss1_t305(struct l3_process *pc, u_char pr, void *arg)
 {
 	u_char tmp[16];
 	u_char *p = tmp;
@@ -589,11 +1155,11 @@
 	struct sk_buff *skb;
 	u_char cause = 0x90;
 
-	L3DelTimer(&st->l3.timer);
-	if (st->pa->cause > 0)
-		cause = st->pa->cause;
+	L3DelTimer(&pc->timer);
+	if (pc->para.cause > 0)
+		cause = pc->para.cause | 0x80;
 
-	MsgHead(p, st->l3.callref, MT_RELEASE);
+	MsgHead(p, pc->callref, MT_RELEASE);
 
 	*p++ = IE_CAUSE;
 	*p++ = 0x2;
@@ -604,49 +1170,180 @@
 	if (!(skb = l3_alloc_skb(l)))
 		return;
 	memcpy(skb_put(skb, l), tmp, l);
-	newl3state(st, 19);
-	st->l3.l3l2(st, DL_DATA, skb);
-	L3AddTimer(&st->l3.timer, st->l3.t308, CC_T308_1);
+	newl3state(pc, 19);
+	pc->st->l3.l3l2(pc->st, DL_DATA, skb);
+	L3AddTimer(&pc->timer, T308, CC_T308_1);
 }
 
 static void
-l3dss1_t310(struct PStack *st, u_char pr, void *arg)
+l3dss1_t310(struct l3_process *pc, u_char pr, void *arg)
 {
-	L3DelTimer(&st->l3.timer);
-	st->pa->cause = 0xE6;
-	l3dss1_disconnect_req(st, pr, NULL);
-	st->l3.l3l4(st, CC_SETUP_ERR, NULL);
+	L3DelTimer(&pc->timer);
+	pc->para.cause = 0xE6;
+	l3dss1_disconnect_req(pc, pr, NULL);
+	pc->st->l3.l3l4(pc, CC_SETUP_ERR, NULL);
 }
 
 static void
-l3dss1_t313(struct PStack *st, u_char pr, void *arg)
+l3dss1_t313(struct l3_process *pc, u_char pr, void *arg)
 {
-	L3DelTimer(&st->l3.timer);
-	st->pa->cause = 0xE6;
-	l3dss1_disconnect_req(st, pr, NULL);
-	st->l3.l3l4(st, CC_CONNECT_ERR, NULL);
+	L3DelTimer(&pc->timer);
+	pc->para.cause = 0xE6;
+	l3dss1_disconnect_req(pc, pr, NULL);
+	pc->st->l3.l3l4(pc, CC_CONNECT_ERR, NULL);
 }
 
 static void
-l3dss1_t308_1(struct PStack *st, u_char pr, void *arg)
+l3dss1_t308_1(struct l3_process *pc, u_char pr, void *arg)
 {
-	newl3state(st, 19);
-	L3DelTimer(&st->l3.timer);
-	l3dss1_message(st, MT_RELEASE);
-	L3AddTimer(&st->l3.timer, st->l3.t308, CC_T308_2);
+	newl3state(pc, 19);
+	L3DelTimer(&pc->timer);
+	l3dss1_message(pc, MT_RELEASE);
+	L3AddTimer(&pc->timer, T308, CC_T308_2);
 }
 
 static void
-l3dss1_t308_2(struct PStack *st, u_char pr, void *arg)
+l3dss1_t308_2(struct l3_process *pc, u_char pr, void *arg)
 {
-	newl3state(st, 0);
-	L3DelTimer(&st->l3.timer);
-	st->l3.l3l4(st, CC_RELEASE_ERR, NULL);
+	L3DelTimer(&pc->timer);
+	pc->st->l3.l3l4(pc, CC_RELEASE_ERR, NULL);
+	release_l3_process(pc);
 }
+
+static void
+l3dss1_restart(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->st->l3.l3l4(pc, CC_DLRL, NULL);
+	release_l3_process(pc);
+}
+
+static void
+l3dss1_status(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char *p;
+	char tmp[64], *t;
+	int l;
+	struct sk_buff *skb = arg;
+	int cause, callState;
+
+	cause = callState = -1;
+	p = skb->data;
+	t = tmp;
+	if ((p = findie(p, skb->len, IE_CAUSE, 0))) {
+		p++;
+		l = *p++;
+		t += sprintf(t,"Status CR %x Cause:", pc->callref);
+		while (l--) {
+		        cause = *p;
+			t += sprintf(t," %2x",*p++);
+		}
+	} else
+		sprintf(t,"Status CR %x no Cause", pc->callref);
+	l3_debug(pc->st, tmp);
+	p = skb->data;
+	t = tmp;
+	t += sprintf(t,"Status state %x ", pc->state);
+	if ((p = findie(p, skb->len, IE_CALL_STATE, 0))) {
+		p++;
+		if (1== *p++) {
+		        callState = *p;
+			t += sprintf(t,"peer state %x" , *p);
+		}
+		else
+			t += sprintf(t,"peer state len error");
+	} else
+		sprintf(t,"no peer state");
+	l3_debug(pc->st, tmp);
+	if(((cause & 0x7f) == 0x6f) && (callState == 0)) {
+		/* ETS 300-104 7.6.1, 8.6.1, 10.6.1... 
+		 * if received MT_STATUS with cause == 0x6f and call 
+		 * state == 0, then we must set down layer 3
+		 */
+		l3dss1_release_ind(pc, pr, arg);
+	} else
+		dev_kfree_skb(skb);
+}
+
+static void
+l3dss1_facility(struct l3_process *pc, u_char pr, void *arg)
+{
+        u_char *p;
+	struct sk_buff *skb = arg;
+
+	p = skb->data;
+	if ((p = findie(p, skb->len, IE_FACILITY, 0))) {
+#ifdef HISAX_DE_AOC
+	    l3dss1_parse_facility(pc,p);
+#else
+		p = NULL;
+#endif
+	}
+}
+
+
+	
+static void
+l3dss1_global_restart(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char tmp[32];
+	u_char *p;
+	u_char ri, chan=0;
+	int l;
+	struct sk_buff *skb = arg;
+	struct l3_process *up;
+	
+	newl3state(pc, 2);
+	L3DelTimer(&pc->timer);
+	p = skb->data;
+	if ((p = findie(p, skb->len, IE_RESTART_IND, 0))) {
+	        ri = p[2];
+	        sprintf(tmp, "Restart %x", ri);
+	} else {
+		sprintf(tmp, "Restart without restart IE");
+		ri = 0x86;
+	}
+	l3_debug(pc->st, tmp);
+	p = skb->data;
+	if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) {
+		chan = p[2] & 3;
+		sprintf(tmp, "Restart for channel %d", chan);
+		l3_debug(pc->st, tmp);
+	}
+	dev_kfree_skb(skb);
+	newl3state(pc, 2);
+	up = pc->st->l3.proc;
+	while (up) {
+		if ((ri & 7)==7)
+			up->st->lli.l4l3(up->st, CC_RESTART, up);
+		else if (up->para.bchannel == chan)
+				up->st->lli.l4l3(up->st, CC_RESTART, up);
+		up = up->next;
+	}
+	p = tmp;
+	MsgHead(p, pc->callref, MT_RESTART_ACKNOWLEDGE);
+	if (chan) {
+		*p++ = IE_CHANNEL_ID;
+		*p++ = 1;
+		*p++ = chan | 0x80;
+	}
+	*p++ = 0x79; /* RESTART Ind */
+	*p++ = 1;
+	*p++ = ri;
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	newl3state(pc, 0);
+	pc->st->l3.l3l2(pc->st, DL_DATA, skb);
+}
+
 /* *INDENT-OFF* */
 static struct stateentry downstatelist[] =
 {
 	{SBIT(0),
+	 CC_ESTABLISH, l3dss1_msg_without_setup},
+	{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},
@@ -654,6 +1351,8 @@
 	 CC_RELEASE_REQ, l3dss1_release_req},
 	{ALL_STATES,
 	 CC_DLRL, l3dss1_reset},
+	{ALL_STATES,
+	 CC_RESTART, l3dss1_restart},
 	{SBIT(6),
 	 CC_IGNORE, l3dss1_reset},
 	{SBIT(6),
@@ -685,63 +1384,216 @@
 {
 	{ALL_STATES,
 	 MT_STATUS_ENQUIRY, l3dss1_status_enq},
+	{ALL_STATES,
+	 MT_FACILITY, l3dss1_facility},
+	{SBIT(19),
+	 MT_STATUS, l3dss1_release_ind},
+	{ALL_STATES,
+	 MT_STATUS, l3dss1_status},
 	{SBIT(0) | SBIT(6),
 	 MT_SETUP, l3dss1_setup},
 	{SBIT(1) | SBIT(2),
 	 MT_CALL_PROCEEDING, l3dss1_call_proc},
+	{SBIT(3) | SBIT(4) | SBIT(8) | SBIT(10) | SBIT(11) | SBIT(19),
+	 MT_CALL_PROCEEDING, l3dss1_status_req},
 	{SBIT(1),
 	 MT_SETUP_ACKNOWLEDGE, l3dss1_setup_ack},
+	{SBIT(2) | SBIT(3) | SBIT(4) | SBIT(8) | SBIT(10) | SBIT(11) | SBIT(19),
+	 MT_SETUP_ACKNOWLEDGE, l3dss1_status_req},
 	{SBIT(1) | SBIT(2) | SBIT(3),
 	 MT_ALERTING, l3dss1_alerting},
+	{SBIT(4) | SBIT(8) | SBIT(10) | SBIT(11) | SBIT(19),
+	 MT_ALERTING, l3dss1_status_req},
 	{SBIT(0) | SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10) |
 	 SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19),
 	 MT_RELEASE_COMPLETE, l3dss1_release_cmpl},
-	{SBIT(0) | SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10) |
-	 SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19),
+	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10) |
+	 SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) /*| SBIT(19)*/,
 	 MT_RELEASE, l3dss1_release},
+	{SBIT(19),  MT_RELEASE, l3dss1_release_ind},
 	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10),
 	 MT_DISCONNECT, l3dss1_disconnect},
+	{SBIT(11),
+	 MT_DISCONNECT, l3dss1_release_req},
 	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4),
 	 MT_CONNECT, l3dss1_connect},
+	{SBIT(8) | SBIT(10) | SBIT(11) | SBIT(19),
+	 MT_CONNECT, l3dss1_status_req},
+	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(11) | SBIT(19),
+	 MT_CONNECT_ACKNOWLEDGE, l3dss1_status_req},
 	{SBIT(8),
 	 MT_CONNECT_ACKNOWLEDGE, l3dss1_connect_ack},
+	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(8) | SBIT(10) | SBIT(11) | SBIT(19),
+	 MT_INVALID, l3dss1_status_req},
 };
-/* *INDENT-ON* */
 
+static int datasllen = sizeof(datastatelist) / sizeof(struct stateentry);
 
-static int datasllen = sizeof(datastatelist) /
-sizeof(struct stateentry);
+static struct stateentry globalmes_list[] =
+{
+	{ALL_STATES,
+         MT_STATUS, l3dss1_status},
+	{SBIT(0),
+	 MT_RESTART, l3dss1_global_restart},
+/*	{SBIT(1),
+	 MT_RESTART_ACKNOWLEDGE, l3dss1_restart_ack},                                  
+*/
+};
+static int globalm_len = sizeof(globalmes_list) / sizeof(struct stateentry);
+
+#if 0
+static struct stateentry globalcmd_list[] =
+{
+	{ALL_STATES,
+         CC_STATUS, l3dss1_status_req},
+	{SBIT(0),
+	 CC_RESTART, l3dss1_restart_req},
+};
+
+static int globalc_len = sizeof(globalcmd_list) / sizeof(struct stateentry);
+#endif
+/* *INDENT-ON* */
+
+static void
+global_handler(struct PStack *st, int mt, struct sk_buff *skb)
+{
+	int i;
+	char tmp[64];
+	struct l3_process *proc = st->l3.global;
+	
+	for (i = 0; i < globalm_len; i++)
+		if ((mt == globalmes_list[i].primitive) &&
+		    ((1 << proc->state) & globalmes_list[i].state))
+			break;
+	if (i == globalm_len) {
+		dev_kfree_skb(skb);
+		if (st->l3.debug & L3_DEB_STATE) {
+			sprintf(tmp, "dss1 global state %d mt %x unhandled",
+				proc->state, mt);
+			l3_debug(st, tmp);
+		}
+		return;
+	} else {
+		if (st->l3.debug & L3_DEB_STATE) {
+			sprintf(tmp, "dss1 global %d mt %x",
+				proc->state, mt);
+			l3_debug(st, tmp);
+		}
+		globalmes_list[i].rout(proc, mt, skb);
+	}
+}
 
 static void
 dss1up(struct PStack *st, int pr, void *arg)
 {
-	int i, mt;
+	int i, mt, cr, cause, callState;
+	char *ptr;
 	struct sk_buff *skb = arg;
+	struct l3_process *proc;
 	char tmp[80];
 
 	if (skb->data[0] != PROTO_DIS_EURO) {
 		if (st->l3.debug & L3_DEB_PROTERR) {
-			sprintf(tmp, "dss1up%sunexpected discriminator %x message len %d state %d",
+			sprintf(tmp, "dss1up%sunexpected discriminator %x message len %d",
 				(pr == DL_DATA) ? " " : "(broadcast) ",
-				skb->data[0], skb->len, st->l3.state);
+				skb->data[0], skb->len);
 			l3_debug(st, tmp);
 		}
-		SET_SKB_FREE(skb);
 		dev_kfree_skb(skb);
 		return;
 	}
+	cr = getcallref(skb->data);
 	mt = skb->data[skb->data[1] + 2];
+	if (!cr) {				/* Global CallRef */
+		global_handler(st, mt, skb);
+		return;
+	} else if (cr == -1) {			/* Dummy Callref */
+		dev_kfree_skb(skb);
+		return;
+	} else if (!(proc = getl3proc(st, cr))) {
+		/* No transaction process exist, that means no call with
+		 * this callreference is active
+		 */
+		if (mt == MT_SETUP) {
+		/* Setup creates a new transaction process */
+			if (!(proc = new_l3_process(st, cr))) {
+				/* May be to answer with RELEASE_COMPLETE and
+				 * CAUSE 0x2f "Resource unavailable", but this
+				 * need a new_l3_process too ... arghh
+				 */
+				dev_kfree_skb(skb);
+				return;
+			}
+		} else if (mt == MT_STATUS) {
+			cause = 0;
+			if((ptr = findie(skb->data, skb->len, IE_CAUSE, 0)) != NULL) {
+				  ptr++;
+				  if (*ptr++ == 2)
+				  	ptr++;
+				  cause = *ptr & 0x7f;
+			}
+			callState = 0;
+			if((ptr = findie(skb->data, skb->len, IE_CALL_STATE, 0)) != NULL) {
+				ptr++;
+				if (*ptr++ == 2)
+					ptr++;
+				callState = *ptr;
+			}
+			if (callState == 0) {
+				/* ETS 300-104 part 2.4.1
+				 * if setup has not been made and a message type
+				 * MT_STATUS is received with call state == 0,
+				 * we must send nothing
+				 */
+				dev_kfree_skb(skb);
+				return;
+			} else {
+				/* ETS 300-104 part 2.4.2
+				 * if setup has not been made and a message type 
+				 * MT_STATUS is received with call state != 0,
+				 * we must send MT_RELEASE_COMPLETE cause 101
+				 */
+				dev_kfree_skb(skb);
+				if ((proc = new_l3_process(st, cr))) {
+					proc->para.cause = 0x65; /* 101 */
+					proc->st->l3.l3l4(proc, CC_ESTABLISH, NULL);
+				}
+				return;
+			}
+		} else if (mt == MT_RELEASE_COMPLETE){
+			dev_kfree_skb(skb);
+			return;
+		} else {
+			/* ETS 300-104 part 2
+			 * if setup has not been made and a message type 
+			 * (except MT_SETUP and RELEASE_COMPLETE) is received,
+			 * we must send MT_RELEASE_COMPLETE cause 81 */
+			dev_kfree_skb(skb);
+			if ((proc = new_l3_process(st, cr))) {
+				proc->para.cause = 0x51; /* 81 */
+				proc->st->l3.l3l4(proc, CC_ESTABLISH, NULL);
+			}
+			return;
+		}
+	} else if (!l3dss1_check_messagetype_validity(mt)) {
+		/* ETS 300-104 7.4.2, 8.4.2, 10.3.2, 11.4.2, 12.4.2, 13.4.2,
+		 * 14.4.2...
+		 * if setup has been made and invalid message type is received,
+		 * we must send MT_STATUS cause 0x62
+		 */
+		mt = MT_INVALID;  /* sorry, not clean, but do the right thing ;-) */
+	}
+
 	for (i = 0; i < datasllen; i++)
 		if ((mt == datastatelist[i].primitive) &&
-		    ((1 << st->l3.state) & datastatelist[i].state))
+		    ((1 << proc->state) & datastatelist[i].state))
 			break;
 	if (i == datasllen) {
-		SET_SKB_FREE(skb);
 		dev_kfree_skb(skb);
 		if (st->l3.debug & L3_DEB_STATE) {
 			sprintf(tmp, "dss1up%sstate %d mt %x unhandled",
 				(pr == DL_DATA) ? " " : "(broadcast) ",
-				st->l3.state, mt);
+				proc->state, mt);
 			l3_debug(st, tmp);
 		}
 		return;
@@ -749,36 +1601,55 @@
 		if (st->l3.debug & L3_DEB_STATE) {
 			sprintf(tmp, "dss1up%sstate %d mt %x",
 				(pr == DL_DATA) ? " " : "(broadcast) ",
-				st->l3.state, mt);
+				proc->state, mt);
 			l3_debug(st, tmp);
 		}
-		datastatelist[i].rout(st, pr, skb);
+		datastatelist[i].rout(proc, pr, skb);
 	}
 }
 
 static void
 dss1down(struct PStack *st, int pr, void *arg)
 {
-	int i;
+	int i, cr;
+	struct l3_process *proc;
+	struct Channel *chan;
 	char tmp[80];
 
+	if (CC_SETUP_REQ == pr) {
+		chan = arg;
+		cr = newcallref();
+		cr |= 0x80;
+		if ((proc = new_l3_process(st, cr))) {
+			proc->chan = chan;
+			chan->proc = proc;
+			proc->para.setup = chan->setup;
+			proc->callref = cr;
+		}
+	} else {
+		proc = arg;
+	}
+	if (!proc) {
+		printk(KERN_ERR "HiSax internal error dss1down without proc\n");
+		return;
+	}
 	for (i = 0; i < downsllen; i++)
 		if ((pr == downstatelist[i].primitive) &&
-		    ((1 << st->l3.state) & downstatelist[i].state))
+		    ((1 << proc->state) & downstatelist[i].state))
 			break;
 	if (i == downsllen) {
 		if (st->l3.debug & L3_DEB_STATE) {
 			sprintf(tmp, "dss1down state %d prim %d unhandled",
-				st->l3.state, pr);
+				proc->state, pr);
 			l3_debug(st, tmp);
 		}
 	} else {
 		if (st->l3.debug & L3_DEB_STATE) {
 			sprintf(tmp, "dss1down state %d prim %d",
-				st->l3.state, pr);
+				proc->state, pr);
 			l3_debug(st, tmp);
 		}
-		downstatelist[i].rout(st, pr, arg);
+		downstatelist[i].rout(proc, pr, arg);
 	}
 }
 
@@ -787,20 +1658,20 @@
 {
 	char tmp[64];
 
-	st->l4.l4l3 = dss1down;
+	st->lli.l4l3 = dss1down;
 	st->l2.l2l3 = dss1up;
-	st->l3.t303 = 4000;
-	st->l3.t304 = 30000;
-	st->l3.t305 = 30000;
-	st->l3.t308 = 4000;
-	st->l3.t310 = 30000;
-	st->l3.t313 = 4000;
-	st->l3.t318 = 4000;
-	st->l3.t319 = 4000;
-	st->l3.n_t303 = 1;
-
-	if (st->l3.channr & 1) {
-		strcpy(tmp, dss1_revision);
-		printk(KERN_NOTICE "HiSax: DSS1 Rev. %s\n", HiSax_getrev(tmp));
+	st->l3.N303 = 1;
+	if (!(st->l3.global = kmalloc(sizeof(struct l3_process), GFP_ATOMIC))) {
+		printk(KERN_ERR "HiSax can't get memory for dss1 global CR\n");
+	} else {
+		st->l3.global->state = 0;
+		st->l3.global->callref = 0;
+		st->l3.global->next = NULL;
+		st->l3.global->debug = L3_DEB_WARN;
+		st->l3.global->st = st;
+		st->l3.global->N303 = 1;
+		L3InitTimer(st->l3.global, &st->l3.global->timer);
 	}
+	strcpy(tmp, dss1_revision);
+	printk(KERN_INFO "HiSax: DSS1 Rev. %s\n", HiSax_getrev(tmp));
 }
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/l3dss1.h linux/drivers/isdn/hisax/l3dss1.h
--- v2.1.91/linux/drivers/isdn/hisax/l3dss1.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/l3dss1.h	Wed Apr  1 16:21:02 1998
@@ -0,0 +1,71 @@
+/* $Id: l3dss1.h,v 1.5 1998/02/02 13:34:30 keil Exp $
+ *
+ *  DSS1 (Euro) D-channel protocol defines
+ *
+ * $Log: l3dss1.h,v $
+ * Revision 1.5  1998/02/02 13:34:30  keil
+ * Support australian Microlink net and german AOCD
+ *
+ * Revision 1.4  1997/10/29 19:07:54  keil
+ * changes for 2.1
+ *
+ * Revision 1.3  1997/08/07 17:44:37  keil
+ * Fix RESTART
+ *
+ * Revision 1.2  1997/08/03 14:36:34  keil
+ * Implement RESTART procedure
+ *
+ * Revision 1.1  1997/07/27 21:08:38  keil
+ * new
+ *
+ *
+ *
+ */
+#define T303	4000
+#define T304	30000
+#define T305	30000
+#define T308	4000
+#define T310	30000
+#define T313	4000
+#define T318	4000
+#define T319	4000
+
+/*
+ * Message-Types
+ */
+
+#define MT_ALERTING            0x01
+#define MT_CALL_PROCEEDING     0x02
+#define MT_CONNECT             0x07
+#define MT_CONNECT_ACKNOWLEDGE 0x0f
+#define MT_PROGRESS            0x03
+#define MT_SETUP               0x05
+#define MT_SETUP_ACKNOWLEDGE   0x0d
+#define MT_RESUME              0x26
+#define MT_RESUME_ACKNOWLEDGE  0x2e
+#define MT_RESUME_REJECT       0x22
+#define MT_SUSPEND             0x25
+#define MT_SUSPEND_ACKNOWLEDGE 0x2d
+#define MT_SUSPEND_REJECT      0x21
+#define MT_USER_INFORMATION    0x20
+#define MT_DISCONNECT          0x45
+#define MT_RELEASE             0x4d
+#define MT_RELEASE_COMPLETE    0x5a
+#define MT_RESTART             0x46
+#define MT_RESTART_ACKNOWLEDGE 0x4e
+#define MT_SEGMENT             0x60
+#define MT_CONGESTION_CONTROL  0x79
+#define MT_INFORMATION         0x7b
+#define MT_FACILITY            0x62
+#define MT_NOTIFY              0x6e
+#define MT_STATUS              0x7d
+#define MT_STATUS_ENQUIRY      0x75
+
+#define MT_INVALID             0xff
+
+#define IE_CAUSE               0x08
+#define IE_BEARER              0x04
+#define IE_FACILITY            0x1c
+#define IE_CALL_STATE          0x14
+#define IE_CHANNEL_ID          0x18
+#define IE_RESTART_IND         0x79
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/lmgr.c linux/drivers/isdn/hisax/lmgr.c
--- v2.1.91/linux/drivers/isdn/hisax/lmgr.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/lmgr.c	Wed Apr  1 16:21:02 1998
@@ -0,0 +1,58 @@
+/* $Id: lmgr.c,v 1.2 1997/10/29 19:09:34 keil Exp $
+
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ *
+ *  Layermanagement module
+ *
+ * $Log: lmgr.c,v $
+ * Revision 1.2  1997/10/29 19:09:34  keil
+ * new L1
+ *
+ * Revision 1.1  1997/06/26 11:17:25  keil
+ * first version
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include "hisax.h"
+
+static void
+error_handling_dchan(struct PStack *st, int Error)
+{
+	switch (Error) {
+		case 'C':
+		case 'D':
+		case 'G':
+		case 'H':
+			st->l2.l2tei(st, MDL_ERROR_REQ, NULL);
+			break;
+	}
+}
+
+static void
+hisax_manager(struct PStack *st, int pr, void *arg)
+{
+	char tm[32], str[256];
+	int Code;
+
+	switch (pr) {
+		case MDL_ERROR_IND:
+			Code = (int) arg;
+			jiftime(tm, jiffies);
+			sprintf(str, "%s manager: MDL_ERROR %c %s\n", tm,
+				Code, test_bit(FLG_LAPD, &st->l2.flag) ?
+				"D-channel" : "B-channel");
+			HiSax_putstatus(st->l1.hardware, str);
+			if (test_bit(FLG_LAPD, &st->l2.flag))
+				error_handling_dchan(st, Code);
+			break;
+	}
+}
+
+void
+setstack_manager(struct PStack *st)
+{
+	st->ma.layer = hisax_manager;
+}
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/mic.c linux/drivers/isdn/hisax/mic.c
--- v2.1.91/linux/drivers/isdn/hisax/mic.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/mic.c	Wed Apr  1 16:21:02 1998
@@ -0,0 +1,284 @@
+/* $Id: mic.c,v 1.6 1998/02/17 15:39:57 keil Exp $
+
+ * mic.c  low level stuff for mic cards
+ *
+ * Copyright (C) 1997 
+ *
+ * Author  Stephan von Krawczynski <skraw@ithnet.com>
+ *
+ *
+ * $Log: mic.c,v $
+ * Revision 1.6  1998/02/17 15:39:57  keil
+ * fix reset problem
+ *
+ * Revision 1.5  1998/02/02 13:29:43  keil
+ * fast io
+ *
+ * Revision 1.4  1997/11/08 21:35:51  keil
+ * new l1 init
+ *
+ * Revision 1.3  1997/11/06 17:09:11  keil
+ * New 2.1 init code
+ *
+ * Revision 1.2  1997/10/29 18:51:17  keil
+ * New files
+ *
+ * Revision 1.1.2.1  1997/10/17 22:10:54  keil
+ * new files on 2.0
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+
+extern const char *CardType[];
+
+const char *mic_revision = "$Revision: 1.6 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define MIC_ISAC	2
+#define MIC_HSCX	1
+#define MIC_ADR		7
+
+/* CARD_ADR (Write) */
+#define MIC_RESET      0x3	/* same as DOS driver */
+
+static inline u_char
+readreg(unsigned int ale, unsigned int adr, u_char off)
+{
+	register u_char ret;
+	long flags;
+
+	save_flags(flags);
+	cli();
+	byteout(ale, off);
+	ret = bytein(adr);
+	restore_flags(flags);
+
+	return (ret);
+}
+
+static inline void
+readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	/* fifo read without cli because it's allready done  */
+
+	byteout(ale, off);
+	insb(adr, data, size);
+}
+
+
+static inline void
+writereg(unsigned int ale, unsigned int adr, u_char off, u_char data)
+{
+	long flags;
+
+	save_flags(flags);
+	cli();
+	byteout(ale, off);
+	byteout(adr, data);
+	restore_flags(flags);
+}
+
+static inline void
+writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	/* fifo write without cli because it's allready done  */
+	byteout(ale, off);
+	outsb(adr, data, size);
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.mic.adr, cs->hw.mic.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.mic.adr, cs->hw.mic.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	readfifo(cs->hw.mic.adr, cs->hw.mic.isac, 0, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	writefifo(cs->hw.mic.adr, cs->hw.mic.isac, 0, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return (readreg(cs->hw.mic.adr,
+			cs->hw.mic.hscx, offset + (hscx ? 0x40 : 0)));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	writereg(cs->hw.mic.adr,
+		 cs->hw.mic.hscx, offset + (hscx ? 0x40 : 0), value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.mic.adr, \
+		cs->hw.mic.hscx, reg + (nr ? 0x40 : 0))
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.mic.adr, \
+		cs->hw.mic.hscx, reg + (nr ? 0x40 : 0), data)
+
+#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.mic.adr, \
+		cs->hw.mic.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.mic.adr, \
+		cs->hw.mic.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#include "hscx_irq.c"
+
+static void
+mic_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val, stat = 0;
+
+	if (!cs) {
+		printk(KERN_WARNING "mic: Spurious interrupt!\n");
+		return;
+	}
+	val = readreg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_ISTA + 0x40);
+      Start_HSCX:
+	if (val) {
+		hscx_int_main(cs, val);
+		stat |= 1;
+	}
+	val = readreg(cs->hw.mic.adr, cs->hw.mic.isac, ISAC_ISTA);
+      Start_ISAC:
+	if (val) {
+		isac_interrupt(cs, val);
+		stat |= 2;
+	}
+	val = readreg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_ISTA + 0x40);
+	if (val) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX IntStat after IntRoutine");
+		goto Start_HSCX;
+	}
+	val = readreg(cs->hw.mic.adr, cs->hw.mic.isac, ISAC_ISTA);
+	if (val) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	if (stat & 1) {
+		writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK, 0xFF);
+		writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK + 0x40, 0xFF);
+		writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK, 0x0);
+		writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK + 0x40, 0x0);
+	}
+	if (stat & 2) {
+		writereg(cs->hw.mic.adr, cs->hw.mic.isac, ISAC_MASK, 0xFF);
+		writereg(cs->hw.mic.adr, cs->hw.mic.isac, ISAC_MASK, 0x0);
+	}
+}
+
+void
+release_io_mic(struct IsdnCardState *cs)
+{
+	int bytecnt = 8;
+
+	if (cs->hw.mic.cfg_reg)
+		release_region(cs->hw.mic.cfg_reg, bytecnt);
+}
+
+static int
+mic_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	switch (mt) {
+		case CARD_RESET:
+			return(0);
+		case CARD_RELEASE:
+			release_io_mic(cs);
+			return(0);
+		case CARD_SETIRQ:
+			return(request_irq(cs->irq, &mic_interrupt,
+					I4L_IRQ_FLAG, "HiSax", cs));
+		case CARD_INIT:
+			inithscx(cs); /* /RTSA := ISAC RST */
+			clear_pending_isac_ints(cs);
+			clear_pending_hscx_ints(cs);
+			initisac(cs);
+			inithscx(cs);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+__initfunc(int
+setup_mic(struct IsdnCard *card))
+{
+	int bytecnt;
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, mic_revision);
+	printk(KERN_INFO "HiSax: mic driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_MIC)
+		return (0);
+
+	bytecnt = 8;
+	cs->hw.mic.cfg_reg = card->para[1];
+	cs->irq = card->para[0];
+	cs->hw.mic.adr = cs->hw.mic.cfg_reg + MIC_ADR;
+	cs->hw.mic.isac = cs->hw.mic.cfg_reg + MIC_ISAC;
+	cs->hw.mic.hscx = cs->hw.mic.cfg_reg + MIC_HSCX;
+
+	if (check_region((cs->hw.mic.cfg_reg), bytecnt)) {
+		printk(KERN_WARNING
+		       "HiSax: %s config port %x-%x already in use\n",
+		       CardType[card->typ],
+		       cs->hw.mic.cfg_reg,
+		       cs->hw.mic.cfg_reg + bytecnt);
+		return (0);
+	} else {
+		request_region(cs->hw.mic.cfg_reg, bytecnt, "mic isdn");
+	}
+
+	printk(KERN_INFO
+	       "mic: defined at 0x%x IRQ %d\n",
+	       cs->hw.mic.cfg_reg,
+	       cs->irq);
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &mic_card_msg;
+	ISACVersion(cs, "mic:");
+	if (HscxVersion(cs, "mic:")) {
+		printk(KERN_WARNING
+		    "mic: wrong HSCX versions check IO address\n");
+		release_io_mic(cs);
+		return (0);
+	}
+	return (1);
+}
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/netjet.c linux/drivers/isdn/hisax/netjet.c
--- v2.1.91/linux/drivers/isdn/hisax/netjet.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/netjet.c	Wed Apr  1 16:21:02 1998
@@ -0,0 +1,1108 @@
+/* $Id: netjet.c,v 1.3 1998/02/12 23:08:05 keil Exp $
+
+ * netjet.c     low level stuff for Traverse Technologie NETJet ISDN cards
+ *
+ * Author     Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ * Thanks to Traverse Technologie Australia for documents and informations
+ *
+ *
+ * $Log: netjet.c,v $
+ * Revision 1.3  1998/02/12 23:08:05  keil
+ * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
+ *
+ * Revision 1.2  1998/02/02 13:32:06  keil
+ * New
+ *
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include <linux/config.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <linux/interrupt.h>
+#define fcstab  ppp_crc16_table
+#include <linux/ppp_defs.h>
+extern __u16 ppp_crc16_table[256]; /* from ppp code */
+
+extern const char *CardType[];
+
+const char *NETjet_revision = "$Revision: 1.3 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+/* PCI stuff */
+#define PCI_VENDOR_TRAVERSE_TECH 0xe159
+#define PCI_NETJET_ID	0x0001
+
+#define NETJET_CTRL	0x00
+#define NETJET_DMACTRL	0x01
+#define NETJET_AUXCTRL	0x02
+#define NETJET_AUXDATA	0x03
+#define NETJET_IRQMASK0 0x04
+#define NETJET_IRQMASK1 0x05
+#define NETJET_IRQSTAT0 0x06
+#define NETJET_IRQSTAT1 0x07
+#define NETJET_DMA_READ_START	0x08
+#define NETJET_DMA_READ_IRQ	0x0c
+#define NETJET_DMA_READ_END	0x10
+#define NETJET_DMA_READ_ADR	0x14
+#define NETJET_DMA_WRITE_START	0x18
+#define NETJET_DMA_WRITE_IRQ	0x1c
+#define NETJET_DMA_WRITE_END	0x20
+#define NETJET_DMA_WRITE_ADR	0x24
+#define NETJET_PULSE_CNT	0x28
+
+#define NETJET_ISAC_OFF	0xc0
+#define NETJET_ISACIRQ	0x10
+
+#define NETJET_DMA_SIZE 512
+
+#define HDLC_ZERO_SEARCH 0
+#define HDLC_FLAG_SEARCH 1
+#define HDLC_FLAG_FOUND  2
+#define HDLC_FRAME_FOUND 3
+#define HDLC_NULL 4
+#define HDLC_PART 5
+#define HDLC_FULL 6
+
+#define HDLC_FLAG_VALUE	0x7e
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	long flags;
+	u_char ret;
+	
+	save_flags(flags);
+	cli();
+	cs->hw.njet.auxd &= 0xfc;
+	cs->hw.njet.auxd |= (offset>>4) & 3;
+	byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+	ret = bytein(cs->hw.njet.isac + ((offset & 0xf)<<2));
+	restore_flags(flags);
+	return(ret);
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	long flags;
+	
+	save_flags(flags);
+	cli();
+	cs->hw.njet.auxd &= 0xfc;
+	cs->hw.njet.auxd |= (offset>>4) & 3;
+	byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+	byteout(cs->hw.njet.isac + ((offset & 0xf)<<2), value);
+	restore_flags(flags);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char *data, int size)
+{
+	cs->hw.njet.auxd &= 0xfc;
+	byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+	insb(cs->hw.njet.isac, data, size);
+}
+
+static void 
+WriteISACfifo(struct IsdnCardState *cs, u_char *data, int size)
+{
+	cs->hw.njet.auxd &= 0xfc;
+	byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+	outsb(cs->hw.njet.isac, data, size);
+}
+
+void fill_mem(struct BCState *bcs, u_int *pos, u_int cnt, int chan, u_char fill)
+{
+	u_int mask=0x000000ff, val = 0, *p=pos;
+	u_int i;
+	
+	val |= fill;
+	if (chan) {
+		val  <<= 8;
+		mask <<= 8;
+	}
+	mask ^= 0xffffffff;
+	for (i=0; i<cnt; i++) {
+		*p   &= mask;
+		*p++ |= val;
+		if (p > bcs->hw.tiger.s_end)
+			p = bcs->hw.tiger.send;
+	}
+}
+
+void
+mode_tiger(struct BCState *bcs, int mode, int bc)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	char tmp[64];
+
+	if (cs->debug & L1_DEB_HSCX) {
+		sprintf(tmp, "Tiger mode %d bchan %d/%d",
+			mode, bc, bcs->channel);
+		debugl1(cs, tmp);
+	}
+	bcs->mode = mode;
+	bcs->channel = bc;
+	switch (mode) {
+		case (L1_MODE_NULL):
+			fill_mem(bcs, bcs->hw.tiger.send,
+				NETJET_DMA_SIZE, bc, 0xff);
+			if (cs->debug & L1_DEB_HSCX) {
+				sprintf(tmp, "Tiger stat rec %d/%d send %d",
+					bcs->hw.tiger.r_tot, bcs->hw.tiger.r_err,
+					bcs->hw.tiger.s_tot); 
+				debugl1(cs, tmp);
+			}
+			if ((cs->bcs[0].mode == L1_MODE_NULL) &&
+				(cs->bcs[1].mode == L1_MODE_NULL)) {
+				cs->hw.njet.dmactrl = 0;
+				byteout(cs->hw.njet.base + NETJET_DMACTRL,
+					cs->hw.njet.dmactrl);
+				byteout(cs->hw.njet.base + NETJET_IRQMASK0, 0);
+			}
+			break;
+		case (L1_MODE_TRANS):
+			break;
+		case (L1_MODE_HDLC): 
+			fill_mem(bcs, bcs->hw.tiger.send,
+				NETJET_DMA_SIZE, bc, 0xff);
+			bcs->hw.tiger.r_state = HDLC_ZERO_SEARCH;
+			bcs->hw.tiger.r_tot = 0;
+			bcs->hw.tiger.r_bitcnt = 0;
+			bcs->hw.tiger.r_one = 0;
+			bcs->hw.tiger.r_err = 0;
+			bcs->hw.tiger.s_tot = 0;
+			if (! cs->hw.njet.dmactrl) {
+				fill_mem(bcs, bcs->hw.tiger.send,
+					NETJET_DMA_SIZE, !bc, 0xff);
+				cs->hw.njet.dmactrl = 1;
+				byteout(cs->hw.njet.base + NETJET_DMACTRL,
+					cs->hw.njet.dmactrl);
+				byteout(cs->hw.njet.base + NETJET_IRQMASK0, 0x3f);
+			}
+			bcs->hw.tiger.sendp = bcs->hw.tiger.send;
+			bcs->hw.tiger.free = NETJET_DMA_SIZE;
+			test_and_set_bit(BC_FLG_EMPTY, &bcs->Flag);
+			break;
+	}
+	if (cs->debug & L1_DEB_HSCX) {
+		sprintf(tmp, "tiger: set %x %x %x  %x/%x  pulse=%d",
+			bytein(cs->hw.njet.base + NETJET_DMACTRL),
+			bytein(cs->hw.njet.base + NETJET_IRQMASK0),
+			bytein(cs->hw.njet.base + NETJET_IRQSTAT0),
+			inl(cs->hw.njet.base + NETJET_DMA_READ_ADR),
+			inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR),
+			bytein(cs->hw.njet.base + NETJET_PULSE_CNT));
+		debugl1(cs, tmp);
+	}
+}
+
+static u_char dummyrr(struct IsdnCardState *cs, int chan, u_char off)
+{
+	return(5);
+}
+
+static void dummywr(struct IsdnCardState *cs, int chan, u_char off, u_char value)
+{
+}
+
+static void printframe(struct IsdnCardState *cs, u_char *buf, int count, char *s) {
+	char tmp[128];
+	char *t = tmp;
+	int i=count,j;
+	u_char *p = buf;
+
+	t += sprintf(t, "tiger %s(%4d)", s, count);
+	while (i>0) {
+		if (i>16)
+			j=16;
+		else
+			j=i;
+		QuickHex(t, p, j);
+		debugl1(cs, tmp);
+		p += j;
+		i -= j;
+		t = tmp;
+		t += sprintf(t, "tiger %s      ", s);
+	}
+}
+
+#define MAKE_RAW_BYTE for (j=0; j<8; j++) { \
+			bitcnt++;\
+			s_val >>= 1;\
+			if (val & 1) {\
+				s_one++;\
+				s_val |= 0x80;\
+			} else {\
+				s_one = 0;\
+				s_val &= 0x7f;\
+			}\
+			if (bitcnt==8) {\
+				bcs->hw.tiger.sendbuf[s_cnt++] = s_val;\
+				bitcnt = 0;\
+			}\
+			if (s_one == 5) {\
+				s_val >>= 1;\
+				s_val &= 0x7f;\
+				bitcnt++;\
+				s_one = 0;\
+			}\
+			if (bitcnt==8) {\
+				bcs->hw.tiger.sendbuf[s_cnt++] = s_val;\
+				bitcnt = 0;\
+			}\
+			val >>= 1;\
+		}
+
+static void make_raw_data(struct BCState *bcs) {
+	register u_int i,s_cnt=0;
+	register u_char j;
+	register u_char val;
+	register u_char s_one = 0;
+	register u_char s_val = 0;
+	register u_char bitcnt = 0;
+	u_int fcs;
+	char tmp[64];
+	
+	
+	bcs->hw.tiger.sendbuf[s_cnt++] = HDLC_FLAG_VALUE;
+	fcs = PPP_INITFCS;
+	for (i=0; i<bcs->hw.tiger.tx_skb->len; i++) {
+		val = bcs->hw.tiger.tx_skb->data[i];
+		fcs = PPP_FCS (fcs, val);
+		MAKE_RAW_BYTE;
+	}
+	fcs ^= 0xffff;
+	val = fcs & 0xff;
+	MAKE_RAW_BYTE;
+	val = (fcs>>8) & 0xff;
+	MAKE_RAW_BYTE;
+	val = HDLC_FLAG_VALUE;
+	for (j=0; j<8; j++) { 
+		bitcnt++;
+		s_val >>= 1;
+		if (val & 1)
+			s_val |= 0x80;
+		else
+			s_val &= 0x7f;
+		if (bitcnt==8) {
+			bcs->hw.tiger.sendbuf[s_cnt++] = s_val;
+			bitcnt = 0;
+		}
+		val >>= 1;
+	}
+	if (bcs->cs->debug & L1_DEB_HSCX) {
+		sprintf(tmp,"tiger make_raw: in %d out %d.%d",
+			bcs->hw.tiger.tx_skb->len, s_cnt, bitcnt);
+		debugl1(bcs->cs,tmp);
+	}
+	if (bitcnt) {
+		while (8>bitcnt++) {
+			s_val >>= 1;
+			s_val |= 0x80;
+		}
+		bcs->hw.tiger.sendbuf[s_cnt++] = s_val;
+	}
+	bcs->hw.tiger.sendcnt = s_cnt;
+	bcs->tx_cnt -= bcs->hw.tiger.tx_skb->len;
+	bcs->hw.tiger.sp = bcs->hw.tiger.sendbuf;
+}
+
+static void got_frame(struct BCState *bcs, int count) {
+	struct sk_buff *skb;
+		
+	if (!(skb = dev_alloc_skb(count)))
+		printk(KERN_WARNING "TIGER: receive out of memory\n");
+	else {
+		memcpy(skb_put(skb, count), bcs->hw.tiger.rcvbuf, count);
+		skb_queue_tail(&bcs->rqueue, skb);
+	}
+	bcs->event |= 1 << B_RCVBUFREADY;
+	queue_task(&bcs->tqueue, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+	
+	if (bcs->cs->debug & L1_DEB_RECEIVE_FRAME)
+		printframe(bcs->cs, bcs->hw.tiger.rcvbuf, count, "rec");
+}
+
+
+
+static void read_raw(struct BCState *bcs, u_int *buf, int cnt){
+	int i;
+	register u_char j;
+	register u_char val;
+	u_int  *pend = bcs->hw.tiger.rec +NETJET_DMA_SIZE -1;
+	register u_char state = bcs->hw.tiger.r_state;
+	register u_char r_one = bcs->hw.tiger.r_one;
+	register u_char r_val = bcs->hw.tiger.r_val;
+	register u_int bitcnt = bcs->hw.tiger.r_bitcnt;
+	u_int *p = buf;
+	char tmp[64];
+        
+	for (i=0;i<cnt;i++) {
+		val = bcs->channel ? ((*p>>8) & 0xff) : (*p & 0xff);
+		p++;
+		if (p > pend)
+			p = bcs->hw.tiger.rec;
+		if (val == 0xff) {
+			state = HDLC_ZERO_SEARCH;
+			bcs->hw.tiger.r_tot++;
+			bitcnt = 0;
+			r_one = 0;
+			continue;
+		}
+		for (j=0;j<8;j++) {
+			if (state == HDLC_ZERO_SEARCH) {
+				if (val & 1) {
+					r_one++;
+				} else {
+					r_one=0;
+					state= HDLC_FLAG_SEARCH;
+					if (bcs->cs->debug & L1_DEB_HSCX) {
+						sprintf(tmp,"tiger read_raw: zBit(%d,%d,%d) %x",
+							bcs->hw.tiger.r_tot,i,j,val);
+						debugl1(bcs->cs,tmp);
+					}
+				}
+			} else if (state == HDLC_FLAG_SEARCH) { 
+				if (val & 1) {
+					r_one++;
+					if (r_one>6) {
+						state=HDLC_ZERO_SEARCH;
+					}
+				} else {
+					if (r_one==6) {
+						bitcnt=0;
+						r_val=0;
+						state=HDLC_FLAG_FOUND;
+						if (bcs->cs->debug & L1_DEB_HSCX) {
+							sprintf(tmp,"tiger read_raw: flag(%d,%d,%d) %x",
+								bcs->hw.tiger.r_tot,i,j,val);
+							debugl1(bcs->cs,tmp);
+						}
+					}
+					r_one=0;
+				}
+			} else if (state ==  HDLC_FLAG_FOUND) {
+				if (val & 1) {
+					r_one++;
+					if (r_one>6) {
+						state=HDLC_ZERO_SEARCH;
+					} else {
+						r_val >>= 1;
+						r_val |= 0x80;
+						bitcnt++;
+					}
+				} else {
+					if (r_one==6) {
+						bitcnt=0;
+						r_val=0;
+						r_one=0;
+						val >>= 1;
+						continue;
+					} else if (r_one!=5) {
+						r_val >>= 1;
+						r_val &= 0x7f;
+						bitcnt++;
+					}
+					r_one=0;	
+				}
+				if ((state != HDLC_ZERO_SEARCH) &&
+					!(bitcnt & 7)) {
+					state=HDLC_FRAME_FOUND;
+					bcs->hw.tiger.r_fcs = PPP_INITFCS;
+					bcs->hw.tiger.rcvbuf[0] = r_val;
+					bcs->hw.tiger.r_fcs = PPP_FCS (bcs->hw.tiger.r_fcs, r_val);
+					if (bcs->cs->debug & L1_DEB_HSCX) {
+						sprintf(tmp,"tiger read_raw: byte1(%d,%d,%d) rval %x val %x i %x",
+							bcs->hw.tiger.r_tot,i,j,r_val,val,
+							bcs->cs->hw.njet.irqstat0);
+						debugl1(bcs->cs,tmp);
+					}
+				}
+			} else if (state ==  HDLC_FRAME_FOUND) {
+				if (val & 1) {
+					r_one++;
+					if (r_one>6) {
+						state=HDLC_ZERO_SEARCH;
+						bitcnt=0;
+					} else {
+						r_val >>= 1;
+						r_val |= 0x80;
+						bitcnt++;
+					}
+				} else {
+					if (r_one==6) {
+						r_val=0; 
+						r_one=0;
+						bitcnt++;
+						if (bitcnt & 7) {
+							debugl1(bcs->cs, "tiger: frame not byte aligned");
+							state=HDLC_FLAG_SEARCH;
+							bcs->hw.tiger.r_err++;
+						} else {
+							if (bcs->cs->debug & L1_DEB_HSCX) {
+								sprintf(tmp,"tiger frame end(%d,%d): fcs(%x) i %x",
+									i,j,bcs->hw.tiger.r_fcs, bcs->cs->hw.njet.irqstat0);
+								debugl1(bcs->cs, tmp);
+							}
+							if (bcs->hw.tiger.r_fcs == PPP_GOODFCS) {
+								got_frame(bcs, (bitcnt>>3)-3);
+							} else
+								if (bcs->cs->debug) {
+									debugl1(bcs->cs, "tiger FCS error");
+									printframe(bcs->cs, bcs->hw.tiger.rcvbuf,
+										(bitcnt>>3)-1, "rec");
+									bcs->hw.tiger.r_err++;
+								}
+							state=HDLC_FLAG_FOUND;
+						}
+						bitcnt=0;
+					} else if (r_one==5) {
+						val >>= 1;
+						r_one=0;
+						continue;
+					} else {
+						r_val >>= 1;
+						r_val &= 0x7f;
+						bitcnt++;
+					}
+					r_one=0;	
+				}
+				if ((state == HDLC_FRAME_FOUND) &&
+					!(bitcnt & 7)) {
+					if ((bitcnt>>3)>=HSCX_BUFMAX) {
+						debugl1(bcs->cs, "tiger: frame to big");
+						r_val=0; 
+						state=HDLC_FLAG_SEARCH;
+						bcs->hw.tiger.r_err++;
+					} else {
+						bcs->hw.tiger.rcvbuf[(bitcnt>>3)-1] = r_val;
+						bcs->hw.tiger.r_fcs = 
+							PPP_FCS (bcs->hw.tiger.r_fcs, r_val);
+					}
+				}
+			}
+			val >>= 1;
+		}
+		bcs->hw.tiger.r_tot++;
+	}
+	bcs->hw.tiger.r_state = state;
+	bcs->hw.tiger.r_one = r_one;
+	bcs->hw.tiger.r_val = r_val;
+	bcs->hw.tiger.r_bitcnt = bitcnt;
+}
+
+static void read_tiger(struct IsdnCardState *cs) {
+	u_int *p;
+	int cnt = NETJET_DMA_SIZE/2;
+	
+	if (cs->hw.njet.irqstat0 & 4)
+		p = cs->bcs[0].hw.tiger.rec + NETJET_DMA_SIZE - 1;
+	else
+		p = cs->bcs[0].hw.tiger.rec + cnt - 1;
+	if (cs->bcs[0].mode == L1_MODE_HDLC)
+		read_raw(cs->bcs, p, cnt);
+	if (cs->bcs[1].mode == L1_MODE_HDLC)
+		read_raw(cs->bcs + 1, p, cnt);
+	cs->hw.njet.irqstat0 &= 0xf3;
+}
+
+static void write_raw(struct BCState *bcs, u_int *buf, int cnt);
+
+static void fill_dma(struct BCState *bcs)
+{
+	char tmp[64];
+	register u_int *p, *sp;
+	register int cnt;
+
+	if (!bcs->hw.tiger.tx_skb)
+		return;
+	if (bcs->cs->debug & L1_DEB_HSCX) {
+		sprintf(tmp,"tiger fill_dma1: c%d %4x", bcs->channel,
+			bcs->Flag);
+			debugl1(bcs->cs,tmp);
+	}
+	if (test_and_set_bit(BC_FLG_BUSY, &bcs->Flag))
+		return;
+	make_raw_data(bcs);
+	if (bcs->cs->debug & L1_DEB_HSCX) {
+		sprintf(tmp,"tiger fill_dma2: c%d %4x", bcs->channel,
+			bcs->Flag);
+			debugl1(bcs->cs,tmp);
+	}
+	if (test_and_clear_bit(BC_FLG_NOFRAME, &bcs->Flag)) {
+		write_raw(bcs, bcs->hw.tiger.sendp, bcs->hw.tiger.free);
+	} else if (test_and_clear_bit(BC_FLG_HALF, &bcs->Flag)) {
+		p = bus_to_virt(inl(bcs->cs->hw.njet.base + NETJET_DMA_READ_ADR));
+		sp = bcs->hw.tiger.sendp;
+		if (p == bcs->hw.tiger.s_end)
+			p = bcs->hw.tiger.send -1;
+		if (sp == bcs->hw.tiger.s_end)
+			sp = bcs->hw.tiger.send -1;
+		cnt = p - sp;
+		if (cnt <0) {
+			write_raw(bcs, bcs->hw.tiger.sendp, bcs->hw.tiger.free);
+		} else {
+			p++;
+			cnt++;
+			if (p > bcs->hw.tiger.s_end)
+				p = bcs->hw.tiger.send;
+			p++;
+			cnt++;
+			if (p > bcs->hw.tiger.s_end)
+				p = bcs->hw.tiger.send;
+			write_raw(bcs, p, bcs->hw.tiger.free - cnt);
+		}
+	} else if (test_and_clear_bit(BC_FLG_EMPTY, &bcs->Flag)) {
+		p = bus_to_virt(inl(bcs->cs->hw.njet.base + NETJET_DMA_READ_ADR));
+		cnt = bcs->hw.tiger.s_end - p;
+		if (cnt < 2) {
+			p = bcs->hw.tiger.send + 1;
+			cnt = NETJET_DMA_SIZE/2 - 2;
+		} else {
+			p++;
+			p++;
+			if (cnt <= (NETJET_DMA_SIZE/2))
+				cnt += NETJET_DMA_SIZE/2;
+			cnt--;
+			cnt--;
+		}
+		write_raw(bcs, p, cnt);
+	}
+	if (bcs->cs->debug & L1_DEB_HSCX) {
+		sprintf(tmp,"tiger fill_dma3: c%d %4x", bcs->channel,
+			bcs->Flag);
+		debugl1(bcs->cs,tmp);
+	}
+}
+
+static void write_raw(struct BCState *bcs, u_int *buf, int cnt) {
+	u_int mask, val, *p=buf;
+	u_int i, s_cnt;
+	char tmp[64];
+        
+        if (cnt <= 0)
+        	return;
+	if (test_bit(BC_FLG_BUSY, &bcs->Flag)) {
+		if (bcs->hw.tiger.sendcnt> cnt) {
+			s_cnt = cnt;
+			bcs->hw.tiger.sendcnt -= cnt;
+		} else {
+			s_cnt = bcs->hw.tiger.sendcnt;
+			bcs->hw.tiger.sendcnt = 0;
+		}
+		if (bcs->channel)
+			mask = 0xffff00ff;
+		else
+			mask = 0xffffff00;
+		for (i=0; i<s_cnt; i++) {
+			val = bcs->channel ? ((bcs->hw.tiger.sp[i] <<8) & 0xff00) :
+				(bcs->hw.tiger.sp[i]);
+			*p   &= mask;
+			*p++ |= val;
+			if (p>bcs->hw.tiger.s_end)
+				p = bcs->hw.tiger.send;
+		}
+		bcs->hw.tiger.s_tot += s_cnt;
+		if (bcs->cs->debug & L1_DEB_HSCX) {
+			sprintf(tmp,"tiger write_raw: c%d %x-%x %d/%d %d %x", bcs->channel,
+			(u_int)buf, (u_int)p, s_cnt, cnt, bcs->hw.tiger.sendcnt,
+			bcs->cs->hw.njet.irqstat0);
+			debugl1(bcs->cs,tmp);
+		}
+		if (bcs->cs->debug & L1_DEB_HSCX_FIFO)
+			printframe(bcs->cs, bcs->hw.tiger.sp, s_cnt, "snd");
+		bcs->hw.tiger.sp += s_cnt;
+		bcs->hw.tiger.sendp = p;
+		if (!bcs->hw.tiger.sendcnt) {
+			if (!bcs->hw.tiger.tx_skb) {
+				sprintf(tmp,"tiger write_raw: NULL skb s_cnt %d", s_cnt);
+				debugl1(bcs->cs, tmp);
+			} else {
+				if (bcs->st->lli.l1writewakeup &&
+					(PACKET_NOACK != bcs->hw.tiger.tx_skb->pkt_type))
+					bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.tiger.tx_skb->len);
+				dev_kfree_skb(bcs->hw.tiger.tx_skb);
+				bcs->hw.tiger.tx_skb = NULL;
+			}
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			bcs->hw.tiger.free = cnt - s_cnt;
+			if (bcs->hw.tiger.free > (NETJET_DMA_SIZE/2))
+				test_and_set_bit(BC_FLG_HALF, &bcs->Flag);
+			else {
+				test_and_clear_bit(BC_FLG_HALF, &bcs->Flag);
+				test_and_set_bit(BC_FLG_NOFRAME, &bcs->Flag);
+			}
+			if ((bcs->hw.tiger.tx_skb = skb_dequeue(&bcs->squeue))) {
+				fill_dma(bcs);
+			} else {
+				mask ^= 0xffffffff;
+				if (s_cnt < cnt) {
+					for (i=s_cnt; i<cnt;i++) {
+						*p++ |= mask;
+						if (p>bcs->hw.tiger.s_end)
+							p = bcs->hw.tiger.send;
+					}
+					if (bcs->cs->debug & L1_DEB_HSCX) {
+						sprintf(tmp, "tiger write_raw: fill rest %d",
+							cnt - s_cnt);
+						debugl1(bcs->cs,tmp);
+					}
+				}
+				bcs->event |= 1 << B_XMTBUFREADY;
+				queue_task(&bcs->tqueue, &tq_immediate);
+				mark_bh(IMMEDIATE_BH);
+			}
+		}
+	} else if (test_and_clear_bit(BC_FLG_NOFRAME, &bcs->Flag)) {
+		test_and_set_bit(BC_FLG_HALF, &bcs->Flag);
+		fill_mem(bcs, buf, cnt, bcs->channel, 0xff);
+		bcs->hw.tiger.free += cnt;
+		if (bcs->cs->debug & L1_DEB_HSCX) {
+			sprintf(tmp,"tiger write_raw: fill half");
+			debugl1(bcs->cs,tmp);
+		}
+	} else if (test_and_clear_bit(BC_FLG_HALF, &bcs->Flag)) {
+		test_and_set_bit(BC_FLG_EMPTY, &bcs->Flag);
+		fill_mem(bcs, buf, cnt, bcs->channel, 0xff);
+		if (bcs->cs->debug & L1_DEB_HSCX) {
+			sprintf(tmp,"tiger write_raw: fill full");
+			debugl1(bcs->cs,tmp);
+		}
+	}
+}
+
+static void write_tiger(struct IsdnCardState *cs) {
+	u_int *p, cnt = NETJET_DMA_SIZE/2;
+	
+	if (cs->hw.njet.irqstat0  & 1)
+		p = cs->bcs[0].hw.tiger.send + NETJET_DMA_SIZE - 1;
+	else
+		p = cs->bcs[0].hw.tiger.send + cnt - 1;
+	if (cs->bcs[0].mode == L1_MODE_HDLC)
+		write_raw(cs->bcs, p, cnt);
+	if (cs->bcs[1].mode == L1_MODE_HDLC)
+		write_raw(cs->bcs + 1, p, cnt);
+	cs->hw.njet.irqstat0 &= 0xfc;
+}
+
+static void
+tiger_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	long flags;
+
+	switch (pr) {
+		case (PH_DATA_REQ):
+			save_flags(flags);
+			cli();
+			if (st->l1.bcs->hw.tiger.tx_skb) {
+				skb_queue_tail(&st->l1.bcs->squeue, skb);
+				restore_flags(flags);
+			} else {
+				st->l1.bcs->hw.tiger.tx_skb = skb;
+				st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
+				restore_flags(flags);
+			}
+			break;
+		case (PH_PULL_IND):
+			if (st->l1.bcs->hw.tiger.tx_skb) {
+				printk(KERN_WARNING "tiger_l2l1: this shouldn't happen\n");
+				break;
+			}
+			save_flags(flags);
+			cli();
+			st->l1.bcs->hw.tiger.tx_skb = skb;
+			st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
+			restore_flags(flags);
+			break;
+		case (PH_PULL_REQ):
+			if (!st->l1.bcs->hw.tiger.tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL_CNF, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+	}
+}
+
+void
+close_tigerstate(struct BCState *bcs)
+{
+	struct sk_buff *skb;
+
+	mode_tiger(bcs, 0, 0);
+	if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (bcs->hw.tiger.rcvbuf) {
+			kfree(bcs->hw.tiger.rcvbuf);
+			bcs->hw.tiger.rcvbuf = NULL;
+		}
+		if (bcs->hw.tiger.sendbuf) {
+			kfree(bcs->hw.tiger.sendbuf);
+			bcs->hw.tiger.sendbuf = NULL;
+		}
+		while ((skb = skb_dequeue(&bcs->rqueue))) {
+			dev_kfree_skb(skb);
+		}
+		while ((skb = skb_dequeue(&bcs->squeue))) {
+			dev_kfree_skb(skb);
+		}
+		if (bcs->hw.tiger.tx_skb) {
+			dev_kfree_skb(bcs->hw.tiger.tx_skb);
+			bcs->hw.tiger.tx_skb = NULL;
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		}
+	}
+}
+
+static int
+open_tigerstate(struct IsdnCardState *cs, int bc)
+{
+	struct BCState *bcs = cs->bcs + bc;
+
+	if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (!(bcs->hw.tiger.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_KERNEL))) {
+			printk(KERN_WARNING
+			       "HiSax: No memory for tiger.rcvbuf\n");
+			return (1);
+		}
+		if (!(bcs->hw.tiger.sendbuf = kmalloc(RAW_BUFMAX, GFP_KERNEL))) {
+			printk(KERN_WARNING
+			       "HiSax: No memory for tiger.sendbuf\n");
+			return (1);
+		}
+		skb_queue_head_init(&bcs->rqueue);
+		skb_queue_head_init(&bcs->squeue);
+	}
+	bcs->hw.tiger.tx_skb = NULL;
+	bcs->hw.tiger.sendcnt = 0;
+	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	bcs->event = 0;
+	bcs->tx_cnt = 0;
+	return (0);
+}
+
+static void
+tiger_manl1(struct PStack *st, int pr,
+	  void *arg)
+{
+	switch (pr) {
+		case (PH_ACTIVATE_REQ):
+			test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+			mode_tiger(st->l1.bcs, st->l1.mode, st->l1.bc);
+			st->l1.l1man(st, PH_ACTIVATE_CNF, NULL);
+			break;
+		case (PH_DEACTIVATE_REQ):
+			if (!test_bit(BC_FLG_BUSY, &st->l1.bcs->Flag))
+				mode_tiger(st->l1.bcs, 0, 0);
+			test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+			break;
+	}
+}
+
+int
+setstack_tiger(struct PStack *st, struct BCState *bcs)
+{
+	if (open_tigerstate(st->l1.hardware, bcs->channel))
+		return (-1);
+	st->l1.bcs = bcs;
+	st->l2.l2l1 = tiger_l2l1;
+	st->ma.manl1 = tiger_manl1;
+	setstack_manager(st);
+	bcs->st = st;
+	return (0);
+}
+
+ 
+__initfunc(void
+inittiger(struct IsdnCardState *cs))
+{
+	char tmp[128];
+
+	if (!(cs->bcs[0].hw.tiger.send = kmalloc(NETJET_DMA_SIZE * sizeof(unsigned int),
+		GFP_KERNEL | GFP_DMA))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for tiger.send\n");
+		return;
+	}
+	cs->bcs[0].hw.tiger.s_irq = cs->bcs[0].hw.tiger.send + NETJET_DMA_SIZE/2 - 1;
+	cs->bcs[0].hw.tiger.s_end = cs->bcs[0].hw.tiger.send + NETJET_DMA_SIZE - 1;
+	cs->bcs[1].hw.tiger.send = cs->bcs[0].hw.tiger.send;
+	cs->bcs[1].hw.tiger.s_irq = cs->bcs[0].hw.tiger.s_irq;
+	cs->bcs[1].hw.tiger.s_end = cs->bcs[0].hw.tiger.s_end;
+	
+	memset(cs->bcs[0].hw.tiger.send, 0xff, NETJET_DMA_SIZE * sizeof(unsigned int));
+	sprintf(tmp, "tiger: send buf %x - %x", (u_int)cs->bcs[0].hw.tiger.send,
+		(u_int)(cs->bcs[0].hw.tiger.send + NETJET_DMA_SIZE - 1));
+	debugl1(cs, tmp);
+	outl(virt_to_bus(cs->bcs[0].hw.tiger.send),
+		cs->hw.njet.base + NETJET_DMA_READ_START);
+	outl(virt_to_bus(cs->bcs[0].hw.tiger.s_irq),
+		cs->hw.njet.base + NETJET_DMA_READ_IRQ);
+	outl(virt_to_bus(cs->bcs[0].hw.tiger.s_end),
+		cs->hw.njet.base + NETJET_DMA_READ_END);
+	if (!(cs->bcs[0].hw.tiger.rec = kmalloc(NETJET_DMA_SIZE * sizeof(unsigned int),
+		GFP_KERNEL | GFP_DMA))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for tiger.rec\n");
+		return;
+	}
+	sprintf(tmp, "tiger: rec buf %x - %x", (u_int)cs->bcs[0].hw.tiger.rec,
+		(u_int)(cs->bcs[0].hw.tiger.rec + NETJET_DMA_SIZE - 1));
+	debugl1(cs, tmp);
+	cs->bcs[1].hw.tiger.rec = cs->bcs[0].hw.tiger.rec;
+	memset(cs->bcs[0].hw.tiger.rec, 0xff, NETJET_DMA_SIZE * sizeof(unsigned int));
+	outl(virt_to_bus(cs->bcs[0].hw.tiger.rec),
+		cs->hw.njet.base + NETJET_DMA_WRITE_START);
+	outl(virt_to_bus(cs->bcs[0].hw.tiger.rec + NETJET_DMA_SIZE/2 - 1),
+		cs->hw.njet.base + NETJET_DMA_WRITE_IRQ);
+	outl(virt_to_bus(cs->bcs[0].hw.tiger.rec + NETJET_DMA_SIZE - 1),
+		cs->hw.njet.base + NETJET_DMA_WRITE_END);
+	sprintf(tmp, "tiger: dmacfg  %x/%x  pulse=%d",
+		inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR),
+		inl(cs->hw.njet.base + NETJET_DMA_READ_ADR),
+		bytein(cs->hw.njet.base + NETJET_PULSE_CNT));
+	debugl1(cs, tmp);
+	cs->hw.njet.last_is0 = 0;
+	cs->bcs[0].BC_SetStack = setstack_tiger;
+	cs->bcs[1].BC_SetStack = setstack_tiger;
+	cs->bcs[0].BC_Close = close_tigerstate;
+	cs->bcs[1].BC_Close = close_tigerstate;
+}
+
+void
+releasetiger(struct IsdnCardState *cs)
+{
+	if (cs->bcs[0].hw.tiger.send) {
+		kfree(cs->bcs[0].hw.tiger.send);
+		cs->bcs[0].hw.tiger.send = NULL;
+	}
+	if (cs->bcs[1].hw.tiger.send) {
+		cs->bcs[1].hw.tiger.send = NULL;
+	}
+	if (cs->bcs[0].hw.tiger.rec) {
+		kfree(cs->bcs[0].hw.tiger.rec);
+		cs->bcs[0].hw.tiger.rec = NULL;
+	}
+	if (cs->bcs[1].hw.tiger.rec) {
+		cs->bcs[1].hw.tiger.rec = NULL;
+	}
+}
+
+static void
+netjet_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val, sval, stat = 1;
+	char tmp[128];
+
+	if (!cs) {
+		printk(KERN_WARNING "NETjet: Spurious interrupt!\n");
+		return;
+	}
+	if (!((sval = bytein(cs->hw.njet.base + NETJET_IRQSTAT1)) &
+		NETJET_ISACIRQ)) {
+		val = ReadISAC(cs, ISAC_ISTA);
+		if (cs->debug & L1_DEB_ISAC) {
+			sprintf(tmp, "tiger: i1 %x %x", sval, val);
+			debugl1(cs, tmp);
+		}
+		if (val) {
+			isac_interrupt(cs, val);
+			stat |= 2;
+		}
+	}
+	if ((cs->hw.njet.irqstat0 = bytein(cs->hw.njet.base + NETJET_IRQSTAT0))) {
+/*		sprintf(tmp, "tiger: ist0 %x  %x %x  %x/%x  pulse=%d",
+			sval, 
+			bytein(cs->hw.njet.base + NETJET_DMACTRL),
+			bytein(cs->hw.njet.base + NETJET_IRQMASK0),
+			inl(cs->hw.njet.base + NETJET_DMA_READ_ADR),
+			inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR),
+			bytein(cs->hw.njet.base + NETJET_PULSE_CNT));
+		debugl1(cs, tmp);
+*/
+		if (cs->hw.njet.last_is0 & cs->hw.njet.irqstat0 & 0xf) {
+			sprintf(tmp, "tiger: ist0 %x->%x irq lost",
+				cs->hw.njet.last_is0, cs->hw.njet.irqstat0);
+			debugl1(cs, tmp);
+		}
+		cs->hw.njet.last_is0 = cs->hw.njet.irqstat0;
+/*		cs->hw.njet.irqmask0 = ((0x0f & cs->hw.njet.irqstat0) ^ 0x0f) | 0x30;
+*/		byteout(cs->hw.njet.base + NETJET_IRQSTAT0, cs->hw.njet.irqstat0);
+/*		byteout(cs->hw.njet.base + NETJET_IRQMASK0, cs->hw.njet.irqmask0);
+*/		if (cs->hw.njet.irqstat0 & 0x0c)
+			read_tiger(cs);
+		if (cs->hw.njet.irqstat0 & 0x03)
+			write_tiger(cs);
+	}
+/*	if (!testcnt--) {
+		cs->hw.njet.dmactrl = 0;
+		byteout(cs->hw.njet.base + NETJET_DMACTRL,
+			cs->hw.njet.dmactrl);
+		byteout(cs->hw.njet.base + NETJET_IRQMASK0, 0);
+	}
+*/	if (stat & 2) {
+		WriteISAC(cs, ISAC_MASK, 0xFF);
+		WriteISAC(cs, ISAC_MASK, 0x0);
+	}
+}
+
+static void
+reset_netjet(struct IsdnCardState *cs)
+{
+	long flags;
+
+	save_flags(flags);
+	sti();
+	cs->hw.njet.ctrl_reg = 0xff;  /* Reset On */
+	byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + (10 * HZ) / 1000;	/* Timeout 10ms */
+	schedule();
+	cs->hw.njet.ctrl_reg = 0x00;  /* Reset Off and status read clear */
+	byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + (10 * HZ) / 1000;	/* Timeout 10ms */
+	schedule();
+	restore_flags(flags);
+	cs->hw.njet.auxd = 0;
+	cs->hw.njet.dmactrl = 0;
+	byteout(cs->hw.njet.base + NETJET_AUXCTRL, ~NETJET_ISACIRQ);
+	byteout(cs->hw.njet.base + NETJET_IRQMASK1, NETJET_ISACIRQ);
+	byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+}
+
+void
+release_io_netjet(struct IsdnCardState *cs)
+{
+	byteout(cs->hw.njet.base + NETJET_IRQMASK0, 0);
+	byteout(cs->hw.njet.base + NETJET_IRQMASK1, 0);
+	releasetiger(cs);
+	release_region(cs->hw.njet.base, 256);
+}
+
+
+static int
+NETjet_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	switch (mt) {
+		case CARD_RESET:
+			reset_netjet(cs);
+			return(0);
+		case CARD_RELEASE:
+			release_io_netjet(cs);
+			return(0);
+		case CARD_SETIRQ:
+			return(request_irq(cs->irq, &netjet_interrupt,
+					I4L_IRQ_FLAG, "HiSax", cs));
+		case CARD_INIT:
+			inittiger(cs);
+			clear_pending_isac_ints(cs);
+			initisac(cs);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+
+
+static 	int pci_index __initdata = 0;
+
+__initfunc(int
+setup_netjet(struct IsdnCard *card))
+{
+	int bytecnt;
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+#if CONFIG_PCI
+	u_char pci_bus, pci_device_fn, pci_irq;
+	u_int pci_ioaddr, found;
+#endif
+
+	strcpy(tmp, NETjet_revision);
+	printk(KERN_INFO "HiSax: Traverse Tech. NETjet driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_NETJET)
+		return(0);
+#if CONFIG_PCI
+	found = 0;
+	for (; pci_index < 0xff; pci_index++) {
+		if (pcibios_find_device(PCI_VENDOR_TRAVERSE_TECH,
+			PCI_NETJET_ID, pci_index, &pci_bus, &pci_device_fn)
+			== PCIBIOS_SUCCESSFUL)
+			found = 1;
+		else
+			break;
+		/* get IRQ */
+		pcibios_read_config_byte(pci_bus, pci_device_fn,
+			PCI_INTERRUPT_LINE, &pci_irq);
+
+		/* get IO address */
+		pcibios_read_config_dword(pci_bus, pci_device_fn,
+			PCI_BASE_ADDRESS_0, &pci_ioaddr);
+		if (found)
+			break;
+	}
+	if (!found) {
+		printk(KERN_WARNING "NETjet: No PCI card found\n");
+		return(0);
+	}
+	if (!pci_irq) {
+		printk(KERN_WARNING "NETjet: No IRQ for PCI card found\n");
+		return(0);
+	}
+	if (!pci_ioaddr) {
+		printk(KERN_WARNING "NETjet: No IO-Adr for PCI card found\n");
+		return(0);
+	}
+	pci_ioaddr &= ~3; /* remove io/mem flag */
+	cs->hw.njet.base = pci_ioaddr; 
+	cs->hw.njet.auxa = pci_ioaddr + NETJET_AUXDATA;
+	cs->hw.njet.isac = pci_ioaddr | NETJET_ISAC_OFF;
+	cs->irq = pci_irq;
+	bytecnt = 256;
+#else
+	printk(KERN_WARNING "NETjet: NO_PCI_BIOS\n");
+	printk(KERN_WARNING "NETjet: unable to config NETJET PCI\n");
+	return (0);
+#endif /* CONFIG_PCI */
+	printk(KERN_INFO
+		"NETjet: PCI card configured at 0x%x IRQ %d\n",
+		cs->hw.njet.base, cs->irq);
+	if (check_region(cs->hw.njet.base, bytecnt)) {
+		printk(KERN_WARNING
+		       "HiSax: %s config port %x-%x already in use\n",
+		       CardType[card->typ],
+		       cs->hw.njet.base,
+		       cs->hw.njet.base + bytecnt);
+		return (0);
+	} else {
+		request_region(cs->hw.njet.base, bytecnt, "netjet isdn");
+	}
+	reset_netjet(cs);
+	cs->readisac  = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo  = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg  = &dummyrr;
+	cs->BC_Write_Reg = &dummywr;
+	cs->BC_Send_Data = &fill_dma;
+	cs->cardmsg = &NETjet_card_msg;
+	ISACVersion(cs, "NETjet:");
+	return (1);
+}
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/niccy.c linux/drivers/isdn/hisax/niccy.c
--- v2.1.91/linux/drivers/isdn/hisax/niccy.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/niccy.c	Wed Apr  1 16:21:02 1998
@@ -0,0 +1,354 @@
+/* $Id: niccy.c,v 1.2 1998/02/11 17:31:04 keil Exp $
+
+ * niccy.c  low level stuff for Dr. Neuhaus NICCY PnP and NICCY PCI and
+ *          compatible (SAGEM cybermodem)
+ *
+ * Author   Karsten Keil
+ * 
+ * Thanks to Dr. Neuhaus and SAGEM for informations
+ *
+ * $Log: niccy.c,v $
+ * Revision 1.2  1998/02/11 17:31:04  keil
+ * new file
+ *
+ *
+ *
+ */
+
+
+#define __NO_VERSION__
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include <linux/bios32.h>
+
+extern const char *CardType[];
+const char *niccy_revision = "$Revision: 1.2 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define ISAC_PCI_DATA	0
+#define HSCX_PCI_DATA	1
+#define ISAC_PCI_ADDR	2
+#define HSCX_PCI_ADDR	3
+#define ISAC_PNP	0
+#define HSCX_PNP	1
+
+/* SUB Types */
+#define NICCY_PNP	1
+#define NICCY_PCI	2
+
+/* PCI stuff */
+#define PCI_VENDOR_DR_NEUHAUS	0x1267
+#define PCI_NICCY_ID		0x1016
+
+static inline u_char
+readreg(unsigned int ale, unsigned int adr, u_char off)
+{
+	register u_char ret;
+	long flags;
+
+	save_flags(flags);
+	cli();
+	byteout(ale, off);
+	ret = bytein(adr);
+	restore_flags(flags);
+	return (ret);
+}
+
+static inline void
+readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	/* fifo read without cli because it's allready done  */
+
+	byteout(ale, off);
+	insb(adr, data, size);
+}
+
+
+static inline void
+writereg(unsigned int ale, unsigned int adr, u_char off, u_char data)
+{
+	long flags;
+
+	save_flags(flags);
+	cli();
+	byteout(ale, off);
+	byteout(adr, data);
+	restore_flags(flags);
+}
+
+static inline void
+writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	/* fifo write without cli because it's allready done  */
+	byteout(ale, off);
+	outsb(adr, data, size);
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	readfifo(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, 0, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	writefifo(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, 0, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return (readreg(cs->hw.niccy.hscx_ale,
+			cs->hw.niccy.hscx, offset + (hscx ? 0x40 : 0)));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	writereg(cs->hw.niccy.hscx_ale,
+		 cs->hw.niccy.hscx, offset + (hscx ? 0x40 : 0), value);
+}
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.niccy.hscx_ale, \
+		cs->hw.niccy.hscx, reg + (nr ? 0x40 : 0))
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.niccy.hscx_ale, \
+		cs->hw.niccy.hscx, reg + (nr ? 0x40 : 0), data)
+
+#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.niccy.hscx_ale, \
+		cs->hw.niccy.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.niccy.hscx_ale, \
+		cs->hw.niccy.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#include "hscx_irq.c"
+
+static void
+niccy_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val, stat = 0;
+
+	if (!cs) {
+		printk(KERN_WARNING "Niccy: Spurious interrupt!\n");
+		return;
+	}
+	val = readreg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_ISTA + 0x40);
+      Start_HSCX:
+	if (val) {
+		hscx_int_main(cs, val);
+		stat |= 1;
+	}
+	val = readreg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_ISTA);
+      Start_ISAC:
+	if (val) {
+		isac_interrupt(cs, val);
+		stat |= 2;
+	}
+	val = readreg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_ISTA + 0x40);
+	if (val) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX IntStat after IntRoutine");
+		goto Start_HSCX;
+	}
+	val = readreg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_ISTA);
+	if (val) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	if (stat & 1) {
+		writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK, 0xFF);
+		writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK + 0x40, 0xFF);
+		writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK, 0);
+		writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK + 0x40, 0);
+	}
+	if (stat & 2) {
+		writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_MASK, 0xFF);
+		writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_MASK, 0);
+	}
+}
+
+void
+release_io_niccy(struct IsdnCardState *cs)
+{
+	if (cs->subtyp == NICCY_PCI)
+		release_region(cs->hw.niccy.isac, 4);
+	else {
+		release_region(cs->hw.niccy.isac, 2);
+		release_region(cs->hw.niccy.isac_ale, 2);
+	}
+}
+
+static void
+niccy_reset(struct IsdnCardState *cs)
+{
+	// No reset procedure known
+}
+
+static int
+niccy_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	switch (mt) {
+		case CARD_RESET:
+			niccy_reset(cs);
+			return(0);
+		case CARD_RELEASE:
+			release_io_niccy(cs);
+			return(0);
+		case CARD_SETIRQ:
+			return(request_irq(cs->irq, &niccy_interrupt,
+					I4L_IRQ_FLAG, "HiSax", cs));
+		case CARD_INIT:
+			clear_pending_isac_ints(cs);
+			clear_pending_hscx_ints(cs);
+			initisac(cs);
+			inithscx(cs);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+static 	int pci_index __initdata = 0;
+
+__initfunc(int
+setup_niccy(struct IsdnCard *card))
+{
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, niccy_revision);
+	printk(KERN_INFO "HiSax: Niccy driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_NICCY)
+		return (0);
+
+	if (card->para[1]) {
+		cs->hw.niccy.isac = card->para[1] + ISAC_PNP;
+		cs->hw.niccy.hscx = card->para[1] + HSCX_PNP;
+		cs->hw.niccy.isac_ale = card->para[2] + ISAC_PNP;
+		cs->hw.niccy.hscx_ale = card->para[2] + HSCX_PNP;
+		cs->hw.niccy.cfg_reg = 0;
+		cs->subtyp = NICCY_PNP;
+		cs->irq = card->para[0];
+		if (check_region((cs->hw.niccy.isac), 2)) {
+			printk(KERN_WARNING
+				"HiSax: %s data port %x-%x already in use\n",
+				CardType[card->typ],
+				cs->hw.niccy.isac,
+				cs->hw.niccy.isac + 1);
+			return (0);
+		} else
+			request_region(cs->hw.niccy.isac, 2, "niccy data");
+		if (check_region((cs->hw.niccy.isac_ale), 2)) {
+			printk(KERN_WARNING
+				"HiSax: %s address port %x-%x already in use\n",
+				CardType[card->typ],
+				cs->hw.niccy.isac_ale,
+				cs->hw.niccy.isac_ale + 1);
+			release_region(cs->hw.niccy.isac, 2);
+			return (0);
+		} else
+			request_region(cs->hw.niccy.isac_ale, 2, "niccy addr");
+	} else {
+#if CONFIG_PCI
+		u_char pci_bus, pci_device_fn, pci_irq;
+		u_int pci_ioaddr;
+
+		cs->subtyp = 0;
+		for (; pci_index < 0xff; pci_index++) {
+			if (pcibios_find_device(PCI_VENDOR_DR_NEUHAUS,
+			   PCI_NICCY_ID, pci_index, &pci_bus, &pci_device_fn)
+			   == PCIBIOS_SUCCESSFUL)
+				cs->subtyp = NICCY_PCI;
+			else
+				break;
+			/* get IRQ */
+			pcibios_read_config_byte(pci_bus, pci_device_fn,
+				PCI_INTERRUPT_LINE, &pci_irq);
+
+			/* get IO address */
+			/* if it won't work try the other PCI addresses
+			 * PCI_BASE_ADDRESS_0 ... PCI_BASE_ADDRESS_5
+			 */
+			pcibios_read_config_dword(pci_bus, pci_device_fn,
+				PCI_BASE_ADDRESS_2, &pci_ioaddr);
+			if (cs->subtyp)
+				break;
+		}
+		if (!cs->subtyp) {
+			printk(KERN_WARNING "Niccy: No PCI card found\n");
+			return(0);
+		}
+		if (!pci_irq) {
+			printk(KERN_WARNING "Niccy: No IRQ for PCI card found\n");
+			return(0);
+		}
+
+		if (!pci_ioaddr) {
+			printk(KERN_WARNING "Niccy: No IO-Adr for PCI card found\n");
+			return(0);
+		}
+		pci_ioaddr &= ~3; /* remove io/mem flag */
+		cs->hw.niccy.isac = pci_ioaddr + ISAC_PCI_DATA;
+		cs->hw.niccy.isac_ale = pci_ioaddr + ISAC_PCI_ADDR;
+		cs->hw.niccy.hscx = pci_ioaddr + HSCX_PCI_DATA;
+		cs->hw.niccy.hscx_ale = pci_ioaddr + HSCX_PCI_ADDR;
+		cs->irq = pci_irq;
+		if (check_region((cs->hw.niccy.isac), 4)) {
+			printk(KERN_WARNING
+				"HiSax: %s data port %x-%x already in use\n",
+				CardType[card->typ],
+				cs->hw.niccy.isac,
+				cs->hw.niccy.isac + 4);
+			return (0);
+		} else
+			request_region(cs->hw.niccy.isac, 4, "niccy");
+#else
+		printk(KERN_WARNING "Niccy: io0 0 and NO_PCI_BIOS\n");
+		printk(KERN_WARNING "Niccy: unable to config NICCY PCI\n");
+		return (0);
+#endif /* CONFIG_PCI */
+	}
+	printk(KERN_INFO
+		"HiSax: %s %s config irq:%d data:0x%X ale:0x%X\n",
+		CardType[cs->typ], (cs->subtyp==1) ? "PnP":"PCI",
+		cs->irq, cs->hw.niccy.isac, cs->hw.niccy.isac_ale);
+	niccy_reset(cs);
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &niccy_card_msg;
+	ISACVersion(cs, "Niccy:");
+	if (HscxVersion(cs, "Niccy:")) {
+		printk(KERN_WARNING
+		    "Niccy: wrong HSCX versions check IO address\n");
+		release_io_niccy(cs);
+		return (0);
+	}
+	return (1);
+}
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/q931.c linux/drivers/isdn/hisax/q931.c
--- v2.1.91/linux/drivers/isdn/hisax/q931.c	Thu May 29 21:53:06 1997
+++ linux/drivers/isdn/hisax/q931.c	Wed Apr  1 16:21:02 1998
@@ -1,4 +1,4 @@
-/* $Id: q931.c,v 1.5 1997/04/06 22:56:43 keil Exp $
+/* $Id: q931.c,v 1.6 1997/07/27 21:09:44 keil Exp $
 
  * q931.c       code to decode ITU Q.931 call control messages
  *
@@ -14,6 +14,9 @@
  *
  *
  * $Log: q931.c,v $
+ * Revision 1.6  1997/07/27 21:09:44  keil
+ * move functions to isdnl3.c
+ *
  * Revision 1.5  1997/04/06 22:56:43  keil
  * Some cosmetic changes
  *
@@ -37,44 +40,6 @@
 #include "hisax.h"
 #include "l3_1tr6.h"
 
-u_char *
-findie(u_char * p, int size, u_char ie, int wanted_set)
-{
-	int l, codeset, maincodeset;
-	u_char *pend = p + size;
-
-	/* skip protocol discriminator, callref and message type */
-	p++;
-	l = (*p++) & 0xf;
-	p += l;
-	p++;
-	codeset = 0;
-	maincodeset = 0;
-	/* while there are bytes left... */
-	while (p < pend) {
-		if ((*p & 0xf0) == 0x90) {
-			codeset = *p & 0x07;
-			if (!(*p & 0x08))
-				maincodeset = codeset;
-		}
-		if (*p & 0x80)
-			p++;
-		else {
-			if (codeset == wanted_set) {
-				if (*p == ie)
-					return (p);
-				if (*p > ie)
-					return (NULL);
-			}
-			p++;
-			l = *p++;
-			p += l;
-			codeset = maincodeset;
-		}
-	}
-	return (NULL);
-}
-
 void
 iecpy(u_char * dest, u_char * iestart, int ieoffset)
 {
@@ -86,14 +51,6 @@
 	while (l--)
 		*dest++ = *p++;
 	*dest++ = '\0';
-}
-
-int
-getcallref(u_char * p)
-{
-	p++;			/* prot discr */
-	p++;			/* callref length */
-	return (*p);		/* assuming one-byte callref */
 }
 
 /*
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/rawhdlc.c linux/drivers/isdn/hisax/rawhdlc.c
--- v2.1.91/linux/drivers/isdn/hisax/rawhdlc.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/rawhdlc.c	Wed Apr  1 16:21:02 1998
@@ -0,0 +1,539 @@
+/* $Id: rawhdlc.c,v 1.2 1998/02/09 10:53:51 keil Exp $
+
+ * rawhdlc.c     support routines for cards that don't support HDLC
+ *
+ * Author     Karsten Keil (keil@temic-ech.spacenet.de)
+ *            Brent Baccala <baccala@FreeSoft.org>
+ *
+ *
+ * Some passive ISDN cards, such as the Traverse NETJet and the AMD 7930,
+ * don't perform HDLC encapsulation over the B channel.  Drivers for
+ * such cards use support routines in this file to perform B channel HDLC.
+ *
+ * Bit-synchronous HDLC encapsulation is a means of encapsulating packets
+ * over a continuously transmitting serial communications link.
+ * It looks like this:
+ *
+ *      11111111101111110...........0111111011111111111
+ *      iiiiiiiiiffffffffdddddddddddffffffffiiiiiiiiiii
+ *
+ *      i = idle     f = flag     d = data
+ *
+ * When idle, the channel sends a continuous string of ones (mark
+ * idle; illustrated), or a continuous string of flag characters (flag
+ * idle).  The beginning of a data frame is marked by a flag character
+ * (01111110), then comes the actual data, followed by another flag
+ * character, after which another frame may be sent immediately (a
+ * single flag may serve as both the end of one frame and the start of
+ * the next), or the link may return to idle.  Obviously, the flag
+ * character can not appear anywhere in the data (or a false
+ * end-of-frame would occur), so the transmitter performs
+ * "bit-stuffing" - inserting a zero bit after every five one bits,
+ * irregardless of the original bit after the five ones.  Byte
+ * ordering is irrelevent at this point - the data is treated as a
+ * string of bits, not bytes.  Since no more than 5 ones may now occur
+ * in a row, the flag sequence, with its 6 ones, is unique.
+ *
+ * Upon reception, a zero bit that occur after 5 one bits is simply
+ * discarded.  A series of 6 one bits is end-of-frame, and a series of
+ * 7 one bits is an abort.  Once bit-stuffing has been corrected for,
+ * an integer number of bytes should now be present.  The last two
+ * of these bytes form the Frame Check Sequence, a CRC that is verified
+ * and then discarded.  Note that bit-stuffing is performed on the FCS
+ * just as if it were regular data.
+ *
+ *
+ *
+ * int make_raw_hdlc_data(u_char *src, u_int slen,
+ *                        u_char *dst, u_int dsize)
+ *
+ *   Used for transmission.  Copies slen bytes from src to dst, performing
+ *   HDLC encapsulation (flag bytes, bit-stuffing, CRC) in the process.
+ *   dsize is size of destination buffer, and should be at least
+ *   ((6*slen)/5)+5 bytes to ensure adequate space will be available.
+ *   Function returns length (in bytes) of valid destination buffer, or
+ *   0 upon destination overflow.
+ *
+ * void init_hdlc_state(struct hdlc_state *stateptr, int mode)
+ *
+ *   Initializes hdlc_state structure before first call to read_raw_hdlc_data
+ *
+ *   mode = 0: Sane mode
+ *   mode = 1/2: 
+ *             Insane mode; NETJet use a shared unsigned int memory block (
+ * 	       with busmaster DMA), the bit pattern of every word is 
+ *  	       <8 B1> <8 B2> <8 Mon> <2 D> <4 C/I> <MX> <MR>
+ *	       according to Siemens IOM-2 interface, so we have to handle
+ *             the src buffer as unsigned int and have to shift/mask the
+ *             B-channel bytes.
+ *             mode 1 -> B1  mode 2  -> B2 data is used
+ *
+ * int read_raw_hdlc_data(struct hdlc_state *saved_state,
+ *                        u_char *src, u_int slen,
+ *                        u_char *dst, u_int dsize)
+ *
+ *   Used for reception.  Scans source buffer bit-by-bit looking for
+ *   valid HDLC frames, which are copied to destination buffer.  HDLC
+ *   state information is stored in a structure, which allows this
+ *   function to process frames spread across several blocks of raw
+ *   HDLC data.  Part of the state information is bit offsets into
+ *   the source and destination buffers.
+ *
+ *   A return value >0 indicates the length of a valid frame, now
+ *   stored in the destination buffer.  In this case, the source
+ *   buffer might not be completely processed, so this function should
+ *   be called again with the same source buffer, possibly with a
+ *   different destination buffer.
+ *
+ *   A return value of zero indicates that the source buffer was
+ *   completely processed without finding a valid end-of-packet;
+ *   however, we might be in the middle of packet reception, so
+ *   the function should be called again with the next block of
+ *   raw HDLC data and the same destination buffer.  It is NOT
+ *   permitted to change the destination buffer in this case,
+ *   since data may already have begun to be stored there.
+ *
+ *   A return value of -1 indicates some kind of error - destination
+ *   buffer overflow, CRC check failed, frame not a multiple of 8
+ *   bits.  Destination buffer probably contains invalid data, which
+ *   should be discarded.  Call function again with same source buffer
+ *   and a new (or same) destination buffer.
+ *
+ *   Suggested calling sequence:
+ *
+ *      init_hdlc_state(...);
+ *      for (EACH_RAW_DATA_BLOCK) {
+ *         while (len = read_raw_hdlc_data(...)) {
+ *             if (len == -1) DISCARD_FRAME;
+ *             else PROCESS_FRAME;
+ *         }
+ *      }
+ *
+ *
+ * Test the code in this file as follows:
+ *    gcc -DDEBUGME -o rawhdlctest rawhdlc.c
+ *    ./rawhdlctest < rawdata
+ *
+ * The file "rawdata" can be easily generated from a HISAX B-channel
+ * hex dump (CF CF CF 02 ...) using the following perl script:
+ *
+ * while(<>) {
+ *     @hexlist = split ' ';
+ *     while ($hexstr = shift(@hexlist)) {
+ *         printf "%c", hex($hexstr);
+ *     }
+ * }
+ *
+ */
+
+#ifdef DEBUGME
+#include <stdio.h>
+#endif
+
+#include <linux/types.h>
+#include <linux/ppp_defs.h>
+#include "rawhdlc.h"
+
+/* There's actually an identical copy of this table in the PPP code
+ * (ppp_crc16_table), but I don't want this code dependant on PPP
+ */
+
+// static 
+__u16 fcstab[256] =
+{
+	0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+	0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+	0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+	0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+	0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+	0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+	0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+	0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+	0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+	0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+	0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+	0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+	0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+	0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+	0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+	0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+	0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+	0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+	0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+	0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+	0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+	0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+	0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+	0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+	0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+	0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+	0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+	0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+	0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+	0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+	0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+	0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
+
+#define HDLC_ZERO_SEARCH 0
+#define HDLC_FLAG_SEARCH 1
+#define HDLC_FLAG_FOUND  2
+#define HDLC_FRAME_FOUND 3
+#define HDLC_NULL 4
+#define HDLC_PART 5
+#define HDLC_FULL 6
+
+#define HDLC_FLAG_VALUE	0x7e
+
+
+#define MAKE_RAW_BYTE for (j=0; j<8; j++) { \
+			bitcnt++;\
+			out_val >>= 1;\
+			if (val & 1) {\
+				s_one++;\
+				out_val |= 0x80;\
+			} else {\
+				s_one = 0;\
+				out_val &= 0x7f;\
+			}\
+			if (bitcnt==8) {\
+				if (d_cnt == dsize) return 0;\
+				dst[d_cnt++] = out_val;\
+				bitcnt = 0;\
+			}\
+			if (s_one == 5) {\
+				out_val >>= 1;\
+				out_val &= 0x7f;\
+				bitcnt++;\
+				s_one = 0;\
+			}\
+			if (bitcnt==8) {\
+				if (d_cnt == dsize) return 0;\
+				dst[d_cnt++] = out_val;\
+				bitcnt = 0;\
+			}\
+			val >>= 1;\
+		}
+
+/* Optimization suggestion: If needed, this function could be
+ * dramatically sped up using a state machine.  Each state would
+ * correspond to having seen N one bits, and being offset M bits into
+ * the current output byte.  N ranges from 0 to 4, M from 0 to 7, so
+ * we need 5*8 = 35 states.  Each state would have a table with 256
+ * entries, one for each input character.  Each entry would contain
+ * three output characters, an output state, an a byte increment
+ * that's either 1 or 2.  All this could fit in four bytes; so we need
+ * 4 bytes * 256 characters = 1 KB for each state (35 KB total).  Zero
+ * the output buffer before you start.  For each character in your
+ * input, you look it up in the current state's table and get three
+ * bytes to be or'ed into the output at the current byte offset, and
+ * an byte increment to move your pointer forward.  A simple Perl
+ * script could generate the tables.  Given HDLC semantics, probably
+ * would be better to set output to all 1s, then use ands instead of ors.
+ * A smaller state machine could operate on nibbles instead of bytes.
+ * A state machine for 32-bit architectures could use word offsets
+ * instead of byte offsets, requiring 5*32 = 160 states; probably
+ * best to work on nibbles in such a case.
+ */
+
+
+int make_raw_hdlc_data(u_char *src, u_int slen, u_char *dst, u_int dsize)
+{
+	register u_int i,d_cnt=0;
+	register u_char j;
+	register u_char val;
+	register u_char s_one = 0;
+	register u_char out_val = 0;
+	register u_char bitcnt = 0;
+	u_int fcs;
+	
+	
+	dst[d_cnt++] = HDLC_FLAG_VALUE;
+	fcs = PPP_INITFCS;
+	for (i=0; i<slen; i++) {
+		val = src[i];
+		fcs = PPP_FCS (fcs, val);
+		MAKE_RAW_BYTE;
+	}
+	fcs ^= 0xffff;
+	val = fcs & 0xff;
+	MAKE_RAW_BYTE;
+	val = (fcs>>8) & 0xff;
+	MAKE_RAW_BYTE;
+	val = HDLC_FLAG_VALUE;
+	for (j=0; j<8; j++) { 
+		bitcnt++;
+		out_val >>= 1;
+		if (val & 1)
+			out_val |= 0x80;
+		else
+			out_val &= 0x7f;
+		if (bitcnt==8) {
+			if (d_cnt == dsize) return 0;
+			dst[d_cnt++] = out_val;
+			bitcnt = 0;
+		}
+		val >>= 1;
+	}
+	if (bitcnt) {
+		while (8>bitcnt++) {
+			out_val >>= 1;
+			out_val |= 0x80;
+		}
+		if (d_cnt == dsize) return 0;
+		dst[d_cnt++] = out_val;
+	}
+
+	return d_cnt;
+}
+
+void init_hdlc_state(struct hdlc_state *stateptr, int mode)
+{
+	stateptr->state = HDLC_ZERO_SEARCH;
+	stateptr->r_one = 0;
+	stateptr->r_val = 0;
+	stateptr->o_bitcnt = 0;
+	stateptr->i_bitcnt = 0;
+	stateptr->insane_mode = mode;
+}
+
+/* Optimization suggestion: A similar state machine could surely
+ * be developed for this function as well.
+ */
+
+int read_raw_hdlc_data(struct hdlc_state *saved_state,
+                       u_char *src, u_int slen, u_char *dst, u_int dsize)
+{
+	int retval=0;
+	register u_char val;
+	register u_char state = saved_state->state;
+	register u_char r_one = saved_state->r_one;
+	register u_char r_val = saved_state->r_val;
+	register u_int o_bitcnt = saved_state->o_bitcnt;
+	register u_int i_bitcnt = saved_state->i_bitcnt;
+	register u_int fcs    = saved_state->fcs;
+	register u_int *isrc = (u_int *) src;
+        
+	/* Use i_bitcnt (bit offset into source buffer) to reload "val"
+	 * in case we're starting up again partway through a source buffer
+	 */
+
+	if ((i_bitcnt >> 3) < slen) {
+		if (saved_state->insane_mode==1) {
+			val = isrc[(i_bitcnt >> 3)] & 0xff;
+		} else if (saved_state->insane_mode==2) {
+			val = (isrc[i_bitcnt >> 3] >>8) & 0xff;
+		} else {
+			val = src[i_bitcnt >> 3];
+		}
+		val >>= i_bitcnt & 7;
+	}
+
+	/* One bit per loop.  Keep going until we've got something to
+	 * report (retval != 0), or we exhaust the source buffer
+	 */
+
+	while ((retval == 0) && ((i_bitcnt >> 3) < slen)) {
+		if ((i_bitcnt & 7) == 0) {
+			if (saved_state->insane_mode==1) {
+				val = isrc[(i_bitcnt >> 3)] & 0xff;
+			} else if (saved_state->insane_mode==2) {
+				val = (isrc[i_bitcnt >> 3] >>8) & 0xff;
+			} else {
+				val = src[i_bitcnt >> 3];
+			}
+#ifdef DEBUGME
+			printf("Input byte %d: 0x%2x\n", i_bitcnt>>3, val);
+#endif
+			if (val == 0xff) {
+				state = HDLC_ZERO_SEARCH;
+				o_bitcnt = 0;
+				r_one = 0;
+				i_bitcnt += 8;
+				continue;
+			}
+		}
+
+#ifdef DEBUGME
+		/* printf("Data bit=%d (%d/%d)\n", val&1, i_bitcnt>>3, i_bitcnt&7);*/
+#endif
+
+		if (state == HDLC_ZERO_SEARCH) {
+			if (val & 1) {
+				r_one++;
+			} else {
+				r_one=0;
+				state= HDLC_FLAG_SEARCH;
+			}
+		} else if (state == HDLC_FLAG_SEARCH) { 
+			if (val & 1) {
+				r_one++;
+				if (r_one>6) {
+					state=HDLC_ZERO_SEARCH;
+				}
+			} else {
+				if (r_one==6) {
+					o_bitcnt=0;
+					r_val=0;
+					state=HDLC_FLAG_FOUND;
+				}
+				r_one=0;
+			}
+		} else if (state ==  HDLC_FLAG_FOUND) {
+			if (val & 1) {
+				r_one++;
+				if (r_one>6) {
+					state=HDLC_ZERO_SEARCH;
+				} else {
+					r_val >>= 1;
+					r_val |= 0x80;
+					o_bitcnt++;
+				}
+			} else {
+				if (r_one==6) {
+					o_bitcnt=0;
+					r_val=0;
+					r_one=0;
+					i_bitcnt++;
+					val >>= 1;
+					continue;
+				} else if (r_one!=5) {
+					r_val >>= 1;
+					r_val &= 0x7f;
+					o_bitcnt++;
+				}
+				r_one=0;	
+			}
+			if ((state != HDLC_ZERO_SEARCH) &&
+				!(o_bitcnt & 7)) {
+#ifdef DEBUGME
+				printf("HDLC_FRAME_FOUND at i_bitcnt:%d\n",i_bitcnt);
+#endif
+				state=HDLC_FRAME_FOUND;
+				fcs = PPP_INITFCS;
+				dst[0] = r_val;
+				fcs = PPP_FCS (fcs, r_val);
+			}
+		} else if (state ==  HDLC_FRAME_FOUND) {
+			if (val & 1) {
+				r_one++;
+				if (r_one>6) {
+					state=HDLC_ZERO_SEARCH;
+					o_bitcnt=0;
+				} else {
+					r_val >>= 1;
+					r_val |= 0x80;
+					o_bitcnt++;
+				}
+			} else {
+				if (r_one==6) {
+					r_val=0; 
+					r_one=0;
+					o_bitcnt++;
+					if (o_bitcnt & 7) {
+						/* Alignment error */
+#ifdef DEBUGME
+						printf("Alignment error\n");
+#endif
+						state=HDLC_FLAG_SEARCH;
+						retval = -1;
+					} else if (fcs==PPP_GOODFCS) {
+						/* Valid frame */
+						state=HDLC_FLAG_FOUND;
+						retval = (o_bitcnt>>3)-3;
+					} else {
+						/* CRC error */
+#ifdef DEBUGME
+						printf("CRC error; fcs was 0x%x, should have been 0x%x\n", fcs, PPP_GOODFCS);
+#endif
+						state=HDLC_FLAG_FOUND;
+						retval = -1;
+					}
+				} else if (r_one==5) {
+					r_one=0;
+					i_bitcnt++;
+					val >>= 1;
+					continue;
+				} else {
+					r_val >>= 1;
+					r_val &= 0x7f;
+					o_bitcnt++;
+				}
+				r_one=0;	
+			}
+			if ((state == HDLC_FRAME_FOUND) &&
+				!(o_bitcnt & 7)) {
+				if ((o_bitcnt>>3)>=dsize) {
+					/* Buffer overflow error */
+#ifdef DEBUGME
+					printf("Buffer overflow error\n");
+#endif
+					r_val=0; 
+					state=HDLC_FLAG_SEARCH;
+					retval = -1;
+				} else {
+					dst[(o_bitcnt>>3)-1] = r_val;
+					fcs = PPP_FCS (fcs, r_val);
+#ifdef DEBUGME
+					printf("Output byte %d: 0x%02x; FCS 0x%04x\n", (o_bitcnt>>3)-1, r_val, fcs);
+#endif
+				}
+			}
+		}
+		i_bitcnt ++;
+		val >>= 1;
+	}
+
+	/* We exhausted the source buffer before anything else happened
+	 * (retval==0).  Reset i_bitcnt in expectation of a new source
+	 * buffer.  Other, we either had an error or a valid frame, so
+	 * reset o_bitcnt in expectation of a new destination buffer.
+	 */
+
+	if (retval == 0) {
+		i_bitcnt = 0;
+	} else {
+		o_bitcnt = 0;
+	}
+
+	saved_state->state = state;
+	saved_state->r_one = r_one;
+	saved_state->r_val = r_val;
+	saved_state->fcs = fcs;
+	saved_state->o_bitcnt = o_bitcnt;
+	saved_state->i_bitcnt = i_bitcnt;
+
+	return (retval);
+}
+
+
+
+#ifdef DEBUGME
+
+char buffer[1024];
+char obuffer[1024];
+
+main()
+{
+  int buflen=0;
+  int len;
+  struct hdlc_state hdlc_state;
+
+  while((buffer[buflen] = getc(stdin)) != EOF && buflen<1024) buflen++;
+
+  printf("buflen = %d\n", buflen);
+
+  init_hdlc_state(&hdlc_state, 0);
+
+  while (len = read_raw_hdlc_data(&hdlc_state,buffer,buflen,obuffer,1024)) {
+    if (len == -1) printf("Error @ byte %d/bit %d\n",
+			  hdlc_state.i_bitcnt>>3, hdlc_state.i_bitcnt & 7);
+    else {
+      printf("Frame received: len %d\n", len);
+    }
+  }
+
+  printf("Done\n");
+}
+
+#endif
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/rawhdlc.h linux/drivers/isdn/hisax/rawhdlc.h
--- v2.1.91/linux/drivers/isdn/hisax/rawhdlc.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/rawhdlc.h	Wed Apr  1 16:21:02 1998
@@ -0,0 +1,26 @@
+/* $Id: rawhdlc.h,v 1.2 1998/02/09 10:53:53 keil Exp $
+
+ * rawhdlc.h     support routines for cards that don't support HDLC
+ *
+ * Author     Brent Baccala <baccala@FreeSoft.org>
+ *
+ */
+
+#ifndef RAWHDLC_H
+struct hdlc_state {
+	char insane_mode;
+	u_char state;
+	u_char r_one;
+	u_char r_val;
+	u_int o_bitcnt;
+	u_int i_bitcnt;
+	u_int fcs;
+};
+
+
+int make_raw_hdlc_data(u_char *src, u_int slen, u_char *dst, u_int dsize);
+void init_hdlc_state(struct hdlc_state *stateptr, int mode);
+int read_raw_hdlc_data(struct hdlc_state *saved_state,
+                       u_char *src, u_int slen, u_char *dst, u_int dsize);
+#define RAWHDLC_H
+#endif
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/sedlbauer.c linux/drivers/isdn/hisax/sedlbauer.c
--- v2.1.91/linux/drivers/isdn/hisax/sedlbauer.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/sedlbauer.c	Wed Apr  1 16:21:02 1998
@@ -0,0 +1,356 @@
+/* $Id: sedlbauer.c,v 1.6 1998/02/09 18:46:06 keil Exp $
+
+ * sedlbauer.c  low level stuff for Sedlbauer cards
+ *              includes Support for the Sedlbauer Speed Star 
+ *              derived from the original file dynalink.c from Karsten Keil
+ *
+ * Copyright (C) 1997,1998 Marcus Niemann (for the modifications to
+ *                                         the original file dynalink.c)
+ *
+ * Author     Marcus Niemann (niemann@www-bib.fh-bielefeld.de)
+ *
+ * Thanks to  Karsten Keil
+ *            Sedlbauer AG for informations
+ *            Edgar Toernig
+ *
+ * $Log: sedlbauer.c,v $
+ * Revision 1.6  1998/02/09 18:46:06  keil
+ * Support for Sedlbauer PCMCIA (Marcus Niemann)
+ *
+ * Revision 1.5  1998/02/02 13:29:45  keil
+ * fast io
+ *
+ * Revision 1.4  1997/11/08 21:35:52  keil
+ * new l1 init
+ *
+ * Revision 1.3  1997/11/06 17:09:28  keil
+ * New 2.1 init code
+ *
+ * Revision 1.2  1997/10/29 18:55:52  keil
+ * changes for 2.1.60 (irq2dev_map)
+ *
+ * Revision 1.1  1997/09/11 17:32:04  keil
+ * new
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+
+extern const char *CardType[];
+
+const char *Sedlbauer_revision = "$Revision: 1.6 $";
+
+const char *Sedlbauer_Types[] =
+{"None", "Speed Card", "Speed Win", "Speed Star"};
+ 
+#define SEDL_SPEED_CARD 1
+#define SEDL_SPEED_WIN  2
+#define SEDL_SPEED_STAR 3
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define SEDL_RESET_ON	0
+#define SEDL_RESET_OFF	1
+#define SEDL_ISAC	2
+#define SEDL_HSCX	3
+#define SEDL_ADR	4
+
+#define SEDL_PCMCIA_RESET	0
+#define SEDL_PCMCIA_ISAC	1
+#define SEDL_PCMCIA_HSCX	2
+#define SEDL_PCMCIA_ADR		4
+
+#define SEDL_RESET      0x3	/* same as DOS driver */
+
+static inline u_char
+readreg(unsigned int ale, unsigned int adr, u_char off)
+{
+	register u_char ret;
+	long flags;
+
+	save_flags(flags);
+	cli();
+	byteout(ale, off);
+	ret = bytein(adr);
+	restore_flags(flags);
+	return (ret);
+}
+
+static inline void
+readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	/* fifo read without cli because it's allready done  */
+
+	byteout(ale, off);
+	insb(adr, data, size);
+}
+
+
+static inline void
+writereg(unsigned int ale, unsigned int adr, u_char off, u_char data)
+{
+	long flags;
+
+	save_flags(flags);
+	cli();
+	byteout(ale, off);
+	byteout(adr, data);
+	restore_flags(flags);
+}
+
+static inline void
+writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	/* fifo write without cli because it's allready done  */
+	byteout(ale, off);
+	outsb(adr, data, size);
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	readfifo(cs->hw.sedl.adr, cs->hw.sedl.isac, 0, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	writefifo(cs->hw.sedl.adr, cs->hw.sedl.isac, 0, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return (readreg(cs->hw.sedl.adr,
+			cs->hw.sedl.hscx, offset + (hscx ? 0x40 : 0)));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	writereg(cs->hw.sedl.adr,
+		 cs->hw.sedl.hscx, offset + (hscx ? 0x40 : 0), value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.sedl.adr, \
+		cs->hw.sedl.hscx, reg + (nr ? 0x40 : 0))
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.sedl.adr, \
+		cs->hw.sedl.hscx, reg + (nr ? 0x40 : 0), data)
+
+#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.sedl.adr, \
+		cs->hw.sedl.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.sedl.adr, \
+		cs->hw.sedl.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#include "hscx_irq.c"
+
+static void
+sedlbauer_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val, stat = 0;
+
+	if (!cs) {
+		printk(KERN_WARNING "Sedlbauer: Spurious interrupt!\n");
+		return;
+	}
+
+        if ((cs->typ == ISDN_CTYPE_SEDLBAUER_PCMCIA) && (*cs->busy_flag == 1)) {
+          /* The card tends to generate interrupts while being removed
+             causing us to just crash the kernel. bad. */
+          printk(KERN_WARNING "Sedlbauer: card not available!\n");
+          return;
+        }
+
+	val = readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_ISTA + 0x40);
+      Start_HSCX:
+	if (val) {
+		hscx_int_main(cs, val);
+		stat |= 1;
+	}
+	val = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_ISTA);
+      Start_ISAC:
+	if (val) {
+		isac_interrupt(cs, val);
+		stat |= 2;
+	}
+	val = readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_ISTA + 0x40);
+	if (val) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX IntStat after IntRoutine");
+		goto Start_HSCX;
+	}
+	val = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_ISTA);
+	if (val) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	if (stat & 1) {
+		writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_MASK, 0xFF);
+		writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_MASK + 0x40, 0xFF);
+		writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_MASK, 0x0);
+		writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_MASK + 0x40, 0x0);
+	}
+	if (stat & 2) {
+		writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_MASK, 0xFF);
+		writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_MASK, 0x0);
+	}
+}
+
+void
+release_io_sedlbauer(struct IsdnCardState *cs)
+{
+	int bytecnt = 8;
+
+	if (cs->hw.sedl.cfg_reg)
+		release_region(cs->hw.sedl.cfg_reg, bytecnt);
+}
+
+static void
+reset_sedlbauer(struct IsdnCardState *cs)
+{
+	long flags;
+
+	if (cs->typ != ISDN_CTYPE_SEDLBAUER_PCMCIA) {
+		byteout(cs->hw.sedl.reset_on, SEDL_RESET);	/* Reset On */
+		save_flags(flags);
+		sti();
+		current->state = TASK_INTERRUPTIBLE;
+		current->timeout = jiffies + 1;
+		schedule();
+		byteout(cs->hw.sedl.reset_off, 0);	/* Reset Off */
+		current->state = TASK_INTERRUPTIBLE;
+		current->timeout = jiffies + 1;
+		schedule();
+		restore_flags(flags);
+	}
+}
+
+static int
+Sedl_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	switch (mt) {
+		case CARD_RESET:
+			reset_sedlbauer(cs);
+			return(0);
+		case CARD_RELEASE:
+			release_io_sedlbauer(cs);
+			return(0);
+		case CARD_SETIRQ:
+			return(request_irq(cs->irq, &sedlbauer_interrupt,
+					I4L_IRQ_FLAG, "HiSax", cs));
+		case CARD_INIT:
+			clear_pending_isac_ints(cs);
+			clear_pending_hscx_ints(cs);
+			initisac(cs);
+			inithscx(cs);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+__initfunc(int
+setup_sedlbauer(struct IsdnCard *card))
+{
+	int bytecnt;
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, Sedlbauer_revision);
+	printk(KERN_INFO "HiSax: Sedlbauer driver Rev. %s\n", HiSax_getrev(tmp));
+ 	if (cs->typ == ISDN_CTYPE_SEDLBAUER) {
+ 		cs->subtyp = SEDL_SPEED_CARD;
+ 	} else if (cs->typ == ISDN_CTYPE_SEDLBAUER_PCMCIA) {	
+ 		cs->subtyp = SEDL_SPEED_STAR;
+ 	} else
+		return (0);
+
+	bytecnt = 8;
+	cs->hw.sedl.cfg_reg = card->para[1];
+	cs->irq = card->para[0];
+	if (cs->subtyp == SEDL_SPEED_STAR) {
+		cs->hw.sedl.adr = cs->hw.sedl.cfg_reg + SEDL_PCMCIA_ADR;
+		cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_PCMCIA_ISAC;
+		cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + SEDL_PCMCIA_HSCX;
+		cs->hw.sedl.reset_on = cs->hw.sedl.cfg_reg + SEDL_PCMCIA_RESET;
+		cs->hw.sedl.reset_off = cs->hw.sedl.cfg_reg + SEDL_PCMCIA_RESET;
+	} else {
+		cs->hw.sedl.adr = cs->hw.sedl.cfg_reg + SEDL_ADR;
+		cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_ISAC;
+		cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + SEDL_HSCX;
+		cs->hw.sedl.reset_on = cs->hw.sedl.cfg_reg + SEDL_RESET_ON;
+		cs->hw.sedl.reset_off = cs->hw.sedl.cfg_reg + SEDL_RESET_OFF;
+	}
+        
+	/* In case of the sedlbauer pcmcia card, this region is in use,
+           reserved for us by the card manager. So we do not check it
+           here, it would fail. */
+	if (cs->typ != ISDN_CTYPE_SEDLBAUER_PCMCIA &&
+	   check_region((cs->hw.sedl.cfg_reg), bytecnt)) {
+		printk(KERN_WARNING
+		       "HiSax: %s config port %x-%x already in use\n",
+		       CardType[card->typ],
+		       cs->hw.sedl.cfg_reg,
+		       cs->hw.sedl.cfg_reg + bytecnt);
+		return (0);
+	} else {
+		request_region(cs->hw.sedl.cfg_reg, bytecnt, "sedlbauer isdn");
+	}
+
+	printk(KERN_INFO
+	       "Sedlbauer: defined at 0x%x IRQ %d\n",
+	       cs->hw.sedl.cfg_reg,
+	       cs->irq);
+	printk(KERN_WARNING
+		       "Sedlbauer %s uses ports 0x%x-0x%x\n",
+		       Sedlbauer_Types[cs->subtyp],
+		       cs->hw.sedl.cfg_reg,
+		       cs->hw.sedl.cfg_reg + bytecnt);
+
+	printk(KERN_INFO "Sedlbauer: resetting card\n");
+	reset_sedlbauer(cs);
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &Sedl_card_msg;
+	ISACVersion(cs, "Sedlbauer:");
+	if (HscxVersion(cs, "Sedlbauer:")) {
+		printk(KERN_WARNING
+		    "Sedlbauer: wrong HSCX versions check IO address\n");
+		release_io_sedlbauer(cs);
+		return (0);
+	}
+	return (1);
+}
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/siemens.h linux/drivers/isdn/hisax/siemens.h
--- v2.1.91/linux/drivers/isdn/hisax/siemens.h	Thu Feb 27 10:57:29 1997
+++ linux/drivers/isdn/hisax/siemens.h	Wed Dec 31 16:00:00 1969
@@ -1,71 +0,0 @@
-/* $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.91/linux/drivers/isdn/hisax/sportster.c linux/drivers/isdn/hisax/sportster.c
--- v2.1.91/linux/drivers/isdn/hisax/sportster.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/sportster.c	Wed Apr  1 16:21:02 1998
@@ -0,0 +1,291 @@
+/* $Id: sportster.c,v 1.5 1998/02/02 13:29:46 keil Exp $
+
+ * sportster.c     low level stuff for USR Sportster internal TA
+ *
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ * Thanks to Christian "naddy" Weisgerber (3Com, US Robotics) for documentation
+ *
+ * $Log: sportster.c,v $
+ * Revision 1.5  1998/02/02 13:29:46  keil
+ * fast io
+ *
+ * Revision 1.4  1997/11/08 21:35:52  keil
+ * new l1 init
+ *
+ * Revision 1.3  1997/11/06 17:09:29  keil
+ * New 2.1 init code
+ *
+ * Revision 1.2  1997/10/29 18:51:18  keil
+ * New files
+ *
+ * Revision 1.1.2.1  1997/10/17 22:10:58  keil
+ * new files on 2.0
+ *
+ */
+#define __NO_VERSION__
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+
+extern const char *CardType[];
+const char *sportster_revision = "$Revision: 1.5 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define	 SPORTSTER_ISAC		0xC000
+#define	 SPORTSTER_HSCXA	0x0000
+#define	 SPORTSTER_HSCXB	0x4000
+#define	 SPORTSTER_RES_IRQ	0x8000
+#define	 SPORTSTER_RESET	0x80
+#define	 SPORTSTER_INTE		0x40
+
+static inline int
+calc_off(unsigned int base, unsigned int off)
+{
+	return(base + ((off & 0xfc)<<8) + ((off & 3)<<1));
+}
+
+static inline void
+read_fifo(unsigned int adr, u_char * data, int size)
+{
+	insb(adr, data, size);
+}
+
+static void
+write_fifo(unsigned int adr, u_char * data, int size)
+{
+	outsb(adr, data, size);
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (bytein(calc_off(cs->hw.spt.isac, offset)));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	byteout(calc_off(cs->hw.spt.isac, offset), value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	read_fifo(cs->hw.spt.isac, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	write_fifo(cs->hw.spt.isac, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return (bytein(calc_off(cs->hw.spt.hscx[hscx], offset)));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	byteout(calc_off(cs->hw.spt.hscx[hscx], offset), value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) bytein(calc_off(cs->hw.spt.hscx[nr], reg))
+#define WRITEHSCX(cs, nr, reg, data) byteout(calc_off(cs->hw.spt.hscx[nr], reg), data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo(cs->hw.spt.hscx[nr], ptr, cnt)
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo(cs->hw.spt.hscx[nr], ptr, cnt)
+
+#include "hscx_irq.c"
+
+static void
+sportster_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val;
+
+	if (!cs) {
+		printk(KERN_WARNING "Sportster: Spurious interrupt!\n");
+		return;
+	}
+	val = READHSCX(cs, 1, HSCX_ISTA);
+      Start_HSCX:
+	if (val)
+		hscx_int_main(cs, val);
+	val = ReadISAC(cs, ISAC_ISTA);
+      Start_ISAC:
+	if (val)
+		isac_interrupt(cs, val);
+	val = READHSCX(cs, 1, HSCX_ISTA);
+	if (val) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX IntStat after IntRoutine");
+		goto Start_HSCX;
+	}
+	val = ReadISAC(cs, ISAC_ISTA);
+	if (val) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	/* get a new irq impulse if there any pending */
+	bytein(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ +1);
+}
+
+void
+release_io_sportster(struct IsdnCardState *cs)
+{
+	int i, adr;
+
+	byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, 0);
+	for (i=0; i<64; i++) {
+		adr = cs->hw.spt.cfg_reg + i *1024;
+		release_region(adr, 8);
+	}
+}
+
+void
+reset_sportster(struct IsdnCardState *cs)
+{
+	long flags;
+
+	cs->hw.spt.res_irq |= SPORTSTER_RESET; /* Reset On */
+	byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, cs->hw.spt.res_irq);
+	save_flags(flags);
+	sti();
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + 1;
+	schedule();
+	cs->hw.spt.res_irq &= ~SPORTSTER_RESET; /* Reset Off */
+	byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, cs->hw.spt.res_irq);
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + 1;
+	schedule();
+	restore_flags(flags);
+}
+
+static int
+Sportster_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	switch (mt) {
+		case CARD_RESET:
+			reset_sportster(cs);
+			return(0);
+		case CARD_RELEASE:
+			release_io_sportster(cs);
+			return(0);
+		case CARD_SETIRQ:
+			return(request_irq(cs->irq, &sportster_interrupt,
+					I4L_IRQ_FLAG, "HiSax", cs));
+		case CARD_INIT:
+			clear_pending_isac_ints(cs);
+			clear_pending_hscx_ints(cs);
+			initisac(cs);
+			inithscx(cs);
+			cs->hw.spt.res_irq |= SPORTSTER_INTE; /* IRQ On */
+			byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, cs->hw.spt.res_irq);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+__initfunc(int
+get_io_range(struct IsdnCardState *cs))
+{
+	int i, j, adr;
+	
+	for (i=0;i<64;i++) {
+		adr = cs->hw.spt.cfg_reg + i *1024;
+		if (check_region(adr, 8)) {
+			printk(KERN_WARNING
+				"HiSax: %s config port %x-%x already in use\n",
+				CardType[cs->typ], adr, adr + 8);
+			break;
+		} else
+			request_region(adr, 8, "sportster");
+	}
+	if (i==64)
+		return(1);
+	else {
+		for (j=0; j<i; j++) {
+			adr = cs->hw.spt.cfg_reg + j *1024;
+			release_region(adr, 8);
+		}
+		return(0);
+	}
+}
+
+__initfunc(int
+setup_sportster(struct IsdnCard *card))
+{
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, sportster_revision);
+	printk(KERN_INFO "HiSax: USR Sportster driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_SPORTSTER)
+		return (0);
+
+	cs->hw.spt.cfg_reg = card->para[1];
+	cs->irq = card->para[0];
+	if (!get_io_range(cs))
+		return (0);
+	cs->hw.spt.isac = cs->hw.spt.cfg_reg + SPORTSTER_ISAC;
+	cs->hw.spt.hscx[0] = cs->hw.spt.cfg_reg + SPORTSTER_HSCXA;
+	cs->hw.spt.hscx[1] = cs->hw.spt.cfg_reg + SPORTSTER_HSCXB;
+	
+	switch(cs->irq) {
+		case 5:	cs->hw.spt.res_irq = 1;
+			break;
+		case 7:	cs->hw.spt.res_irq = 2;
+			break;
+		case 10:cs->hw.spt.res_irq = 3;
+			break;
+		case 11:cs->hw.spt.res_irq = 4;
+			break;
+		case 12:cs->hw.spt.res_irq = 5;
+			break;
+		case 14:cs->hw.spt.res_irq = 6;
+			break;
+		case 15:cs->hw.spt.res_irq = 7;
+			break;
+		default:release_io_sportster(cs);
+			printk(KERN_WARNING "Sportster: wrong IRQ\n");
+			return(0);
+	}
+	reset_sportster(cs);
+	printk(KERN_INFO
+	       "HiSax: %s config irq:%d cfg:0x%X\n",
+	       CardType[cs->typ], cs->irq,
+	       cs->hw.spt.cfg_reg);
+
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &Sportster_card_msg;
+	ISACVersion(cs, "Sportster:");
+	if (HscxVersion(cs, "Sportster:")) {
+		printk(KERN_WARNING
+		       "Sportster: wrong HSCX versions check IO address\n");
+		release_io_sportster(cs);
+		return (0);
+	}
+	return (1);
+}
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/tei.c linux/drivers/isdn/hisax/tei.c
--- v2.1.91/linux/drivers/isdn/hisax/tei.c	Thu Feb 12 20:56:06 1998
+++ linux/drivers/isdn/hisax/tei.c	Wed Apr  1 16:21:02 1998
@@ -1,4 +1,4 @@
-/* $Id: tei.c,v 1.8 1997/04/07 22:59:08 keil Exp $
+/* $Id: tei.c,v 2.7 1998/02/12 23:08:11 keil Exp $
 
  * Author       Karsten Keil (keil@temic-ech.spacenet.de)
  *              based on the teles driver from Jan den Ouden
@@ -7,52 +7,112 @@
  *              Fritz Elfert
  *
  * $Log: tei.c,v $
- * Revision 1.8  1997/04/07 22:59:08  keil
- * GFP_KERNEL --> GFP_ATOMIC
+ * Revision 2.7  1998/02/12 23:08:11  keil
+ * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
  *
- * Revision 1.7  1997/04/06 22:54:03  keil
- * Using SKB's
+ * Revision 2.6  1998/02/02 13:41:42  keil
+ * fix MDL_ASSIGN for PtP
  *
- * Revision 1.6  1997/02/09 00:25:12  keil
- * new interface handling, one interface per card
+ * Revision 2.5  1997/11/06 17:09:12  keil
+ * New 2.1 init code
  *
- * Revision 1.5  1997/01/27 15:57:51  keil
- * cosmetics
+ * Revision 2.4  1997/10/29 19:04:46  keil
+ * changes for 2.1
  *
- * Revision 1.4  1997/01/21 22:32:44  keil
- * Tei verify request
+ * Revision 2.3  1997/10/01 09:21:43  fritz
+ * Removed old compatibility stuff for 2.0.X kernels.
+ * From now on, this code is for 2.1.X ONLY!
+ * Old stuff is still in the separate branch.
  *
- * Revision 1.3  1997/01/04 13:45:02  keil
- * cleanup,adding remove tei request (thanks to Sim Yskes)
+ * Revision 2.2  1997/07/31 19:24:39  keil
+ * fixed a warning
  *
- * Revision 1.2  1996/12/08 19:52:39  keil
- * minor debug fix
+ * Revision 2.1  1997/07/31 11:50:16  keil
+ * ONE TEI and FIXED TEI handling
  *
- * Revision 1.1  1996/10/13 20:04:57  keil
- * Initial revision
+ * Revision 2.0  1997/07/27 21:13:30  keil
+ * New TEI managment
+ *
+ * Revision 1.9  1997/06/26 11:18:02  keil
+ * New managment
+ *
+ * Revision 1.8  1997/04/07 22:59:08  keil
+ * GFP_KERNEL --> GFP_ATOMIC
  *
+ * Revision 1.7  1997/04/06 22:54:03  keil
+ * Using SKB's
  *
+ * Old log removed/ KKe
  *
  */
 #define __NO_VERSION__
 #include "hisax.h"
+#include "isdnl2.h"
+#include <linux/random.h>
 
-extern struct IsdnCard cards[];
-extern int nrcards;
+const char *tei_revision = "$Revision: 2.7 $";
 
-const char *tei_revision = "$Revision: 1.8 $";
+#define ID_REQUEST	1
+#define ID_ASSIGNED	2
+#define ID_DENIED	3
+#define ID_CHK_REQ	4
+#define ID_CHK_RES	5
+#define ID_REMOVE	6
+#define ID_VERIFY	7
+
+#define TEI_ENTITY_ID	0xf
+
+static
+struct Fsm teifsm =
+{NULL, 0, 0, NULL, NULL};
+
+void tei_handler(struct PStack *st, u_char pr, struct sk_buff *skb);
+
+enum {
+	ST_TEI_NOP,
+	ST_TEI_IDREQ,
+	ST_TEI_IDVERIFY,
+};
+
+#define TEI_STATE_COUNT (ST_TEI_IDVERIFY+1)
+
+static char *strTeiState[] =
+{
+	"ST_TEI_NOP",
+	"ST_TEI_IDREQ",
+	"ST_TEI_IDVERIFY",
+};
+
+enum {
+	EV_IDREQ,
+	EV_ASSIGN,
+	EV_DENIED,
+	EV_CHKREQ,
+	EV_REMOVE,
+	EV_VERIFY,
+	EV_T202,
+};
+
+#define TEI_EVENT_COUNT (EV_T202+1)
+
+static char *strTeiEvent[] =
+{
+	"EV_IDREQ",
+	"EV_ASSIGN",
+	"EV_DENIED",
+	"EV_CHKREQ",
+	"EV_REMOVE",
+	"EV_VERIFY",
+	"EV_T202",
+};
 
-static struct PStack *
-findces(struct PStack *st, int ces)
+unsigned int
+random_ri(void)
 {
-	struct PStack *ptr = *(st->l1.stlistp);
+	unsigned int x;
 
-	while (ptr)
-		if (ptr->l2.ces == ces)
-			return (ptr);
-		else
-			ptr = ptr->next;
-	return (NULL);
+	get_random_bytes(&x, sizeof(x));
+	return (x & 0xffff);
 }
 
 static struct PStack *
@@ -72,246 +132,357 @@
 }
 
 static void
-mdl_unit_data_res(struct PStack *st, unsigned int ri, u_char mt, u_char ai)
+put_tei_msg(struct PStack *st, u_char m_id, unsigned int ri, u_char tei)
 {
 	struct sk_buff *skb;
 	u_char *bp;
 
-	if (!(skb = alloc_skb(6 + MAX_HEADER_LEN, GFP_ATOMIC))) {
+	if (!(skb = alloc_skb(8, GFP_ATOMIC))) {
 		printk(KERN_WARNING "HiSax: No skb for TEI manager\n");
 		return;
 	}
-	skb_reserve(skb, MAX_HEADER_LEN);
+	bp = skb_put(skb, 3);
+	bp[0] = (TEI_SAPI << 2);
+	bp[1] = (GROUP_TEI << 1) | 0x1;
+	bp[2] = UI;
 	bp = skb_put(skb, 5);
-	bp[0] = 0xf;
+	bp[0] = TEI_ENTITY_ID;
 	bp[1] = ri >> 8;
 	bp[2] = ri & 0xff;
-	bp[3] = mt;
-	bp[4] = (ai << 1) | 1;
-	st->l3.l3l2(st, DL_UNIT_DATA, skb);
+	bp[3] = m_id;
+	bp[4] = (tei << 1) | 1;
+	st->l2.l2l1(st, PH_DATA_REQ, skb);
 }
 
 static void
-mdl_unit_data_ind(struct PStack *st, unsigned int ri, u_char mt, u_char ai)
+tei_id_request(struct FsmInst *fi, int event, void *arg)
 {
-	unsigned int tces;
-	struct PStack *otsp, *ptr;
+	struct PStack *st = fi->userdata;
 	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);
-			}
+	if (st->l2.tei != -1) {
+		sprintf(tmp, "assign request for allready asigned tei %d",
+			st->l2.tei);
+		st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
+		return;
 	}
+	st->ma.ri = random_ri();
+	if (st->ma.debug) {
+		sprintf(tmp, "assign request ri %d", st->ma.ri);
+		st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
+	}
+	put_tei_msg(st, ID_REQUEST, st->ma.ri, 127);
+	FsmChangeState(&st->ma.tei_m, ST_TEI_IDREQ);
+	FsmAddTimer(&st->ma.t202, st->ma.T202, EV_T202, NULL, 1);
+	st->ma.N202 = 3;
 }
 
-void
-tei_handler(struct PStack *st,
-	    u_char pr, struct sk_buff *skb)
+static void
+tei_id_assign(struct FsmInst *fi, int event, void *arg)
 {
-	u_char *bp;
-	unsigned int data;
-	char tmp[32];
+	struct PStack *ost, *st = fi->userdata;
+	struct sk_buff *skb = arg;
+	struct IsdnCardState *cs;
+	int ri, tei;
+	char tmp[64];
 
-	switch (pr) {
-		case (MDL_ASSIGN):
-			data = (unsigned int) skb;
-			if (st->l3.debug) {
-				sprintf(tmp, "ces %d assign request", data);
-				st->l2.l2m.printdebug(&st->l2.l2m, tmp);
-			}
-			mdl_unit_data_res(st, data, 1, 127);
-			break;
-		case (MDL_VERIFY):
-			data = (unsigned int) skb;
-			if (st->l3.debug) {
-				sprintf(tmp, "%d id verify request", data);
-				st->l2.l2m.printdebug(&st->l2.l2m, tmp);
-			}
-			mdl_unit_data_res(st, 0, 7, data);
-			break;
-		case (DL_UNIT_DATA):
-			bp = skb->data;
-			if (bp[0] != 0xf) {
-				/* wrong management entity identifier, ignore */
-				/* shouldn't ibh be released??? */
-				printk(KERN_WARNING "tei handler wrong entity id %x\n", bp[0]);
-			} else
-				mdl_unit_data_ind(st, (bp[1] << 8) | bp[2], bp[3], bp[4] >> 1);
-			SET_SKB_FREE(skb);
-			dev_kfree_skb(skb);
-			break;
-		default:
-			break;
+	ri = ((unsigned int) skb->data[1] << 8) + skb->data[2];
+	tei = skb->data[4] >> 1;
+	if (st->ma.debug) {
+		sprintf(tmp, "identity assign ri %d tei %d", ri, tei);
+		st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
+	}
+	if ((ost = findtei(st, tei))) {		/* same tei is in use */
+		if (ri != ost->ma.ri) {
+			sprintf(tmp, "possible duplicate assignment tei %d", tei);
+			st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
+			ost->l2.l2tei(ost, MDL_ERROR_REQ, NULL);
+		}
+	} else if (ri == st->ma.ri) {
+		FsmDelTimer(&st->ma.t202, 1);
+		FsmChangeState(&st->ma.tei_m, ST_TEI_NOP);
+		st->ma.manl2(st, MDL_ASSIGN_REQ, (void *) (int) tei);
+		cs = (struct IsdnCardState *) st->l1.hardware;
+		cs->cardmsg(cs, MDL_ASSIGN_REQ, NULL);
 	}
 }
 
-unsigned int
-randomces(void)
+static void
+tei_id_denied(struct FsmInst *fi, int event, void *arg)
 {
-	int x = jiffies & 0xffff;
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+	int ri, tei;
+	char tmp[64];
 
-	return (x);
+	ri = ((unsigned int) skb->data[1] << 8) + skb->data[2];
+	tei = skb->data[4] >> 1;
+	if (st->ma.debug) {
+		sprintf(tmp, "identity denied ri %d tei %d", ri, tei);
+		st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
+	}
 }
 
 static void
-tei_man(struct PStack *sp, int i, void *v)
+tei_id_chk_req(struct FsmInst *fi, int event, void *arg)
 {
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+	int tei;
+	char tmp[64];
 
-	printk(KERN_DEBUG "tei_man\n");
+	tei = skb->data[4] >> 1;
+	if (st->ma.debug) {
+		sprintf(tmp, "identity check req tei %d", tei);
+		st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
+	}
+	if ((st->l2.tei != -1) && ((tei == GROUP_TEI) || (tei == st->l2.tei))) {
+		FsmDelTimer(&st->ma.t202, 4);
+		FsmChangeState(&st->ma.tei_m, ST_TEI_NOP);
+		put_tei_msg(st, ID_CHK_RES, random_ri(), st->l2.tei);
+	}
+}
+
+static void
+tei_id_remove(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+	struct IsdnCardState *cs;
+	int tei;
+	char tmp[64];
+
+	tei = skb->data[4] >> 1;
+	if (st->ma.debug) {
+		sprintf(tmp, "identity remove tei %d", tei);
+		st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
+	}
+	if ((st->l2.tei != -1) && ((tei == GROUP_TEI) || (tei == st->l2.tei))) {
+		FsmDelTimer(&st->ma.t202, 5);
+		FsmChangeState(&st->ma.tei_m, ST_TEI_NOP);
+		st->ma.manl2(st, MDL_REMOVE_REQ, 0);
+		cs = (struct IsdnCardState *) st->l1.hardware;
+		cs->cardmsg(cs, MDL_REMOVE_REQ, NULL);
+	}
+}
+
+static void
+tei_id_verify(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	char tmp[64];
+
+	if (st->ma.debug) {
+		sprintf(tmp, "id verify request for tei %d", st->l2.tei);
+		st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
+	}
+	put_tei_msg(st, ID_VERIFY, 0, st->l2.tei);
+	FsmChangeState(&st->ma.tei_m, ST_TEI_IDVERIFY);
+	FsmAddTimer(&st->ma.t202, st->ma.T202, EV_T202, NULL, 2);
+	st->ma.N202 = 2;
+}
+
+static void
+tei_id_req_tout(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	char tmp[64];
+	struct IsdnCardState *cs;
+
+	if (--st->ma.N202) {
+		st->ma.ri = random_ri();
+		if (st->ma.debug) {
+			sprintf(tmp, "assign req(%d) ri %d",
+				4 - st->ma.N202, st->ma.ri);
+			st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
+		}
+		put_tei_msg(st, ID_REQUEST, st->ma.ri, 127);
+		FsmAddTimer(&st->ma.t202, st->ma.T202, EV_T202, NULL, 3);
+	} else {
+		sprintf(tmp, "assign req failed");
+		st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
+		st->ma.manl2(st, MDL_ERROR_IND, 0);
+		cs = (struct IsdnCardState *) st->l1.hardware;
+		cs->cardmsg(cs, MDL_REMOVE_REQ, NULL);
+		FsmChangeState(fi, ST_TEI_NOP);
+	}
+}
+
+static void
+tei_id_ver_tout(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	char tmp[64];
+	struct IsdnCardState *cs;
+
+	if (--st->ma.N202) {
+		if (st->ma.debug) {
+			sprintf(tmp, "id verify req(%d) for tei %d",
+				3 - st->ma.N202, st->l2.tei);
+			st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
+		}
+		put_tei_msg(st, ID_VERIFY, 0, st->l2.tei);
+		FsmAddTimer(&st->ma.t202, st->ma.T202, EV_T202, NULL, 4);
+	} else {
+		sprintf(tmp, "verify req for tei %d failed", st->l2.tei);
+		st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
+		st->ma.manl2(st, MDL_REMOVE_REQ, 0);
+		cs = (struct IsdnCardState *) st->l1.hardware;
+		cs->cardmsg(cs, MDL_REMOVE_REQ, NULL);
+		FsmChangeState(fi, ST_TEI_NOP);
+	}
+}
+
+static void
+tei_l1l2(struct PStack *st, int pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int mt;
+	char tmp[64];
+
+	if (pr == PH_DATA_IND) {
+		if (skb->len < 3) {
+			sprintf(tmp, "short mgr frame %d/3", skb->len);
+			st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
+		} else if (((skb->data[0] >> 2) != TEI_SAPI) ||
+			   ((skb->data[1] >> 1) != GROUP_TEI)) {
+			sprintf(tmp, "wrong mgr sapi/tei %x/%x",
+				skb->data[0], skb->data[1]);
+			st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
+		} else if ((skb->data[2] & 0xef) != UI) {
+			sprintf(tmp, "mgr frame is not ui %x",
+				skb->data[2]);
+			st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
+		} else {
+			skb_pull(skb, 3);
+			if (skb->len < 5) {
+				sprintf(tmp, "short mgr frame %d/5", skb->len);
+				st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
+			} else if (skb->data[0] != TEI_ENTITY_ID) {
+				/* wrong management entity identifier, ignore */
+				sprintf(tmp, "tei handler wrong entity id %x\n",
+					skb->data[0]);
+				st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
+			} else {
+				mt = skb->data[3];
+				if (mt == ID_ASSIGNED)
+					FsmEvent(&st->ma.tei_m, EV_ASSIGN, skb);
+				else if (mt == ID_DENIED)
+					FsmEvent(&st->ma.tei_m, EV_DENIED, skb);
+				else if (mt == ID_CHK_REQ)
+					FsmEvent(&st->ma.tei_m, EV_CHKREQ, skb);
+				else if (mt == ID_REMOVE)
+					FsmEvent(&st->ma.tei_m, EV_REMOVE, skb);
+				else {
+					sprintf(tmp, "tei handler wrong mt %x\n",
+						mt);
+					st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
+				}
+			}
+		}
+	} else {
+		sprintf(tmp, "tei handler wrong pr %x\n", pr);
+		st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
+	}
+	dev_kfree_skb(skb);
 }
 
 static void
 tei_l2tei(struct PStack *st, int pr, void *arg)
 {
-	struct IsdnCardState *sp = st->l1.hardware;
+	switch (pr) {
+		case (MDL_ASSIGN_IND):
+#ifdef TEI_FIXED
+			if (st->ma.debug) {
+				char tmp[64];
+				sprintf(tmp, "fixed assign tei %d", TEI_FIXED);
+				st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
+			}
+			st->ma.manl2(st, MDL_ASSIGN_REQ, (void *) (int) TEI_FIXED);
+#else
+			FsmEvent(&st->ma.tei_m, EV_IDREQ, arg);
+#endif
+			break;
+		case (MDL_ERROR_REQ):
+			FsmEvent(&st->ma.tei_m, EV_VERIFY, arg);
+			break;
+		default:
+			break;
+	}
+}
+
+static void
+tei_debug(struct FsmInst *fi, char *s)
+{
+	struct PStack *st = fi->userdata;
+	char tm[32], str[256];
 
-	tei_handler(sp->teistack, pr, arg);
+	jiftime(tm, jiffies);
+	sprintf(str, "%s Tei %s\n", tm, s);
+	HiSax_putstatus(st->l1.hardware, str);
 }
 
 void
 setstack_tei(struct PStack *st)
 {
 	st->l2.l2tei = tei_l2tei;
+	st->ma.T202 = 2000;	/* T202  2000 milliseconds */
+	st->l1.l1tei = tei_l1l2;
+	st->ma.debug = 1;
+	st->ma.tei_m.fsm = &teifsm;
+	st->ma.tei_m.state = ST_TEI_NOP;
+	st->ma.tei_m.debug = 1;
+	st->ma.tei_m.userdata = st;
+	st->ma.tei_m.userint = 0;
+	st->ma.tei_m.printdebug = tei_debug;
+	FsmInitTimer(&st->ma.tei_m, &st->ma.t202);
 }
 
 void
 init_tei(struct IsdnCardState *sp, int protocol)
 {
-	struct PStack *st;
-	char tmp[128];
 
-	st = (struct PStack *) kmalloc(sizeof(struct PStack), GFP_ATOMIC);
-	setstack_HiSax(st, sp);
-	st->l2.extended = !0;
-	st->l2.laptype = LAPD;
-	st->l2.window = 1;
-	st->l2.orig = !0;
-	st->protocol = protocol;
-/*
- * the following is not necessary for tei mng. (broadcast only)
- */
-	st->l2.t200 = 500;	/* 500 milliseconds */
-	st->l2.n200 = 4;	/* try 4 times */
+}
 
-	st->l2.sap = 63;
-	st->l2.tei = 127;
+void
+release_tei(struct IsdnCardState *cs)
+{
+	struct PStack *st = cs->stlist;
 
-	sprintf(tmp, "Card %d tei", sp->cardnr + 1);
-	setstack_isdnl2(st, tmp);
-	st->l2.debug = 0;
-	st->l3.debug = 0;
-
-	st->ma.manl2(st, MDL_NOTEIPROC, NULL);
-
-	st->l2.l2l3 = (void *) tei_handler;
-	st->l1.l1man = tei_man;
-	st->l2.l2man = tei_man;
-	st->l4.l2writewakeup = NULL;
+	while (st) {
+		FsmDelTimer(&st->ma.t202, 1);
+		st = st->next;
+	}
+}
 
-	HiSax_addlist(sp, st);
-	sp->teistack = st;
+static struct FsmNode TeiFnList[] HISAX_INITDATA =
+{
+	{ST_TEI_NOP, EV_IDREQ, tei_id_request},
+	{ST_TEI_NOP, EV_VERIFY, tei_id_verify},
+	{ST_TEI_NOP, EV_REMOVE, tei_id_remove},
+	{ST_TEI_NOP, EV_CHKREQ, tei_id_chk_req},
+	{ST_TEI_IDREQ, EV_T202, tei_id_req_tout},
+	{ST_TEI_IDREQ, EV_ASSIGN, tei_id_assign},
+	{ST_TEI_IDREQ, EV_DENIED, tei_id_denied},
+	{ST_TEI_IDVERIFY, EV_T202, tei_id_ver_tout},
+	{ST_TEI_IDVERIFY, EV_REMOVE, tei_id_remove},
+	{ST_TEI_IDVERIFY, EV_CHKREQ, tei_id_chk_req},
+};
+
+#define TEI_FN_COUNT (sizeof(TeiFnList)/sizeof(struct FsmNode))
+
+HISAX_INITFUNC(void
+TeiNew(void))
+{
+	teifsm.state_count = TEI_STATE_COUNT;
+	teifsm.event_count = TEI_EVENT_COUNT;
+	teifsm.strEvent = strTeiEvent;
+	teifsm.strState = strTeiState;
+	FsmNew(&teifsm, TeiFnList, TEI_FN_COUNT);
 }
 
 void
-release_tei(struct IsdnCardState *sp)
+TeiFree(void)
 {
-	struct PStack *st = sp->teistack;
-
-	HiSax_rmlist(sp, st);
-	kfree((void *) st);
+	FsmFree(&teifsm);
 }
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/teleint.c linux/drivers/isdn/hisax/teleint.c
--- v2.1.91/linux/drivers/isdn/hisax/teleint.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/teleint.c	Wed Apr  1 16:21:02 1998
@@ -0,0 +1,363 @@
+/* $Id: teleint.c,v 1.5 1998/02/02 13:40:47 keil Exp $
+
+ * teleint.c     low level stuff for TeleInt isdn cards
+ *
+ * Author     Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ *
+ * $Log: teleint.c,v $
+ * Revision 1.5  1998/02/02 13:40:47  keil
+ * fast io
+ *
+ * Revision 1.4  1997/11/08 21:35:53  keil
+ * new l1 init
+ *
+ * Revision 1.3  1997/11/06 17:09:30  keil
+ * New 2.1 init code
+ *
+ * Revision 1.2  1997/10/29 18:55:53  keil
+ * changes for 2.1.60 (irq2dev_map)
+ *
+ * Revision 1.1  1997/09/11 17:32:32  keil
+ * new
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include "hisax.h"
+#include "isac.h"
+#include "hfc_2bs0.h"
+#include "isdnl1.h"
+
+extern const char *CardType[];
+
+const char *TeleInt_revision = "$Revision: 1.5 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+static inline u_char
+readreg(unsigned int ale, unsigned int adr, u_char off)
+{
+	register u_char ret;
+	int max_delay = 2000;
+	long flags;
+
+	save_flags(flags);
+	cli();
+	byteout(ale, off);
+	ret = HFC_BUSY & bytein(ale);
+	while (ret && --max_delay)
+		ret = HFC_BUSY & bytein(ale);
+	if (!max_delay) {
+		printk(KERN_WARNING "TeleInt Busy not inaktive\n");
+		restore_flags(flags);
+		return (0);
+	}
+	ret = bytein(adr);
+	restore_flags(flags);
+	return (ret);
+}
+
+static inline void
+readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	register u_char ret;
+	int max_delay = 2000;
+	byteout(ale, off);
+
+	ret = HFC_BUSY & bytein(ale);
+	while (ret && --max_delay)
+		ret = HFC_BUSY & bytein(ale);
+	if (!max_delay) {
+		printk(KERN_WARNING "TeleInt Busy not inaktive\n");
+		return;
+	}
+	insb(adr, data, size);
+}
+
+
+static inline void
+writereg(unsigned int ale, unsigned int adr, u_char off, u_char data)
+{
+	register u_char ret;
+	int max_delay = 2000;
+	long flags;
+
+	save_flags(flags);
+	cli();
+	byteout(ale, off);
+	ret = HFC_BUSY & bytein(ale);
+	while (ret && --max_delay)
+		ret = HFC_BUSY & bytein(ale);
+	if (!max_delay) {
+		printk(KERN_WARNING "TeleInt Busy not inaktive\n");
+		restore_flags(flags);
+		return;
+	}
+	byteout(adr, data);
+	restore_flags(flags);
+}
+
+static inline void
+writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	register u_char ret;
+	int max_delay = 2000;
+
+	/* fifo write without cli because it's allready done  */
+	byteout(ale, off);
+	ret = HFC_BUSY & bytein(ale);
+	while (ret && --max_delay)
+		ret = HFC_BUSY & bytein(ale);
+	if (!max_delay) {
+		printk(KERN_WARNING "TeleInt Busy not inaktive\n");
+		return;
+	}
+	outsb(adr, data, size);
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	cs->hw.hfc.cip = offset;
+	return (readreg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	cs->hw.hfc.cip = offset;
+	writereg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	cs->hw.hfc.cip = 0;
+	readfifo(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, 0, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	cs->hw.hfc.cip = 0;
+	writefifo(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, 0, data, size);
+}
+
+static u_char
+ReadHFC(struct IsdnCardState *cs, int data, u_char reg)
+{
+	register u_char ret;
+
+	if (data) {
+		cs->hw.hfc.cip = reg;
+		byteout(cs->hw.hfc.addr | 1, reg);
+		ret = bytein(cs->hw.hfc.addr);
+		if (cs->debug & L1_DEB_HSCX_FIFO && (data != 2)) {
+			char tmp[32];
+			sprintf(tmp, "hfc RD %02x %02x", reg, ret);
+			debugl1(cs, tmp);
+		}
+	} else
+		ret = bytein(cs->hw.hfc.addr | 1);
+	return (ret);
+}
+
+static void
+WriteHFC(struct IsdnCardState *cs, int data, u_char reg, u_char value)
+{
+	byteout(cs->hw.hfc.addr | 1, reg);
+	cs->hw.hfc.cip = reg;
+	if (data)
+		byteout(cs->hw.hfc.addr, value);
+	if (cs->debug & L1_DEB_HSCX_FIFO && (data != 2)) {
+		char tmp[32];
+		sprintf(tmp, "hfc W%c %02x %02x", data ? 'D' : 'C', reg, value);
+		debugl1(cs, tmp);
+	}
+}
+
+static void
+TeleInt_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val, stat = 0;
+
+	if (!cs) {
+		printk(KERN_WARNING "TeleInt: Spurious interrupt!\n");
+		return;
+	}
+	val = readreg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, ISAC_ISTA);
+      Start_ISAC:
+	if (val) {
+		isac_interrupt(cs, val);
+		stat |= 2;
+	}
+	val = readreg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, ISAC_ISTA);
+	if (val) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	if (stat & 2) {
+		writereg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, ISAC_MASK, 0xFF);
+		writereg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, ISAC_MASK, 0x0);
+	}
+}
+
+static void
+TeleInt_Timer(struct IsdnCardState *cs)
+{
+	int stat = 0;
+
+	if (cs->bcs[0].mode) {
+		stat |= 1;
+		main_irq_hfc(&cs->bcs[0]);
+	}
+	if (cs->bcs[1].mode) {
+		stat |= 2;
+		main_irq_hfc(&cs->bcs[1]);
+	}
+	cs->hw.hfc.timer.expires = jiffies + 1;
+	add_timer(&cs->hw.hfc.timer);
+}
+
+void
+release_io_TeleInt(struct IsdnCardState *cs)
+{
+	del_timer(&cs->hw.hfc.timer);
+	releasehfc(cs);
+	if (cs->hw.hfc.addr)
+		release_region(cs->hw.hfc.addr, 2);
+}
+
+static void
+reset_TeleInt(struct IsdnCardState *cs)
+{
+	long flags;
+
+	printk(KERN_INFO "TeleInt: resetting card\n");
+	cs->hw.hfc.cirm |= HFC_RESET;
+	byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.cirm);	/* Reset On */
+	save_flags(flags);
+	sti();
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + 3;
+	schedule();
+	cs->hw.hfc.cirm &= ~HFC_RESET;
+	byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.cirm);	/* Reset Off */
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + 1;
+	schedule();
+	restore_flags(flags);
+}
+
+static int
+TeleInt_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	switch (mt) {
+		case CARD_RESET:
+			reset_TeleInt(cs);
+			return(0);
+		case CARD_RELEASE:
+			release_io_TeleInt(cs);
+			return(0);
+		case CARD_SETIRQ:
+			return(request_irq(cs->irq, &TeleInt_interrupt,
+					I4L_IRQ_FLAG, "HiSax", cs));
+		case CARD_INIT:
+			inithfc(cs);
+			clear_pending_isac_ints(cs);
+			initisac(cs);
+			cs->hw.hfc.timer.expires = jiffies + 1;
+			add_timer(&cs->hw.hfc.timer);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+__initfunc(int
+setup_TeleInt(struct IsdnCard *card))
+{
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, TeleInt_revision);
+	printk(KERN_INFO "HiSax: TeleInt driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_TELEINT)
+		return (0);
+
+	cs->hw.hfc.addr = card->para[1] & 0x3fe;
+	cs->irq = card->para[0];
+	cs->hw.hfc.cirm = HFC_CIRM;
+	cs->hw.hfc.isac_spcr = 0x00;
+	cs->hw.hfc.cip = 0;
+	cs->hw.hfc.ctmt = HFC_CTMT | HFC_CLTIMER;
+	cs->bcs[0].hw.hfc.send = NULL;
+	cs->bcs[1].hw.hfc.send = NULL;
+	cs->hw.hfc.fifosize = 7 * 1024 + 512;
+	cs->hw.hfc.timer.function = (void *) TeleInt_Timer;
+	cs->hw.hfc.timer.data = (long) cs;
+	init_timer(&cs->hw.hfc.timer);
+	if (check_region((cs->hw.hfc.addr), 2)) {
+		printk(KERN_WARNING
+		       "HiSax: %s config port %x-%x already in use\n",
+		       CardType[card->typ],
+		       cs->hw.hfc.addr,
+		       cs->hw.hfc.addr + 2);
+		return (0);
+	} else {
+		request_region(cs->hw.hfc.addr, 2, "TeleInt isdn");
+	}
+	/* HW IO = IO */
+	byteout(cs->hw.hfc.addr, cs->hw.hfc.addr & 0xff);
+	byteout(cs->hw.hfc.addr | 1, ((cs->hw.hfc.addr & 0x300) >> 8) | 0x54);
+	switch (cs->irq) {
+		case 3:
+			cs->hw.hfc.cirm |= HFC_INTA;
+			break;
+		case 4:
+			cs->hw.hfc.cirm |= HFC_INTB;
+			break;
+		case 5:
+			cs->hw.hfc.cirm |= HFC_INTC;
+			break;
+		case 7:
+			cs->hw.hfc.cirm |= HFC_INTD;
+			break;
+		case 10:
+			cs->hw.hfc.cirm |= HFC_INTE;
+			break;
+		case 11:
+			cs->hw.hfc.cirm |= HFC_INTF;
+			break;
+		default:
+			printk(KERN_WARNING "TeleInt: wrong IRQ\n");
+			release_io_TeleInt(cs);
+			return (0);
+	}
+	byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.cirm);
+	byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.ctmt);
+
+	printk(KERN_INFO
+	       "TeleInt: defined at 0x%x IRQ %d\n",
+	       cs->hw.hfc.addr,
+	       cs->irq);
+
+	reset_TeleInt(cs);
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHFC;
+	cs->BC_Write_Reg = &WriteHFC;
+	cs->cardmsg = &TeleInt_card_msg;
+	ISACVersion(cs, "TeleInt:");
+	return (1);
+}
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/teles0.c linux/drivers/isdn/hisax/teles0.c
--- v2.1.91/linux/drivers/isdn/hisax/teles0.c	Thu Feb 12 20:56:06 1998
+++ linux/drivers/isdn/hisax/teles0.c	Wed Apr  1 16:21:02 1998
@@ -1,4 +1,4 @@
-/* $Id: teles0.c,v 1.8 1997/04/13 19:54:04 keil Exp $
+/* $Id: teles0.c,v 2.6 1998/02/03 23:27:47 keil Exp $
 
  * teles0.c     low level stuff for Teles Memory IO isdn cards
  *              based on the teles driver from Jan den Ouden
@@ -10,71 +10,73 @@
  *              Beat Doebeli
  *
  * $Log: teles0.c,v $
- * Revision 1.8  1997/04/13 19:54:04  keil
- * Change in IRQ check delay for SMP
+ * Revision 2.6  1998/02/03 23:27:47  keil
+ * IRQ 9
  *
- * Revision 1.7  1997/04/06 22:54:04  keil
- * Using SKB's
+ * Revision 2.5  1998/02/02 13:29:47  keil
+ * fast io
  *
- * Revision 1.6  1997/01/27 15:52:18  keil
- * SMP proof,cosmetics
+ * Revision 2.4  1997/11/08 21:35:54  keil
+ * new l1 init
  *
- * Revision 1.5  1997/01/21 22:25:59  keil
- * cleanups
+ * Revision 2.3  1997/11/06 17:09:31  keil
+ * New 2.1 init code
  *
- * Revision 1.4  1996/11/05 19:41:27  keil
- * more changes for 2.1
+ * Revision 2.2  1997/10/29 18:55:57  keil
+ * changes for 2.1.60 (irq2dev_map)
  *
- * Revision 1.3  1996/10/30 10:22:58  keil
- * Changes for 2.1 kernels
+ * Revision 2.1  1997/07/27 21:47:10  keil
+ * new interface structures
  *
- * Revision 1.2  1996/10/27 22:08:34  keil
- * cosmetic changes
+ * Revision 2.0  1997/06/26 11:02:43  keil
+ * New Layer and card interface
  *
- * Revision 1.1  1996/10/13 20:04:58  keil
- * Initial revision
+ * Revision 1.8  1997/04/13 19:54:04  keil
+ * Change in IRQ check delay for SMP
  *
+ * Revision 1.7  1997/04/06 22:54:04  keil
+ * Using SKB's
  *
+ * removed old log info /KKe
  *
  */
 #define __NO_VERSION__
-#include "siemens.h"
 #include "hisax.h"
-#include "teles0.h"
 #include "isdnl1.h"
-#include <linux/kernel_stat.h>
+#include "isac.h"
+#include "hscx.h"
 
 extern const char *CardType[];
 
-const char *teles0_revision = "$Revision: 1.8 $";
+const char *teles0_revision = "$Revision: 2.6 $";
 
-#define byteout(addr,val) outb_p(val,addr)
-#define bytein(addr) inb_p(addr)
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
 
 static inline u_char
 readisac(unsigned int adr, u_char off)
 {
-	return readb(adr + 0x120 + ((off & 1) ? 0x1ff : 0) + off);
+	return readb(adr + ((off & 1) ? 0x2ff : 0x100) + off);
 }
 
 static inline void
 writeisac(unsigned int adr, u_char off, u_char data)
 {
-	writeb(data, adr + 0x120 + ((off & 1) ? 0x1ff : 0) + off);
+	writeb(data, adr + ((off & 1) ? 0x2ff : 0x100) + off);
 }
 
 
 static inline u_char
 readhscx(unsigned int adr, int hscx, u_char off)
 {
-	return readb(adr + (hscx ? 0x1e0 : 0x1a0) +
+	return readb(adr + (hscx ? 0x1c0 : 0x180) +
 		     ((off & 1) ? 0x1ff : 0) + off);
 }
 
 static inline void
 writehscx(unsigned int adr, int hscx, u_char off, u_char data)
 {
-	writeb(data, adr + (hscx ? 0x1e0 : 0x1a0) +
+	writeb(data, adr + (hscx ? 0x1c0 : 0x180) +
 	       ((off & 1) ? 0x1ff : 0) + off);
 }
 
@@ -87,7 +89,7 @@
 		data[i] = readb(ad);
 }
 
-static void
+static inline void
 write_fifo_isac(unsigned int adr, u_char * data, int size)
 {
 	register int i;
@@ -113,745 +115,204 @@
 	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");
-}
 
+/* Interface functions */
 
-static inline void
-waitforXFW(int adr, int hscx)
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
 {
-	int to = 50;
-
-	while ((!(readhscx(adr, hscx, HSCX_STAR) & 0x44) == 0x40) && to) {
-		udelay(1);
-		to--;
-	}
-	if (!to)
-		printk(KERN_WARNING "Teles0: waitforXFW timeout\n");
+	return (readisac(cs->hw.teles0.membase, offset));
 }
 
-static inline void
-writehscxCMDR(int adr, int hscx, u_char data)
-{
-	long flags;
-
-	save_flags(flags);
-	cli();
-	waitforCEC(adr, hscx);
-	writehscx(adr, hscx, HSCX_CMDR, data);
-	restore_flags(flags);
-}
-
-/*
- * fast interrupt here
- */
-
-
 static void
-hscxreport(struct IsdnCardState *sp, int hscx)
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
 {
-	printk(KERN_DEBUG "HSCX %d\n", hscx);
-	printk(KERN_DEBUG "ISTA %x\n", readhscx(sp->membase, hscx, HSCX_ISTA));
-	printk(KERN_DEBUG "STAR %x\n", readhscx(sp->membase, hscx, HSCX_STAR));
-	printk(KERN_DEBUG "EXIR %x\n", readhscx(sp->membase, hscx, HSCX_EXIR));
+	writeisac(cs->hw.teles0.membase, offset, value);
 }
 
-void
-teles0_report(struct IsdnCardState *sp)
-{
-	printk(KERN_DEBUG "ISAC\n");
-	printk(KERN_DEBUG "ISTA %x\n", readisac(sp->membase, ISAC_ISTA));
-	printk(KERN_DEBUG "STAR %x\n", readisac(sp->membase, ISAC_STAR));
-	printk(KERN_DEBUG "EXIR %x\n", readisac(sp->membase, ISAC_EXIR));
-	hscxreport(sp, 0);
-	hscxreport(sp, 1);
-}
-
-/*
- * HSCX stuff goes here
- */
-
 static void
-hscx_empty_fifo(struct HscxState *hsp, int count)
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
 {
-	u_char *ptr;
-	struct IsdnCardState *sp = hsp->sp;
-	long flags;
-
-	if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO))
-		debugl1(sp, "hscx_empty_fifo");
-
-	if (hsp->rcvidx + count > HSCX_BUFMAX) {
-		if (sp->debug & L1_DEB_WARN)
-			debugl1(sp, "hscx_empty_fifo: incoming packet too large");
-		writehscxCMDR(sp->membase, hsp->hscx, 0x80);
-		hsp->rcvidx = 0;
-		return;
-	}
-	ptr = hsp->rcvbuf + hsp->rcvidx;
-	hsp->rcvidx += count;
-	save_flags(flags);
-	cli();
-	read_fifo_hscx(sp->membase, hsp->hscx, ptr, count);
-	writehscxCMDR(sp->membase, hsp->hscx, 0x80);
-	restore_flags(flags);
-	if (sp->debug & L1_DEB_HSCX_FIFO) {
-		char tmp[128];
-		char *t = tmp;
-
-		t += sprintf(t, "hscx_empty_fifo %c cnt %d",
-			     hsp->hscx ? 'B' : 'A', count);
-		QuickHex(t, ptr, count);
-		debugl1(sp, tmp);
-	}
+	read_fifo_isac(cs->hw.teles0.membase, data, size);
 }
 
 static void
-hscx_fill_fifo(struct HscxState *hsp)
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
 {
-	struct IsdnCardState *sp = hsp->sp;
-	int more, count;
-	u_char *ptr;
-	long flags;
-
-	if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO))
-		debugl1(sp, "hscx_fill_fifo");
-
-	if (!hsp->tx_skb)
-		return;
-	if (hsp->tx_skb->len <= 0)
-		return;
-
-	more = (hsp->mode == 1) ? 1 : 0;
-	if (hsp->tx_skb->len > 32) {
-		more = !0;
-		count = 32;
-	} else
-		count = hsp->tx_skb->len;
-
-	waitforXFW(sp->membase, hsp->hscx);
-	save_flags(flags);
-	cli();
-	ptr = hsp->tx_skb->data;
-	skb_pull(hsp->tx_skb, count);
-	hsp->tx_cnt -= count;
-	hsp->count += count;
-	write_fifo_hscx(sp->membase, hsp->hscx, ptr, count);
-	writehscxCMDR(sp->membase, hsp->hscx, more ? 0x8 : 0xa);
-	restore_flags(flags);
-	if (sp->debug & L1_DEB_HSCX_FIFO) {
-		char tmp[128];
-		char *t = tmp;
-
-		t += sprintf(t, "hscx_fill_fifo %c cnt %d",
-			     hsp->hscx ? 'B' : 'A', count);
-		QuickHex(t, ptr, count);
-		debugl1(sp, tmp);
-	}
+	write_fifo_isac(cs->hw.teles0.membase, data, size);
 }
 
-static inline void
-hscx_interrupt(struct IsdnCardState *sp, u_char val, u_char hscx)
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
 {
-	u_char r;
-	struct HscxState *hsp = sp->hs + hscx;
-	struct sk_buff *skb;
-	int count;
-	char tmp[32];
-
-	if (!hsp->init)
-		return;
-
-	if (val & 0x80) {	/* RME */
-
-		r = readhscx(sp->membase, hsp->hscx, HSCX_RSTA);
-		if ((r & 0xf0) != 0xa0) {
-			if (!r & 0x80)
-				if (sp->debug & L1_DEB_WARN)
-					debugl1(sp, "HSCX invalid frame");
-			if ((r & 0x40) && hsp->mode)
-				if (sp->debug & L1_DEB_WARN) {
-					sprintf(tmp, "HSCX RDO mode=%d",
-						hsp->mode);
-					debugl1(sp, tmp);
-				}
-			if (!r & 0x20)
-				if (sp->debug & L1_DEB_WARN)
-					debugl1(sp, "HSCX CRC error");
-			writehscxCMDR(sp->membase, hsp->hscx, 0x80);
-		} else {
-			count = readhscx(sp->membase, hsp->hscx, HSCX_RBCL) & 0x1f;
-			if (count == 0)
-				count = 32;
-			hscx_empty_fifo(hsp, count);
-			if ((count = hsp->rcvidx - 1) > 0) {
-				if (!(skb = dev_alloc_skb(count)))
-					printk(KERN_WARNING "AVM: receive out of memory\n");
-				else {
-					memcpy(skb_put(skb, count), hsp->rcvbuf, count);
-					skb_queue_tail(&hsp->rqueue, skb);
-				}
-			}
-		}
-		hsp->rcvidx = 0;
-		hscx_sched_event(hsp, HSCX_RCVBUFREADY);
-	}
-	if (val & 0x40) {	/* RPF */
-		hscx_empty_fifo(hsp, 32);
-		if (hsp->mode == 1) {
-			/* receive audio data */
-			if (!(skb = dev_alloc_skb(32)))
-				printk(KERN_WARNING "AVM: receive out of memory\n");
-			else {
-				memcpy(skb_put(skb, 32), hsp->rcvbuf, 32);
-				skb_queue_tail(&hsp->rqueue, skb);
-			}
-			hsp->rcvidx = 0;
-			hscx_sched_event(hsp, HSCX_RCVBUFREADY);
-		}
-	}
-	if (val & 0x10) {	/* XPR */
-		if (hsp->tx_skb)
-			if (hsp->tx_skb->len) {
-				hscx_fill_fifo(hsp);
-				return;
-			} else {
-				SET_SKB_FREE(hsp->tx_skb);
-				dev_kfree_skb(hsp->tx_skb);
-				hsp->count = 0;
-				if (hsp->st->l4.l1writewakeup)
-					hsp->st->l4.l1writewakeup(hsp->st);
-				hsp->tx_skb = NULL;
-			}
-		if ((hsp->tx_skb = skb_dequeue(&hsp->squeue))) {
-			hsp->count = 0;
-			hscx_fill_fifo(hsp);
-		} else
-			hscx_sched_event(hsp, HSCX_XMTBUFREADY);
-	}
+	return (readhscx(cs->hw.teles0.membase, hscx, offset));
 }
 
-/*
- * ISAC stuff goes here
- */
-
 static void
-isac_empty_fifo(struct IsdnCardState *sp, int count)
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
 {
-	u_char *ptr;
-	long flags;
-
-	if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO))
-		debugl1(sp, "isac_empty_fifo");
-
-	if ((sp->rcvidx + count) >= MAX_DFRAME_LEN) {
-		if (sp->debug & L1_DEB_WARN) {
-			char tmp[40];
-			sprintf(tmp, "isac_empty_fifo overrun %d",
-				sp->rcvidx + count);
-			debugl1(sp, tmp);
-		}
-		writeisac(sp->membase, ISAC_CMDR, 0x80);
-		sp->rcvidx = 0;
-		return;
-	}
-	ptr = sp->rcvbuf + sp->rcvidx;
-	sp->rcvidx += count;
-	save_flags(flags);
-	cli();
-	read_fifo_isac(sp->membase, ptr, count);
-	writeisac(sp->membase, ISAC_CMDR, 0x80);
-	restore_flags(flags);
-	if (sp->debug & L1_DEB_ISAC_FIFO) {
-		char tmp[128];
-		char *t = tmp;
-
-		t += sprintf(t, "isac_empty_fifo cnt %d", count);
-		QuickHex(t, ptr, count);
-		debugl1(sp, tmp);
-	}
+	writehscx(cs->hw.teles0.membase, hscx, offset, value);
 }
 
-static void
-isac_fill_fifo(struct IsdnCardState *sp)
-{
-	int count, more;
-	u_char *ptr;
-	long flags;
-
-	if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO))
-		debugl1(sp, "isac_fill_fifo");
-
-	if (!sp->tx_skb)
-		return;
-
-	count = sp->tx_skb->len;
-	if (count <= 0)
-		return;
-
-	more = 0;
-	if (count > 32) {
-		more = !0;
-		count = 32;
-	}
-	save_flags(flags);
-	cli();
-	ptr = sp->tx_skb->data;
-	skb_pull(sp->tx_skb, count);
-	sp->tx_cnt += count;
-	write_fifo_isac(sp->membase, ptr, count);
-	writeisac(sp->membase, ISAC_CMDR, more ? 0x8 : 0xa);
-	restore_flags(flags);
-	if (sp->debug & L1_DEB_ISAC_FIFO) {
-		char tmp[128];
-		char *t = tmp;
-
-		t += sprintf(t, "isac_fill_fifo cnt %d", count);
-		QuickHex(t, ptr, count);
-		debugl1(sp, tmp);
-	}
-}
-
-static void
-ph_command(struct IsdnCardState *sp, unsigned int command)
-{
-	if (sp->debug & L1_DEB_ISAC) {
-		char tmp[32];
-		sprintf(tmp, "ph_command %d", command);
-		debugl1(sp, tmp);
-	}
-	writeisac(sp->membase, ISAC_CIX0, (command << 2) | 3);
-}
-
-static inline void
-isac_interrupt(struct IsdnCardState *sp, u_char val)
-{
-	u_char exval;
-	struct sk_buff *skb;
-	unsigned int count;
-	char tmp[32];
-
-	if (sp->debug & L1_DEB_ISAC) {
-		sprintf(tmp, "ISAC interrupt %x", val);
-		debugl1(sp, tmp);
-	}
-	if (val & 0x80) {	/* RME */
-		exval = readisac(sp->membase, ISAC_RSTA);
-		if ((exval & 0x70) != 0x20) {
-			if (exval & 0x40)
-				if (sp->debug & L1_DEB_WARN)
-					debugl1(sp, "ISAC RDO");
-			if (!exval & 0x20)
-				if (sp->debug & L1_DEB_WARN)
-					debugl1(sp, "ISAC CRC error");
-			writeisac(sp->membase, ISAC_CMDR, 0x80);
-		} else {
-			count = readisac(sp->membase, ISAC_RBCL) & 0x1f;
-			if (count == 0)
-				count = 32;
-			isac_empty_fifo(sp, count);
-			if ((count = sp->rcvidx) > 0) {
-				if (!(skb = alloc_skb(count, GFP_ATOMIC)))
-					printk(KERN_WARNING "AVM: D receive out of memory\n");
-				else {
-					memcpy(skb_put(skb, count), sp->rcvbuf, count);
-					skb_queue_tail(&sp->rq, skb);
-				}
-			}
-		}
-		sp->rcvidx = 0;
-		isac_sched_event(sp, ISAC_RCVBUFREADY);
-	}
-	if (val & 0x40) {	/* RPF */
-		isac_empty_fifo(sp, 32);
-	}
-	if (val & 0x20) {	/* RSC */
-		/* never */
-		if (sp->debug & L1_DEB_WARN)
-			debugl1(sp, "ISAC RSC interrupt");
-	}
-	if (val & 0x10) {	/* XPR */
-		if (sp->tx_skb)
-			if (sp->tx_skb->len) {
-				isac_fill_fifo(sp);
-				goto afterXPR;
-			} else {
-				SET_SKB_FREE(sp->tx_skb);
-				dev_kfree_skb(sp->tx_skb);
-				sp->tx_cnt = 0;
-				sp->tx_skb = NULL;
-			}
-		if ((sp->tx_skb = skb_dequeue(&sp->sq))) {
-			sp->tx_cnt = 0;
-			isac_fill_fifo(sp);
-		} else
-			isac_sched_event(sp, ISAC_XMTBUFREADY);
-	}
-      afterXPR:
-	if (val & 0x04) {	/* CISQ */
-		sp->ph_state = (readisac(sp->membase, ISAC_CIX0) >> 2)
-		    & 0xf;
-		if (sp->debug & L1_DEB_ISAC) {
-			sprintf(tmp, "l1state %d", sp->ph_state);
-			debugl1(sp, tmp);
-		}
-		isac_new_ph(sp);
-	}
-	if (val & 0x02) {	/* SIN */
-		/* never */
-		if (sp->debug & L1_DEB_WARN)
-			debugl1(sp, "ISAC SIN interrupt");
-	}
-	if (val & 0x01) {	/* EXI */
-		exval = readisac(sp->membase, ISAC_EXIR);
-		if (sp->debug & L1_DEB_WARN) {
-			sprintf(tmp, "ISAC EXIR %02x", exval);
-			debugl1(sp, tmp);
-		}
-	}
-}
+/*
+ * fast interrupt HSCX stuff goes here
+ */
 
-static inline void
-hscx_int_main(struct IsdnCardState *sp, u_char val)
-{
+#define READHSCX(cs, nr, reg) readhscx(cs->hw.teles0.membase, nr, reg)
+#define WRITEHSCX(cs, nr, reg, data) writehscx(cs->hw.teles0.membase, nr, reg, data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo_hscx(cs->hw.teles0.membase, nr, ptr, cnt)
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo_hscx(cs->hw.teles0.membase, nr, ptr, cnt)
 
-	u_char exval;
-	struct HscxState *hsp;
-	char tmp[32];
-
-
-	if (val & 0x01) {
-		hsp = sp->hs + 1;
-		exval = readhscx(sp->membase, 1, HSCX_EXIR);
-		if (exval == 0x40) {
-			if (hsp->mode == 1)
-				hscx_fill_fifo(hsp);
-			else {
-				/* Here we lost an TX interrupt, so
-				   * restart transmitting the whole frame.
-				 */
-				if (hsp->tx_skb) {
-					skb_push(hsp->tx_skb, hsp->count);
-					hsp->tx_cnt += hsp->count;
-					hsp->count = 0;
-				}
-				writehscxCMDR(sp->membase, hsp->hscx, 0x01);
-				if (sp->debug & L1_DEB_WARN) {
-					sprintf(tmp, "HSCX B EXIR %x Lost TX", exval);
-					debugl1(sp, tmp);
-				}
-			}
-		} else if (sp->debug & L1_DEB_HSCX) {
-			sprintf(tmp, "HSCX B EXIR %x", exval);
-			debugl1(sp, tmp);
-		}
-	}
-	if (val & 0xf8) {
-		if (sp->debug & L1_DEB_HSCX) {
-			sprintf(tmp, "HSCX B interrupt %x", val);
-			debugl1(sp, tmp);
-		}
-		hscx_interrupt(sp, val, 1);
-	}
-	if (val & 0x02) {
-		hsp = sp->hs;
-		exval = readhscx(sp->membase, 0, HSCX_EXIR);
-		if (exval == 0x40) {
-			if (hsp->mode == 1)
-				hscx_fill_fifo(hsp);
-			else {
-				/* Here we lost an TX interrupt, so
-				   * restart transmitting the whole frame.
-				 */
-				if (hsp->tx_skb) {
-					skb_push(hsp->tx_skb, hsp->count);
-					hsp->tx_cnt += hsp->count;
-					hsp->count = 0;
-				}
-				writehscxCMDR(sp->membase, hsp->hscx, 0x01);
-				if (sp->debug & L1_DEB_WARN) {
-					sprintf(tmp, "HSCX A EXIR %x Lost TX", exval);
-					debugl1(sp, tmp);
-				}
-			}
-		} else if (sp->debug & L1_DEB_HSCX) {
-			sprintf(tmp, "HSCX A EXIR %x", exval);
-			debugl1(sp, tmp);
-		}
-	}
-	if (val & 0x04) {
-		exval = readhscx(sp->membase, 0, HSCX_ISTA);
-		if (sp->debug & L1_DEB_HSCX) {
-			sprintf(tmp, "HSCX A interrupt %x", exval);
-			debugl1(sp, tmp);
-		}
-		hscx_interrupt(sp, exval, 0);
-	}
-}
+#include "hscx_irq.c"
 
 static void
-telesS0_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+teles0_interrupt(int intno, void *dev_id, struct pt_regs *regs)
 {
-	struct IsdnCardState *sp;
+	struct IsdnCardState *cs = dev_id;
 	u_char val, stat = 0;
+	int count = 0;
 
-	sp = (struct IsdnCardState *) dev_id;
-
-	if (!sp) {
+	if (!cs) {
 		printk(KERN_WARNING "Teles0: Spurious interrupt!\n");
 		return;
 	}
-	val = readhscx(sp->membase, 1, HSCX_ISTA);
+	val = readhscx(cs->hw.teles0.membase, 1, HSCX_ISTA);
       Start_HSCX:
 	if (val) {
-		hscx_int_main(sp, val);
+		hscx_int_main(cs, val);
 		stat |= 1;
 	}
-	val = readisac(sp->membase, ISAC_ISTA);
+	val = readisac(cs->hw.teles0.membase, ISAC_ISTA);
       Start_ISAC:
 	if (val) {
-		isac_interrupt(sp, val);
+		isac_interrupt(cs, val);
 		stat |= 2;
 	}
-	val = readhscx(sp->membase, 1, HSCX_ISTA);
-	if (val) {
-		if (sp->debug & L1_DEB_HSCX)
-			debugl1(sp, "HSCX IntStat after IntRoutine");
+	count++;
+	val = readhscx(cs->hw.teles0.membase, 1, HSCX_ISTA);
+	if (val && count < 20) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX IntStat after IntRoutine");
 		goto Start_HSCX;
 	}
-	val = readisac(sp->membase, ISAC_ISTA);
-	if (val) {
-		if (sp->debug & L1_DEB_ISAC)
-			debugl1(sp, "ISAC IntStat after IntRoutine");
+	val = readisac(cs->hw.teles0.membase, ISAC_ISTA);
+	if (val && count < 20) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
 		goto Start_ISAC;
 	}
 	if (stat & 1) {
-		writehscx(sp->membase, 0, HSCX_MASK, 0xFF);
-		writehscx(sp->membase, 1, HSCX_MASK, 0xFF);
-		writehscx(sp->membase, 0, HSCX_MASK, 0x0);
-		writehscx(sp->membase, 1, HSCX_MASK, 0x0);
+		writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0xFF);
+		writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0xFF);
+		writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0x0);
+		writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0x0);
 	}
 	if (stat & 2) {
-		writeisac(sp->membase, ISAC_MASK, 0xFF);
-		writeisac(sp->membase, ISAC_MASK, 0x0);
+		writeisac(cs->hw.teles0.membase, ISAC_MASK, 0xFF);
+		writeisac(cs->hw.teles0.membase, ISAC_MASK, 0x0);
 	}
 }
 
-
-static void
-initisac(struct IsdnCardState *sp)
+void
+release_io_teles0(struct IsdnCardState *cs)
 {
-	unsigned int adr = sp->membase;
-
-	/* 16.0 IOM 1 Mode */
-	writeisac(adr, ISAC_MASK, 0xff);
-	writeisac(adr, ISAC_ADF2, 0x0);
-	writeisac(adr, ISAC_SPCR, 0xa);
-	writeisac(adr, ISAC_ADF1, 0x2);
-	writeisac(adr, ISAC_STCR, 0x70);
-	writeisac(adr, ISAC_MODE, 0xc9);
-	writeisac(adr, ISAC_CMDR, 0x41);
-	writeisac(adr, ISAC_CIX0, (1 << 2) | 3);
-	writeisac(adr, ISAC_MASK, 0xff);
-	writeisac(adr, ISAC_MASK, 0x0);
+	if (cs->hw.teles0.cfg_reg)
+		release_region(cs->hw.teles0.cfg_reg, 8);
 }
 
-static void
-modehscx(struct HscxState *hs, int mode, int ichan)
+static int
+reset_teles0(struct IsdnCardState *cs)
 {
-	struct IsdnCardState *sp = hs->sp;
-	int hscx = hs->hscx;
+	u_char cfval;
+	long flags;
 
-	if (sp->debug & L1_DEB_HSCX) {
-		char tmp[40];
-		sprintf(tmp, "hscx %c mode %d ichan %d",
-			'A' + hscx, mode, ichan);
-		debugl1(sp, tmp);
-	}
-	hs->mode = mode;
-	writehscx(sp->membase, hscx, HSCX_CCR1, 0x85);
-	writehscx(sp->membase, hscx, HSCX_XAD1, 0xFF);
-	writehscx(sp->membase, hscx, HSCX_XAD2, 0xFF);
-	writehscx(sp->membase, hscx, HSCX_RAH2, 0xFF);
-	writehscx(sp->membase, hscx, HSCX_XBCH, 0x0);
-
-	/* Switch IOM 1 SSI */
-	if (hscx == 0)
-		ichan = 1 - ichan;
-
-	switch (mode) {
-		case (0):
-			writehscx(sp->membase, hscx, HSCX_CCR2, 0x30);
-			writehscx(sp->membase, hscx, HSCX_TSAX, 0xff);
-			writehscx(sp->membase, hscx, HSCX_TSAR, 0xff);
-			writehscx(sp->membase, hscx, HSCX_XCCR, 7);
-			writehscx(sp->membase, hscx, HSCX_RCCR, 7);
-			writehscx(sp->membase, hscx, HSCX_MODE, 0x84);
-			break;
-		case (1):
-			if (ichan == 0) {
-				writehscx(sp->membase, hscx, HSCX_CCR2, 0x30);
-				writehscx(sp->membase, hscx, HSCX_TSAX, 0x7);
-				writehscx(sp->membase, hscx, HSCX_TSAR, 0x7);
-				writehscx(sp->membase, hscx, HSCX_XCCR, 7);
-				writehscx(sp->membase, hscx, HSCX_RCCR, 7);
-			} else {
-				writehscx(sp->membase, hscx, HSCX_CCR2, 0x30);
-				writehscx(sp->membase, hscx, HSCX_TSAX, 0x3);
-				writehscx(sp->membase, hscx, HSCX_TSAR, 0x3);
-				writehscx(sp->membase, hscx, HSCX_XCCR, 7);
-				writehscx(sp->membase, hscx, HSCX_RCCR, 7);
-			}
-			writehscx(sp->membase, hscx, HSCX_MODE, 0xe4);
-			writehscx(sp->membase, hscx, HSCX_CMDR, 0x41);
-			break;
-		case (2):
-			if (ichan == 0) {
-				writehscx(sp->membase, hscx, HSCX_CCR2, 0x30);
-				writehscx(sp->membase, hscx, HSCX_TSAX, 0x7);
-				writehscx(sp->membase, hscx, HSCX_TSAR, 0x7);
-				writehscx(sp->membase, hscx, HSCX_XCCR, 7);
-				writehscx(sp->membase, hscx, HSCX_RCCR, 7);
-			} else {
-				writehscx(sp->membase, hscx, HSCX_CCR2, 0x30);
-				writehscx(sp->membase, hscx, HSCX_TSAX, 0x3);
-				writehscx(sp->membase, hscx, HSCX_TSAR, 0x3);
-				writehscx(sp->membase, hscx, HSCX_XCCR, 7);
-				writehscx(sp->membase, hscx, HSCX_RCCR, 7);
-			}
-			writehscx(sp->membase, hscx, HSCX_MODE, 0x8c);
-			writehscx(sp->membase, hscx, HSCX_CMDR, 0x41);
-			break;
+	save_flags(flags);
+	sti();
+	if (cs->hw.teles0.cfg_reg) {
+		switch (cs->irq) {
+			case 2:
+			case 9:
+				cfval = 0x00;
+				break;
+			case 3:
+				cfval = 0x02;
+				break;
+			case 4:
+				cfval = 0x04;
+				break;
+			case 5:
+				cfval = 0x06;
+				break;
+			case 10:
+				cfval = 0x08;
+				break;
+			case 11:
+				cfval = 0x0A;
+				break;
+			case 12:
+				cfval = 0x0C;
+				break;
+			case 15:
+				cfval = 0x0E;
+				break;
+			default:
+				return(1);
+		}
+		cfval |= ((cs->hw.teles0.membase >> 9) & 0xF0);
+		byteout(cs->hw.teles0.cfg_reg + 4, cfval);
+		HZDELAY(HZ / 10 + 1);
+		byteout(cs->hw.teles0.cfg_reg + 4, cfval | 1);
+		HZDELAY(HZ / 10 + 1);
 	}
-	writehscx(sp->membase, hscx, HSCX_ISTA, 0x00);
-}
-
-void
-release_io_teles0(struct IsdnCard *card)
-{
-	if (card->sp->cfg_reg)
-		release_region(card->sp->cfg_reg, 8);
+	writeb(0, cs->hw.teles0.membase + 0x80);
+	HZDELAY(HZ / 5 + 1);
+	writeb(1, cs->hw.teles0.membase + 0x80);
+	HZDELAY(HZ / 5 + 1);
+	restore_flags(flags);
+	return(0);
 }
 
-static void
-clear_pending_ints(struct IsdnCardState *sp)
+static int
+Teles_card_msg(struct IsdnCardState *cs, int mt, void *arg)
 {
-	int val;
-	char tmp[64];
-
-	val = readhscx(sp->membase, 1, HSCX_ISTA);
-	sprintf(tmp, "HSCX B ISTA %x", val);
-	debugl1(sp, tmp);
-	if (val & 0x01) {
-		val = readhscx(sp->membase, 1, HSCX_EXIR);
-		sprintf(tmp, "HSCX B EXIR %x", val);
-		debugl1(sp, tmp);
-	} else if (val & 0x02) {
-		val = readhscx(sp->membase, 0, HSCX_EXIR);
-		sprintf(tmp, "HSCX A EXIR %x", val);
-		debugl1(sp, tmp);
-	}
-	val = readhscx(sp->membase, 0, HSCX_ISTA);
-	sprintf(tmp, "HSCX A ISTA %x", val);
-	debugl1(sp, tmp);
-	val = readhscx(sp->membase, 1, HSCX_STAR);
-	sprintf(tmp, "HSCX B STAR %x", val);
-	debugl1(sp, tmp);
-	val = readhscx(sp->membase, 0, HSCX_STAR);
-	sprintf(tmp, "HSCX A STAR %x", val);
-	debugl1(sp, tmp);
-	val = readisac(sp->membase, ISAC_STAR);
-	sprintf(tmp, "ISAC STAR %x", val);
-	debugl1(sp, tmp);
-	val = readisac(sp->membase, ISAC_MODE);
-	sprintf(tmp, "ISAC MODE %x", val);
-	debugl1(sp, tmp);
-	val = readisac(sp->membase, ISAC_ADF2);
-	sprintf(tmp, "ISAC ADF2 %x", val);
-	debugl1(sp, tmp);
-	val = readisac(sp->membase, ISAC_ISTA);
-	sprintf(tmp, "ISAC ISTA %x", val);
-	debugl1(sp, tmp);
-	if (val & 0x01) {
-		val = readisac(sp->membase, ISAC_EXIR);
-		sprintf(tmp, "ISAC EXIR %x", val);
-		debugl1(sp, tmp);
-	} else if (val & 0x04) {
-		val = readisac(sp->membase, ISAC_CIR0);
-		sprintf(tmp, "ISAC CIR0 %x", val);
-		debugl1(sp, tmp);
-	}
-	writeisac(sp->membase, ISAC_MASK, 0);
-	writeisac(sp->membase, ISAC_CMDR, 0x41);
-}
-
-int
-initteles0(struct IsdnCardState *sp)
-{
-	int ret;
-	int loop = 0;
-	char tmp[40];
-
-	sp->counter = kstat_irqs(sp->irq);
-	sprintf(tmp, "IRQ %d count %d", sp->irq, sp->counter);
-	debugl1(sp, tmp);
-	clear_pending_ints(sp);
-	ret = get_irq(sp->cardnr, &telesS0_interrupt);
-	if (ret) {
-		initisac(sp);
-		sp->modehscx(sp->hs, 0, 0);
-		sp->modehscx(sp->hs + 1, 0, 0);
-		while (loop++ < 10) {
-			/* At least 1-3 irqs must happen
-			 * (one from HSCX A, one from HSCX B, 3rd from ISAC)
-			 */
-			if (kstat_irqs(sp->irq) > sp->counter)
-				break;
-			current->state = TASK_INTERRUPTIBLE;
-			current->timeout = jiffies + 1;
-			schedule();
-		}
-		sprintf(tmp, "IRQ %d count %d", sp->irq,
-			kstat_irqs(sp->irq));
-		debugl1(sp, tmp);
-		if (kstat_irqs(sp->irq) == sp->counter) {
-			printk(KERN_WARNING
-			       "Teles0: IRQ(%d) getting no interrupts during init\n",
-			       sp->irq);
-			free_irq(sp->irq, sp);
-			return (0);
-		}
+	switch (mt) {
+		case CARD_RESET:
+			reset_teles0(cs);
+			return(0);
+		case CARD_RELEASE:
+			release_io_teles0(cs);
+			return(0);
+		case CARD_SETIRQ:
+			return(request_irq(cs->irq, &teles0_interrupt,
+					I4L_IRQ_FLAG, "HiSax", cs));
+		case CARD_INIT:
+			clear_pending_isac_ints(cs);
+			clear_pending_hscx_ints(cs);
+			initisac(cs);
+			inithscx(cs);
+			return(0);
+		case CARD_TEST:
+			return(0);
 	}
-	return (ret);
+	return(0);
 }
 
-int
-setup_teles0(struct IsdnCard *card)
+__initfunc(int
+setup_teles0(struct IsdnCard *card))
 {
-	u_char cfval, val, verA, verB;
-	struct IsdnCardState *sp = card->sp;
-	long flags;
+	u_char val;
+	struct IsdnCardState *cs = card->cs;
 	char tmp[64];
 
 	strcpy(tmp, teles0_revision);
-	printk(KERN_NOTICE "HiSax: Teles 8.0/16.0 driver Rev. %s\n", HiSax_getrev(tmp));
-	if ((sp->typ != ISDN_CTYPE_16_0) && (sp->typ != ISDN_CTYPE_8_0))
+	printk(KERN_INFO "HiSax: Teles 8.0/16.0 driver Rev. %s\n", HiSax_getrev(tmp));
+	if ((cs->typ != ISDN_CTYPE_16_0) && (cs->typ != ISDN_CTYPE_8_0))
 		return (0);
 
-	if (sp->typ == ISDN_CTYPE_16_0)
-		sp->cfg_reg = card->para[2];
+	if (cs->typ == ISDN_CTYPE_16_0)
+		cs->hw.teles0.cfg_reg = card->para[2];
 	else			/* 8.0 */
-		sp->cfg_reg = 0;
+		cs->hw.teles0.cfg_reg = 0;
 
 	if (card->para[1] < 0x10000) {
 		card->para[1] <<= 4;
@@ -859,110 +320,69 @@
 		   "Teles0: membase configured DOSish, assuming 0x%lx\n",
 		       (unsigned long) card->para[1]);
 	}
-	sp->membase = card->para[1];
-	sp->irq = card->para[0];
-	if (sp->cfg_reg) {
-		if (check_region((sp->cfg_reg), 8)) {
+	cs->hw.teles0.membase = card->para[1];
+	cs->irq = card->para[0];
+	if (cs->hw.teles0.cfg_reg) {
+		if (check_region((cs->hw.teles0.cfg_reg), 8)) {
 			printk(KERN_WARNING
 			  "HiSax: %s config port %x-%x already in use\n",
 			       CardType[card->typ],
-			       sp->cfg_reg,
-			       sp->cfg_reg + 8);
+			       cs->hw.teles0.cfg_reg,
+			       cs->hw.teles0.cfg_reg + 8);
 			return (0);
 		} else {
-			request_region(sp->cfg_reg, 8, "teles cfg");
+			request_region(cs->hw.teles0.cfg_reg, 8, "teles cfg");
 		}
 	}
-	switch (sp->irq) {
-		case 2:
-			cfval = 0x00;
-			break;
-		case 3:
-			cfval = 0x02;
-			break;
-		case 4:
-			cfval = 0x04;
-			break;
-		case 5:
-			cfval = 0x06;
-			break;
-		case 10:
-			cfval = 0x08;
-			break;
-		case 11:
-			cfval = 0x0A;
-			break;
-		case 12:
-			cfval = 0x0C;
-			break;
-		case 15:
-			cfval = 0x0E;
-			break;
-		default:
-			cfval = 0x00;
-			break;
-	}
-	cfval |= ((card->para[1] >> 9) & 0xF0);
-	if (sp->cfg_reg) {
-		if ((val = bytein(sp->cfg_reg + 0)) != 0x51) {
+	if (cs->hw.teles0.cfg_reg) {
+		if ((val = bytein(cs->hw.teles0.cfg_reg + 0)) != 0x51) {
 			printk(KERN_WARNING "Teles0: 16.0 Byte at %x is %x\n",
-			       sp->cfg_reg + 0, val);
-			release_region(sp->cfg_reg, 8);
+			       cs->hw.teles0.cfg_reg + 0, val);
+			release_region(cs->hw.teles0.cfg_reg, 8);
 			return (0);
 		}
-		if ((val = bytein(sp->cfg_reg + 1)) != 0x93) {
+		if ((val = bytein(cs->hw.teles0.cfg_reg + 1)) != 0x93) {
 			printk(KERN_WARNING "Teles0: 16.0 Byte at %x is %x\n",
-			       sp->cfg_reg + 1, val);
-			release_region(sp->cfg_reg, 8);
+			       cs->hw.teles0.cfg_reg + 1, val);
+			release_region(cs->hw.teles0.cfg_reg, 8);
 			return (0);
 		}
-		val = bytein(sp->cfg_reg + 2);	/* 0x1e=without AB
-						   * 0x1f=with AB
-						   * 0x1c 16.3 ???
-						 */
+		val = bytein(cs->hw.teles0.cfg_reg + 2);	/* 0x1e=without AB
+								   * 0x1f=with AB
+								   * 0x1c 16.3 ???
+								 */
 		if (val != 0x1e && val != 0x1f) {
 			printk(KERN_WARNING "Teles0: 16.0 Byte at %x is %x\n",
-			       sp->cfg_reg + 2, val);
-			release_region(sp->cfg_reg, 8);
+			       cs->hw.teles0.cfg_reg + 2, val);
+			release_region(cs->hw.teles0.cfg_reg, 8);
 			return (0);
 		}
-		save_flags(flags);
-		byteout(sp->cfg_reg + 4, cfval);
-		sti();
-		HZDELAY(HZ / 10 + 1);
-		byteout(sp->cfg_reg + 4, cfval | 1);
-		HZDELAY(HZ / 10 + 1);
-		restore_flags(flags);
 	}
-	printk(KERN_NOTICE
-	       "HiSax: %s config irq:%d mem:%x cfg:%x\n",
-	       CardType[sp->typ], sp->irq,
-	       sp->membase, sp->cfg_reg);
-	verA = readhscx(sp->membase, 0, HSCX_VSTR) & 0xf;
-	verB = readhscx(sp->membase, 1, HSCX_VSTR) & 0xf;
-	printk(KERN_INFO "Teles0: HSCX version A: %s  B: %s\n",
-	       HscxVersion(verA), HscxVersion(verB));
-	val = readisac(sp->membase, ISAC_RBCH);
-	printk(KERN_INFO "Teles0: ISAC %s\n",
-	       ISACVersion(val));
-
-	if ((verA == 0) | (verA == 0xf) | (verB == 0) | (verB == 0xf)) {
+	/* 16.0 and 8.0 designed for IOM1 */
+	test_and_set_bit(HW_IOM1, &cs->HW_Flags);
+	printk(KERN_INFO
+	       "HiSax: %s config irq:%d mem:0x%X cfg:0x%X\n",
+	       CardType[cs->typ], cs->irq,
+	       cs->hw.teles0.membase, cs->hw.teles0.cfg_reg);
+	if (reset_teles0(cs)) {
+		printk(KERN_WARNING "Teles0: wrong IRQ\n");
+		release_io_teles0(cs);
+		return (0);
+	}
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &Teles_card_msg;
+	ISACVersion(cs, "Teles0:");
+	if (HscxVersion(cs, "Teles0:")) {
 		printk(KERN_WARNING
 		 "Teles0: wrong HSCX versions check IO/MEM addresses\n");
-		release_io_teles0(card);
+		release_io_teles0(cs);
 		return (0);
 	}
-	save_flags(flags);
-	writeb(0, sp->membase + 0x80);
-	sti();
-	HZDELAY(HZ / 5 + 1);
-	writeb(1, sp->membase + 0x80);
-	HZDELAY(HZ / 5 + 1);
-	restore_flags(flags);
-
-	sp->modehscx = &modehscx;
-	sp->ph_command = &ph_command;
-	sp->hscx_fill_fifo = &hscx_fill_fifo;
-	sp->isac_fill_fifo = &isac_fill_fifo;
 	return (1);
 }
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/teles0.h linux/drivers/isdn/hisax/teles0.h
--- v2.1.91/linux/drivers/isdn/hisax/teles0.h	Thu Feb 27 10:57:29 1997
+++ linux/drivers/isdn/hisax/teles0.h	Wed Dec 31 16:00:00 1969
@@ -1,21 +0,0 @@
-/* $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.91/linux/drivers/isdn/hisax/teles3.c linux/drivers/isdn/hisax/teles3.c
--- v2.1.91/linux/drivers/isdn/hisax/teles3.c	Thu Feb 12 20:56:06 1998
+++ linux/drivers/isdn/hisax/teles3.c	Wed Apr  1 16:21:02 1998
@@ -1,4 +1,4 @@
-/* $Id: teles3.c,v 1.11 1997/04/13 19:54:05 keil Exp $
+/* $Id: teles3.c,v 2.7 1998/02/02 13:29:48 keil Exp $
 
  * teles3.c     low level stuff for Teles 16.3 & PNP isdn cards
  *
@@ -11,6 +11,30 @@
  *              Beat Doebeli
  *
  * $Log: teles3.c,v $
+ * Revision 2.7  1998/02/02 13:29:48  keil
+ * fast io
+ *
+ * Revision 2.6  1997/11/13 16:22:44  keil
+ * COMPAQ_ISA reset
+ *
+ * Revision 2.5  1997/11/12 15:01:25  keil
+ * COMPAQ_ISA changes
+ *
+ * Revision 2.4  1997/11/08 21:35:56  keil
+ * new l1 init
+ *
+ * Revision 2.3  1997/11/06 17:09:33  keil
+ * New 2.1 init code
+ *
+ * Revision 2.2  1997/10/29 18:55:59  keil
+ * changes for 2.1.60 (irq2dev_map)
+ *
+ * Revision 2.1  1997/07/27 21:47:12  keil
+ * new interface structures
+ *
+ * Revision 2.0  1997/06/26 11:02:46  keil
+ * New Layer and card interface
+ *
  * Revision 1.11  1997/04/13 19:54:05  keil
  * Change in IRQ check delay for SMP
  *
@@ -35,36 +59,20 @@
  * Revision 1.6  1997/01/27 15:52:55  keil
  * SMP proof,cosmetics, PCMCIA added
  *
- * Revision 1.5  1997/01/21 22:28:32  keil
- * cleanups
- *
- * Revision 1.4  1996/12/14 21:05:41  keil
- * Reset for 16.3 PnP
- *
- * Revision 1.3  1996/11/05 19:56:54  keil
- * debug output fixed
- *
- * Revision 1.2  1996/10/27 22:09:15  keil
- * cosmetic changes
- *
- * Revision 1.1  1996/10/13 20:04:59  keil
- * Initial revision
- *
- *
+ * removed old log info /KKe
  *
  */
 #define __NO_VERSION__
-#include "siemens.h"
 #include "hisax.h"
-#include "teles3.h"
+#include "isac.h"
+#include "hscx.h"
 #include "isdnl1.h"
-#include <linux/kernel_stat.h>
 
 extern const char *CardType[];
-const char *teles3_revision = "$Revision: 1.11 $";
+const char *teles3_revision = "$Revision: 2.7 $";
 
-#define byteout(addr,val) outb_p(val,addr)
-#define bytein(addr) inb_p(addr)
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
 
 static inline u_char
 readreg(unsigned int adr, u_char off)
@@ -82,953 +90,410 @@
 static inline void
 read_fifo(unsigned int adr, u_char * data, int size)
 {
-	insb(adr + 0x1e, data, size);
+	insb(adr, data, size);
 }
 
 static void
 write_fifo(unsigned int adr, u_char * data, int size)
 {
-	outsb(adr + 0x1e, data, size);
-}
-
-static inline void
-waitforCEC(int adr)
-{
-	int to = 50;
-
-	while ((readreg(adr, HSCX_STAR) & 0x04) && to) {
-		udelay(1);
-		to--;
-	}
-	if (!to)
-		printk(KERN_WARNING "Teles3: waitforCEC timeout\n");
-}
-
-
-static inline void
-waitforXFW(int adr)
-{
-	int to = 50;
-
-	while ((!(readreg(adr, HSCX_STAR) & 0x44) == 0x40) && to) {
-		udelay(1);
-		to--;
-	}
-	if (!to)
-		printk(KERN_WARNING "Teles3: waitforXFW timeout\n");
+	outsb(adr, data, size);
 }
-static inline void
-writehscxCMDR(int adr, u_char data)
-{
-	long flags;
 
-	save_flags(flags);
-	cli();
-	waitforCEC(adr);
-	writereg(adr, HSCX_CMDR, data);
-	restore_flags(flags);
-}
+/* Interface functions */
 
-/*
- * fast interrupt here
- */
-
-static void
-hscxreport(struct IsdnCardState *sp, int hscx)
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
 {
-	printk(KERN_DEBUG "HSCX %d\n", hscx);
-	printk(KERN_DEBUG "ISTA %x\n", readreg(sp->hscx[hscx], HSCX_ISTA));
-	printk(KERN_DEBUG "STAR %x\n", readreg(sp->hscx[hscx], HSCX_STAR));
-	printk(KERN_DEBUG "EXIR %x\n", readreg(sp->hscx[hscx], HSCX_EXIR));
+	return (readreg(cs->hw.teles3.isac, offset));
 }
 
-void
-teles3_report(struct IsdnCardState *sp)
-{
-	printk(KERN_DEBUG "ISAC\n");
-	printk(KERN_DEBUG "ISTA %x\n", readreg(sp->isac, ISAC_ISTA));
-	printk(KERN_DEBUG "STAR %x\n", readreg(sp->isac, ISAC_STAR));
-	printk(KERN_DEBUG "EXIR %x\n", readreg(sp->isac, ISAC_EXIR));
-	hscxreport(sp, 0);
-	hscxreport(sp, 1);
-}
-
-/*
- * HSCX stuff goes here
- */
-
 static void
-hscx_empty_fifo(struct HscxState *hsp, int count)
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
 {
-	u_char *ptr;
-	struct IsdnCardState *sp = hsp->sp;
-	long flags;
-
-	if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO))
-		debugl1(sp, "hscx_empty_fifo");
-
-	if (hsp->rcvidx + count > HSCX_BUFMAX) {
-		if (sp->debug & L1_DEB_WARN)
-			debugl1(sp, "hscx_empty_fifo: incoming packet too large");
-		writehscxCMDR(sp->hscx[hsp->hscx], 0x80);
-		hsp->rcvidx = 0;
-		return;
-	}
-	ptr = hsp->rcvbuf + hsp->rcvidx;
-	hsp->rcvidx += count;
-	save_flags(flags);
-	cli();
-	read_fifo(sp->hscx[hsp->hscx], ptr, count);
-	writehscxCMDR(sp->hscx[hsp->hscx], 0x80);
-	restore_flags(flags);
-	if (sp->debug & L1_DEB_HSCX_FIFO) {
-		char tmp[128];
-		char *t = tmp;
-
-		t += sprintf(t, "hscx_empty_fifo %c cnt %d",
-			     hsp->hscx ? 'B' : 'A', count);
-		QuickHex(t, ptr, count);
-		debugl1(sp, tmp);
-	}
+	writereg(cs->hw.teles3.isac, offset, value);
 }
 
 static void
-hscx_fill_fifo(struct HscxState *hsp)
-{
-	struct IsdnCardState *sp = hsp->sp;
-	int more, count;
-	u_char *ptr;
-	long flags;
-
-	if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO))
-		debugl1(sp, "hscx_fill_fifo");
-
-	if (!hsp->tx_skb)
-		return;
-	if (hsp->tx_skb->len <= 0)
-		return;
-
-	more = (hsp->mode == 1) ? 1 : 0;
-	if (hsp->tx_skb->len > 32) {
-		more = !0;
-		count = 32;
-	} else
-		count = hsp->tx_skb->len;
-
-	waitforXFW(sp->hscx[hsp->hscx]);
-	save_flags(flags);
-	cli();
-	ptr = hsp->tx_skb->data;
-	skb_pull(hsp->tx_skb, count);
-	hsp->tx_cnt -= count;
-	hsp->count += count;
-	write_fifo(sp->hscx[hsp->hscx], ptr, count);
-	writehscxCMDR(sp->hscx[hsp->hscx], more ? 0x8 : 0xa);
-	restore_flags(flags);
-	if (sp->debug & L1_DEB_HSCX_FIFO) {
-		char tmp[128];
-		char *t = tmp;
-
-		t += sprintf(t, "hscx_fill_fifo %c cnt %d",
-			     hsp->hscx ? 'B' : 'A', count);
-		QuickHex(t, ptr, count);
-		debugl1(sp, tmp);
-	}
-}
-
-static inline void
-hscx_interrupt(struct IsdnCardState *sp, u_char val, u_char hscx)
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
 {
-	u_char r;
-	struct HscxState *hsp = sp->hs + hscx;
-	struct sk_buff *skb;
-	int count;
-	char tmp[32];
-
-	if (!hsp->init)
-		return;
-
-	if (val & 0x80) {	/* RME */
-
-		r = readreg(sp->hscx[hsp->hscx], HSCX_RSTA);
-		if ((r & 0xf0) != 0xa0) {
-			if (!r & 0x80)
-				if (sp->debug & L1_DEB_WARN)
-					debugl1(sp, "HSCX invalid frame");
-			if ((r & 0x40) && hsp->mode)
-				if (sp->debug & L1_DEB_WARN) {
-					sprintf(tmp, "HSCX RDO mode=%d",
-						hsp->mode);
-					debugl1(sp, tmp);
-				}
-			if (!r & 0x20)
-				if (sp->debug & L1_DEB_WARN)
-					debugl1(sp, "HSCX CRC error");
-			writehscxCMDR(sp->hscx[hsp->hscx], 0x80);
-		} else {
-			count = readreg(sp->hscx[hsp->hscx], HSCX_RBCL) & 0x1f;
-			if (count == 0)
-				count = 32;
-			hscx_empty_fifo(hsp, count);
-			if ((count = hsp->rcvidx - 1) > 0) {
-				if (!(skb = dev_alloc_skb(count)))
-					printk(KERN_WARNING "AVM: receive out of memory\n");
-				else {
-					memcpy(skb_put(skb, count), hsp->rcvbuf, count);
-					skb_queue_tail(&hsp->rqueue, skb);
-				}
-			}
-		}
-		hsp->rcvidx = 0;
-		hscx_sched_event(hsp, HSCX_RCVBUFREADY);
-	}
-	if (val & 0x40) {	/* RPF */
-		hscx_empty_fifo(hsp, 32);
-		if (hsp->mode == 1) {
-			/* receive audio data */
-			if (!(skb = dev_alloc_skb(32)))
-				printk(KERN_WARNING "AVM: receive out of memory\n");
-			else {
-				memcpy(skb_put(skb, 32), hsp->rcvbuf, 32);
-				skb_queue_tail(&hsp->rqueue, skb);
-			}
-			hsp->rcvidx = 0;
-			hscx_sched_event(hsp, HSCX_RCVBUFREADY);
-		}
-	}
-	if (val & 0x10) {	/* XPR */
-		if (hsp->tx_skb)
-			if (hsp->tx_skb->len) {
-				hscx_fill_fifo(hsp);
-				return;
-			} else {
-				SET_SKB_FREE(hsp->tx_skb);
-				dev_kfree_skb(hsp->tx_skb);
-				hsp->count = 0;
-				if (hsp->st->l4.l1writewakeup)
-					hsp->st->l4.l1writewakeup(hsp->st);
-				hsp->tx_skb = NULL;
-			}
-		if ((hsp->tx_skb = skb_dequeue(&hsp->squeue))) {
-			hsp->count = 0;
-			hscx_fill_fifo(hsp);
-		} else
-			hscx_sched_event(hsp, HSCX_XMTBUFREADY);
-	}
+	read_fifo(cs->hw.teles3.isacfifo, data, size);
 }
 
-/*
- * ISAC stuff goes here
- */
-
 static void
-isac_empty_fifo(struct IsdnCardState *sp, int count)
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
 {
-	u_char *ptr;
-	long flags;
-
-	if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO))
-		if (sp->debug & L1_DEB_ISAC)
-			debugl1(sp, "isac_empty_fifo");
-
-	if ((sp->rcvidx + count) >= MAX_DFRAME_LEN) {
-		if (sp->debug & L1_DEB_WARN) {
-			char tmp[40];
-			sprintf(tmp, "isac_empty_fifo overrun %d",
-				sp->rcvidx + count);
-			debugl1(sp, tmp);
-		}
-		writereg(sp->isac, ISAC_CMDR, 0x80);
-		sp->rcvidx = 0;
-		return;
-	}
-	ptr = sp->rcvbuf + sp->rcvidx;
-	sp->rcvidx += count;
-	save_flags(flags);
-	cli();
-	read_fifo(sp->isac, ptr, count);
-	writereg(sp->isac, ISAC_CMDR, 0x80);
-	restore_flags(flags);
-	if (sp->debug & L1_DEB_ISAC_FIFO) {
-		char tmp[128];
-		char *t = tmp;
-
-		t += sprintf(t, "isac_empty_fifo cnt %d", count);
-		QuickHex(t, ptr, count);
-		debugl1(sp, tmp);
-	}
+	write_fifo(cs->hw.teles3.isacfifo, data, size);
 }
 
-static void
-isac_fill_fifo(struct IsdnCardState *sp)
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
 {
-	int count, more;
-	u_char *ptr;
-	long flags;
-
-	if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO))
-		debugl1(sp, "isac_fill_fifo");
-
-	if (!sp->tx_skb)
-		return;
-
-	count = sp->tx_skb->len;
-	if (count <= 0)
-		return;
-
-	more = 0;
-	if (count > 32) {
-		more = !0;
-		count = 32;
-	}
-	save_flags(flags);
-	cli();
-	ptr = sp->tx_skb->data;
-	skb_pull(sp->tx_skb, count);
-	sp->tx_cnt += count;
-	write_fifo(sp->isac, ptr, count);
-	writereg(sp->isac, ISAC_CMDR, more ? 0x8 : 0xa);
-	restore_flags(flags);
-	if (sp->debug & L1_DEB_ISAC_FIFO) {
-		char tmp[128];
-		char *t = tmp;
-
-		t += sprintf(t, "isac_fill_fifo cnt %d", count);
-		QuickHex(t, ptr, count);
-		debugl1(sp, tmp);
-	}
+	return (readreg(cs->hw.teles3.hscx[hscx], offset));
 }
 
 static void
-ph_command(struct IsdnCardState *sp, unsigned int command)
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
 {
-	if (sp->debug & L1_DEB_ISAC) {
-		char tmp[32];
-		sprintf(tmp, "ph_command %d", command);
-		debugl1(sp, tmp);
-	}
-	writereg(sp->isac, ISAC_CIX0, (command << 2) | 3);
+	writereg(cs->hw.teles3.hscx[hscx], offset, value);
 }
 
+/*
+ * fast interrupt HSCX stuff goes here
+ */
 
-static inline void
-isac_interrupt(struct IsdnCardState *sp, u_char val)
-{
-	u_char exval;
-	struct sk_buff *skb;
-	unsigned int count;
-	char tmp[32];
-
-	if (sp->debug & L1_DEB_ISAC) {
-		sprintf(tmp, "ISAC interrupt %x", val);
-		debugl1(sp, tmp);
-	}
-	if (val & 0x80) {	/* RME */
-		exval = readreg(sp->isac, ISAC_RSTA);
-		if ((exval & 0x70) != 0x20) {
-			if (exval & 0x40)
-				if (sp->debug & L1_DEB_WARN)
-					debugl1(sp, "ISAC RDO");
-			if (!exval & 0x20)
-				if (sp->debug & L1_DEB_WARN)
-					debugl1(sp, "ISAC CRC error");
-			writereg(sp->isac, ISAC_CMDR, 0x80);
-		} else {
-			count = readreg(sp->isac, ISAC_RBCL) & 0x1f;
-			if (count == 0)
-				count = 32;
-			isac_empty_fifo(sp, count);
-			if ((count = sp->rcvidx) > 0) {
-				if (!(skb = alloc_skb(count, GFP_ATOMIC)))
-					printk(KERN_WARNING "AVM: D receive out of memory\n");
-				else {
-					memcpy(skb_put(skb, count), sp->rcvbuf, count);
-					skb_queue_tail(&sp->rq, skb);
-				}
-			}
-		}
-		sp->rcvidx = 0;
-		isac_sched_event(sp, ISAC_RCVBUFREADY);
-	}
-	if (val & 0x40) {	/* RPF */
-		isac_empty_fifo(sp, 32);
-	}
-	if (val & 0x20) {	/* RSC */
-		/* never */
-		if (sp->debug & L1_DEB_WARN)
-			debugl1(sp, "ISAC RSC interrupt");
-	}
-	if (val & 0x10) {	/* XPR */
-		if (sp->tx_skb)
-			if (sp->tx_skb->len) {
-				isac_fill_fifo(sp);
-				goto afterXPR;
-			} else {
-				SET_SKB_FREE(sp->tx_skb);
-				dev_kfree_skb(sp->tx_skb);
-				sp->tx_cnt = 0;
-				sp->tx_skb = NULL;
-			}
-		if ((sp->tx_skb = skb_dequeue(&sp->sq))) {
-			sp->tx_cnt = 0;
-			isac_fill_fifo(sp);
-		} else
-			isac_sched_event(sp, ISAC_XMTBUFREADY);
-	}
-      afterXPR:
-	if (val & 0x04) {	/* CISQ */
-		sp->ph_state = (readreg(sp->isac, ISAC_CIX0) >> 2)
-		    & 0xf;
-		if (sp->debug & L1_DEB_ISAC) {
-			sprintf(tmp, "l1state %d", sp->ph_state);
-			debugl1(sp, tmp);
-		}
-		isac_new_ph(sp);
-	}
-	if (val & 0x02) {	/* SIN */
-		/* never */
-		if (sp->debug & L1_DEB_WARN)
-			debugl1(sp, "ISAC SIN interrupt");
-	}
-	if (val & 0x01) {	/* EXI */
-		exval = readreg(sp->isac, ISAC_EXIR);
-		if (sp->debug & L1_DEB_WARN) {
-			sprintf(tmp, "ISAC EXIR %02x", exval);
-			debugl1(sp, tmp);
-		}
-	}
-}
-
-static inline void
-hscx_int_main(struct IsdnCardState *sp, u_char val)
-{
+#define READHSCX(cs, nr, reg) readreg(cs->hw.teles3.hscx[nr], reg)
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.teles3.hscx[nr], reg, data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo(cs->hw.teles3.hscxfifo[nr], ptr, cnt)
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo(cs->hw.teles3.hscxfifo[nr], ptr, cnt)
 
-	u_char exval;
-	struct HscxState *hsp;
-	char tmp[32];
-
-
-	if (val & 0x01) {
-		hsp = sp->hs + 1;
-		exval = readreg(sp->hscx[1], HSCX_EXIR);
-		if (exval == 0x40) {
-			if (hsp->mode == 1)
-				hscx_fill_fifo(hsp);
-			else {
-				/* Here we lost an TX interrupt, so
-				   * restart transmitting the whole frame.
-				 */
-				if (hsp->tx_skb) {
-					skb_push(hsp->tx_skb, hsp->count);
-					hsp->tx_cnt += hsp->count;
-					hsp->count = 0;
-				}
-				writehscxCMDR(sp->hscx[hsp->hscx], 0x01);
-				if (sp->debug & L1_DEB_WARN) {
-					sprintf(tmp, "HSCX B EXIR %x Lost TX", exval);
-					debugl1(sp, tmp);
-				}
-			}
-		} else if (sp->debug & L1_DEB_HSCX) {
-			sprintf(tmp, "HSCX B EXIR %x", exval);
-			debugl1(sp, tmp);
-		}
-	}
-	if (val & 0xf8) {
-		if (sp->debug & L1_DEB_HSCX) {
-			sprintf(tmp, "HSCX B interrupt %x", val);
-			debugl1(sp, tmp);
-		}
-		hscx_interrupt(sp, val, 1);
-	}
-	if (val & 0x02) {
-		hsp = sp->hs;
-		exval = readreg(sp->hscx[0], HSCX_EXIR);
-		if (exval == 0x40) {
-			if (hsp->mode == 1)
-				hscx_fill_fifo(hsp);
-			else {
-				/* Here we lost an TX interrupt, so
-				   * restart transmitting the whole frame.
-				 */
-				if (hsp->tx_skb) {
-					skb_push(hsp->tx_skb, hsp->count);
-					hsp->tx_cnt += hsp->count;
-					hsp->count = 0;
-				}
-				writehscxCMDR(sp->hscx[hsp->hscx], 0x01);
-				if (sp->debug & L1_DEB_WARN) {
-					sprintf(tmp, "HSCX A EXIR %x Lost TX", exval);
-					debugl1(sp, tmp);
-				}
-			}
-		} else if (sp->debug & L1_DEB_HSCX) {
-			sprintf(tmp, "HSCX A EXIR %x", exval);
-			debugl1(sp, tmp);
-		}
-	}
-	if (val & 0x04) {
-		exval = readreg(sp->hscx[0], HSCX_ISTA);
-		if (sp->debug & L1_DEB_HSCX) {
-			sprintf(tmp, "HSCX A interrupt %x", exval);
-			debugl1(sp, tmp);
-		}
-		hscx_interrupt(sp, exval, 0);
-	}
-}
+#include "hscx_irq.c"
 
 static void
 teles3_interrupt(int intno, void *dev_id, struct pt_regs *regs)
 {
 #define MAXCOUNT 20
-	struct IsdnCardState *sp;
+	struct IsdnCardState *cs = dev_id;
 	u_char val, stat = 0;
 	int count = 0;
 
-	sp = (struct IsdnCardState *) dev_id;
-
-	if (!sp) {
+	if (!cs) {
 		printk(KERN_WARNING "Teles: Spurious interrupt!\n");
 		return;
 	}
-	val = readreg(sp->hscx[1], HSCX_ISTA);
+	val = readreg(cs->hw.teles3.hscx[1], HSCX_ISTA);
       Start_HSCX:
 	if (val) {
-		hscx_int_main(sp, val);
+		hscx_int_main(cs, val);
 		stat |= 1;
 	}
-	val = readreg(sp->isac, ISAC_ISTA);
+	val = readreg(cs->hw.teles3.isac, ISAC_ISTA);
       Start_ISAC:
 	if (val) {
-		isac_interrupt(sp, val);
+		isac_interrupt(cs, val);
 		stat |= 2;
 	}
 	count++;
-	val = readreg(sp->hscx[1], HSCX_ISTA);
+	val = readreg(cs->hw.teles3.hscx[1], HSCX_ISTA);
 	if (val && count < MAXCOUNT) {
-		if (sp->debug & L1_DEB_HSCX)
-			debugl1(sp, "HSCX IntStat after IntRoutine");
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX IntStat after IntRoutine");
 		goto Start_HSCX;
 	}
-	val = readreg(sp->isac, ISAC_ISTA);
+	val = readreg(cs->hw.teles3.isac, ISAC_ISTA);
 	if (val && count < MAXCOUNT) {
-		if (sp->debug & L1_DEB_ISAC)
-			debugl1(sp, "ISAC IntStat after IntRoutine");
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
 		goto Start_ISAC;
 	}
 	if (count >= MAXCOUNT)
 		printk(KERN_WARNING "Teles3: more than %d loops in teles3_interrupt\n", count);
 	if (stat & 1) {
-		writereg(sp->hscx[0], HSCX_MASK, 0xFF);
-		writereg(sp->hscx[1], HSCX_MASK, 0xFF);
-		writereg(sp->hscx[0], HSCX_MASK, 0x0);
-		writereg(sp->hscx[1], HSCX_MASK, 0x0);
+		writereg(cs->hw.teles3.hscx[0], HSCX_MASK, 0xFF);
+		writereg(cs->hw.teles3.hscx[1], HSCX_MASK, 0xFF);
+		writereg(cs->hw.teles3.hscx[0], HSCX_MASK, 0x0);
+		writereg(cs->hw.teles3.hscx[1], HSCX_MASK, 0x0);
 	}
 	if (stat & 2) {
-		writereg(sp->isac, ISAC_MASK, 0xFF);
-		writereg(sp->isac, ISAC_MASK, 0x0);
-	}
-}
-
-
-static void
-initisac(struct IsdnCardState *sp)
-{
-	unsigned int adr = sp->isac;
-
-	/* 16.3 IOM 2 Mode */
-	writereg(adr, ISAC_MASK, 0xff);
-	writereg(adr, ISAC_ADF2, 0x80);
-	writereg(adr, ISAC_SQXR, 0x2f);
-	writereg(adr, ISAC_SPCR, 0x0);
-	writereg(adr, ISAC_ADF1, 0x2);
-	writereg(adr, ISAC_STCR, 0x70);
-	writereg(adr, ISAC_MODE, 0xc9);
-	writereg(adr, ISAC_TIMR, 0x0);
-	writereg(adr, ISAC_ADF1, 0x0);
-	writereg(adr, ISAC_CMDR, 0x41);
-	writereg(adr, ISAC_CIX0, (1 << 2) | 3);
-	writereg(adr, ISAC_MASK, 0xff);
-	writereg(adr, ISAC_MASK, 0x0);
-}
-
-static void
-modehscx(struct HscxState *hs, int mode, int ichan)
-{
-	struct IsdnCardState *sp = hs->sp;
-	int hscx = hs->hscx;
-
-	if (sp->debug & L1_DEB_HSCX) {
-		char tmp[40];
-		sprintf(tmp, "hscx %c mode %d ichan %d",
-			'A' + hscx, mode, ichan);
-		debugl1(sp, tmp);
-	}
-	hs->mode = mode;
-	writereg(sp->hscx[hscx], HSCX_CCR1, 0x85);
-	writereg(sp->hscx[hscx], HSCX_XAD1, 0xFF);
-	writereg(sp->hscx[hscx], HSCX_XAD2, 0xFF);
-	writereg(sp->hscx[hscx], HSCX_RAH2, 0xFF);
-	writereg(sp->hscx[hscx], HSCX_XBCH, 0x0);
-	writereg(sp->hscx[hscx], HSCX_RLCR, 0x0);
-
-	switch (mode) {
-		case (0):
-			writereg(sp->hscx[hscx], HSCX_CCR2, 0x30);
-			writereg(sp->hscx[hscx], HSCX_TSAX, 0xff);
-			writereg(sp->hscx[hscx], HSCX_TSAR, 0xff);
-			writereg(sp->hscx[hscx], HSCX_XCCR, 7);
-			writereg(sp->hscx[hscx], HSCX_RCCR, 7);
-			writereg(sp->hscx[hscx], HSCX_MODE, 0x84);
-			break;
-		case (1):
-			if (ichan == 0) {
-				writereg(sp->hscx[hscx], HSCX_CCR2, 0x30);
-				writereg(sp->hscx[hscx], HSCX_TSAX, 0x2f);
-				writereg(sp->hscx[hscx], HSCX_TSAR, 0x2f);
-				writereg(sp->hscx[hscx], HSCX_XCCR, 7);
-				writereg(sp->hscx[hscx], HSCX_RCCR, 7);
-			} else {
-				writereg(sp->hscx[hscx], HSCX_CCR2, 0x30);
-				writereg(sp->hscx[hscx], HSCX_TSAX, 0x3);
-				writereg(sp->hscx[hscx], HSCX_TSAR, 0x3);
-				writereg(sp->hscx[hscx], HSCX_XCCR, 7);
-				writereg(sp->hscx[hscx], HSCX_RCCR, 7);
-			}
-			writereg(sp->hscx[hscx], HSCX_MODE, 0xe4);
-			writereg(sp->hscx[hscx], HSCX_CMDR, 0x41);
-			break;
-		case (2):
-			if (ichan == 0) {
-				writereg(sp->hscx[hscx], HSCX_CCR2, 0x30);
-				writereg(sp->hscx[hscx], HSCX_TSAX, 0x2f);
-				writereg(sp->hscx[hscx], HSCX_TSAR, 0x2f);
-				writereg(sp->hscx[hscx], HSCX_XCCR, 7);
-				writereg(sp->hscx[hscx], HSCX_RCCR, 7);
-			} else {
-				writereg(sp->hscx[hscx], HSCX_CCR2, 0x30);
-				writereg(sp->hscx[hscx], HSCX_TSAX, 0x3);
-				writereg(sp->hscx[hscx], HSCX_TSAR, 0x3);
-				writereg(sp->hscx[hscx], HSCX_XCCR, 7);
-				writereg(sp->hscx[hscx], HSCX_RCCR, 7);
-			}
-			writereg(sp->hscx[hscx], HSCX_MODE, 0x8c);
-			writereg(sp->hscx[hscx], HSCX_CMDR, 0x41);
-			break;
+		writereg(cs->hw.teles3.isac, ISAC_MASK, 0xFF);
+		writereg(cs->hw.teles3.isac, ISAC_MASK, 0x0);
 	}
-	writereg(sp->hscx[hscx], HSCX_ISTA, 0x00);
 }
 
 inline static void
-release_ioregs(struct IsdnCard *card, int mask)
+release_ioregs(struct IsdnCardState *cs, int mask)
 {
 	if (mask & 1)
-		release_region(card->sp->isac, 32);
+		release_region(cs->hw.teles3.isac + 32, 32);
 	if (mask & 2)
-		release_region(card->sp->hscx[0], 32);
+		release_region(cs->hw.teles3.hscx[0] + 32, 32);
 	if (mask & 4)
-		release_region(card->sp->hscx[1], 32);
+		release_region(cs->hw.teles3.hscx[1] + 32, 32);
 }
 
 void
-release_io_teles3(struct IsdnCard *card)
+release_io_teles3(struct IsdnCardState *cs)
 {
-	if (card->sp->typ == ISDN_CTYPE_TELESPCMCIA)
-		release_region(card->sp->hscx[0], 97);
+	if (cs->typ == ISDN_CTYPE_TELESPCMCIA)
+		release_region(cs->hw.teles3.cfg_reg, 97);
 	else {
-		if (card->sp->cfg_reg)
-			release_region(card->sp->cfg_reg, 8);
-		release_ioregs(card, 0x7);
+		if (cs->hw.teles3.cfg_reg)
+			if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
+				release_region(cs->hw.teles3.cfg_reg, 1);
+			} else {
+				release_region(cs->hw.teles3.cfg_reg, 8);
+			}
+		release_ioregs(cs, 0x7);
 	}
 }
 
-static void
-clear_pending_ints(struct IsdnCardState *sp)
+static int
+reset_teles3(struct IsdnCardState *cs)
 {
-	int val;
-	char tmp[64];
-
-	val = readreg(sp->hscx[1], HSCX_ISTA);
-	sprintf(tmp, "HSCX B ISTA %x", val);
-	debugl1(sp, tmp);
-	if (val & 0x01) {
-		val = readreg(sp->hscx[1], HSCX_EXIR);
-		sprintf(tmp, "HSCX B EXIR %x", val);
-		debugl1(sp, tmp);
-	} else if (val & 0x02) {
-		val = readreg(sp->hscx[0], HSCX_EXIR);
-		sprintf(tmp, "HSCX A EXIR %x", val);
-		debugl1(sp, tmp);
-	}
-	val = readreg(sp->hscx[0], HSCX_ISTA);
-	sprintf(tmp, "HSCX A ISTA %x", val);
-	debugl1(sp, tmp);
-	val = readreg(sp->hscx[1], HSCX_STAR);
-	sprintf(tmp, "HSCX B STAR %x", val);
-	debugl1(sp, tmp);
-	val = readreg(sp->hscx[0], HSCX_STAR);
-	sprintf(tmp, "HSCX A STAR %x", val);
-	debugl1(sp, tmp);
-	val = readreg(sp->isac, ISAC_STAR);
-	sprintf(tmp, "ISAC STAR %x", val);
-	debugl1(sp, tmp);
-	val = readreg(sp->isac, ISAC_MODE);
-	sprintf(tmp, "ISAC MODE %x", val);
-	debugl1(sp, tmp);
-	val = readreg(sp->isac, ISAC_ADF2);
-	sprintf(tmp, "ISAC ADF2 %x", val);
-	debugl1(sp, tmp);
-	val = readreg(sp->isac, ISAC_ISTA);
-	sprintf(tmp, "ISAC ISTA %x", val);
-	debugl1(sp, tmp);
-	if (val & 0x01) {
-		val = readreg(sp->isac, ISAC_EXIR);
-		sprintf(tmp, "ISAC EXIR %x", val);
-		debugl1(sp, tmp);
-	} else if (val & 0x04) {
-		val = readreg(sp->isac, ISAC_CIR0);
-		sprintf(tmp, "ISAC CIR0 %x", val);
-		debugl1(sp, tmp);
-	}
-	writereg(sp->isac, ISAC_MASK, 0);
-	writereg(sp->isac, ISAC_CMDR, 0x41);
-}
+	long flags;
+	u_char irqcfg;
 
-int
-initteles3(struct IsdnCardState *sp)
-{
-	int ret;
-	int loop = 0;
-	char tmp[40];
-
-	sp->counter = kstat_irqs(sp->irq);
-	sprintf(tmp, "IRQ %d count %d", sp->irq, sp->counter);
-	debugl1(sp, tmp);
-	clear_pending_ints(sp);
-	ret = get_irq(sp->cardnr, &teles3_interrupt);
-	if (ret) {
-		initisac(sp);
-		sp->modehscx(sp->hs, 0, 0);
-		writereg(sp->hscx[sp->hs->hscx], HSCX_CMDR, 0x01);
-		sp->modehscx(sp->hs + 1, 0, 0);
-		writereg(sp->hscx[(sp->hs + 1)->hscx], HSCX_CMDR, 0x01);
-		while (loop++ < 10) {
-			/* At least 1-3 irqs must happen
-			 * (one from HSCX A, one from HSCX B, 3rd from ISAC)
-			 */
-			if (kstat_irqs(sp->irq) > sp->counter)
-				break;
-			current->state = TASK_INTERRUPTIBLE;
-			current->timeout = jiffies + 1;
-			schedule();
-		}
-		sprintf(tmp, "IRQ %d count %d loop %d", sp->irq,
-			kstat_irqs(sp->irq), loop);
-		debugl1(sp, tmp);
-		if (kstat_irqs(sp->irq) <= sp->counter) {
-			printk(KERN_WARNING
-			       "Teles3: IRQ(%d) getting no interrupts during init\n",
-			       sp->irq);
-			free_irq(sp->irq, sp);
-			return (0);
-		}
+	if (cs->typ != ISDN_CTYPE_TELESPCMCIA) {
+		if ((cs->hw.teles3.cfg_reg) && (cs->typ != ISDN_CTYPE_COMPAQ_ISA)) {
+			switch (cs->irq) {
+				case 2:
+				case 9:
+					irqcfg = 0x00;
+					break;
+				case 3:
+					irqcfg = 0x02;
+					break;
+				case 4:
+					irqcfg = 0x04;
+					break;
+				case 5:
+					irqcfg = 0x06;
+					break;
+				case 10:
+					irqcfg = 0x08;
+					break;
+				case 11:
+					irqcfg = 0x0A;
+					break;
+				case 12:
+					irqcfg = 0x0C;
+					break;
+				case 15:
+					irqcfg = 0x0E;
+					break;
+				default:
+					return(1);
+			}
+			save_flags(flags);
+			byteout(cs->hw.teles3.cfg_reg + 4, irqcfg);
+			sti();
+			HZDELAY(HZ / 10 + 1);
+			byteout(cs->hw.teles3.cfg_reg + 4, irqcfg | 1);
+			HZDELAY(HZ / 10 + 1);
+			restore_flags(flags);
+		} else if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
+			save_flags(flags);
+			byteout(cs->hw.teles3.cfg_reg, 0xff);
+			HZDELAY(2);
+			byteout(cs->hw.teles3.cfg_reg, 0x00);
+			HZDELAY(2);
+			restore_flags(flags);
+		} else {
+			/* Reset off for 16.3 PnP , thanks to Georg Acher */
+			save_flags(flags);
+			byteout(cs->hw.teles3.isac + 0x3c, 0);
+			HZDELAY(2);
+			byteout(cs->hw.teles3.isac + 0x3c, 1);
+			HZDELAY(2);
+			restore_flags(flags);
+		}
+	}
+	return(0);
+}
+
+static int
+Teles_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	switch (mt) {
+		case CARD_RESET:
+			reset_teles3(cs);
+			return(0);
+		case CARD_RELEASE:
+			release_io_teles3(cs);
+			return(0);
+		case CARD_SETIRQ:
+			return(request_irq(cs->irq, &teles3_interrupt,
+					I4L_IRQ_FLAG, "HiSax", cs));
+		case CARD_INIT:
+			clear_pending_isac_ints(cs);
+			clear_pending_hscx_ints(cs);
+			initisac(cs);
+			inithscx(cs);
+			return(0);
+		case CARD_TEST:
+			return(0);
 	}
-	return (ret);
+	return(0);
 }
 
-int
-setup_teles3(struct IsdnCard *card)
+__initfunc(int
+setup_teles3(struct IsdnCard *card))
 {
-	u_char cfval = 0, val, verA, verB;
-	struct IsdnCardState *sp = card->sp;
-	long flags;
+	u_char val;
+	struct IsdnCardState *cs = card->cs;
 	char tmp[64];
 
 	strcpy(tmp, teles3_revision);
-	printk(KERN_NOTICE "HiSax: Teles IO driver Rev. %s\n", HiSax_getrev(tmp));
-	if ((sp->typ != ISDN_CTYPE_16_3) && (sp->typ != ISDN_CTYPE_PNP)
-	    && (sp->typ != ISDN_CTYPE_TELESPCMCIA))
+	printk(KERN_INFO "HiSax: Teles IO driver Rev. %s\n", HiSax_getrev(tmp));
+	if ((cs->typ != ISDN_CTYPE_16_3) && (cs->typ != ISDN_CTYPE_PNP)
+	    && (cs->typ != ISDN_CTYPE_TELESPCMCIA) && (cs->typ != ISDN_CTYPE_COMPAQ_ISA))
 		return (0);
 
-	if (sp->typ == ISDN_CTYPE_16_3) {
-		sp->cfg_reg = card->para[1];
-		switch (sp->cfg_reg) {
+	if (cs->typ == ISDN_CTYPE_16_3) {
+		cs->hw.teles3.cfg_reg = card->para[1];
+		switch (cs->hw.teles3.cfg_reg) {
 			case 0x180:
 			case 0x280:
 			case 0x380:
-				sp->cfg_reg |= 0xc00;
+				cs->hw.teles3.cfg_reg |= 0xc00;
 				break;
 		}
-		sp->isac = sp->cfg_reg - 0x400;
-		sp->hscx[0] = sp->cfg_reg - 0xc00;
-		sp->hscx[1] = sp->cfg_reg - 0x800;
-	} else if (sp->typ == ISDN_CTYPE_TELESPCMCIA) {
-		sp->cfg_reg = 0;
-		sp->hscx[0] = card->para[1];
-		sp->hscx[1] = card->para[1] + 0x20;
-		sp->isac = card->para[1] + 0x40;
-	} else {		/* PNP */
-		sp->cfg_reg = 0;
-		sp->isac = card->para[1];
-		sp->hscx[0] = card->para[2];
-		sp->hscx[1] = card->para[2] + 0x20;
-	}
-	sp->irq = card->para[0];
-	if (sp->typ == ISDN_CTYPE_TELESPCMCIA) {
-		if (check_region((sp->hscx[0]), 97)) {
+		cs->hw.teles3.isac = cs->hw.teles3.cfg_reg - 0x420;
+		cs->hw.teles3.hscx[0] = cs->hw.teles3.cfg_reg - 0xc20;
+		cs->hw.teles3.hscx[1] = cs->hw.teles3.cfg_reg - 0x820;
+	} else if (cs->typ == ISDN_CTYPE_TELESPCMCIA) {
+		cs->hw.teles3.cfg_reg = card->para[1];
+		cs->hw.teles3.hscx[0] = card->para[1] - 0x20;
+		cs->hw.teles3.hscx[1] = card->para[1];
+		cs->hw.teles3.isac = card->para[1] + 0x20;
+	} else if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
+		cs->hw.teles3.cfg_reg = card->para[3];
+		cs->hw.teles3.isac = card->para[2] - 32;
+		cs->hw.teles3.hscx[0] = card->para[1] - 32;
+		cs->hw.teles3.hscx[1] = card->para[1];
+	} else {	/* PNP */
+		cs->hw.teles3.cfg_reg = 0;
+		cs->hw.teles3.isac = card->para[1] - 32;
+		cs->hw.teles3.hscx[0] = card->para[2] - 32;
+		cs->hw.teles3.hscx[1] = card->para[2];
+	}
+	cs->irq = card->para[0];
+	cs->hw.teles3.isacfifo = cs->hw.teles3.isac + 0x3e;
+	cs->hw.teles3.hscxfifo[0] = cs->hw.teles3.hscx[0] + 0x3e;
+	cs->hw.teles3.hscxfifo[1] = cs->hw.teles3.hscx[1] + 0x3e;
+	if (cs->typ == ISDN_CTYPE_TELESPCMCIA) {
+		if (check_region((cs->hw.teles3.cfg_reg), 97)) {
 			printk(KERN_WARNING
 			       "HiSax: %s ports %x-%x already in use\n",
-			       CardType[sp->typ],
-			       sp->hscx[0],
-			       sp->hscx[0] + 96);
+			       CardType[cs->typ],
+			       cs->hw.teles3.cfg_reg,
+			       cs->hw.teles3.cfg_reg + 96);
 			return (0);
 		} else
-			request_region(sp->hscx[0], 97, "HiSax Teles PCMCIA");
+			request_region(cs->hw.teles3.hscx[0], 97, "HiSax Teles PCMCIA");
 	} else {
-		if (sp->cfg_reg) {
-			if (check_region((sp->cfg_reg), 8)) {
-				printk(KERN_WARNING
-				       "HiSax: %s config port %x-%x already in use\n",
-				       CardType[card->typ],
-				       sp->cfg_reg,
-				       sp->cfg_reg + 8);
-				return (0);
+		if (cs->hw.teles3.cfg_reg) {
+			if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
+				if (check_region((cs->hw.teles3.cfg_reg), 1)) {
+					printk(KERN_WARNING
+						"HiSax: %s config port %x already in use\n",
+						CardType[card->typ],
+						cs->hw.teles3.cfg_reg);
+					return (0);
+				} else
+					request_region(cs->hw.teles3.cfg_reg, 1, "teles3 cfg");
 			} else {
-				request_region(sp->cfg_reg, 8, "teles3 cfg");
+				if (check_region((cs->hw.teles3.cfg_reg), 8)) {
+					printk(KERN_WARNING
+					       "HiSax: %s config port %x-%x already in use\n",
+					       CardType[card->typ],
+					       cs->hw.teles3.cfg_reg,
+						cs->hw.teles3.cfg_reg + 8);
+					return (0);
+				} else
+					request_region(cs->hw.teles3.cfg_reg, 8, "teles3 cfg");
 			}
 		}
-		if (check_region((sp->isac), 32)) {
+		if (check_region((cs->hw.teles3.isac + 32), 32)) {
 			printk(KERN_WARNING
 			   "HiSax: %s isac ports %x-%x already in use\n",
-			       CardType[sp->typ],
-			       sp->isac,
-			       sp->isac + 32);
-			if (sp->cfg_reg) {
-				release_region(sp->cfg_reg, 8);
-			}
+			       CardType[cs->typ],
+			       cs->hw.teles3.isac + 32,
+			       cs->hw.teles3.isac + 64);
+			if (cs->hw.teles3.cfg_reg)
+				if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
+					release_region(cs->hw.teles3.cfg_reg, 1);
+				} else {
+					release_region(cs->hw.teles3.cfg_reg, 8);
+				}
 			return (0);
-		} else {
-			request_region(sp->isac, 32, "HiSax isac");
-		}
-		if (check_region((sp->hscx[0]), 32)) {
+		} else
+			request_region(cs->hw.teles3.isac + 32, 32, "HiSax isac");
+		if (check_region((cs->hw.teles3.hscx[0] + 32), 32)) {
 			printk(KERN_WARNING
 			 "HiSax: %s hscx A ports %x-%x already in use\n",
-			       CardType[sp->typ],
-			       sp->hscx[0],
-			       sp->hscx[0] + 32);
-			if (sp->cfg_reg) {
-				release_region(sp->cfg_reg, 8);
-			}
-			release_ioregs(card, 1);
+			       CardType[cs->typ],
+			       cs->hw.teles3.hscx[0] + 32,
+			       cs->hw.teles3.hscx[0] + 64);
+			if (cs->hw.teles3.cfg_reg)
+				if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
+					release_region(cs->hw.teles3.cfg_reg, 1);
+				} else {
+					release_region(cs->hw.teles3.cfg_reg, 8);
+				}
+			release_ioregs(cs, 1);
 			return (0);
-		} else {
-			request_region(sp->hscx[0], 32, "HiSax hscx A");
-		}
-		if (check_region((sp->hscx[1]), 32)) {
+		} else
+			request_region(cs->hw.teles3.hscx[0] + 32, 32, "HiSax hscx A");
+		if (check_region((cs->hw.teles3.hscx[1] + 32), 32)) {
 			printk(KERN_WARNING
 			 "HiSax: %s hscx B ports %x-%x already in use\n",
-			       CardType[sp->typ],
-			       sp->hscx[1],
-			       sp->hscx[1] + 32);
-			if (sp->cfg_reg) {
-				release_region(sp->cfg_reg, 8);
-			}
-			release_ioregs(card, 3);
+			       CardType[cs->typ],
+			       cs->hw.teles3.hscx[1] + 32,
+			       cs->hw.teles3.hscx[1] + 64);
+			if (cs->hw.teles3.cfg_reg)
+				if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
+					release_region(cs->hw.teles3.cfg_reg, 1);
+				} else {
+					release_region(cs->hw.teles3.cfg_reg, 8);
+				}
+			release_ioregs(cs, 3);
 			return (0);
-		} else {
-			request_region(sp->hscx[1], 32, "HiSax hscx B");
-		}
-		switch (sp->irq) {
-			case 2:
-				cfval = 0x00;
-				break;
-			case 3:
-				cfval = 0x02;
-				break;
-			case 4:
-				cfval = 0x04;
-				break;
-			case 5:
-				cfval = 0x06;
-				break;
-			case 10:
-				cfval = 0x08;
-				break;
-			case 11:
-				cfval = 0x0A;
-				break;
-			case 12:
-				cfval = 0x0C;
-				break;
-			case 15:
-				cfval = 0x0E;
-				break;
-			default:
-				cfval = 0x00;
-				break;
-		}
+		} else
+			request_region(cs->hw.teles3.hscx[1] + 32, 32, "HiSax hscx B");
 	}
-	if (sp->cfg_reg) {
-		if ((val = bytein(sp->cfg_reg + 0)) != 0x51) {
+	if ((cs->hw.teles3.cfg_reg) && (cs->typ != ISDN_CTYPE_COMPAQ_ISA)) {
+		if ((val = bytein(cs->hw.teles3.cfg_reg + 0)) != 0x51) {
 			printk(KERN_WARNING "Teles: 16.3 Byte at %x is %x\n",
-			       sp->cfg_reg + 0, val);
-			release_io_teles3(card);
+			       cs->hw.teles3.cfg_reg + 0, val);
+			release_io_teles3(cs);
 			return (0);
 		}
-		if ((val = bytein(sp->cfg_reg + 1)) != 0x93) {
+		if ((val = bytein(cs->hw.teles3.cfg_reg + 1)) != 0x93) {
 			printk(KERN_WARNING "Teles: 16.3 Byte at %x is %x\n",
-			       sp->cfg_reg + 1, val);
-			release_io_teles3(card);
+			       cs->hw.teles3.cfg_reg + 1, val);
+			release_io_teles3(cs);
 			return (0);
 		}
-		val = bytein(sp->cfg_reg + 2);	/* 0x1e=without AB
-						   * 0x1f=with AB
-						   * 0x1c 16.3 ???
-						   * 0x46 16.3 with AB + Video (Teles-Vision)
-						 */
-		if (val != 0x46 && val != 0x1c && val != 0x1e && val != 0x1f) {
+		val = bytein(cs->hw.teles3.cfg_reg + 2);/* 0x1e=without AB
+							 * 0x1f=with AB
+							 * 0x1c 16.3 ???
+							 * 0x39 16.3 1.1
+							 * 0x46 16.3 with AB + Video (Teles-Vision)
+							 */
+		if (val != 0x46 && val != 0x39 && val != 0x1c && val != 0x1e && val != 0x1f) {
 			printk(KERN_WARNING "Teles: 16.3 Byte at %x is %x\n",
-			       sp->cfg_reg + 2, val);
-			release_io_teles3(card);
+			       cs->hw.teles3.cfg_reg + 2, val);
+			release_io_teles3(cs);
 			return (0);
 		}
-		save_flags(flags);
-		byteout(sp->cfg_reg + 4, cfval);
-		sti();
-		HZDELAY(HZ / 10 + 1);
-		byteout(sp->cfg_reg + 4, cfval | 1);
-		HZDELAY(HZ / 10 + 1);
-		restore_flags(flags);
-	} else {
-		/* Reset off for 16.3 PnP , thanks to Georg Acher */
-		save_flags(flags);
-		byteout(sp->isac + 0x1c, 1);
-		HZDELAY(2);
-		restore_flags(flags);
 	}
-	printk(KERN_NOTICE
-	       "HiSax: %s config irq:%d isac:%x  cfg:%x\n",
-	       CardType[sp->typ], sp->irq,
-	       sp->isac, sp->cfg_reg);
-	printk(KERN_NOTICE
-	       "HiSax: hscx A:%x  hscx B:%x\n",
-	       sp->hscx[0], sp->hscx[1]);
-	verA = readreg(sp->hscx[0], HSCX_VSTR) & 0xf;
-	verB = readreg(sp->hscx[1], HSCX_VSTR) & 0xf;
-	printk(KERN_INFO "Teles3: HSCX version A: %s  B: %s\n",
-	       HscxVersion(verA), HscxVersion(verB));
-	val = readreg(sp->isac, ISAC_RBCH);
-	printk(KERN_INFO "Teles3: ISAC %s\n",
-	       ISACVersion(val));
-	if ((verA == 0) | (verA == 0xf) | (verB == 0) | (verB == 0xf)) {
+	printk(KERN_INFO
+	       "HiSax: %s config irq:%d isac:0x%X  cfg:0x%X\n",
+	       CardType[cs->typ], cs->irq,
+	       cs->hw.teles3.isac + 32, cs->hw.teles3.cfg_reg);
+	printk(KERN_INFO
+	       "HiSax: hscx A:0x%X  hscx B:0x%X\n",
+	       cs->hw.teles3.hscx[0] + 32, cs->hw.teles3.hscx[1] + 32);
+
+	if (reset_teles3(cs)) {
+		printk(KERN_WARNING "Teles3: wrong IRQ\n");
+		release_io_teles3(cs);
+		return (0);
+	}
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &Teles_card_msg;
+	ISACVersion(cs, "Teles3:");
+	if (HscxVersion(cs, "Teles3:")) {
 		printk(KERN_WARNING
 		       "Teles3: wrong HSCX versions check IO address\n");
-		release_io_teles3(card);
+		release_io_teles3(cs);
 		return (0);
 	}
-	sp->modehscx = &modehscx;
-	sp->ph_command = &ph_command;
-	sp->hscx_fill_fifo = &hscx_fill_fifo;
-	sp->isac_fill_fifo = &isac_fill_fifo;
 	return (1);
 }
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/hisax/teles3.h linux/drivers/isdn/hisax/teles3.h
--- v2.1.91/linux/drivers/isdn/hisax/teles3.h	Thu Feb 27 10:57:29 1997
+++ linux/drivers/isdn/hisax/teles3.h	Wed Dec 31 16:00:00 1969
@@ -1,21 +0,0 @@
-/* $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.91/linux/drivers/isdn/hisax/teles3c.c linux/drivers/isdn/hisax/teles3c.c
--- v2.1.91/linux/drivers/isdn/hisax/teles3c.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/teles3c.c	Wed Apr  1 16:21:02 1998
@@ -0,0 +1,199 @@
+/* $Id: teles3c.c,v 1.2 1998/02/02 13:27:07 keil Exp $
+
+ * teles3c.c     low level stuff for teles 16.3c
+ *
+ * Author     Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ *
+ * $Log: teles3c.c,v $
+ * Revision 1.2  1998/02/02 13:27:07  keil
+ * New
+ *
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include "hisax.h"
+#include "hfc_2bds0.h"
+#include "isdnl1.h"
+
+extern const char *CardType[];
+
+const char *teles163c_revision = "$Revision: 1.2 $";
+
+static void
+t163c_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val, stat;
+	char tmp[32];
+
+	if (!cs) {
+		printk(KERN_WARNING "teles3c: Spurious interrupt!\n");
+		return;
+	}
+	if ((HFCD_ANYINT | HFCD_BUSY_NBUSY) & 
+		(stat = cs->BC_Read_Reg(cs, HFCD_DATA, HFCD_STAT))) {
+		val = cs->BC_Read_Reg(cs, HFCD_DATA, HFCD_INT_S1);
+		if (cs->debug & L1_DEB_ISAC) {
+			sprintf(tmp, "teles3c: stat(%02x) s1(%02x)", stat, val);
+			debugl1(cs, tmp);
+		}
+		hfc2bds0_interrupt(cs, val);
+	} else {
+		if (cs->debug & L1_DEB_ISAC) {
+			sprintf(tmp, "teles3c: irq_no_irq stat(%02x)", stat);
+			debugl1(cs, tmp);
+		}
+	}
+}
+
+static void
+t163c_Timer(struct IsdnCardState *cs)
+{
+	cs->hw.hfcD.timer.expires = jiffies + 75;
+	/* WD RESET */
+/*	WriteReg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt | 0x80);
+	add_timer(&cs->hw.hfcD.timer);
+*/
+}
+
+void
+release_io_t163c(struct IsdnCardState *cs)
+{
+	release2bds0(cs);
+	del_timer(&cs->hw.hfcD.timer);
+	if (cs->hw.hfcD.addr)
+		release_region(cs->hw.hfcD.addr, 2);
+}
+
+static void
+reset_t163c(struct IsdnCardState *cs)
+{
+	long flags;
+
+	printk(KERN_INFO "teles3c: resetting card\n");
+	cs->hw.hfcD.cirm = HFCD_RESET | HFCD_MEM8K;
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CIRM, cs->hw.hfcD.cirm);	/* Reset On */
+	save_flags(flags);
+	sti();
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + 3;
+	schedule();
+	cs->hw.hfcD.cirm = HFCD_MEM8K;
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CIRM, cs->hw.hfcD.cirm);	/* Reset Off */
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + 1;
+	schedule();
+	cs->hw.hfcD.cirm |= HFCD_INTB;
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CIRM, cs->hw.hfcD.cirm);	/* INT B */
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CLKDEL, 0x0e);
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_TEST, HFCD_AUTO_AWAKE); /* S/T Auto awake */
+	cs->hw.hfcD.ctmt = HFCD_TIM25 | HFCD_AUTO_TIMER;
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt);
+	cs->hw.hfcD.int_m2 = HFCD_IRQ_ENABLE;
+	cs->hw.hfcD.int_m1 = HFCD_INTS_B1TRANS | HFCD_INTS_B2TRANS |
+		HFCD_INTS_DTRANS | HFCD_INTS_B1REC | HFCD_INTS_B2REC |
+		HFCD_INTS_DREC | HFCD_INTS_L1STATE;
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_INT_M1, cs->hw.hfcD.int_m1);
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_INT_M2, cs->hw.hfcD.int_m2);
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_STATES, HFCD_LOAD_STATE | 2); /* HFC ST 2 */
+	udelay(10);
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_STATES, 2); /* HFC ST 2 */
+	cs->hw.hfcD.mst_m = 0;
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_MST_MODE, HFCD_MASTER); /* HFC Master */
+	cs->hw.hfcD.sctrl = 0;
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_SCTRL, cs->hw.hfcD.sctrl);
+	restore_flags(flags);
+}
+
+static int
+t163c_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	long flags;
+	char tmp[32];
+
+	if (cs->debug & L1_DEB_ISAC) {
+		
+		sprintf(tmp, "teles3c: card_msg %x", mt);
+		debugl1(cs, tmp);
+	}
+	switch (mt) {
+		case CARD_RESET:
+			reset_t163c(cs);
+			return(0);
+		case CARD_RELEASE:
+			release_io_t163c(cs);
+			return(0);
+		case CARD_SETIRQ:
+			cs->hw.hfcD.timer.expires = jiffies + 75;
+			add_timer(&cs->hw.hfcD.timer);
+			return(request_irq(cs->irq, &t163c_interrupt,
+					I4L_IRQ_FLAG, "HiSax", cs));
+		case CARD_INIT:
+			init2bds0(cs);
+			save_flags(flags);
+			sti();
+			current->state = TASK_INTERRUPTIBLE;
+			current->timeout = jiffies + (80*HZ)/1000;
+			schedule();
+			cs->hw.hfcD.ctmt |= HFCD_TIM800;
+			cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt); 
+			cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_MST_MODE, cs->hw.hfcD.mst_m);
+			restore_flags(flags);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+__initfunc(int
+setup_t163c(struct IsdnCard *card))
+{
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, teles163c_revision);
+	printk(KERN_INFO "HiSax: Teles 16.3c driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_TELES3C)
+		return (0);
+	cs->debug = 0xff;
+	cs->hw.hfcD.addr = card->para[1] & 0xfffe;
+	cs->irq = card->para[0];
+	cs->hw.hfcD.cip = 0;
+	cs->hw.hfcD.int_s1 = 0;
+	cs->hw.hfcD.send = NULL;
+	cs->bcs[0].hw.hfc.send = NULL;
+	cs->bcs[1].hw.hfc.send = NULL;
+	cs->hw.hfcD.bfifosize = 1024 + 512;
+	cs->hw.hfcD.dfifosize = 512;
+	cs->ph_state = 0;
+	cs->hw.hfcD.fifo = 255;
+	if (check_region((cs->hw.hfcD.addr), 2)) {
+		printk(KERN_WARNING
+		       "HiSax: %s config port %x-%x already in use\n",
+		       CardType[card->typ],
+		       cs->hw.hfcD.addr,
+		       cs->hw.hfcD.addr + 2);
+		return (0);
+	} else {
+		request_region(cs->hw.hfcD.addr, 2, "teles3c isdn");
+	}
+	/* Teles 16.3c IO ADR is 0x200 | YY0U (YY Bit 15/14 address) */
+	outb(0x00, cs->hw.hfcD.addr);
+	outb(0x56, cs->hw.hfcD.addr | 1);
+	printk(KERN_INFO
+	       "teles3c: defined at 0x%x IRQ %d HZ %d\n",
+	       cs->hw.hfcD.addr,
+	       cs->irq, HZ);
+
+	set_cs_func(cs);
+	cs->hw.hfcD.timer.function = (void *) t163c_Timer;
+	cs->hw.hfcD.timer.data = (long) cs;
+	init_timer(&cs->hw.hfcD.timer);
+	reset_t163c(cs);
+	cs->cardmsg = &t163c_card_msg;
+	return (1);
+}
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/icn/icn.c linux/drivers/isdn/icn/icn.c
--- v2.1.91/linux/drivers/isdn/icn/icn.c	Thu Feb 12 20:56:06 1998
+++ linux/drivers/isdn/icn/icn.c	Wed Apr  1 16:21:02 1998
@@ -1,4 +1,4 @@
-/* $Id: icn.c,v 1.44 1997/03/30 16:51:26 calle Exp $
+/* $Id: icn.c,v 1.49 1998/02/13 11:14:15 keil Exp $
 
  * ISDN low-level module for the ICN active ISDN-Card.
  *
@@ -19,6 +19,25 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: icn.c,v $
+ * Revision 1.49  1998/02/13 11:14:15  keil
+ * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
+ *
+ * Revision 1.48  1997/10/10 15:56:14  fritz
+ * New HL<->LL interface:
+ *   New BSENT callback with nr. of bytes included.
+ *   Sending without ACK.
+ *
+ * Revision 1.47  1997/10/01 09:21:51  fritz
+ * Removed old compatibility stuff for 2.0.X kernels.
+ * From now on, this code is for 2.1.X ONLY!
+ * Old stuff is still in the separate branch.
+ *
+ * Revision 1.46  1997/08/21 22:38:33  fritz
+ * Fixed a typo.
+ *
+ * Revision 1.45  1997/06/21 10:42:06  fritz
+ * Added availability to select leased mode on only one channel.
+ *
  * Revision 1.44  1997/03/30 16:51:26  calle
  * changed calls to copy_from_user/copy_to_user and removed verify_area
  * were possible.
@@ -190,7 +209,7 @@
 #undef MAP_DEBUG
 
 static char
-*revision = "$Revision: 1.44 $";
+*revision = "$Revision: 1.49 $";
 
 static int icn_addcard(int, char *, char *);
 
@@ -205,10 +224,20 @@
 {
 	struct sk_buff_head *queue = &card->spqueue[channel];
 	struct sk_buff *skb;
+	unsigned long flags;
 
 	while ((skb = skb_dequeue(queue)))
 		dev_kfree_skb(skb);
+	save_flags(flags);
+	cli();
+	card->xlen[channel] = 0;
 	card->sndcount[channel] = 0;
+	if (card->xskb[channel]) {
+		card->xskb[channel] = NULL;
+		restore_flags(flags);
+		dev_kfree_skb(card->xskb[channel]);
+	} else
+		restore_flags(flags);
 }
 
 /* Put a value into a shift-register, highest bit first.
@@ -440,12 +469,14 @@
 	struct sk_buff *skb;
 	isdn_ctrl cmd;
 
-	if (!(card->sndcount[channel] ||
+	if (!(card->sndcount[channel] || card->xskb[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]))) {
+		while (sbfree && 
+		       (card->sndcount[channel] ||
+			skb_queue_len(&card->spqueue[channel]) ||
+			card->xskb[channel])) {
 			save_flags(flags);
 			cli();
 			if (card->xmit_lock[channel]) {
@@ -454,7 +485,19 @@
 			}
 			card->xmit_lock[channel]++;
 			restore_flags(flags);
-			skb = skb_dequeue(&card->spqueue[channel]);
+			skb = card->xskb[channel];
+			if (!skb) {
+				skb = skb_dequeue(&card->spqueue[channel]);
+				if (skb) {
+					/* Pop ACK-flag off skb.
+					 * Store length to xlen.
+					 */
+					if (*(skb_pull(skb,1)))
+						card->xlen[channel] = skb->len;
+					else
+						card->xlen[channel] = 0;
+				}
+			}
 			if (!skb)
 				break;
 			if (skb->len > ICN_FRAGSIZE) {
@@ -471,13 +514,22 @@
 			sbnext; /* switch to next buffer        */
 			icn_maprelease_channel(card, mch & 2);
 			if (!skb->len) {
-				dev_kfree_skb(skb);
-				cmd.command = ISDN_STAT_BSENT;
-				cmd.driver = card->myid;
-				cmd.arg = channel;
-				card->interface.statcallb(&cmd);
-			} else
-				skb_queue_head(&card->spqueue[channel], skb);
+				save_flags(flags);
+				cli();
+				if (card->xskb[channel]) {
+					card->xskb[channel] = NULL;
+					restore_flags(flags);
+					dev_kfree_skb(skb);
+				} else
+					restore_flags(flags);
+				if (card->xlen[channel]) {
+					cmd.command = ISDN_STAT_BSENT;
+					cmd.driver = card->myid;
+					cmd.arg = channel;
+					cmd.parm.length = card->xlen[channel];
+					card->interface.statcallb(&cmd);
+				}
+			}
 			card->xmit_lock[channel] = 0;
 			if (!icn_trymaplock_channel(card, mch))
 				break;
@@ -557,12 +609,11 @@
  * This routine is called periodically via timer.
  */
 
-static int
+static void
 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;
 
@@ -575,7 +626,7 @@
 		s++;
 	}
 	if (action == -1)
-		return 0;
+		return;
 	cmd.driver = card->myid;
 	cmd.arg = channel;
 	switch (action) {
@@ -591,7 +642,6 @@
 			cli();
 			card->rcvidx[channel] = 0;
 			restore_flags(flags);
-			dflag |= (channel + 1);
 			break;
 		case 3:
 			{
@@ -645,7 +695,6 @@
 				strncpy(cmd.parm.num, status + 1, sizeof(cmd.parm.num) - 1);
 			break;
 		case 8:
-			dflag = 3;
 			card->flags &= ~ICN_FLAGS_B1ACTIVE;
 			icn_free_queue(card, 0);
 			save_flags(flags);
@@ -675,7 +724,7 @@
 			break;
 	}
 	card->interface.statcallb(&cmd);
-	return dflag;
+	return;
 }
 
 static void
@@ -701,7 +750,6 @@
 	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;
@@ -722,7 +770,7 @@
 				    card->imsg[1] <= '2' && card->imsg[2] == ';') {
 					ch = (card->imsg[1] - '0') - 1;
 					p = &card->imsg[3];
-					dflag |= icn_parse_status(p, ch, card);
+					icn_parse_status(p, ch, card);
 				} else {
 					p = card->imsg;
 					if (!strncmp(p, "DRV1.", 5)) {
@@ -771,10 +819,6 @@
 		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 */
@@ -807,7 +851,7 @@
  */
 
 static int
-icn_sendbuf(int channel, struct sk_buff *skb, icn_card * card)
+icn_sendbuf(int channel, int ack, struct sk_buff *skb, icn_card * card)
 {
 	int len = skb->len;
 	unsigned long flags;
@@ -827,6 +871,10 @@
 		cli();
 		nskb = skb_clone(skb, GFP_ATOMIC);
 		if (nskb) {
+			/* Push ACK flag as one
+			 * byte in front of data.
+			 */
+			*(skb_push(nskb, 1)) = ack?1:0;
 			skb_queue_tail(&card->spqueue[channel], nskb);
 			dev_kfree_skb(skb);
 		} else
@@ -1382,7 +1430,8 @@
 							}
 							current->timeout = jiffies + ICN_BOOT_TIMEOUT1;
 							schedule();
-							sprintf(cbuf, "00;FV2ON\n01;EAZ1\n02;EAZ2\n");
+							sprintf(cbuf, "00;FV2ON\n01;EAZ%c\n02;EAZ%c\n",
+								(a & 1)?'1':'C', (a & 2)?'2':'C');
 							i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
 							printk(KERN_INFO
 							       "icn: (%s) Leased-line mode enabled\n",
@@ -1638,14 +1687,14 @@
 }
 
 static int
-if_sendbuf(int id, int channel, struct sk_buff *skb)
+if_sendbuf(int id, int channel, int ack, 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));
+		return (icn_sendbuf(channel, ack, skb, card));
 	}
 	printk(KERN_ERR
 	       "icn: if_sendbuf called with invalid driverId!\n");
@@ -1669,6 +1718,7 @@
 	}
 	memset((char *) card, 0, sizeof(icn_card));
 	card->port = port;
+	card->interface.hl_hdrlen = 1;
 	card->interface.channels = ICN_BCH;
 	card->interface.maxbufsize = 4000;
 	card->interface.command = if_command;
@@ -1781,11 +1831,7 @@
 	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);
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/icn/icn.h linux/drivers/isdn/icn/icn.h
--- v2.1.91/linux/drivers/isdn/icn/icn.h	Thu Feb 27 10:57:29 1997
+++ linux/drivers/isdn/icn/icn.h	Wed Apr  1 16:21:02 1998
@@ -1,4 +1,4 @@
-/* $Id: icn.h,v 1.26 1997/02/14 12:23:16 fritz Exp $
+/* $Id: icn.h,v 1.28 1997/10/10 15:56:18 fritz Exp $
 
  * ISDN lowlevel-module for the ICN active ISDN-Card.
  *
@@ -19,6 +19,16 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: icn.h,v $
+ * Revision 1.28  1997/10/10 15:56:18  fritz
+ * New HL<->LL interface:
+ *   New BSENT callback with nr. of bytes included.
+ *   Sending without ACK.
+ *
+ * Revision 1.27  1997/10/01 09:21:56  fritz
+ * Removed old compatibility stuff for 2.0.X kernels.
+ * From now on, this code is for 2.1.X ONLY!
+ * Old stuff is still in the separate branch.
+ *
  * Revision 1.26  1997/02/14 12:23:16  fritz
  * Added support for new insmod parameter handling.
  *
@@ -265,6 +275,9 @@
 	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     */
+	int xlen[ICN_BCH];      /* Byte-counters/Flags for sent-ACK */
+	struct sk_buff *xskb[ICN_BCH];
+	                        /* Current transmitted skb          */
 	struct sk_buff_head
 	 spqueue[ICN_BCH];      /* Sendqueue                        */
 	char regname[35];       /* Name used for request_region     */
@@ -303,7 +316,6 @@
 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");
@@ -313,7 +325,6 @@
 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__ */
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/isdn_audio.c linux/drivers/isdn/isdn_audio.c
--- v2.1.91/linux/drivers/isdn/isdn_audio.c	Thu Feb 12 20:56:06 1998
+++ linux/drivers/isdn/isdn_audio.c	Wed Apr  1 16:21:03 1998
@@ -1,4 +1,4 @@
-/* $Id: isdn_audio.c,v 1.8 1997/03/02 14:29:16 fritz Exp $
+/* $Id: isdn_audio.c,v 1.10 1998/02/20 17:09:40 fritz Exp $
 
  * Linux ISDN subsystem, audio conversion and compression (linklevel).
  *
@@ -20,6 +20,14 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_audio.c,v $
+ * Revision 1.10  1998/02/20 17:09:40  fritz
+ * Changes for recent kernels.
+ *
+ * Revision 1.9  1997/10/01 09:20:25  fritz
+ * Removed old compatibility stuff for 2.0.X kernels.
+ * From now on, this code is for 2.1.X ONLY!
+ * Old stuff is still in the separate branch.
+ *
  * Revision 1.8  1997/03/02 14:29:16  fritz
  * More ttyI related cleanup.
  *
@@ -53,7 +61,7 @@
 #include "isdn_audio.h"
 #include "isdn_common.h"
 
-char *isdn_audio_revision = "$Revision: 1.8 $";
+char *isdn_audio_revision = "$Revision: 1.10 $";
 
 /*
  * Misc. lookup-tables.
@@ -531,7 +539,6 @@
 		       info->line);
 		return;
 	}
-	SET_SKB_FREE(skb);
 	result = (int *) skb_put(skb, sizeof(int) * NCOEFF);
 	for (k = 0; k < NCOEFF; k++) {
 		sk = sk1 = sk2 = 0;
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/isdn_cards.c linux/drivers/isdn/isdn_cards.c
--- v2.1.91/linux/drivers/isdn/isdn_cards.c	Thu May 29 21:53:06 1997
+++ linux/drivers/isdn/isdn_cards.c	Wed Apr  1 16:21:03 1998
@@ -1,4 +1,4 @@
-/* $Id: isdn_cards.c,v 1.6 1997/04/23 18:56:03 fritz Exp $
+/* $Id: isdn_cards.c,v 1.7 1998/02/20 17:24:28 fritz Exp $
 
  * Linux ISDN subsystem, initialization for non-modularized drivers.
  *
@@ -19,6 +19,9 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_cards.c,v $
+ * Revision 1.7  1998/02/20 17:24:28  fritz
+ * Added ACT2000 init.
+ *
  * Revision 1.6  1997/04/23 18:56:03  fritz
  * Old Teles driver removed, Changed doc and scripts accordingly.
  *
@@ -81,5 +84,8 @@
 #endif
 	capi_init();
 	capidrv_init();
+#endif
+#if CONFIG_ISDN_DRV_ACT2000
+	act2000_init();
 #endif
 }
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/isdn_common.c linux/drivers/isdn/isdn_common.c
--- v2.1.91/linux/drivers/isdn/isdn_common.c	Tue Mar 10 10:03:32 1998
+++ linux/drivers/isdn/isdn_common.c	Wed Apr  1 16:21:03 1998
@@ -1,4 +1,4 @@
-/* $Id: isdn_common.c,v 1.44 1997/05/27 15:17:23 fritz Exp $
+/* $Id: isdn_common.c,v 1.55 1998/02/23 23:35:32 fritz Exp $
 
  * Linux ISDN subsystem, common used functions (linklevel).
  *
@@ -21,6 +21,55 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_common.c,v $
+ * Revision 1.55  1998/02/23 23:35:32  fritz
+ * Eliminated some compiler warnings.
+ *
+ * Revision 1.54  1998/02/22 19:44:19  fritz
+ * Bugfixes and improvements regarding V.110, V.110 now running.
+ *
+ * Revision 1.53  1998/02/20 17:18:05  fritz
+ * Changes for recent kernels.
+ * Added common stub for sending commands to lowlevel.
+ * Added V.110.
+ *
+ * Revision 1.52  1998/01/31 22:05:57  keil
+ * Lots of changes for X.25 support:
+ * Added generic support for connection-controlling encapsulation protocols
+ * Added support of BHUP status message
+ * Added support for additional p_encap X25IFACE
+ * Added support for kernels >= 2.1.72
+ *
+ * Revision 1.51  1998/01/31 19:17:29  calle
+ * merged changes from and for 2.1.82
+ *
+ * Revision 1.50  1997/12/12 06:12:11  calle
+ * moved EXPORT_SYMBOL(register_isdn) from isdn_syms.c to isdn_common.c
+ *
+ * Revision 1.49  1997/11/06 17:16:52  keil
+ * Sync to 2.1.62 changes
+ *
+ * Revision 1.48  1997/11/02 23:55:50  keil
+ * Andi Kleen's changes for 2.1.60
+ * without it the isdninfo and isdnctrl devices won't work
+ *
+ * Revision 1.47  1997/10/09 21:28:46  fritz
+ * New HL<->LL interface:
+ *   New BSENT callback with nr. of bytes included.
+ *   Sending without ACK.
+ *   New L1 error status (not yet in use).
+ *   Cleaned up obsolete structures.
+ * Implemented Cisco-SLARP.
+ * Changed local net-interface data to be dynamically allocated.
+ * Removed old 2.0 compatibility stuff.
+ *
+ * Revision 1.46  1997/10/01 09:20:27  fritz
+ * Removed old compatibility stuff for 2.0.X kernels.
+ * From now on, this code is for 2.1.X ONLY!
+ * Old stuff is still in the separate branch.
+ *
+ * Revision 1.45  1997/08/21 23:11:41  fritz
+ * Added changes for kernels >= 2.1.45
+ *
  * Revision 1.44  1997/05/27 15:17:23  fritz
  * Added changes for recent 2.1.x kernels:
  *   changed return type of isdn_close
@@ -197,12 +246,9 @@
  */
 
 #include <linux/config.h>
-#define __NO_VERSION__
 #include <linux/module.h>
 #include <linux/version.h>
-#if (LINUX_VERSION_CODE >= 0x020117)
 #include <linux/poll.h>
-#endif
 #include <linux/isdn.h>
 #include "isdn_common.h"
 #include "isdn_tty.h"
@@ -211,6 +257,7 @@
 #ifdef CONFIG_ISDN_AUDIO
 #include "isdn_audio.h"
 #endif
+#include "isdn_v110.h"
 #include "isdn_cards.h"
 
 /* Debugflags */
@@ -218,7 +265,7 @@
 
 isdn_dev *dev = (isdn_dev *) 0;
 
-static char *isdn_revision = "$Revision: 1.44 $";
+static char *isdn_revision = "$Revision: 1.55 $";
 
 extern char *isdn_net_revision;
 extern char *isdn_tty_revision;
@@ -232,6 +279,7 @@
 #else
 static char *isdn_audio_revision = ": none $";
 #endif
+extern char *isdn_v110_revision;
 
 static int isdn_writebuf_stub(int, int, const u_char *, int, int);
 
@@ -260,13 +308,6 @@
 }
 #endif
 
-static __inline void
-isdn_trash_skb(struct sk_buff *skb)
-{
-	SET_SKB_FREE(skb);
-	kfree_skb(skb);
-}
-
 static void
 isdn_free_queue(struct sk_buff_head *queue)
 {
@@ -277,7 +318,7 @@
 	cli();
 	if (skb_queue_len(queue))
 		while ((skb = skb_dequeue(queue)))
-			isdn_trash_skb(skb);
+			dev_kfree_skb(skb);
 	restore_flags(flags);
 }
 
@@ -294,6 +335,7 @@
 static int isdn_timer_cnt1 = 0;
 static int isdn_timer_cnt2 = 0;
 static int isdn_timer_cnt3 = 0;
+static int isdn_timer_cnt4 = 0;
 
 static void
 isdn_timer_funct(ulong dummy)
@@ -323,6 +365,11 @@
 				if (tf & ISDN_TIMER_MODEMRING)
 					isdn_tty_modem_ring();
 			}
+			if (++isdn_timer_cnt4 > ISDN_TIMER_KEEPINT) {
+				isdn_timer_cnt4 = 0;
+				if (tf & ISDN_TIMER_KEEPALIVE)
+					isdn_net_slarp_out();
+			}
 #if (defined CONFIG_ISDN_PPP) && (defined CONFIG_ISDN_MPP)
 			if (tf & ISDN_TIMER_IPPP)
 				isdn_ppp_timer_timeout();
@@ -374,22 +421,71 @@
 	int i;
 
 	if ((i = isdn_dc2minor(di, channel)) == -1) {
-		isdn_trash_skb(skb);
+		dev_kfree_skb(skb);
 		return;
 	}
 	/* Update statistics */
 	dev->ibytes[i] += skb->len;
+	
 	/* First, try to deliver data to network-device */
 	if (isdn_net_rcv_skb(i, skb))
 		return;
+
+	/* V.110 handling
+	 * makes sense for async streams only, so it is
+	 * called after possible net-device delivery.
+	 */
+	if (dev->v110[i]) {
+		atomic_inc(&dev->v110use[i]);
+		skb = isdn_v110_decode(dev->v110[i], skb);
+		atomic_dec(&dev->v110use[i]);
+		if (!skb)
+			return;
+	}
+
 	/* No network-device found, deliver to tty or raw-channel */
-	SET_SKB_FREE(skb);
 	if (skb->len) {
 		if (isdn_tty_rcv_skb(i, di, channel, skb))
 			return;
 		wake_up_interruptible(&dev->drv[di]->rcv_waitq[channel]);
 	} else
-		isdn_trash_skb(skb);
+		dev_kfree_skb(skb);
+}
+
+/*
+ * Intercept command from Linklevel to Lowlevel.
+ * If layer 2 protocol is V.110 and this is not supported by current
+ * lowlevel-driver, use driver's transparent mode and handle V.110 in
+ * linklevel instead.
+ */
+int
+isdn_command(isdn_ctrl *cmd)
+{
+	if (cmd->command == ISDN_CMD_SETL2) {
+			int idx = isdn_dc2minor(cmd->driver, cmd->arg & 255);
+			unsigned long l2prot = (cmd->arg >> 8) & 255;
+			unsigned long features = (dev->drv[cmd->driver]->interface->features
+						 >> ISDN_FEATURE_L2_SHIFT) &
+				ISDN_FEATURE_L2_MASK;
+			unsigned long l2_feature = (1 << l2prot);
+
+			switch (l2prot) {
+				case ISDN_PROTO_L2_V11096:
+				case ISDN_PROTO_L2_V11019:
+				case ISDN_PROTO_L2_V11038:
+						/* If V.110 requested, but not supported by
+						 * HL-driver, set emulator-flag and change
+						 * Layer-2 to transparent
+						 */
+					if (!(features & l2_feature)) {
+						dev->v110emu[idx] = l2prot;
+						cmd->arg = (cmd->arg & 255) |
+								   (ISDN_PROTO_L2_TRANS << 8);
+					} else
+						dev->v110emu[idx] = 0;
+			}
+	}
+	return dev->drv[cmd->driver]->interface->command(cmd);
 }
 
 void
@@ -403,7 +499,7 @@
 	cmd.arg = ch;
 	cmd.command = ISDN_CMD_SETEAZ;
 	cmd.parm.num[0] = '\0';
-	(void) dev->drv[di]->interface->command(&cmd);
+	isdn_command(&cmd);
 }
 
 static int
@@ -424,7 +520,9 @@
 				return -1;
 			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
 				return 0;
-			if (isdn_net_stat_callback(i, c->command))
+			if (isdn_net_stat_callback(i, c))
+				return 0;
+			if (isdn_v110_stat_callback(i, c))
 				return 0;
 			if (isdn_tty_stat_callback(i, c))
 				return 0;
@@ -456,14 +554,14 @@
 				cmd.driver = di;
 				cmd.arg = c->arg;
 				cmd.command = ISDN_CMD_HANGUP;
-				dev->drv[di]->interface->command(&cmd);
+				isdn_command(&cmd);
 				return 0;
 			}
 			/* Try to find a network-interface which will accept incoming call */
 			cmd.driver = di;
 			cmd.arg = c->arg;
 			cmd.command = ISDN_CMD_LOCK;
-			dev->drv[di]->interface->command(&cmd);
+			isdn_command(&cmd);
 			r = isdn_net_find_icall(di, c->arg, i, c->parm.setup);
 			switch (r) {
 				case 0:
@@ -476,7 +574,7 @@
 						cmd.driver = di;
 						cmd.arg = c->arg;
 						cmd.command = ISDN_CMD_HANGUP;
-						dev->drv[di]->interface->command(&cmd);
+						isdn_command(&cmd);
 						retval = 2;
 					}
 					break;
@@ -486,7 +584,7 @@
 					cmd.driver = di;
 					cmd.arg = c->arg;
 					cmd.command = ISDN_CMD_ACCEPTD;
-					dev->drv[di]->interface->command(&cmd);
+					isdn_command(&cmd);
 					retval = 1;
 					break;
 				case 2:	/* For calling back, first reject incoming call ... */
@@ -496,7 +594,7 @@
 					cmd.driver = di;
 					cmd.arg = c->arg;
 					cmd.command = ISDN_CMD_HANGUP;
-					dev->drv[di]->interface->command(&cmd);
+					isdn_command(&cmd);
 					if (r == 3)
 						break;
 					/* Fall through */
@@ -509,7 +607,7 @@
 				cmd.driver = di;
 				cmd.arg = c->arg;
 				cmd.command = ISDN_CMD_UNLOCK;
-				dev->drv[di]->interface->command(&cmd);
+				isdn_command(&cmd);
 			}
 			return retval;
 			break;
@@ -522,7 +620,8 @@
 			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
 				return 0;
 			if (strcmp(c->parm.num, "0"))
-				isdn_net_stat_callback(i, c->command);
+				isdn_net_stat_callback(i, c);
+			isdn_tty_stat_callback(i, c);
 			break;
 		case ISDN_STAT_CAUSE:
 #ifdef ISDN_DEBUG_STATCALLB
@@ -541,14 +640,15 @@
 			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
 				return 0;
 			/* Find any net-device, waiting for D-channel setup */
-			if (isdn_net_stat_callback(i, c->command))
+			if (isdn_net_stat_callback(i, c))
 				break;
+			isdn_v110_stat_callback(i, c);
 			/* Find any ttyI, waiting for D-channel setup */
 			if (isdn_tty_stat_callback(i, c)) {
 				cmd.driver = di;
 				cmd.arg = c->arg;
 				cmd.command = ISDN_CMD_ACCEPTB;
-				dev->drv[di]->interface->command(&cmd);
+				isdn_command(&cmd);
 				break;
 			}
 			break;
@@ -563,8 +663,9 @@
 			dev->drv[di]->flags &= ~(1 << (c->arg));
 			isdn_info_update();
 			/* Signal hangup to network-devices */
-			if (isdn_net_stat_callback(i, c->command))
+			if (isdn_net_stat_callback(i, c))
 				break;
+			isdn_v110_stat_callback(i, c);
 			if (isdn_tty_stat_callback(i, c))
 				break;
 			break;
@@ -579,8 +680,9 @@
 				return 0;
 			dev->drv[di]->flags |= (1 << (c->arg));
 			isdn_info_update();
-			if (isdn_net_stat_callback(i, c->command))
+			if (isdn_net_stat_callback(i, c))
 				break;
+			isdn_v110_stat_callback(i, c);
 			if (isdn_tty_stat_callback(i, c))
 				break;
 			break;
@@ -594,6 +696,12 @@
 				return 0;
 			dev->drv[di]->flags &= ~(1 << (c->arg));
 			isdn_info_update();
+#ifdef CONFIG_ISDN_X25
+			/* Signal hangup to network-devices */
+			if (isdn_net_stat_callback(i, c))
+				break;
+#endif
+			isdn_v110_stat_callback(i, c);
 			if (isdn_tty_stat_callback(i, c))
 				break;
 			break;
@@ -605,7 +713,7 @@
 #endif
 			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
 				return 0;
-			if (isdn_net_stat_callback(i, c->command))
+			if (isdn_net_stat_callback(i, c))
 				break;
 			if (isdn_tty_stat_callback(i, c))
 				break;
@@ -636,6 +744,8 @@
 			isdn_info_update();
 			restore_flags(flags);
 			return 0;
+		case ISDN_STAT_L1ERR:
+			break;
 		default:
 			return -1;
 	}
@@ -752,7 +862,7 @@
 			ISDN_AUDIO_SKB_LOCK(skb) = 0;
 #endif
 			skb = skb_dequeue(&dev->drv[di]->rpqueue[channel]);
-			isdn_trash_skb(skb);
+			dev_kfree_skb(skb);
 		} else {
 			/* Not yet emptied this buff, so it
 			 * must stay in the queue, for further calls
@@ -848,8 +958,8 @@
 	wake_up_interruptible(&(dev->info_waitq));
 }
 
-static RWTYPE
-isdn_read(struct file *file, char *buf, RWARG count, loff_t *off)
+static ssize_t
+isdn_read(struct file *file, char *buf, size_t count, loff_t * off)
 {
 	uint minor = MINOR(file->f_dentry->d_inode->i_rdev);
 	int len = 0;
@@ -857,6 +967,9 @@
 	int drvidx;
 	int chidx;
 
+	if (off != &file->f_pos)
+		return -ESPIPE;
+
 	if (minor == ISDN_MINOR_STATUS) {
 		char *p;
 		if (!file->private_data) {
@@ -922,18 +1035,22 @@
 	return -ENODEV;
 }
 
-static LSTYPE isdn_lseek(struct file *file, LSARG offset, int orig)
+static loff_t
+isdn_lseek(struct file *file, loff_t offset, int orig)
 {
 	return -ESPIPE;
 }
 
-static RWTYPE
-isdn_write(struct file *file, const char *buf, RWARG count, loff_t * off)
+static ssize_t
+isdn_write(struct file *file, const char *buf, size_t count, loff_t * off)
 {
 	uint minor = MINOR(file->f_dentry->d_inode->i_rdev);
 	int drvidx;
 	int chidx;
 
+	if (off != &file->f_pos)
+		return -ESPIPE;
+
 	if (minor == ISDN_MINOR_STATUS)
 		return -EPERM;
 	if (!dev->drivers)
@@ -972,41 +1089,6 @@
 	return -ENODEV;
 }
 
-#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);
-
-	if (minor == ISDN_MINOR_STATUS) {
-		if (file->private_data)
-			return 1;
-		else {
-			if (st)
-				select_wait(&(dev->info_waitq), st);
-			return 0;
-		}
-	}
-	if (minor >= ISDN_MINOR_CTRL && minor <= ISDN_MINOR_CTRLMAX) {
-		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;
-	}
-#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)
 {
@@ -1041,7 +1123,6 @@
 	printk(KERN_ERR "isdn_common: isdn_poll 2 -> what the hell\n");
 	return POLLERR;
 }
-#endif
 
 static int
 isdn_set_allcfg(char *src)
@@ -1055,7 +1136,7 @@
 	if ((ret = isdn_net_rmall()))
 		return ret;
 	if ((ret = copy_from_user((char *) &i, src, sizeof(int))))
-		return ret;
+		 return ret;
 	save_flags(flags);
 	cli();
 	src += sizeof(int);
@@ -1082,7 +1163,7 @@
 				restore_flags(flags);
 				return ret;
 			}
-			GET_USER(phone.phone[phone_len], src++);
+			get_user(phone.phone[phone_len], src++);
 			if ((phone.phone[phone_len] == ' ') ||
 			    (phone.phone[phone_len] == '\0')) {
 				if (phone_len) {
@@ -1122,28 +1203,30 @@
 	cli();
 	p = dev->netdev;
 	while (p) {
+		isdn_net_local *lp = p->local;
+
 		if ((ret = verify_area(VERIFY_WRITE, (void *) dest, sizeof(cfg) + 200))) {
 			restore_flags(flags);
 			return ret;
 		}
-		strcpy(cfg.eaz, p->local.msn);
-		cfg.exclusive = p->local.exclusive;
-		if (p->local.pre_device >= 0) {
-			sprintf(cfg.drvid, "%s,%d", dev->drvid[p->local.pre_device],
-				p->local.pre_channel);
+		strcpy(cfg.eaz, lp->msn);
+		cfg.exclusive = lp->exclusive;
+		if (lp->pre_device >= 0) {
+			sprintf(cfg.drvid, "%s,%d", dev->drvid[lp->pre_device],
+				lp->pre_channel);
 		} else
 			cfg.drvid[0] = '\0';
-		cfg.onhtime = p->local.onhtime;
-		cfg.charge = p->local.charge;
-		cfg.l2_proto = p->local.l2_proto;
-		cfg.l3_proto = p->local.l3_proto;
-		cfg.p_encap = p->local.p_encap;
-		cfg.secure = (p->local.flags & ISDN_NET_SECURE) ? 1 : 0;
-		cfg.callback = (p->local.flags & ISDN_NET_CALLBACK) ? 1 : 0;
-		cfg.chargehup = (p->local.hupflags & ISDN_CHARGEHUP) ? 1 : 0;
-		cfg.ihup = (p->local.hupflags & ISDN_INHUP) ? 1 : 0;
-		cfg.chargeint = p->local.chargeint;
-		if (copy_to_user(dest, p->local.name, 10)) {
+		cfg.onhtime = lp->onhtime;
+		cfg.charge = lp->charge;
+		cfg.l2_proto = lp->l2_proto;
+		cfg.l3_proto = lp->l3_proto;
+		cfg.p_encap = lp->p_encap;
+		cfg.secure = (lp->flags & ISDN_NET_SECURE) ? 1 : 0;
+		cfg.callback = (lp->flags & ISDN_NET_CALLBACK) ? 1 : 0;
+		cfg.chargehup = (lp->hupflags & ISDN_CHARGEHUP) ? 1 : 0;
+		cfg.ihup = (lp->hupflags & ISDN_INHUP) ? 1 : 0;
+		cfg.chargeint = lp->chargeint;
+		if (copy_to_user(dest, lp->name, 10)) {
 			restore_flags(flags);
 			return -EFAULT;
 		}
@@ -1153,14 +1236,14 @@
 			return -EFAULT;
 		}
 		dest += sizeof(cfg);
-		strcpy(phone.name, p->local.name);
+		strcpy(phone.name, lp->name);
 		phone.outgoing = 0;
 		if ((ret = isdn_net_getphones(&phone, dest)) < 0) {
 			restore_flags(flags);
 			return ret;
 		} else
 			dest += ret;
-		strcpy(phone.name, p->local.name);
+		strcpy(phone.name, lp->name);
 		phone.outgoing = 1;
 		if ((ret = isdn_net_getphones(&phone, dest)) < 0) {
 			restore_flags(flags);
@@ -1343,9 +1426,9 @@
 				/* Force hangup of a network-interface */
 				if (!arg)
 					return -EINVAL;
-			        if ((ret = copy_from_user(name, (char *) arg, sizeof(name))))
-			        	return ret;
-			        return isdn_net_force_hangup(name);
+				if ((ret = copy_from_user(name, (char *) arg, sizeof(name))))
+					return ret;
+				return isdn_net_force_hangup(name);
 				break;
 #endif                          /* CONFIG_NETDEVICES */
 			case IIOCSETVER:
@@ -1366,7 +1449,7 @@
 					int i;
 					char *p;
 					if ((ret = copy_from_user((char *) &iocts, (char *) arg,
-					      sizeof(isdn_ioctl_struct))))
+					     sizeof(isdn_ioctl_struct))))
 						return ret;
 					if (strlen(iocts.drvid)) {
 						if ((p = strchr(iocts.drvid, ',')))
@@ -1440,7 +1523,7 @@
 
 					for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
 						if ((ret = copy_from_user(dev->mdm.info[i].emu.profile, p,
-						      ISDN_MODEM_ANZREG)))
+						     ISDN_MODEM_ANZREG)))
 							return ret;
 						p += ISDN_MODEM_ANZREG;
 						if ((ret = copy_from_user(dev->mdm.info[i].emu.pmsn, p, ISDN_MSNLEN)))
@@ -1482,7 +1565,7 @@
 							while (1) {
 								if ((ret = verify_area(VERIFY_READ, p, 1)))
 									return ret;
-								GET_USER(bname[j], p++);
+								get_user(bname[j], p++);
 								switch (bname[j]) {
 									case '\0':
 										loop = 0;
@@ -1554,7 +1637,7 @@
 					c.command = ISDN_CMD_IOCTL;
 					c.arg = cmd;
 					memcpy(c.parm.num, (char *) &iocts.arg, sizeof(ulong));
-					ret = dev->drv[drvidx]->interface->command(&c);
+					ret = isdn_command(&c);
 					memcpy((char *) &iocts.arg, c.parm.num, sizeof(ulong));
 					if ((copy_to_user((char *) arg, &iocts, sizeof(isdn_ioctl_struct))))
 						return -EFAULT;
@@ -1616,7 +1699,7 @@
 			return -ENODEV;
 		c.command = ISDN_CMD_LOCK;
 		c.driver = drvidx;
-		(void) dev->drv[drvidx]->interface->command(&c);
+		isdn_command(&c);
 		MOD_INC_USE_COUNT;
 		return 0;
 	}
@@ -1627,7 +1710,7 @@
 		c.command = ISDN_CMD_LOCK;
 		c.driver = drvidx;
 		MOD_INC_USE_COUNT;
-		(void) dev->drv[drvidx]->interface->command(&c);
+		isdn_command(&c);
 		return 0;
 	}
 #ifdef CONFIG_ISDN_PPP
@@ -1641,7 +1724,7 @@
 	return -ENODEV;
 }
 
-static CLOSETYPE
+static int
 isdn_close(struct inode *ino, struct file *filep)
 {
 	uint minor = MINOR(ino->i_rdev);
@@ -1659,39 +1742,39 @@
 				else
 					dev->infochain = (infostruct *) (p->next);
 				kfree(p);
-				return CLOSEVAL;
+				return 0;
 			}
 			q = p;
 			p = (infostruct *) (p->next);
 		}
 		printk(KERN_WARNING "isdn: No private data while closing isdnctrl\n");
-		return CLOSEVAL;
+		return 0;
 	}
 	if (minor < ISDN_MINOR_CTRL) {
 		drvidx = isdn_minor2drv(minor);
 		if (drvidx < 0)
-			return CLOSEVAL;
+			return 0;
 		c.command = ISDN_CMD_UNLOCK;
 		c.driver = drvidx;
-		(void) dev->drv[drvidx]->interface->command(&c);
-		return CLOSEVAL;
+		isdn_command(&c);
+		return 0;
 	}
 	if (minor <= ISDN_MINOR_CTRLMAX) {
 		drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
 		if (drvidx < 0)
-			return CLOSEVAL;
+			return 0;
 		if (dev->profd == current)
 			dev->profd = NULL;
 		c.command = ISDN_CMD_UNLOCK;
 		c.driver = drvidx;
-		(void) dev->drv[drvidx]->interface->command(&c);
-		return CLOSEVAL;
+		isdn_command(&c);
+		return 0;
 	}
 #ifdef CONFIG_ISDN_PPP
 	if (minor <= ISDN_MINOR_PPPMAX)
 		isdn_ppp_release(minor - ISDN_MINOR_PPP, filep);
 #endif
-	return CLOSEVAL;
+	return 0;
 }
 
 static struct file_operations isdn_fops =
@@ -1700,11 +1783,7 @@
 	isdn_read,
 	isdn_write,
 	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,
@@ -1731,6 +1810,8 @@
  * Find an unused ISDN-channel, whose feature-flags match the
  * given L2- and L3-protocols.
  */
+#define L2V (~(ISDN_FEATURE_L2_V11096|ISDN_FEATURE_L2_V11019|ISDN_FEATURE_L2_V11038))
+
 int
 isdn_get_free_channel(int usage, int l2_proto, int l3_proto, int pre_dev
 		      ,int pre_chan)
@@ -1738,11 +1819,18 @@
 	int i;
 	ulong flags;
 	ulong features;
+	ulong vfeatures;
 	isdn_ctrl cmd;
 
 	save_flags(flags);
 	cli();
-	features = (1 << l2_proto) | (0x100 << l3_proto);
+	features = ((1 << l2_proto) | (0x10000 << l3_proto));
+	vfeatures = (((1 << l2_proto) | (0x10000 << l3_proto)) &
+		     ~(ISDN_FEATURE_L2_V11096|ISDN_FEATURE_L2_V11019|ISDN_FEATURE_L2_V11038));
+	/* If Layer-2 protocol is V.110, accept drivers with
+	 * transparent feature even if these don't support V.110
+	 * because we can emulate this in linklevel.
+	 */
 	for (i = 0; i < ISDN_MAX_CHANNELS; i++)
 		if (USG_NONE(dev->usage[i]) &&
 		    (dev->drvmap[i] != -1)) {
@@ -1751,7 +1839,9 @@
 			((pre_dev != d) || (pre_chan != dev->chanmap[i])))
 				continue;
 			if ((dev->drv[d]->running)) {
-				if ((dev->drv[d]->interface->features & features) == features) {
+				if (((dev->drv[d]->interface->features & features) == features) ||
+				    (((dev->drv[d]->interface->features & vfeatures) == vfeatures) &&
+				     (dev->drv[d]->interface->features & ISDN_FEATURE_L2_TRANS))) {
 					if ((pre_dev < 0) || (pre_chan < 0)) {
 						dev->usage[i] &= ISDN_USAGE_EXCLUSIVE;
 						dev->usage[i] |= usage;
@@ -1759,7 +1849,7 @@
 						cmd.driver = d;
 						cmd.arg = 0;
 						cmd.command = ISDN_CMD_LOCK;
-						(void) dev->drv[d]->interface->command(&cmd);
+						isdn_command(&cmd);
 						restore_flags(flags);
 						return i;
 					} else {
@@ -1770,7 +1860,7 @@
 							cmd.driver = d;
 							cmd.arg = 0;
 							cmd.command = ISDN_CMD_LOCK;
-							(void) dev->drv[d]->interface->command(&cmd);
+							isdn_command(&cmd);
 							restore_flags(flags);
 							return i;
 						}
@@ -1808,7 +1898,7 @@
 			cmd.arg = ch;
 			cmd.command = ISDN_CMD_UNLOCK;
 			restore_flags(flags);
-			(void) dev->drv[di]->interface->command(&cmd);
+			isdn_command(&cmd);
 			return;
 		}
 	restore_flags(flags);
@@ -1837,31 +1927,6 @@
 }
 
 /*
- * receive callback handler for drivers not supporting sk_buff's.
- * Parameters:
- *
- * di      = Driver-Index.
- * channel = Number of B-Channel (0...)
- * buf     = pointer to packet-data
- * len     = Length of packet-data
- *
- */
-static void
-isdn_receive_callback(int drvidx, int chan, u_char * buf, int len)
-{
-	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");
-}
-
-/*
  *  writebuf replacement for SKB_ABLE drivers
  */
 static int
@@ -1869,60 +1934,69 @@
 		   int user)
 {
 	int ret;
+	int hl = dev->drv[drvidx]->interface->hl_hdrlen;
+	struct sk_buff *skb = alloc_skb(hl + len, GFP_ATOMIC);
 
-	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);
+	if (!skb)
+		return 0;
+	skb_reserve(skb, hl);
+	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);
-	}
+	ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, 1, skb);
+	if (ret <= 0)
+		dev_kfree_skb(skb);
 	if (ret > 0)
 		dev->obytes[isdn_dc2minor(drvidx, chan)] += ret;
 	return ret;
 }
 
 /*
- * writebuf_skb replacement for NON SKB_ABLE drivers
- * If lowlevel-device does not support supports skbufs, use
- * standard send-routine, else sind directly.
- *
  * Return: length of data on success, -ERRcode on failure.
  */
-
 int
-isdn_writebuf_skb_stub(int drvidx, int chan, struct sk_buff *skb)
+isdn_writebuf_skb_stub(int drvidx, int chan, int ack, struct sk_buff *skb)
 {
 	int ret;
-	int len = skb->len;     /* skb pointer no longer valid after free */
-
-	if (dev->drv[drvidx]->interface->writebuf_skb)
-		ret = dev->drv[drvidx]->interface->
-		    writebuf_skb(drvidx, chan, skb);
-	else {
-		if ((ret = dev->drv[drvidx]->interface->
-		  writebuf(drvidx, chan, skb->data, skb->len, 0)) == len)
+	struct sk_buff *nskb = NULL;
+	int v110_ret = skb->len;
+	int idx = isdn_dc2minor(drvidx, chan);
+
+	if (dev->v110[idx]) {
+		atomic_inc(&dev->v110use[idx]);
+		nskb = isdn_v110_encode(dev->v110[idx], skb);
+		atomic_dec(&dev->v110use[idx]);
+		if (!nskb)
+			return 0;
+		v110_ret = *((int *)nskb->data);
+		skb_pull(nskb, sizeof(int));
+		if (!nskb->len) {
+			dev_kfree_skb(nskb);
 			dev_kfree_skb(skb);
-	}
-	if (ret > 0)
-		dev->obytes[isdn_dc2minor(drvidx, chan)] += len;
+			return v110_ret;
+		}
+		/* V.110 must always be acknowledged */
+		ack = 1;
+		ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, ack, nskb);
+	} else
+		ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, ack, skb);
+	if (ret > 0) {
+		dev->obytes[idx] += ret;
+		if (dev->v110[idx]) {
+			atomic_inc(&dev->v110use[idx]);
+			dev->v110[idx]->skbuser++;
+			atomic_dec(&dev->v110use[idx]);
+			dev_kfree_skb(skb);
+			/* For V.110 return unencoded data length */
+			ret = v110_ret;
+			if (ret == skb->len)
+				dev_kfree_skb(skb);
+		}
+	} else
+		if (dev->v110[idx])
+			dev_kfree_skb(nskb);
 	return ret;
 }
 
@@ -1930,6 +2004,8 @@
  * Low-level-driver registration
  */
 
+EXPORT_SYMBOL(register_isdn);
+
 int
 register_isdn(isdn_if * i)
 {
@@ -1951,7 +2027,7 @@
 		       ISDN_MAX_CHANNELS);
 		return 0;
 	}
-	if ((!i->writebuf_skb) && (!i->writebuf)) {
+	if (!i->writebuf_skb) {
 		printk(KERN_WARNING "register_isdn: No write routine given.\n");
 		return 0;
 	}
@@ -2019,7 +2095,6 @@
 	i->channels = drvidx;
 
 	i->rcvcallb_skb = isdn_receive_skb_callback;
-	i->rcvcallb = isdn_receive_callback;
 	i->statcallb = isdn_status_callback;
 	if (!strlen(i->id))
 		sprintf(i->id, "line%d", drvidx);
@@ -2078,11 +2153,7 @@
 isdn_init(void)
 {
 	int i;
-	char irev[50];
-	char trev[50];
-	char nrev[50];
-	char prev[50];
-	char arev[50];
+	char tmprev[50];
 
 	sti();
 	if (!(dev = (isdn_dev *) kmalloc(sizeof(isdn_dev), GFP_KERNEL))) {
@@ -2126,18 +2197,18 @@
 	}
 #endif                          /* CONFIG_ISDN_PPP */
 
-	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);
-	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));
+	strcpy(tmprev, isdn_revision);
+	printk(KERN_NOTICE "ISDN subsystem Rev: %s/", isdn_getrev(tmprev));
+	strcpy(tmprev, isdn_tty_revision);
+	printk("%s/", isdn_getrev(tmprev));
+	strcpy(tmprev, isdn_net_revision);
+	printk("%s/", isdn_getrev(tmprev));
+	strcpy(tmprev, isdn_ppp_revision);
+	printk("%s/", isdn_getrev(tmprev));
+	strcpy(tmprev, isdn_audio_revision);
+	printk("%s/", isdn_getrev(tmprev));
+	strcpy(tmprev, isdn_v110_revision);
+	printk("%s", isdn_getrev(tmprev));
 
 #ifdef MODULE
 	printk(" loaded\n");
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/isdn_common.h linux/drivers/isdn/isdn_common.h
--- v2.1.91/linux/drivers/isdn/isdn_common.h	Thu May 29 21:53:06 1997
+++ linux/drivers/isdn/isdn_common.h	Wed Apr  1 16:21:03 1998
@@ -1,4 +1,4 @@
-/* $Id: isdn_common.h,v 1.6 1997/02/28 02:32:44 fritz Exp $
+/* $Id: isdn_common.h,v 1.9 1998/02/20 17:19:01 fritz Exp $
 
  * header for Linux ISDN subsystem, common used functions and debugging-switches (linklevel).
  *
@@ -21,6 +21,24 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_common.h,v $
+ * Revision 1.9  1998/02/20 17:19:01  fritz
+ * Added common stub for sending commands to lowlevel.
+ *
+ * Revision 1.8  1997/10/09 21:28:49  fritz
+ * New HL<->LL interface:
+ *   New BSENT callback with nr. of bytes included.
+ *   Sending without ACK.
+ *   New L1 error status (not yet in use).
+ *   Cleaned up obsolete structures.
+ * Implemented Cisco-SLARP.
+ * Changed local net-interface data to be dynamically allocated.
+ * Removed old 2.0 compatibility stuff.
+ *
+ * Revision 1.7  1997/10/01 09:20:30  fritz
+ * Removed old compatibility stuff for 2.0.X kernels.
+ * From now on, this code is for 2.1.X ONLY!
+ * Old stuff is still in the separate branch.
+ *
  * Revision 1.6  1997/02/28 02:32:44  fritz
  * Cleanup: Moved some tty related stuff from isdn_common.c
  *          to isdn_tty.c
@@ -61,6 +79,7 @@
 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_command(isdn_ctrl *);
 extern int isdn_dc2minor(int di, int ch);
 extern void isdn_info_update(void);
 extern char *isdn_map_eaz2msn(char *msn, int di);
@@ -69,13 +88,8 @@
 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 isdn_writebuf_skb_stub(int, 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);
 #endif
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/isdn_concap.c linux/drivers/isdn/isdn_concap.c
--- v2.1.91/linux/drivers/isdn/isdn_concap.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/isdn_concap.c	Wed Apr  1 16:21:03 1998
@@ -0,0 +1,108 @@
+/* $Id: isdn_concap.c,v 1.2 1998/01/31 22:49:21 keil Exp $
+ 
+ * Stuff to support the concap_proto by isdn4linux. isdn4linux - specific
+ * stuff goes here. Stuff that depends only on the concap protocol goes to
+ * another -- protocol specific -- source file.
+ *
+ * $Log: isdn_concap.c,v $
+ * Revision 1.2  1998/01/31 22:49:21  keil
+ * correct comments
+ *
+ * Revision 1.1  1998/01/31 22:27:57  keil
+ * New files from Henner Eisen for X.25 support
+ *
+ */
+
+
+#include <linux/isdn.h>
+#include "isdn_x25iface.h"
+#include "isdn_net.h"
+#include <linux/concap.h>
+#include "isdn_concap.h"
+
+/* The declaration of this (or a plublic variant thereof) should really go
+   in linux/isdn.h. But we really need it here (and isdn_ppp, like us, also
+   refers to that private function currently owned by isdn_net.c) */
+extern int isdn_net_force_dial_lp(isdn_net_local *);
+
+
+/* The following set of device service operations are for encapsulation
+   protocols that require for reliable datalink sematics. That means:
+
+   - before any data is to be submitted the connection must explicitly
+     be set up.
+   - after the successful set up of the connection is signalled the
+     connection is considered to be reliably up.
+
+   Auto-dialing ist not compatible with this requirements. Thus, auto-dialing 
+   is completely bypassed.
+
+   It might be possible to implement a (non standardized) datalink protocol
+   that provides a reliable data link service while using some auto dialing
+   mechanism. Such a protocol would need an auxiliary channel (i.e. user-user-
+   signaling on the D-channel) while the B-channel is down.
+   */
+
+
+int isdn_concap_dl_data_req(struct concap_proto *concap, struct sk_buff *skb)
+{
+	int tmp;
+	struct device *ndev = concap -> net_dev;
+	isdn_net_local *lp = (isdn_net_local *) ndev->priv;
+
+	IX25DEBUG( "isdn_concap_dl_data_req: %s \n", concap->net_dev->name);
+	lp->huptimer = 0;
+	tmp=isdn_net_send_skb(ndev, lp, skb);
+	IX25DEBUG( "isdn_concap_dl_data_req: %s : isdn_net_send_skb returned %d\n", concap -> net_dev -> name, tmp);
+	return tmp;
+}
+
+
+int isdn_concap_dl_connect_req(struct concap_proto *concap)
+{
+	struct device *ndev = concap -> net_dev;
+	isdn_net_local *lp = (isdn_net_local *) ndev->priv;
+	int ret;
+	IX25DEBUG( "isdn_concap_dl_connect_req: %s \n", ndev -> name);
+
+	/* dial ... */
+	ret = isdn_net_force_dial_lp( lp );
+	if ( ret ) IX25DEBUG("dialing failed\n");
+	return 0;
+}
+
+int isdn_concap_dl_disconn_req(struct concap_proto *concap)
+{
+	IX25DEBUG( "isdn_concap_dl_disconn_req: %s \n", concap -> net_dev -> name);
+
+	isdn_net_hangup( concap -> net_dev );
+	return 0;
+}
+
+struct concap_device_ops isdn_concap_reliable_dl_dops = {
+	&isdn_concap_dl_data_req,
+	&isdn_concap_dl_connect_req,
+	&isdn_concap_dl_disconn_req
+};
+
+struct concap_device_ops isdn_concap_demand_dial_dops = {
+	NULL, /* set this first entry to something like &isdn_net_start_xmit,
+		 but the entry part of the current isdn_net_start_xmit must be
+		 separated first. */
+	/* no connection control for demand dial semantics */
+	NULL,
+	NULL,
+};
+
+/* The following should better go into a dedicated source file such that
+   this sourcefile does not need to include any protocol specific header
+   files. For now:
+   */
+struct concap_proto * isdn_concap_new( int encap )
+{
+	switch ( encap ) {
+	case ISDN_NET_ENCAP_X25IFACE:
+		return isdn_x25iface_proto_new();
+	}
+	return NULL;
+}
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/isdn_concap.h linux/drivers/isdn/isdn_concap.h
--- v2.1.91/linux/drivers/isdn/isdn_concap.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/isdn_concap.h	Wed Apr  1 16:21:03 1998
@@ -0,0 +1,7 @@
+/* $Id: isdn_concap.h,v 1.2 1998/01/31 22:49:21 keil Exp $
+ */
+extern struct concap_device_ops isdn_concap_reliable_dl_dops;
+extern struct concap_device_ops isdn_concap_demand_dial_dops;
+extern struct concap_proto * isdn_concap_new( int );
+
+
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/isdn_net.c linux/drivers/isdn/isdn_net.c
--- v2.1.91/linux/drivers/isdn/isdn_net.c	Thu Feb 12 20:56:06 1998
+++ linux/drivers/isdn/isdn_net.c	Wed Apr  1 16:21:03 1998
@@ -1,4 +1,4 @@
-/* $Id: isdn_net.c,v 1.44 1997/05/27 15:17:26 fritz Exp $
+/* $Id: isdn_net.c,v 1.55 1998/02/23 19:38:22 fritz Exp $
 
  * Linux ISDN subsystem, network interfaces and related functions (linklevel).
  *
@@ -21,6 +21,54 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_net.c,v $
+ * Revision 1.55  1998/02/23 19:38:22  fritz
+ * Corrected check for modified feature-flags.
+ *
+ * Revision 1.54  1998/02/20 17:15:07  fritz
+ * Changes for recent kernels.
+ * Ugly workaround for adjusting Ethernet frames with recent kernels.
+ * replaced direct calls to lowlevel-driver command by common hook.
+ *
+ * Revision 1.53  1998/01/31 22:05:54  keil
+ * Lots of changes for X.25 support:
+ * Added generic support for connection-controlling encapsulation protocols
+ * Added support of BHUP status message
+ * Added support for additional p_encap X25IFACE
+ * Added support for kernels >= 2.1.72
+ *
+ * Revision 1.52  1998/01/31 19:29:51  calle
+ * Merged changes from and for 2.1.82, not tested only compiled ...
+ *
+ * Revision 1.51  1997/10/09 21:28:50  fritz
+ * New HL<->LL interface:
+ *   New BSENT callback with nr. of bytes included.
+ *   Sending without ACK.
+ *   New L1 error status (not yet in use).
+ *   Cleaned up obsolete structures.
+ * Implemented Cisco-SLARP.
+ * Changed local net-interface data to be dynamically allocated.
+ * Removed old 2.0 compatibility stuff.
+ *
+ * Revision 1.50  1997/10/01 09:20:32  fritz
+ * Removed old compatibility stuff for 2.0.X kernels.
+ * From now on, this code is for 2.1.X ONLY!
+ * Old stuff is still in the separate branch.
+ *
+ * Revision 1.49  1997/08/21 14:38:13  fritz
+ * Bugfix: Did not compile without SyncPPP.
+ *
+ * Revision 1.48  1997/06/22 11:57:15  fritz
+ * Added ability to adjust slave triggerlevel.
+ *
+ * Revision 1.47  1997/06/21 10:52:05  fritz
+ * Removed wrong SET_SKB_FREE in isdn_net_send_skb()
+ *
+ * Revision 1.46  1997/06/17 13:05:24  hipp
+ * Applied Eric's underflow-patches (slightly modified)
+ *
+ * Revision 1.45  1997/06/10 16:24:22  hipp
+ * hard_header changes for syncPPP (now behaves like RAWIP)
+ *
  * Revision 1.44  1997/05/27 15:17:26  fritz
  * Added changes for recent 2.1.x kernels:
  *   changed return type of isdn_close
@@ -196,16 +244,18 @@
 #include <linux/isdn.h>
 #include <net/arp.h>
 #include <net/icmp.h>
-#if (LINUX_VERSION_CODE >= 0x020117)
-#include <linux/poll.h>
+#ifndef DEV_NUMBUFFS
+#include <net/pkt_sched.h>
 #endif
+#include <linux/inetdevice.h>
 #include "isdn_common.h"
 #include "isdn_net.h"
 #ifdef CONFIG_ISDN_PPP
 #include "isdn_ppp.h"
 #endif
-#ifndef DEV_NUMBUFFS
-#include <net/pkt_sched.h>
+#ifdef CONFIG_ISDN_X25
+#include <linux/concap.h>
+#include "isdn_concap.h"
 #endif
 
 /* Prototypes */
@@ -218,7 +268,7 @@
 static void dev_purge_queues(struct device *dev);	/* move this to net/core/dev.c */
 #endif
 
-char *isdn_net_revision = "$Revision: 1.44 $";
+char *isdn_net_revision = "$Revision: 1.55 $";
 
  /*
   * Code for raw-networking over ISDN
@@ -229,22 +279,28 @@
 {
 	printk(KERN_DEBUG "isdn_net: %s: %s, send ICMP\n",
 	       dev->name, reason);
-	icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0
-#if (LINUX_VERSION_CODE < 0x02010f)	/* 2.1.15 */
-		  ,dev
-#endif
-	    );
+	icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0);
 }
 
 static void
 isdn_net_reset(struct device *dev)
 {
+#ifdef CONFIG_ISDN_X25
+	struct concap_device_ops * dops = 
+		( (isdn_net_local *) dev->priv ) -> dops;
+	struct concap_proto * cprot = 
+		( (isdn_net_local *) dev->priv ) -> netdev -> cprot; 
+#endif
 	ulong flags;
 
 	save_flags(flags);
 	cli();                  /* Avoid glitch on writes to CMD regs */
 	dev->interrupt = 0;
 	dev->tbusy = 0;
+#ifdef CONFIG_ISDN_X25
+	if( cprot && cprot -> pops && dops ) 
+		cprot -> pops -> restart ( cprot, dev, dops );
+#endif
 	restore_flags(flags);
 }
 
@@ -254,14 +310,22 @@
 {
 	int i;
 	struct device *p;
+	struct in_device *in_dev;
 
 	isdn_net_reset(dev);
 	dev->start = 1;
-	/* Fill in the MAC-level header. */
+	/* Fill in the MAC-level header (not needed, but for compatibility... */
 	for (i = 0; i < ETH_ALEN - sizeof(u32); i++)
 		dev->dev_addr[i] = 0xfc;
-	memset(&(dev->dev_addr[i]), 0, sizeof(u32));
-
+	if ((in_dev = dev->ip_ptr) != NULL) {
+		/*
+		 *      Any address will do - we take the first
+		 */
+		struct in_ifaddr *ifa = in_dev->ifa_list;
+		if (ifa != NULL)
+			memcpy(dev->dev_addr+2, &ifa->ifa_local, 4);
+	}
+ 
 	/* If this interface has slaves, start them also */
 
 	if ((p = (((isdn_net_local *) dev->priv)->slave))) {
@@ -356,7 +420,7 @@
 
 	anymore = 0;
 	while (p) {
-		isdn_net_local *l = (isdn_net_local *) & (p->local);
+		isdn_net_local *l = p->local;
 		if ((jiffies - last_jiffies) == 0)
 			l->cps = l->transcount;
 		else
@@ -405,18 +469,24 @@
  * Return: 1 = Event handled, 0 = not for us or unknown Event.
  */
 int
-isdn_net_stat_callback(int idx, int cmd)
+isdn_net_stat_callback(int idx, isdn_ctrl *c)
 {
 	isdn_net_dev *p = dev->st_netdev[idx];
-
+	int cmd = c->command;
+	
 	if (p) {
-		isdn_net_local *lp = &(p->local);
+		isdn_net_local *lp = p->local;
+#ifdef CONFIG_ISDN_X25
+		struct concap_proto *cprot = lp -> netdev -> cprot;
+		struct concap_proto_ops *pops = cprot ? cprot -> pops : 0;
+#endif
 		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++;
+					lp->stats.tx_bytes += c->parm.length;
 					if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP && lp->sav_skb) {
 						struct device *mdev;
 						if (lp->master)
@@ -449,6 +519,16 @@
 				break;
 			case ISDN_STAT_DHUP:
 				/* Either D-Channel-hangup or error during dialout */
+#ifdef CONFIG_ISDN_X25
+				/* If we are not connencted then dialing had
+				   failed. If there are generic encap protocol
+				   receiver routines signal the closure of
+				   the link*/
+				
+				if( !(lp->flags & ISDN_NET_CONNECTED) 
+				    && pops && pops -> disconn_ind )
+					pops -> disconn_ind(cprot);
+#endif /* CONFIG_ISDN_X25 */
 				if ((!lp->dialstate) && (lp->flags & ISDN_NET_CONNECTED)) {
 					lp->flags &= ~ISDN_NET_CONNECTED;
 					if (lp->first_skb) {
@@ -475,6 +555,18 @@
 					return 1;
 				}
 				break;
+#ifdef CONFIG_ISDN_X25
+			case ISDN_STAT_BHUP:
+				/* B-Channel-hangup */
+				/* try if there are generic encap protocol
+				   receiver routines and signal the closure of
+				   the link */
+				if( pops  &&  pops -> disconn_ind ){
+						pops -> disconn_ind(cprot);
+						return 1;
+					}
+				break;
+#endif /* CONFIG_ISDN_X25 */
 			case ISDN_STAT_BCONN:
 				/* B-Channel is up */
 				switch (lp->dialstate) {
@@ -492,6 +584,8 @@
 							dev->rx_netdev[idx] = p;
 						lp->dialstate = 0;
 						isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, 1);
+						if (lp->p_encap == ISDN_NET_ENCAP_CISCOHDLCK)
+							isdn_timer_ctrl(ISDN_TIMER_KEEPALIVE, 1);
 						printk(KERN_INFO "isdn_net: %s connected\n", lp->name);
 						/* If first Chargeinfo comes before B-Channel connect,
 						 * we correct the timestamp here.
@@ -504,7 +598,15 @@
 						if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
 							isdn_ppp_wakeup_daemon(lp);
 #endif
+#ifdef CONFIG_ISDN_X25
+						/* try if there are generic concap receiver routines */
+						if( pops )
+							if( pops->connect_ind)
+								pops->connect_ind(cprot);
+
+#endif /* CONFIG_ISDN_X25 */
 						if (lp->first_skb) {
+							
 							if (!(isdn_net_xmit(&p->dev, lp, lp->first_skb)))
 								lp->first_skb = NULL;
 						}
@@ -573,11 +675,13 @@
 	isdn_ctrl cmd;
 
 	while (p) {
+		isdn_net_local *lp = p->local;
+
 #ifdef ISDN_DEBUG_NET_DIAL
-		if (p->local.dialstate)
-			printk(KERN_DEBUG "%s: dialstate=%d\n", p->local.name, p->local.dialstate);
+		if (lp->dialstate)
+			printk(KERN_DEBUG "%s: dialstate=%d\n", lp->name, lp->dialstate);
 #endif
-		switch (p->local.dialstate) {
+		switch (lp->dialstate) {
 			case 0:
 				/* Nothing to do for this interface */
 				break;
@@ -587,132 +691,133 @@
 				 */
 				save_flags(flags);
 				cli();
-				p->local.dial = p->local.phone[1];
+				lp->dial = lp->phone[1];
 				restore_flags(flags);
-				if (!p->local.dial) {
+				if (!lp->dial) {
 					printk(KERN_WARNING "%s: phone number deleted?\n",
-					       p->local.name);
+					       lp->name);
 					isdn_net_hangup(&p->dev);
 					break;
 				}
 				anymore = 1;
-				p->local.dialstate++;
+				lp->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.driver = lp->isdn_device;
+				cmd.arg = lp->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));
+				isdn_command(&cmd);
+				sprintf(cmd.parm.num, "%s", isdn_map_eaz2msn(lp->msn, cmd.driver));
 				cmd.command = ISDN_CMD_SETEAZ;
-				dev->drv[p->local.isdn_device]->interface->command(&cmd);
-				p->local.dialretry = 0;
+				isdn_command(&cmd);
+				lp->dialretry = 0;
 				anymore = 1;
-				p->local.dialstate++;
+				lp->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.
 				 */
-				cmd.driver = p->local.isdn_device;
+				cmd.driver = lp->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.arg = lp->isdn_channel + (lp->l2_proto << 8);
+				isdn_command(&cmd);
+				cmd.driver = lp->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;
+				cmd.arg = lp->isdn_channel + (lp->l3_proto << 8);
+				isdn_command(&cmd);
+				cmd.driver = lp->isdn_device;
+				cmd.arg = lp->isdn_channel;
 				save_flags(flags);
 				cli();
-				if (!p->local.dial) {
+				if (!lp->dial) {
 					restore_flags(flags);
 					printk(KERN_WARNING "%s: phone number deleted?\n",
-					       p->local.name);
+					       lp->name);
 					isdn_net_hangup(&p->dev);
 					break;
 				}
-				if (!strcmp(p->local.dial->num, "LEASED")) {
+				if (!strcmp(lp->dial->num, "LEASED")) {
 					restore_flags(flags);
-					p->local.dialstate = 4;
-					printk(KERN_INFO "%s: Open leased line ...\n", p->local.name);
+					lp->dialstate = 4;
+					printk(KERN_INFO "%s: Open leased line ...\n", lp->name);
 				} else {
-					sprintf(cmd.parm.setup.phone, "%s", p->local.dial->num);
+					sprintf(cmd.parm.setup.phone, "%s", lp->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++;
+					if (!(lp->dial = (isdn_net_phone *) lp->dial->next)) {
+						lp->dial = lp->phone[1];
+						lp->dialretry++;
 					}
 					restore_flags(flags);
+					cmd.driver = lp->isdn_device;
 					cmd.command = ISDN_CMD_DIAL;
 					cmd.parm.setup.si1 = 7;
 					cmd.parm.setup.si2 = 0;
 					sprintf(cmd.parm.setup.eazmsn, "%s",
-						isdn_map_eaz2msn(p->local.msn, cmd.driver));
-					i = isdn_dc2minor(p->local.isdn_device, p->local.isdn_channel);
+						isdn_map_eaz2msn(lp->msn, cmd.driver));
+					i = isdn_dc2minor(lp->isdn_device, lp->isdn_channel);
 					if (i >= 0) {
 						strcpy(dev->num[i], cmd.parm.setup.phone);
 						isdn_info_update();
 					}
-					printk(KERN_INFO "%s: dialing %d %s...\n", p->local.name,
-					       p->local.dialretry - 1, cmd.parm.setup.phone);
-					p->local.dtimer = 0;
+					printk(KERN_INFO "%s: dialing %d %s...\n", lp->name,
+					       lp->dialretry - 1, cmd.parm.setup.phone);
+					lp->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", lp->isdn_device,
+					       lp->isdn_channel);
 #endif
-					dev->drv[p->local.isdn_device]->interface->command(&cmd);
+					isdn_command(&cmd);
 				}
-				p->local.huptimer = 0;
-				p->local.outgoing = 1;
-				if (p->local.chargeint) {
-					p->local.hupflags |= ISDN_HAVECHARGE;
-					p->local.hupflags &= ~ISDN_WAITCHARGE;
+				lp->huptimer = 0;
+				lp->outgoing = 1;
+				if (lp->chargeint) {
+					lp->hupflags |= ISDN_HAVECHARGE;
+					lp->hupflags &= ~ISDN_WAITCHARGE;
 				} else {
-					p->local.hupflags |= ISDN_WAITCHARGE;
-					p->local.hupflags &= ~ISDN_HAVECHARGE;
+					lp->hupflags |= ISDN_WAITCHARGE;
+					lp->hupflags &= ~ISDN_HAVECHARGE;
 				}
 				anymore = 1;
-				p->local.dialstate =
-				    (p->local.cbdelay &&
-				     (p->local.flags & ISDN_NET_CBOUT)) ? 12 : 4;
+				lp->dialstate =
+				    (lp->cbdelay &&
+				     (lp->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;
+				if (lp->dtimer++ > ISDN_TIMER_DTIMEOUT10)
+					if (lp->dialretry < lp->dialmax) {
+						lp->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.driver = lp->isdn_device;
+				cmd.arg = lp->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);
+				lp->dtimer = 0;
+				lp->dialstate++;
+				isdn_command(&cmd);
 				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", lp->dtimer);
 #endif
-				if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT10)
-					p->local.dialstate = 3;
+				if (lp->dtimer++ > ISDN_TIMER_DTIMEOUT10)
+					lp->dialstate = 3;
 				anymore = 1;
 				break;
 			case 7:
@@ -720,69 +825,69 @@
 				 * 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", lp->dtimer);
 #endif
-				cmd.driver = p->local.isdn_device;
+				cmd.driver = lp->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.arg = lp->isdn_channel + (lp->l2_proto << 8);
+				isdn_command(&cmd);
+				cmd.driver = lp->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)
+				cmd.arg = lp->isdn_channel + (lp->l3_proto << 8);
+				isdn_command(&cmd);
+				if (lp->dtimer++ > ISDN_TIMER_DTIMEOUT15)
 					isdn_net_hangup(&p->dev);
 				else {
 					anymore = 1;
-					p->local.dialstate++;
+					lp->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.driver = lp->isdn_device;
+				cmd.arg = lp->isdn_channel;
 				cmd.command = ISDN_CMD_ACCEPTB;
-				dev->drv[p->local.isdn_device]->interface->command(&cmd);
+				isdn_command(&cmd);
 				anymore = 1;
-				p->local.dtimer = 0;
-				p->local.dialstate++;
+				lp->dtimer = 0;
+				lp->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", lp->dtimer);
 #endif
-				if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT10)
+				if (lp->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;
+				if (lp->dtimer++ > lp->cbdelay)
+					lp->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;
+				if (lp->dtimer++ > lp->cbdelay) {
+					printk(KERN_INFO "%s: hangup waiting for callback ...\n", lp->name);
+					lp->dtimer = 0;
+					lp->dialstate = 4;
+					cmd.driver = lp->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);
+					cmd.arg = lp->isdn_channel;
+					isdn_command(&cmd);
+					isdn_all_eaz(lp->isdn_device, lp->isdn_channel);
 				}
 				anymore = 1;
 				break;
 			default:
 				printk(KERN_WARNING "isdn_net: Illegal dialstate %d for device %s\n",
-				       p->local.dialstate, p->local.name);
+				       lp->dialstate, lp->name);
 		}
 		p = (isdn_net_dev *) p->next;
 	}
@@ -797,6 +902,10 @@
 {
 	isdn_net_local *lp = (isdn_net_local *) d->priv;
 	isdn_ctrl cmd;
+#ifdef CONFIG_ISDN_X25
+	struct concap_proto *cprot = lp -> netdev -> cprot;
+	struct concap_proto_ops *pops = cprot ? cprot -> pops : 0;
+#endif
 
 	if (lp->flags & ISDN_NET_CONNECTED) {
 		lp->flags &= ~ISDN_NET_CONNECTED;
@@ -804,10 +913,18 @@
 #ifdef CONFIG_ISDN_PPP
 		isdn_ppp_free(lp);
 #endif
+#ifdef CONFIG_ISDN_X25
+		/* try if there are generic encap protocol
+		   receiver routines and signal the closure of
+		   the link */		
+		if( pops && pops -> disconn_ind )
+		  pops -> disconn_ind(cprot);
+#endif /* CONFIG_ISDN_X25 */
+
 		cmd.driver = lp->isdn_device;
 		cmd.command = ISDN_CMD_HANGUP;
 		cmd.arg = lp->isdn_channel;
-		(void) dev->drv[cmd.driver]->interface->command(&cmd);
+		isdn_command(&cmd);
 		printk(KERN_INFO "%s: Chargesum is %d\n", lp->name, lp->charge);
 		isdn_all_eaz(lp->isdn_device, lp->isdn_channel);
 	}
@@ -820,28 +937,43 @@
 } ip_ports;
 
 static void
-isdn_net_log_packet(u_char * buf, isdn_net_local * lp)
+isdn_net_log_skb(struct sk_buff * skb, isdn_net_local * lp)
 {
-	u_char *p = buf;
-	unsigned short proto = ETH_P_IP;
+	u_char *p = skb->nh.raw; /* hopefully, this was set correctly */
+	unsigned short proto = ntohs(skb->protocol);
 	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;
+	/* This check stolen from 2.1.72 dev_queue_xmit_nit() */ 
+	if (skb->nh.raw < skb->data || skb->nh.raw >= skb->tail) {
+		/* fall back to old isdn_net_log_packet method() */
+		char * buf = skb->data;
+
+		printk(KERN_DEBUG "isdn_net: protocol %04x is buggy, dev %s\n", skb->protocol, lp->name);
+		p = buf;
+		proto = ETH_P_IP;
+		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;
+#ifdef CONFIG_ISDN_PPP
+			case ISDN_NET_ENCAP_SYNCPPP:
+				proto = ntohs(skb->protocol);
+				p = &buf[IPPP_MAX_HEADER];
+				break;
+#endif
+		}
 	}
 	data_ofs = ((p[0] & 15) * 4);
 	switch (proto) {
@@ -891,7 +1023,7 @@
 
 /*
  * Generic routine to send out an skbuf.
- * If lowlevel-device does not support supports skbufs, use
+ * If lowlevel-device does not support support skbufs, use
  * standard send-routine, else send directly.
  *
  * Return: 0 on success, !0 on failure.
@@ -904,14 +1036,13 @@
 	int ret;
 	int len = skb->len;     /* save len */
 
-	ret = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, skb);
+	ret = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, 1, skb);
 	if (ret == len) {
 		lp->transcount += len;
 		clear_bit(0, (void *) &(ndev->tbusy));
 		return 0;
 	}
 	if (ret < 0) {
-		SET_SKB_FREE(skb);
 		dev_kfree_skb(skb);
 		lp->stats.tx_errors++;
 		clear_bit(0, (void *) &(ndev->tbusy));
@@ -945,7 +1076,7 @@
 #endif
 	/* Reset hangup-timeout */
 	lp->huptimer = 0;
-	if (lp->cps > 7000) {
+	if (lp->cps > lp->triggercps) {
 		/* Device overloaded */
 
 		/*
@@ -988,35 +1119,64 @@
 	return ret;
 }
 
+static void
+isdn_net_adjust_hdr(struct sk_buff *skb, struct device *dev)
+{
+	isdn_net_local *lp = (isdn_net_local *) dev->priv;
+	if (!skb)
+		return;
+	if (lp->p_encap == ISDN_NET_ENCAP_ETHER) {
+		ulong pullsize = (ulong)skb->nh.raw - (ulong)skb->data - ETH_HLEN;
+		if (pullsize)
+			skb_pull(skb, pullsize);
+	}
+}
+
 /*
  * Try sending a packet.
  * If this interface isn't connected to a ISDN-Channel, find a free channel,
  * and start dialing.
  */
-int
+static int
 isdn_net_start_xmit(struct sk_buff *skb, struct device *ndev)
 {
 	isdn_net_local *lp = (isdn_net_local *) ndev->priv;
+#ifdef CONFIG_ISDN_X25
+	struct concap_proto * cprot = lp -> netdev -> cprot; 
+#endif
 
 	if (ndev->tbusy) {
 		if (jiffies - ndev->trans_start < (2 * HZ))
 			return 1;
 		if (!lp->dialstate)
 			lp->stats.tx_errors++;
-		ndev->tbusy = 0;
 		ndev->trans_start = jiffies;
 	}
-	if (skb == NULL) {
-		return 0;
-	}
-	/* Avoid timer-based retransmission conflicts. */
-	if (test_and_set_bit(0, (void *) &ndev->tbusy) != 0)
-		printk(KERN_WARNING
-		       "%s: Transmitter access conflict.\n",
-		       ndev->name);
-	else {
-		u_char *buf = skb->data;
+	ndev->tbusy = 1; /* left instead of obsolete test_and_set_bit() */
+#ifdef CONFIG_ISDN_X25
+/* At this point hard_start_xmit() passes control to the encapsulation
+   protocol (if present). 
+   For X.25 auto-dialing is completly bypassed because:
+   - It does not conform with the semantics of a reliable datalink
+     service as needed by X.25 PLP.
+   - I don't want that the interface starts dialing when the network layer
+     sends a message which requests to disconnect the lapb link (or if it
+     sends any other message not resulting in data transmission).
+   Instead, dialing will be initiated by the encapsulation protocol entity
+   when a dl_establish request is received from the upper layer.
+*/
+	if( cprot ) {
+		return  cprot -> pops -> encap_and_xmit ( cprot , skb);
+	} else
+#endif
+	/* auto-dialing xmit function */
+	{
 #ifdef ISDN_DEBUG_NET_DUMP
+		u_char *buf;
+#endif
+		isdn_net_adjust_hdr(skb, ndev);
+#ifdef ISDN_DEBUG_NET_DUMP
+		buf = skb->data;
 		isdn_dumppkt("S:", buf, skb->len, 40);
 #endif
 		if (!(lp->flags & ISDN_NET_CONNECTED)) {
@@ -1033,24 +1193,15 @@
 							   lp->pre_device,
 						 lp->pre_channel)) < 0) {
 					restore_flags(flags);
-#if 0
-					printk(KERN_WARNING
-					       "isdn_net_start_xmit: No channel for %s\n",
-					       ndev->name);
-					/* we probably should drop the skb here and return 0 to omit
-					   'socket destroy delayed' messages */
-					return 1;
-#else
 					isdn_net_unreachable(ndev, skb,
 							   "No channel");
 					dev_kfree_skb(skb);
 					ndev->tbusy = 0;
 					return 0;
-#endif
 				}
 				/* Log packet, which triggered dialing */
 				if (dev->net_verbose)
-					isdn_net_log_packet(buf, lp);
+					isdn_net_log_skb(skb, lp);
 				lp->dialstate = 1;
 				lp->flags |= ISDN_NET_CONNECTED;
 				/* Connect interface with channel */
@@ -1114,12 +1265,26 @@
 isdn_net_close(struct device *dev)
 {
 	struct device *p;
+#ifdef CONFIG_ISDN_X25
+	struct concap_proto * cprot = 
+		( (isdn_net_local *) dev->priv ) -> netdev -> cprot; 
+	/* printk(KERN_DEBUG "isdn_net_close %s\n" , dev-> name ); */
+#endif
 
+#ifdef CONFIG_ISDN_X25
+	if( cprot && cprot -> pops ) cprot -> pops -> close( cprot );
+#endif
 	dev->tbusy = 1;
 	dev->start = 0;
 	if ((p = (((isdn_net_local *) dev->priv)->slave))) {
 		/* If this interface has slaves, stop them also */
 		while (p) {
+#ifdef CONFIG_ISDN_X25
+			cprot = ( (isdn_net_local *) p->priv ) 
+				-> netdev -> cprot; 
+			if( cprot && cprot -> pops ) 
+				cprot -> pops -> close( cprot );
+#endif
 			isdn_net_hangup(p);
 			p->tbusy = 1;
 			p->start = 0;
@@ -1156,6 +1321,7 @@
 	struct ethhdr *eth;
 	unsigned char *rawp;
 
+	skb->mac.raw = skb->data;
 	skb_pull(skb, ETH_HLEN);
 	eth = skb->mac.ethernet;
 
@@ -1170,7 +1336,7 @@
 	 *      so don't forget to remove it.
 	 */
 
-	else if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) {
+	else if (dev->flags & (IFF_PROMISC /*| IFF_ALLMULTI*/)) {
 		if (memcmp(eth->h_dest, dev->dev_addr, ETH_ALEN))
 			skb->pkt_type = PACKET_OTHERHOST;
 	}
@@ -1193,6 +1359,97 @@
 	return htons(ETH_P_802_2);
 }
 
+static void
+isdn_net_slarp_send(isdn_net_local *lp, int is_reply)
+{
+	unsigned short hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen;
+	struct sk_buff *skb = dev_alloc_skb(hl + sizeof(cisco_hdr) + sizeof(cisco_slarp));
+	unsigned long t = (jiffies / HZ * 1000000);
+	int len;
+	cisco_hdr *ch;
+	cisco_slarp *s;
+	
+	if (!skb) {
+		printk(KERN_WARNING
+		       "%s: Could not allocate SLARP reply\n", lp->name);
+		return;
+	}
+	skb_reserve(skb, hl);
+	ch = (cisco_hdr *)skb_put(skb, sizeof(cisco_hdr));
+	ch->addr = CISCO_ADDR_UNICAST;
+	ch->ctrl = 0;
+	ch->type = htons(CISCO_TYPE_SLARP);
+	s = (cisco_slarp *)skb_put(skb, sizeof(cisco_slarp));
+	if (is_reply) {
+		s->code = htonl(CISCO_SLARP_REPLY);
+ 		memset(&s->slarp.reply.ifaddr, 0, sizeof(__u32));
+		memset(&s->slarp.reply.netmask, 0, sizeof(__u32));
+	} else {
+		lp->cisco_myseq++;
+		s->code = htonl(CISCO_SLARP_KEEPALIVE);
+		s->slarp.keepalive.my_seq = htonl(lp->cisco_myseq);
+		s->slarp.keepalive.your_seq = htonl(lp->cisco_yourseq);
+	}
+	s->rel = 0xffff;
+	s->t1 = t >> 16;
+	s->t0 = t & 0xffff;
+	len = skb->len;
+	if (isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, 0, skb) != len)
+		dev_kfree_skb(skb);
+}
+
+static void
+isdn_net_slarp_in(isdn_net_local *lp, struct sk_buff *skb)
+{
+	cisco_slarp *s = (cisco_slarp *)skb->data;
+
+	switch (ntohl(s->code)) {
+		case CISCO_SLARP_REQUEST:
+			isdn_net_slarp_send(lp, 1);
+			break;
+		case CISCO_SLARP_REPLY:
+			/* Ignore replies */
+			break;
+		case CISCO_SLARP_KEEPALIVE:
+			lp->cisco_yourseq = s->slarp.keepalive.my_seq;
+			if (ntohl(s->slarp.keepalive.my_seq == lp->cisco_myseq)) {
+				if (lp->cisco_loop++ == 2) {
+					printk(KERN_WARNING "%s: Keepalive Loop\n",
+					       lp->name);
+					lp->cisco_myseq ^= jiffies;
+				}
+			} else
+				lp->cisco_loop = 0;
+			break;
+	}
+	kfree_skb(skb);
+}
+
+/*
+ * Called every 10 sec. via timer-interrupt if
+ * any network-interface has Cisco-Keepalive-Encapsulation
+ * and is online.
+ * Send Keepalive-Packet and re-schedule.
+ */
+void
+isdn_net_slarp_out(void)
+{
+	isdn_net_dev *p = dev->netdev;
+	int anymore = 0;
+
+	while (p) {
+		isdn_net_local *l = p->local;
+		if ((l->p_encap == ISDN_NET_ENCAP_CISCOHDLCK) &&
+		    (l->flags & ISDN_NET_CONNECTED) &&
+		    (!l->dialstate)                           ) {
+			anymore = 1;
+			isdn_net_slarp_send(l, 0);
+		}
+		p = (isdn_net_dev *) p->next;
+	}
+	isdn_timer_ctrl(ISDN_TIMER_KEEPALIVE, anymore);
+}
+
 /*
  * Got a packet from ISDN-Channel.
  */
@@ -1200,23 +1457,19 @@
 isdn_net_receive(struct device *ndev, struct sk_buff *skb)
 {
 	isdn_net_local *lp = (isdn_net_local *) ndev->priv;
-#ifdef CONFIG_ISDN_PPP
 	isdn_net_local *olp = lp;	/* original 'lp' */
+#ifdef CONFIG_ISDN_PPP
 	int proto = PPP_PROTOCOL(skb->data);
 #endif
+#ifdef CONFIG_ISDN_X25
+	struct concap_proto *cprot = lp -> netdev -> cprot;
+#endif
+	cisco_hdr *ch;
 
 	lp->transcount += skb->len;
-	lp->stats.rx_packets++;
-#ifdef CONFIG_ISDN_PPP
-	/*
-	 * If encapsulation is syncppp, don't reset
-	 * huptimer on LCP packets.
-	 */
-	if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP ||
-	    (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP && proto != PPP_LCP))
-#endif
-		lp->huptimer = 0;
 
+	lp->stats.rx_packets++;
+	lp->stats.rx_bytes += skb->len;
 	if (lp->master) {
 		/* Bundling: If device is a slave-device, deliver to master, also
 		 * handle master's statistics and hangup-timeout
@@ -1224,16 +1477,9 @@
 		ndev = lp->master;
 		lp = (isdn_net_local *) ndev->priv;
 		lp->stats.rx_packets++;
-#ifdef CONFIG_ISDN_PPP
-		/*
-		 * If encapsulation is syncppp, don't reset
-		 * huptimer on LCP packets.
-		 */
-		if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP ||
-		    (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP && proto != PPP_LCP))
-#endif
-			lp->huptimer = 0;
+		lp->stats.rx_bytes += skb->len;
 	}
+
 	skb->dev = ndev;
 	skb->pkt_type = PACKET_HOST;
 	skb->mac.raw = skb->data;
@@ -1243,22 +1489,61 @@
 	switch (lp->p_encap) {
 		case ISDN_NET_ENCAP_ETHER:
 			/* Ethernet over ISDN */
+			olp->huptimer = 0;
+			lp->huptimer = 0;
 			skb->protocol = isdn_net_type_trans(skb, ndev);
 			break;
 		case ISDN_NET_ENCAP_UIHDLC:
 			/* HDLC with UI-frame (for ispa with -h1 option) */
+			olp->huptimer = 0;
+			lp->huptimer = 0;
 			skb_pull(skb, 2);
 			/* Fall through */
 		case ISDN_NET_ENCAP_RAWIP:
 			/* RAW-IP without MAC-Header */
+			olp->huptimer = 0;
+			lp->huptimer = 0;
 			skb->protocol = htons(ETH_P_IP);
 			break;
+		case ISDN_NET_ENCAP_CISCOHDLCK:
+			ch = (cisco_hdr *)skb->data;
+			if ((ch->addr != CISCO_ADDR_UNICAST) &&
+			    (ch->addr != CISCO_ADDR_BROADCAST)  ) {
+				printk(KERN_WARNING "%s: Unknown Cisco addr 0x%02x\n",
+				       lp->name, ch->addr);
+				kfree_skb(skb);
+				return;
+			}
+			if (ch->ctrl != 0) {
+				printk(KERN_WARNING "%s: Unknown Cisco ctrl 0x%02x\n",
+				       lp->name, ch->ctrl);
+				kfree_skb(skb);
+				return;
+			}
+			switch (ntohs(ch->type)) {
+				case CISCO_TYPE_INET:
+					skb_pull(skb, 4);
+					skb->protocol = htons(ETH_P_IP);
+					break;
+				case CISCO_TYPE_SLARP:
+					skb_pull(skb, 4);
+					isdn_net_slarp_in(olp, skb);
+					return;
+				default:
+					printk(KERN_WARNING "%s: Unknown Cisco type 0x%04x\n",
+					       lp->name, ch->type);
+					kfree_skb(skb);
+					return;
+			}
+			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 */
+			olp->huptimer = 0;
+			lp->huptimer = 0;
 			skb->protocol = *(unsigned short *) &(skb->data[0]);
 			skb_pull(skb, 2);
 			if (*(unsigned short *) skb->data == 0xFFFF)
@@ -1266,10 +1551,26 @@
 			break;
 #ifdef CONFIG_ISDN_PPP
 		case ISDN_NET_ENCAP_SYNCPPP:
+			/*
+			 * If encapsulation is syncppp, don't reset
+			 * huptimer on LCP packets.
+			 */
+			if (proto != PPP_LCP) {
+				olp->huptimer = 0;
+				lp->huptimer = 0;
+			}
 			isdn_ppp_receive(lp->netdev, olp, skb);
 			return;
 #endif
 		default:
+#ifdef CONFIG_ISDN_X25
+		  /* try if there are generic sync_device receiver routines */
+			if(cprot) if(cprot -> pops) 
+				if( cprot -> pops -> data_ind){
+					cprot -> pops -> data_ind(cprot,skb);
+					return;
+				};
+#endif /* CONFIG_ISDN_X25 */
 			printk(KERN_WARNING "%s: unknown encapsulation, dropping\n",
 			       lp->name);
 			kfree_skb(skb);
@@ -1290,7 +1591,7 @@
 	isdn_net_dev *p = dev->rx_netdev[idx];
 
 	if (p) {
-		isdn_net_local *lp = &p->local;
+		isdn_net_local *lp = p->local;
 		if ((lp->flags & ISDN_NET_CONNECTED) &&
 		    (!lp->dialstate)) {
 			isdn_net_receive(&p->dev, skb);
@@ -1329,15 +1630,15 @@
 	 * Anyway, the loopback-device should never use this function...
 	 */
 
-	if (dev->flags & IFF_LOOPBACK) {
+	if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) {
 		memset(eth->h_dest, 0, dev->addr_len);
-		return (dev->hard_header_len);
+		return ETH_HLEN /*(dev->hard_header_len)*/;
 	}
 	if (daddr) {
 		memcpy(eth->h_dest, daddr, dev->addr_len);
-		return dev->hard_header_len;
+		return ETH_HLEN /*dev->hard_header_len*/;
 	}
-	return -dev->hard_header_len;
+	return -ETH_HLEN /*dev->hard_header_len*/;
 }
 
 /*
@@ -1356,6 +1657,13 @@
 		case ISDN_NET_ENCAP_ETHER:
 			len = my_eth_header(skb, dev, type, daddr, saddr, plen);
 			break;
+#ifdef CONFIG_ISDN_PPP
+		case ISDN_NET_ENCAP_SYNCPPP:
+			/* stick on a fake header to keep fragmentation code happy. */
+			len = IPPP_MAX_HEADER;
+			skb_push(skb,len);
+			break;
+#endif
 		case ISDN_NET_ENCAP_RAWIP:
 			printk(KERN_WARNING "isdn_net_header called with RAW_IP!\n");
 			len = 0;
@@ -1377,43 +1685,21 @@
 			*((ushort *) & skb->data[2]) = htons(type);
 			len = 4;
 			break;
+#ifdef CONFIG_ISDN_X25
+		default:
+		  /* try if there are generic concap protocol routines */
+			if( lp-> netdev -> cprot ){
+				printk(KERN_WARNING "isdn_net_header called with concap_proto!\n");
+				len = 0;
+				break;
+			}
+			break;
+#endif /* CONFIG_ISDN_X25 */
 	}
 	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)
-{
-	isdn_net_local *lp = dev->priv;
-	int ret = 0;
-
-	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
-	}
-	return ret;
-}
-#else
 static int
 isdn_net_rebuild_header(struct sk_buff *skb)
 {
@@ -1439,12 +1725,12 @@
 		 *      Try to get ARP to resolve the header.
 		 */
 #ifdef CONFIG_INET
-		ret = arp_find(eth->h_dest, skb) ? 1 : 0;
+		ret = arp_find(eth->h_dest, skb);
 #endif
 	}
 	return ret;
 }
-#endif
+
 /*
  * Interface-setup. (called just after registering a new interface)
  */
@@ -1465,21 +1751,13 @@
 		return -ENODEV;
 	}
 	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;
-#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|IFF_POINTOPOINT;
@@ -1599,13 +1877,13 @@
 #endif
 	p = dev->netdev;
 	while (p) {
-		if (p->local.pre_device == drvidx)
-			switch (p->local.pre_channel) {
+		if (p->local->pre_device == drvidx)
+			switch (p->local->pre_channel) {
 				case 0:
-					p->local.pre_channel = 1;
+					p->local->pre_channel = 1;
 					break;
 				case 1:
-					p->local.pre_channel = 0;
+					p->local->pre_channel = 0;
 					break;
 			}
 		p = (isdn_net_dev *) p->next;
@@ -1676,6 +1954,7 @@
 		printk(KERN_INFO "isdn_net: call from %s,%d,%d -> %s\n", nr, si1, si2, eaz);
 	/* Accept only calls with Si1 = 7 (Data-Transmission) */
 	if (si1 != 7) {
+		restore_flags(flags);
 		if (dev->net_verbose > 1)
 			printk(KERN_INFO "isdn_net: Service-Indicator not 7, ignored\n");
 		return 0;
@@ -1689,6 +1968,8 @@
 #endif
 	swapped = 0;
 	while (p) {
+		isdn_net_local *lp = p->local;
+
 		/* If last check has triggered as binding-swap, revert it */
 		switch (swapped) {
 			case 2:
@@ -1699,25 +1980,25 @@
 				break;
 		}
 		swapped = 0;
-		if (!strcmp(isdn_map_eaz2msn(p->local.msn, di), eaz))
+		if (!strcmp(isdn_map_eaz2msn(lp->msn, di), eaz))
 			ematch = 1;
 #ifdef ISDN_DEBUG_NET_ICALL
 		printk(KERN_DEBUG "n_fi: if='%s', l.msn=%s, l.flags=%d, l.dstate=%d\n",
-		       p->local.name, p->local.msn, p->local.flags, p->local.dialstate);
+		       lp->name, lp->msn, lp->flags, lp->dialstate);
 #endif
-		if ((!strcmp(isdn_map_eaz2msn(p->local.msn, di), eaz)) &&	/* EAZ is matching   */
-		    (((!(p->local.flags & ISDN_NET_CONNECTED)) &&	/* but not connected */
+		if ((!strcmp(isdn_map_eaz2msn(lp->msn, di), eaz)) &&	/* EAZ is matching   */
+		    (((!(lp->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   */
+		     ((((lp->dialstate == 4) || (lp->dialstate == 12)) &&	/* if dialing        */
+		       (!(lp->flags & ISDN_NET_CALLBACK)))	/* but no callback   */
 		     ))) {
 #ifdef ISDN_DEBUG_NET_ICALL
 			printk(KERN_DEBUG "n_fi: match1, pdev=%d pch=%d\n",
-			       p->local.pre_device, p->local.pre_channel);
+			       lp->pre_device, lp->pre_channel);
 #endif
 			if (dev->usage[idx] & ISDN_USAGE_EXCLUSIVE) {
-				if ((p->local.pre_channel != ch) ||
-				    (p->local.pre_device != di)) {
+				if ((lp->pre_channel != ch) ||
+				    (lp->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
@@ -1741,8 +2022,8 @@
 #endif
 								/* Yes, swap bindings only, if the original
 								 * binding is bound to channel 1 of this driver */
-								if ((p->local.pre_device == di) &&
-								    (p->local.pre_channel == 1)) {
+								if ((lp->pre_device == di) &&
+								    (lp->pre_channel == 1)) {
 									isdn_net_swapbind(di);
 									swapped = 1;
 								} else {
@@ -1764,8 +2045,8 @@
 							printk(KERN_DEBUG "n_fi: final check\n");
 #endif
 							if ((dev->usage[idx] & ISDN_USAGE_EXCLUSIVE) &&
-							    ((p->local.pre_channel != ch) ||
-							     (p->local.pre_device != di))) {
+							    ((lp->pre_channel != ch) ||
+							     (lp->pre_device != di))) {
 #ifdef ISDN_DEBUG_NET_ICALL
 								printk(KERN_DEBUG "n_fi: final check failed\n");
 #endif
@@ -1786,16 +2067,15 @@
 #ifdef ISDN_DEBUG_NET_ICALL
 			printk(KERN_DEBUG "n_fi: match2\n");
 #endif
-			n = p->local.phone[0];
-			if (p->local.flags & ISDN_NET_SECURE) {
+			n = lp->phone[0];
+			if (lp->flags & ISDN_NET_SECURE) {
 				while (n) {
 					if (isdn_net_wildmat(nr, n->num))
 						break;
 					n = (isdn_net_phone *) n->next;
 				}
 			}
-			if (n || (!(p->local.flags & ISDN_NET_SECURE))) {
-				isdn_net_local *lp = &(p->local);
+			if (n || (!(lp->flags & ISDN_NET_SECURE))) {
 #ifdef ISDN_DEBUG_NET_ICALL
 				printk(KERN_DEBUG "n_fi: match3\n");
 #endif
@@ -1804,7 +2084,7 @@
 				 */
 				if (!p->dev.start) {
 					restore_flags(flags);
-					printk(KERN_INFO "%s: incoming call, if down -> rejected\n",
+					printk(KERN_INFO "%s: incoming call, interface down -> rejected\n",
 					       lp->name);
 					return 3;
 				}
@@ -1872,12 +2152,12 @@
 					       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 ((lp->dialstate == 4) || (lp->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_free_channel(lp->isdn_device, lp->isdn_channel,
 							 ISDN_USAGE_NET);
 					}
 					dev->usage[idx] &= ISDN_USAGE_EXCLUSIVE;
@@ -1885,16 +2165,16 @@
 					strcpy(dev->num[idx], nr);
 					isdn_info_update();
 					dev->st_netdev[idx] = lp->netdev;
-					p->local.isdn_device = di;
-					p->local.isdn_channel = ch;
-					p->local.ppp_slot = -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 |= ISDN_WAITCHARGE;
-					p->local.hupflags &= ~ISDN_HAVECHARGE;
+					lp->isdn_device = di;
+					lp->isdn_channel = ch;
+					lp->ppp_slot = -1;
+					lp->flags |= ISDN_NET_CONNECTED;
+					lp->dialstate = 7;
+					lp->dtimer = 0;
+					lp->outgoing = 0;
+					lp->huptimer = 0;
+					lp->hupflags |= ISDN_WAITCHARGE;
+					lp->hupflags &= ~ISDN_HAVECHARGE;
 #ifdef CONFIG_ISDN_PPP
 					if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
 						if (isdn_ppp_bind(lp) < 0) {
@@ -1926,7 +2206,7 @@
 	isdn_net_dev *p = dev->netdev;
 
 	while (p) {
-		if (!strcmp(p->local.name, name))
+		if (!strcmp(p->local->name, name))
 			return p;
 		p = (isdn_net_dev *) p->next;
 	}
@@ -1989,7 +2269,7 @@
 
 	if (!p)
 		return -ENODEV;
-	return (isdn_net_force_dial_lp(&p->local));
+	return (isdn_net_force_dial_lp(p->local));
 }
 
 /*
@@ -2010,20 +2290,25 @@
 		return NULL;
 	}
 	memset(netdev, 0, sizeof(isdn_net_dev));
+	if (!(netdev->local = (isdn_net_local *) kmalloc(sizeof(isdn_net_local), GFP_KERNEL))) {
+		printk(KERN_WARNING "isdn_net: Could not allocate device locals\n");
+		return NULL;
+	}
+	memset(netdev->local, 0, sizeof(isdn_net_local));
 	if (name == NULL)
-		strcpy(netdev->local.name, "         ");
+		strcpy(netdev->local->name, "         ");
 	else
-		strcpy(netdev->local.name, name);
-	netdev->dev.name = netdev->local.name;
-	netdev->dev.priv = &netdev->local;
+		strcpy(netdev->local->name, name);
+	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;
+	netdev->local->p_encap = ISDN_NET_ENCAP_RAWIP;
 	if (master) {
 		/* Device shall be a slave */
 		struct device *p = (((isdn_net_local *) master->priv)->slave);
 		struct device *q = master;
 
-		netdev->local.master = master;
+		netdev->local->master = master;
 		/* Put device at end of slave-chain */
 		while (p) {
 			q = p;
@@ -2037,41 +2322,43 @@
 		/* Device shall be a master */
 		if (register_netdev(&netdev->dev) != 0) {
 			printk(KERN_WARNING "isdn_net: Could not register net-device\n");
+			kfree(netdev->local);
 			kfree(netdev);
 			return NULL;
 		}
 	}
-	netdev->local.magic = ISDN_NET_MAGIC;
+	netdev->local->magic = ISDN_NET_MAGIC;
 
 #ifdef CONFIG_ISDN_PPP
 	netdev->mp_last = NULL; /* mpqueue is empty */
 	netdev->ib.next_num = 0;
 	netdev->ib.last = NULL;
 #endif
-	netdev->queue = &netdev->local;
-	netdev->local.last = &netdev->local;
-	netdev->local.netdev = netdev;
-	netdev->local.next = &netdev->local;
-
-	netdev->local.isdn_device = -1;
-	netdev->local.isdn_channel = -1;
-	netdev->local.pre_device = -1;
-	netdev->local.pre_channel = -1;
-	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 = ISDN_INHUP;	/* Do hangup even on incoming calls */
-	netdev->local.onhtime = 10;	/* Default hangup-time for saving costs
+	netdev->queue = netdev->local;
+	netdev->local->last = netdev->local;
+	netdev->local->netdev = netdev;
+	netdev->local->next = netdev->local;
+
+	netdev->local->isdn_device = -1;
+	netdev->local->isdn_channel = -1;
+	netdev->local->pre_device = -1;
+	netdev->local->pre_channel = -1;
+	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->triggercps = 6000;
+	netdev->local->slavedelay = 10 * HZ;
+	netdev->local->srobin = &netdev->dev;
+	netdev->local->hupflags = ISDN_INHUP;	/* Do hangup even on incoming calls */
+	netdev->local->onhtime = 10;	/* Default hangup-time for saving costs
 	   of those who forget configuring this */
-	netdev->local.dialmax = 1;
-	netdev->local.flags = ISDN_NET_CBHUP;	/* Hangup before Callback */
-	netdev->local.cbdelay = 25;	/* Wait 5 secs before Callback */
+	netdev->local->dialmax = 1;
+	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;
@@ -2095,7 +2382,7 @@
 		if (!(n = isdn_net_findif(parm)))
 			return NULL;
 		/* Master must be a real interface, not a slave */
-		if (n->local.master)
+		if (n->local->master)
 			return NULL;
 		/* Master must not be started yet */
 		if (n->dev.start)
@@ -2120,10 +2407,15 @@
 	int drvidx;
 	int chidx;
 	char drvid[25];
-
+#ifdef CONFIG_ISDN_X25
+	ulong flags;
+#endif
 	if (p) {
+		isdn_net_local *lp = p->local;
+
 		/* See if any registered driver supports the features we want */
-		features = (1 << cfg->l2_proto) | (256 << cfg->l3_proto);
+		features = ((1 << cfg->l2_proto) << ISDN_FEATURE_L2_SHIFT) |
+			((1 << cfg->l3_proto) << ISDN_FEATURE_L3_SHIFT);
 		for (i = 0; i < ISDN_MAX_DRIVERS; i++)
 			if (dev->drv[i])
 				if ((dev->drv[i]->interface->features & features) == features)
@@ -2132,22 +2424,68 @@
 			printk(KERN_WARNING "isdn_net: No driver with selected features\n");
 			return -ENODEV;
 		}
-		if (p->local.p_encap != cfg->p_encap)
+		if (lp->p_encap != cfg->p_encap){
+#ifdef CONFIG_ISDN_X25
+			struct concap_proto * cprot = p -> cprot;
+#endif
 			if (p->dev.start) {
 				printk(KERN_WARNING
 				"%s: cannot change encap when if is up\n",
-				       p->local.name);
+				       lp->name);
 				return -EBUSY;
 			}
-		if (cfg->p_encap == ISDN_NET_ENCAP_SYNCPPP) {
+#ifdef CONFIG_ISDN_X25
+			/* delete old encapsulation protocol if present ... */
+			save_flags(flags);
+			cli(); /* avoid races with incoming events trying to
+				  call cprot->pops methods */
+			if( cprot && cprot -> pops ) 
+				cprot -> pops -> proto_del ( cprot );
+			p -> cprot = NULL;
+			lp -> dops = NULL;
+			restore_flags(flags);
+			/* ... ,  prepare for configuration of new one ... */
+			switch ( cfg -> p_encap ){
+			case ISDN_NET_ENCAP_X25IFACE:
+				lp -> dops = &isdn_concap_reliable_dl_dops;
+			}
+			/* ... and allocate new one ... */
+			p -> cprot = isdn_concap_new( cfg -> p_encap );
+			/* p -> cprot == NULL now if p_encap is not supported
+			   by means of the concap_proto mechanism */
+			/* the protocol is not configured yet; this will 
+			   happen later when isdn_net_reset() is called */
+#endif
+		}
+		switch ( cfg->p_encap ) {
+		case ISDN_NET_ENCAP_SYNCPPP:
 #ifndef CONFIG_ISDN_PPP
 			printk(KERN_WARNING "%s: SyncPPP support not configured\n",
-			       p->local.name);
+			       lp->name);
 			return -EINVAL;
 #else
 			p->dev.type = ARPHRD_PPP;	/* change ARP type */
 			p->dev.addr_len = 0;
 #endif
+			break;
+		case ISDN_NET_ENCAP_X25IFACE:
+#ifndef CONFIG_ISDN_X25
+			printk(KERN_WARNING "%s: isdn-x25 support not configured\n",
+			       p->local->name);
+			return -EINVAL;
+#else
+			p->dev.type = ARPHRD_X25;	/* change ARP type */
+			p->dev.addr_len = 0;
+#endif
+			break;
+		default:
+			if( cfg->p_encap >= 0 &&
+			    cfg->p_encap <= ISDN_NET_ENCAP_MAX_ENCAP )
+				break;
+			printk(KERN_WARNING 
+			       "%s: encapsulation protocol %d not supported\n",
+			       p->local->name, cfg->p_encap);
+			return -EINVAL;
 		}
 		if (strlen(cfg->drvid)) {
 			/* A bind has been requested ... */
@@ -2175,20 +2513,20 @@
 				return -ENODEV;
 		} else {
 			/* Parameters are valid, so get them */
-			drvidx = p->local.pre_device;
-			chidx = p->local.pre_channel;
+			drvidx = lp->pre_device;
+			chidx = lp->pre_channel;
 		}
 		if (cfg->exclusive > 0) {
 			int flags;
 
 			/* 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,
+			if ((i = isdn_get_free_channel(ISDN_USAGE_NET, lp->l2_proto,
+						       lp->l3_proto,
 						       drvidx,
 						       chidx)) < 0) {
 				/* Grab failed, because desired channel is in use */
-				p->local.exclusive = -1;
+				lp->exclusive = -1;
 				restore_flags(flags);
 				return -EBUSY;
 			}
@@ -2196,93 +2534,82 @@
 			dev->usage[i] = ISDN_USAGE_EXCLUSIVE;
 			isdn_info_update();
 			restore_flags(flags);
-			p->local.exclusive = i;
+			lp->exclusive = i;
 		} else {
 			/* Non-exclusive binding or unbind. */
-			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);
+			lp->exclusive = -1;
+			if ((lp->pre_device != -1) && (cfg->exclusive == -1)) {
+				isdn_unexclusive_channel(lp->pre_device, lp->pre_channel);
+				isdn_free_channel(lp->pre_device, lp->pre_channel, ISDN_USAGE_NET);
 				drvidx = -1;
 				chidx = -1;
 			}
 		}
-		strcpy(p->local.msn, cfg->eaz);
-		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;
+		strcpy(lp->msn, cfg->eaz);
+		lp->pre_device = drvidx;
+		lp->pre_channel = chidx;
+		lp->onhtime = cfg->onhtime;
+		lp->charge = cfg->charge;
+		lp->l2_proto = cfg->l2_proto;
+		lp->l3_proto = cfg->l3_proto;
+		lp->cbdelay = cfg->cbdelay;
+		lp->dialmax = cfg->dialmax;
+		lp->triggercps = cfg->triggercps;
+		lp->slavedelay = cfg->slavedelay * HZ;
+		lp->pppbind = cfg->pppbind;
 		if (cfg->secure)
-			p->local.flags |= ISDN_NET_SECURE;
+			lp->flags |= ISDN_NET_SECURE;
 		else
-			p->local.flags &= ~ISDN_NET_SECURE;
+			lp->flags &= ~ISDN_NET_SECURE;
 		if (cfg->cbhup)
-			p->local.flags |= ISDN_NET_CBHUP;
+			lp->flags |= ISDN_NET_CBHUP;
 		else
-			p->local.flags &= ~ISDN_NET_CBHUP;
+			lp->flags &= ~ISDN_NET_CBHUP;
 		switch (cfg->callback) {
 			case 0:
-				p->local.flags &= ~(ISDN_NET_CALLBACK | ISDN_NET_CBOUT);
+				lp->flags &= ~(ISDN_NET_CALLBACK | ISDN_NET_CBOUT);
 				break;
 			case 1:
-				p->local.flags |= ISDN_NET_CALLBACK;
-				p->local.flags &= ~ISDN_NET_CBOUT;
+				lp->flags |= ISDN_NET_CALLBACK;
+				lp->flags &= ~ISDN_NET_CBOUT;
 				break;
 			case 2:
-				p->local.flags |= ISDN_NET_CBOUT;
-				p->local.flags &= ~ISDN_NET_CALLBACK;
+				lp->flags |= ISDN_NET_CBOUT;
+				lp->flags &= ~ISDN_NET_CALLBACK;
 				break;
 		}
 		if (cfg->chargehup)
-			p->local.hupflags |= ISDN_CHARGEHUP;
+			lp->hupflags |= ISDN_CHARGEHUP;
 		else
-			p->local.hupflags &= ~ISDN_CHARGEHUP;
+			lp->hupflags &= ~ISDN_CHARGEHUP;
 		if (cfg->ihup)
-			p->local.hupflags |= ISDN_INHUP;
+			lp->hupflags |= ISDN_INHUP;
 		else
-			p->local.hupflags &= ~ISDN_INHUP;
+			lp->hupflags &= ~ISDN_INHUP;
 		if (cfg->chargeint > 10) {
-			p->local.hupflags |= ISDN_CHARGEHUP | ISDN_HAVECHARGE | ISDN_MANCHARGE;
-			p->local.chargeint = cfg->chargeint * HZ;
+			lp->hupflags |= ISDN_CHARGEHUP | ISDN_HAVECHARGE | ISDN_MANCHARGE;
+			lp->chargeint = cfg->chargeint * HZ;
 		}
-		if (cfg->p_encap != p->local.p_encap) {
+		if (cfg->p_encap != lp->p_encap) {
 			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|IFF_POINTOPOINT;
 			} 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.hard_header_cache = lp->org_hhc;
+					p->dev.header_cache_update = lp->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|IFF_POINTOPOINT;
 				}
 			}
 		}
-		p->local.p_encap = cfg->p_encap;
+		lp->p_encap = cfg->p_encap;
 		return 0;
 	}
 	return -ENODEV;
@@ -2297,39 +2624,42 @@
 	isdn_net_dev *p = isdn_net_findif(cfg->name);
 
 	if (p) {
-		strcpy(cfg->eaz, p->local.msn);
-		cfg->exclusive = p->local.exclusive;
-		if (p->local.pre_device >= 0) {
-			sprintf(cfg->drvid, "%s,%d", dev->drvid[p->local.pre_device],
-				p->local.pre_channel);
+		isdn_net_local *lp = p->local;
+
+		strcpy(cfg->eaz, lp->msn);
+		cfg->exclusive = lp->exclusive;
+		if (lp->pre_device >= 0) {
+			sprintf(cfg->drvid, "%s,%d", dev->drvid[lp->pre_device],
+				lp->pre_channel);
 		} else
 			cfg->drvid[0] = '\0';
-		cfg->onhtime = p->local.onhtime;
-		cfg->charge = p->local.charge;
-		cfg->l2_proto = p->local.l2_proto;
-		cfg->l3_proto = p->local.l3_proto;
-		cfg->p_encap = p->local.p_encap;
-		cfg->secure = (p->local.flags & ISDN_NET_SECURE) ? 1 : 0;
+		cfg->onhtime = lp->onhtime;
+		cfg->charge = lp->charge;
+		cfg->l2_proto = lp->l2_proto;
+		cfg->l3_proto = lp->l3_proto;
+		cfg->p_encap = lp->p_encap;
+		cfg->secure = (lp->flags & ISDN_NET_SECURE) ? 1 : 0;
 		cfg->callback = 0;
-		if (p->local.flags & ISDN_NET_CALLBACK)
+		if (lp->flags & ISDN_NET_CALLBACK)
 			cfg->callback = 1;
-		if (p->local.flags & ISDN_NET_CBOUT)
+		if (lp->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->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);
+		cfg->cbhup = (lp->flags & ISDN_NET_CBHUP) ? 1 : 0;
+		cfg->chargehup = (lp->hupflags & 4) ? 1 : 0;
+		cfg->ihup = (lp->hupflags & 8) ? 1 : 0;
+		cfg->cbdelay = lp->cbdelay;
+		cfg->dialmax = lp->dialmax;
+		cfg->triggercps = lp->triggercps;
+		cfg->slavedelay = lp->slavedelay / HZ;
+		cfg->chargeint = (lp->hupflags & ISDN_CHARGEHUP) ?
+		    (lp->chargeint / HZ) : 0;
+		cfg->pppbind = lp->pppbind;
+		if (lp->slave)
+			strcpy(cfg->slave, ((isdn_net_local *) lp->slave->priv)->name);
 		else
 			cfg->slave[0] = '\0';
-		if (p->local.master)
-			strcpy(cfg->master, ((isdn_net_local *) p->local.master->priv)->name);
+		if (lp->master)
+			strcpy(cfg->master, ((isdn_net_local *) lp->master->priv)->name);
 		else
 			cfg->master[0] = '\0';
 		return 0;
@@ -2352,8 +2682,8 @@
 		if (!(n = (isdn_net_phone *) kmalloc(sizeof(isdn_net_phone), GFP_KERNEL)))
 			return -ENOMEM;
 		strcpy(n->num, phone->phone);
-		n->next = p->local.phone[phone->outgoing & 1];
-		p->local.phone[phone->outgoing & 1] = n;
+		n->next = p->local->phone[phone->outgoing & 1];
+		p->local->phone[phone->outgoing & 1] = n;
 		return 0;
 	}
 	return -ENODEV;
@@ -2378,7 +2708,7 @@
 	save_flags(flags);
 	cli();
 	inout &= 1;
-	for (n = p->local.phone[inout]; n; n = n->next) {
+	for (n = p->local->phone[inout]; n; n = n->next) {
 		if (more) {
 			put_user(' ', phones++);
 			count++;
@@ -2413,16 +2743,16 @@
 	if (p) {
 		save_flags(flags);
 		cli();
-		n = p->local.phone[inout];
+		n = p->local->phone[inout];
 		m = NULL;
 		while (n) {
 			if (!strcmp(n->num, phone->phone)) {
-				if (p->local.dial == n)
-					p->local.dial = n->next;
+				if (p->local->dial == n)
+					p->local->dial = n->next;
 				if (m)
 					m->next = n->next;
 				else
-					p->local.phone[inout] = n->next;
+					p->local->phone[inout] = n->next;
 				kfree(n);
 				return 0;
 			}
@@ -2449,15 +2779,15 @@
 	save_flags(flags);
 	cli();
 	for (i = 0; i < 2; i++) {
-		n = p->local.phone[i];
+		n = p->local->phone[i];
 		while (n) {
 			m = n->next;
 			kfree(n);
 			n = m;
 		}
-		p->local.phone[i] = NULL;
+		p->local->phone[i] = NULL;
 	}
-	p->local.dial = NULL;
+	p->local->dial = NULL;
 	restore_flags(flags);
 	return 0;
 }
@@ -2472,9 +2802,9 @@
 	struct device *q;
 
 	if (p) {
-		if (p->local.isdn_device < 0)
+		if (p->local->isdn_device < 0)
 			return 1;
-		q = p->local.slave;
+		q = p->local->slave;
 		/* If this interface has slaves, do a hangup for them also. */
 		while (q) {
 			isdn_net_hangup(q);
@@ -2496,7 +2826,7 @@
 
 	save_flags(flags);
 	cli();
-	if (p->local.master) {
+	if (p->local->master) {
 		/* If it's a slave, it may be removed even if it is busy. However
 		 * it has to be hung up first.
 		 */
@@ -2507,30 +2837,37 @@
 		restore_flags(flags);
 		return -EBUSY;
 	}
+#ifdef CONFIG_ISDN_X25
+	if( p -> cprot && p -> cprot -> pops )
+		p -> cprot -> pops -> proto_del ( p -> cprot );
+#endif
 	/* Free all phone-entries */
 	isdn_net_rmallphone(p);
 	/* If interface is bound exclusive, free channel-usage */
-	if (p->local.exclusive != -1)
-		isdn_unexclusive_channel(p->local.pre_device, p->local.pre_channel);
-	if (p->local.master) {
+	if (p->local->exclusive != -1)
+		isdn_unexclusive_channel(p->local->pre_device, p->local->pre_channel);
+	if (p->local->master) {
 		/* It's a slave-device, so update master's slave-pointer if necessary */
-		if (((isdn_net_local *) (p->local.master->priv))->slave == &p->dev)
-			((isdn_net_local *) (p->local.master->priv))->slave = p->local.slave;
-	} else
+		if (((isdn_net_local *) (p->local->master->priv))->slave == &p->dev)
+			((isdn_net_local *) (p->local->master->priv))->slave = p->local->slave;
+	} else {
 		/* Unregister only if it's a master-device */
+		p->dev.hard_header_cache = p->local->org_hhc;
+		p->dev.header_cache_update = p->local->org_hcu;
 		unregister_netdev(&p->dev);
+	}
 	/* Unlink device from chain */
 	if (q)
 		q->next = p->next;
 	else
 		dev->netdev = p->next;
-	if (p->local.slave) {
+	if (p->local->slave) {
 		/* If this interface has a slave, remove it also */
-		char *slavename = ((isdn_net_local *) (p->local.slave->priv))->name;
+		char *slavename = ((isdn_net_local *) (p->local->slave->priv))->name;
 		isdn_net_dev *n = dev->netdev;
 		q = NULL;
 		while (n) {
-			if (!strcmp(n->local.name, slavename)) {
+			if (!strcmp(n->local->name, slavename)) {
 				isdn_net_realrm(n, q);
 				break;
 			}
@@ -2543,6 +2880,7 @@
 		isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, 0);
 	restore_flags(flags);
 
+	kfree(p->local);
 	kfree(p);
 
 	return 0;
@@ -2561,7 +2899,7 @@
 	p = dev->netdev;
 	q = NULL;
 	while (p) {
-		if (!strcmp(p->local.name, name))
+		if (!strcmp(p->local->name, name))
 			return (isdn_net_realrm(p, q));
 		q = p;
 		p = (isdn_net_dev *) p->next;
@@ -2585,7 +2923,7 @@
 	save_flags(flags);
 	cli();
 	while (dev->netdev) {
-		if (!dev->netdev->local.master) {
+		if (!dev->netdev->local->master) {
 			/* Remove master-devices only, slaves get removed with their master */
 			if ((ret = isdn_net_realrm(dev->netdev, NULL))) {
 				restore_flags(flags);
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/isdn_net.h linux/drivers/isdn/isdn_net.h
--- v2.1.91/linux/drivers/isdn/isdn_net.h	Thu Feb 27 10:57:30 1997
+++ linux/drivers/isdn/isdn_net.h	Wed Apr  1 16:21:03 1998
@@ -1,4 +1,4 @@
-/* $Id: isdn_net.h,v 1.5 1997/02/10 20:12:47 fritz Exp $
+/* $Id: isdn_net.h,v 1.6 1997/10/09 21:28:54 fritz Exp $
 
  * header for Linux ISDN subsystem, network related functions (linklevel).
  *
@@ -21,6 +21,16 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_net.h,v $
+ * Revision 1.6  1997/10/09 21:28:54  fritz
+ * New HL<->LL interface:
+ *   New BSENT callback with nr. of bytes included.
+ *   Sending without ACK.
+ *   New L1 error status (not yet in use).
+ *   Cleaned up obsolete structures.
+ * Implemented Cisco-SLARP.
+ * Changed local net-interface data to be dynamically allocated.
+ * Removed old 2.0 compatibility stuff.
+ *
  * Revision 1.5  1997/02/10 20:12:47  fritz
  * Changed interface for reporting incoming calls.
  *
@@ -45,11 +55,46 @@
 #define ISDN_INHUP       8      /* Even if incoming, close after huptimeout */
 #define ISDN_MANCHARGE  16      /* Charge Interval manually set             */
 
+/*
+ * Definitions for Cisco-HDLC header.
+ */
+
+typedef struct cisco_hdr {
+	__u8  addr; /* unicast/broadcast */
+	__u8  ctrl; /* Always 0          */
+	__u16 type; /* IP-typefield      */
+} cisco_hdr;
+
+typedef struct cisco_slarp {
+	__u32 code;                     /* SLREQ/SLREPLY/KEEPALIVE */
+	union {
+		struct {
+			__u32 ifaddr;   /* My interface address     */
+			__u32 netmask;  /* My interface netmask     */
+		} reply;
+		struct {
+			__u32 my_seq;   /* Packet sequence number   */
+			__u32 your_seq;
+		} keepalive;
+	} slarp;
+	__u16 rel;                      /* Always 0xffff            */
+	__u16 t1;                       /* Uptime in usec >> 16     */
+	__u16 t0;                       /* Uptime in usec & 0xffff  */
+} cisco_slarp;
+
+#define CISCO_ADDR_UNICAST    0x0f
+#define CISCO_ADDR_BROADCAST  0x8f
+#define CISCO_TYPE_INET       0x0800
+#define CISCO_TYPE_SLARP      0x8035
+#define CISCO_SLARP_REPLY     0
+#define CISCO_SLARP_REQUEST   1
+#define CISCO_SLARP_KEEPALIVE 2
+
 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_stat_callback(int, isdn_ctrl *);
 extern int isdn_net_setcfg(isdn_net_ioctl_cfg *);
 extern int isdn_net_getcfg(isdn_net_ioctl_cfg *);
 extern int isdn_net_addphone(isdn_net_ioctl_phone *);
@@ -65,3 +110,4 @@
 extern int isdn_net_send_skb(struct device *, isdn_net_local *,
 			     struct sk_buff *);
 extern int isdn_net_rcv_skb(int, struct sk_buff *);
+extern void isdn_net_slarp_out(void);
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/isdn_ppp.c linux/drivers/isdn/isdn_ppp.c
--- v2.1.91/linux/drivers/isdn/isdn_ppp.c	Tue Mar 10 10:03:32 1998
+++ linux/drivers/isdn/isdn_ppp.c	Wed Apr  1 16:21:03 1998
@@ -1,4 +1,4 @@
-/* $Id: isdn_ppp.c,v 1.27 1997/03/30 16:51:17 calle Exp $
+/* $Id: isdn_ppp.c,v 1.33 1998/02/20 17:11:54 fritz Exp $
  *
  * Linux ISDN subsystem, functions for synchronous PPP (linklevel).
  *
@@ -19,6 +19,37 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_ppp.c,v $
+ * Revision 1.33  1998/02/20 17:11:54  fritz
+ * Changes for recent kernels.
+ *
+ * Revision 1.32  1998/01/31 19:29:55  calle
+ * Merged changes from and for 2.1.82, not tested only compiled ...
+ *
+ * Revision 1.31  1997/10/09 21:29:01  fritz
+ * New HL<->LL interface:
+ *   New BSENT callback with nr. of bytes included.
+ *   Sending without ACK.
+ *   New L1 error status (not yet in use).
+ *   Cleaned up obsolete structures.
+ * Implemented Cisco-SLARP.
+ * Changed local net-interface data to be dynamically allocated.
+ * Removed old 2.0 compatibility stuff.
+ *
+ * Revision 1.30  1997/10/01 09:20:38  fritz
+ * Removed old compatibility stuff for 2.0.X kernels.
+ * From now on, this code is for 2.1.X ONLY!
+ * Old stuff is still in the separate branch.
+ *
+ * Revision 1.29  1997/08/21 23:11:44  fritz
+ * Added changes for kernels >= 2.1.45
+ *
+ * Revision 1.28  1997/06/17 13:05:57  hipp
+ * Applied Eric's underflow-patches (slightly modified)
+ * more compression changes (but disabled at the moment)
+ * changed one copy_to_user() to run with enabled IRQs
+ * a few MP changes
+ * changed 'proto' handling in the isdn_ppp receive code
+ *
  * Revision 1.27  1997/03/30 16:51:17  calle
  * changed calls to copy_from_user/copy_to_user and removed verify_area
  * were possible.
@@ -128,9 +159,7 @@
 #include <linux/module.h>
 #include <linux/version.h>
 #include <linux/isdn.h>
-#if (LINUX_VERSION_CODE >= 0x020117)
 #include <linux/poll.h>
-#endif
 #include "isdn_common.h"
 #include "isdn_ppp.h"
 #include "isdn_net.h"
@@ -148,6 +177,12 @@
 				 struct sk_buff *skb, int proto);
 static int isdn_ppp_if_get_unit(char *namebuf);
 static int isdn_ppp_set_compressor(struct ippp_struct *is,int num);
+static struct sk_buff *isdn_ppp_decompress(struct sk_buff *,
+				struct ippp_struct *,struct ippp_struct *);
+static void isdn_ppp_receive_ccp(isdn_net_dev * net_dev, isdn_net_local * lp,
+				struct sk_buff *skb);
+static struct sk_buff *isdn_ppp_compress(struct sk_buff *skb_in,int *proto,
+	struct ippp_struct *is,struct ippp_struct *master,int type);
 
 #ifdef CONFIG_ISDN_MPP
 static int isdn_ppp_bundle(struct ippp_struct *, int unit);
@@ -160,7 +195,7 @@
 static void isdn_ppp_free_mpqueue(isdn_net_dev *);
 #endif
 
-char *isdn_ppp_revision = "$Revision: 1.27 $";
+char *isdn_ppp_revision = "$Revision: 1.33 $";
 
 static struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS];
 static struct isdn_ppp_compressor *ipc_head = NULL;
@@ -267,7 +302,7 @@
 		char exclusive[ISDN_MAX_CHANNELS];	/* exclusive flags */
 		memset(exclusive, 0, ISDN_MAX_CHANNELS);
 		while (net_dev) {	/* step through net devices to find exclusive minors */
-			isdn_net_local *lp = &net_dev->local;
+			isdn_net_local *lp = net_dev->local;
 			if (lp->pppbind >= 0)
 				exclusive[lp->pppbind] = 1;
 			net_dev = net_dev->next;
@@ -382,7 +417,12 @@
 	if (is->debug & 0x1)
 		printk(KERN_DEBUG "ippp, open, slot: %d, minor: %d, state: %04x\n", slot, min, is->state);
 
+	/* compression stuff */
 	is->compressor = NULL;
+	is->decomp_stat = is->comp_stat = NULL;
+	is->link_compressor = NULL;
+	is->link_decomp_stat = is->link_comp_stat = NULL;
+
 	is->lp = NULL;
 	is->mp_seqno = 0;       /* MP sequence number */
 	is->pppcfg = 0;         /* ppp configuration */
@@ -644,50 +684,6 @@
 	return 0;
 }
 
-#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;
-	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->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);
-				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;
-	}
-	return 1;
-}
-#else
 unsigned int
 isdn_ppp_poll(struct file *file, poll_table * wait)
 {
@@ -700,7 +696,8 @@
 	is = file->private_data;
 
 	if (is->debug & 0x2)
-		printk(KERN_DEBUG "isdn_ppp_poll: minor: %d\n", MINOR(file->f_dentry->d_inode->i_rdev));
+		printk(KERN_DEBUG "isdn_ppp_poll: minor: %d\n",
+				MINOR(file->f_dentry->d_inode->i_rdev));
 
 	poll_wait(file, &is->wq, wait);
 
@@ -725,8 +722,6 @@
 	restore_flags(flags);
 	return mask;
 }
-#endif
-
 
 /*
  *  fill up isdn_ppp_read() queue ..
@@ -798,31 +793,35 @@
 	struct ippp_buf_queue *b;
 	int r;
 	unsigned long flags;
+	unsigned char *save_buf;
 
 	is = file->private_data;
 
 	if (!(is->state & IPPP_OPEN))
 		return 0;
 
+	if ((r = verify_area(VERIFY_WRITE, (void *) buf, count)))
+		return r;
+
 	save_flags(flags);
 	cli();
 
 	b = is->first->next;
-	if (!b->buf) {
+	save_buf = b->buf;
+	if (!save_buf) {
 		restore_flags(flags);
 		return -EAGAIN;
 	}
 	if (b->len < count)
 		count = b->len;
-	if ((r = copy_to_user(buf, b->buf, count))) {
-		restore_flags(flags);
-		return r;
-	}
-	kfree(b->buf);
 	b->buf = NULL;
 	is->first = b;
+
 	restore_flags(flags);
 
+	copy_to_user(buf, save_buf, count);
+	kfree(save_buf);
+
 	return count;
 }
 
@@ -872,14 +871,13 @@
 				printk(KERN_WARNING "isdn_ppp_write: out of memory!\n");
 				return count;
 			}
-			SET_SKB_FREE(skb);
 			if (copy_from_user(skb_put(skb, count), buf, count))
 				return -EFAULT;
 			if (is->debug & 0x40) {
 				printk(KERN_DEBUG "ppp xmit: len %d\n", (int) skb->len);
 				isdn_ppp_frame_log("xmit", skb->data, skb->len, 32);
 			}
-			if ((cnt = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, skb)) != count) {
+			if ((cnt = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, 1, skb)) != count) {
 				if (lp->sav_skb) {
 					dev_kfree_skb(lp->sav_skb);
 					printk(KERN_INFO "isdn_ppp_write: freeing sav_skb (%d,%d)!\n", cnt, count);
@@ -935,45 +933,65 @@
 }
 
 /*
+ * get the PPP protocol header and pull skb
+ */
+static int isdn_ppp_strip_proto(struct sk_buff *skb) 
+{
+	int proto;
+	if (skb->data[0] & 0x1) {
+		proto = skb->data[0];
+		skb_pull(skb, 1);   /* protocol ID is only 8 bit */
+	} else {
+		proto = ((int) skb->data[0] << 8) + skb->data[1];
+		skb_pull(skb, 2);
+	}
+	return proto;
+}
+
+
+/*
  * handler for incoming packets on a syncPPP interface
  */
-void
-isdn_ppp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *skb)
+void isdn_ppp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *skb)
 {
 	struct ippp_struct *is;
+	int proto;
+
 	is = ippp_table[lp->ppp_slot];
 
 	if (is->debug & 0x4) {
 		printk(KERN_DEBUG "ippp_receive: len: %d\n", (int) skb->len);
 		isdn_ppp_frame_log("receive", skb->data, skb->len, 32);
 	}
-	if (net_dev->local.master) {
+	if (net_dev->local->master) {
 		printk(KERN_WARNING "isdn_ppp_receice: net_dev != master\n");
-		net_dev = ((isdn_net_local *) net_dev->local.master->priv)->netdev;
+		net_dev = ((isdn_net_local *) net_dev->local->master->priv)->netdev;
 	}
 	if (skb->data[0] == 0xff && skb->data[1] == 0x03)
 		skb_pull(skb, 2);
 	else if (is->pppcfg & SC_REJ_COMP_AC) {
-		SET_SKB_FREE(skb);
 		dev_kfree_skb(skb);
 		return;         /* discard it silently */
 	}
+
+	proto = isdn_ppp_strip_proto(skb);
+
 #ifdef CONFIG_ISDN_MPP
 	if (!(is->mpppcfg & SC_REJ_MP_PROT)) {
-		int proto;
 		int sqno_end;
-		if (skb->data[0] & 0x1) {
-			proto = skb->data[0];
-			skb_pull(skb, 1);	/* protocol ID is only 8 bit */
-		} else {
-			proto = ((int) skb->data[0] << 8) + skb->data[1];
-			skb_pull(skb, 2);
+		
+		if(proto == PPP_LINK_COMP) {
+			printk(KERN_DEBUG "received single link compressed frame\n");
+			skb = isdn_ppp_decompress(skb,is,NULL);
+			if(!skb)
+				return;
+			proto = isdn_ppp_strip_proto(skb);
 		}
+
 		if (proto == PPP_MP) {
 			isdn_net_local *lpq;
-			long sqno,
-			 min_sqno,
-			 tseq;
+			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,
@@ -987,6 +1005,10 @@
 				skb_pull(skb, 2);
 			}
 
+			/*
+			 * new sequence number lower than last number? (this is only allowed
+			 * for overflow case)
+			 */
 			if ((tseq = is->last_link_seqno) >= sqno) {
 				int range = is->range;
 				if (tseq + 1024 < range + sqno)	/* redundancy check .. not MP conform */
@@ -995,9 +1017,14 @@
 					sqno += range;
 					is->last_link_seqno = sqno;
 				}
-			} else
+			} else {
+				/* here, we should also add an redundancy check */
 				is->last_link_seqno = sqno;
+			}
 
+			/* 
+			 * step over all links to find lowest link number
+			 */
 			for (min_sqno = LONG_MAX, lpq = net_dev->queue;;) {
 				long lls = ippp_table[lpq->ppp_slot]->last_link_seqno;
 				if (lls >= 0 && lls < min_sqno)
@@ -1006,11 +1033,14 @@
 				if (lpq == net_dev->queue)
 					break;
 			}
-			if (min_sqno >= ippp_table[lpq->ppp_slot]->range) {	/* OK, every link overflowed */
-				int mask = ippp_table[lpq->ppp_slot]->range - 1;	/* range is a power of 2 */
-#if 0
-				isdn_ppp_cleanup_queue(net_dev, min_sqno);
-#endif
+
+			/*
+			 * for the case, that the last frame numbers of all 
+			 * links are overflowed: mask/reduce the sequenece number to
+			 * 'normal' numbering.
+			 */
+			if (min_sqno >= ippp_table[lpq->ppp_slot]->range) {
+				int mask = ippp_table[lpq->ppp_slot]->range-1;	/* range is power of two, so a mask will do the job */
 				isdn_ppp_mask_queue(net_dev, mask);
 				net_dev->ib.next_num &= mask;
 				{
@@ -1064,7 +1094,6 @@
 				if (!q) {
 					net_dev->ib.modify = 0;
 					printk(KERN_WARNING "ippp/MPPP: Bad! Can't alloc sq node!\n");
-					SET_SKB_FREE(skb);
 					dev_kfree_skb(skb);
 					return;	/* discard */
 				}
@@ -1093,7 +1122,8 @@
 				 * packet was 'in order' .. push it higher
 				 */
 				net_dev->ib.next_num = sqno_end + 1;
-				isdn_ppp_push_higher(net_dev, lp, skb, -1);
+				proto = isdn_ppp_strip_proto(skb);
+				isdn_ppp_push_higher(net_dev, lp, skb, proto);
 			}
 			isdn_ppp_cleanup_sqqueue(net_dev, lp, min_sqno);
 			net_dev->ib.modify = 0;
@@ -1102,7 +1132,7 @@
 			isdn_ppp_push_higher(net_dev, lp, skb, proto);
 	} else
 #endif
-		isdn_ppp_push_higher(net_dev, lp, skb, -1);
+		isdn_ppp_push_higher(net_dev, lp, skb, proto);
 }
 
 /*
@@ -1115,19 +1145,21 @@
 	struct device *dev = &net_dev->dev;
 	struct ippp_struct *is = ippp_table[lp->ppp_slot];
 
-	if (proto < 0) {        /* MP, oder normales Paket bei REJ_MP, MP Pakete gehen bei REJ zum pppd */
-		if (skb->data[0] & 0x01) {	/* is it odd? */
-			proto = (unsigned char) skb->data[0];
-			skb_pull(skb, 1);	/* protocol ID is only 8 bit */
-		} else {
-			proto = ((int) (unsigned char) skb->data[0] << 8) + (unsigned char) skb->data[1];
-			skb_pull(skb, 2);
-		}
-	}
 	if (is->debug & 0x10) {
 		printk(KERN_DEBUG "push, skb %d %04x\n", (int) skb->len, proto);
 		isdn_ppp_frame_log("rpush", skb->data, skb->len, 32);
 	}
+
+	if(proto == PPP_COMP) {
+		if(!lp->master)
+			skb = isdn_ppp_decompress(skb,is,is);
+		else
+			skb = isdn_ppp_decompress(skb,is,ippp_table[((isdn_net_local *) (lp->master->priv))->ppp_slot]);
+		if(!skb)
+			return;
+		proto = isdn_ppp_strip_proto(skb);
+	}
+
 	switch (proto) {
 		case PPP_IPX:  /* untested */
 			if (is->debug & 0x20)
@@ -1140,10 +1172,9 @@
 		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) {
+			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++;
-				SET_SKB_FREE(skb);
+				net_dev->local->stats.rx_dropped++;
 				dev_kfree_skb(skb);
 				return;
 			}
@@ -1164,11 +1195,9 @@
 				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++;
+					net_dev->local->stats.rx_dropped++;
 					dev_kfree_skb(skb_old);
 					return;
 				}
@@ -1176,11 +1205,10 @@
 				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,
+				pkt_len = slhc_uncompress(ippp_table[net_dev->local->ppp_slot]->slcomp,
 						skb->data, skb_old->len);
 				dev_kfree_skb(skb_old);
 				if (pkt_len < 0) {
-					SET_SKB_FREE(skb);
 					dev_kfree_skb(skb);
 					lp->stats.rx_dropped++;
 					return;
@@ -1191,20 +1219,21 @@
 #else
 			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);
 			return;
 #endif
 			break;
+		case PPP_CCP:
+			isdn_ppp_receive_ccp(net_dev,lp,skb);
+			/* fall through */
 		default:
 			isdn_ppp_fill_rq(skb->data, skb->len, proto, lp->ppp_slot);	/* push data to pppd device */
-			SET_SKB_FREE(skb);
 			dev_kfree_skb(skb);
 			return;
 	}
 
 	netif_rx(skb);
-	/* net_dev->local.stats.rx_packets++; *//* done in isdn_net.c */
+	/* net_dev->local->stats.rx_packets++; *//* done in isdn_net.c */
 	/* Reset hangup-timer */
 	lp->huptimer = 0;
 
@@ -1212,6 +1241,24 @@
 }
 
 /*
+ * isdn_ppp_skb_push ..
+ * checks whether we have enough space at the beginning of the SKB
+ * and allocs a new SKB if necessary
+ */
+static unsigned char *isdn_ppp_skb_push(struct sk_buff **skb_p,int len)
+{
+	struct sk_buff *skb = *skb_p;
+
+	if(skb_headroom(skb) < len) {
+		printk(KERN_ERR "isdn_ppp_skb_push:under %d %d\n",skb_headroom(skb),len);
+		dev_kfree_skb(skb);
+		return NULL;
+	}
+	return skb_push(skb,len);
+}
+
+
+/*
  * send ppp frame .. we expect a PIDCOMPressable proto --
  *  (here: currently always PPP_IP,PPP_VJC_COMP,PPP_VJC_UNCOMP)
  *
@@ -1223,12 +1270,10 @@
 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;
+	isdn_net_local *lp,*mlp;
 	isdn_net_dev *nd;
 	unsigned int proto = PPP_IP;     /* 0x21 */
-	struct ippp_struct *ipt,
-	*ipts;
+	struct ippp_struct *ipt,*ipts;
 
 	if (mdev)
 		mlp = (isdn_net_local *) (mdev->priv);
@@ -1250,6 +1295,7 @@
 			printk(KERN_INFO "%s: IP frame delayed.\n", dev->name);
 		return 1;
 	}
+
 	switch (ntohs(skb->protocol)) {
 		case ETH_P_IP:
 			proto = PPP_IP;
@@ -1297,11 +1343,18 @@
 	 * after this line .. requeueing in the device queue is no longer allowed!!!
 	 */
 
+	/* Pull off the fake header we stuck on earlier to keep
+     * the fragemntation code happy.
+     * this will break the ISDN_SYNCPPP_READDRESS hack a few lines
+     * above. So, enabling this is no longer allowed
+     */
+	skb_pull(skb,IPPP_MAX_HEADER);
+
 	if (ipt->debug & 0x4)
 		printk(KERN_DEBUG "xmit skb, len %d\n", (int) skb->len);
 
 #ifdef CONFIG_ISDN_PPP_VJ
-	if (proto == PPP_IP && ipts->pppcfg & SC_COMP_TCP) {	/* ipts here? probably yes .. but this check again */
+	if (proto == PPP_IP && ipts->pppcfg & SC_COMP_TCP) {	/* ipts here? probably yes, but check this again */
 		struct sk_buff *new_skb;
 
 		new_skb = dev_alloc_skb(skb->len);
@@ -1310,14 +1363,13 @@
 			int pktlen;
 
 			new_skb->dev = skb->dev;
-			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));
 
-			if (buf != skb->data) {	/* copied to new buffer ??? (btw: WHY must slhc copy it?? *sigh*)  */
+			if (buf != skb->data) {	
 				if (new_skb->data != buf)
 					printk(KERN_ERR "isdn_ppp: FATAL error after slhc_compress!!\n");
 				dev_kfree_skb(skb);
@@ -1339,6 +1391,11 @@
 	}
 #endif
 
+    /*
+     * normal or bundle compression
+     */
+	skb = isdn_ppp_compress(skb,&proto,ipt,ipts,0);
+
 	if (ipt->debug & 0x24)
 		printk(KERN_DEBUG "xmit2 skb, len %d, proto %04x\n", (int) skb->len, proto);
 
@@ -1349,13 +1406,17 @@
 		ipts->mp_seqno++;
 		nd->queue = nd->queue->next;
 		if (ipt->mpppcfg & SC_OUT_SHORT_SEQ) {
-			unsigned char *data = skb_push(skb, 3);
+			unsigned char *data = isdn_ppp_skb_push(&skb, 3);
+			if(!data)
+				return 0;
 			mp_seqno &= 0xfff;
-			data[0] = MP_BEGIN_FRAG | MP_END_FRAG | (mp_seqno >> 8);	/* (B)egin & (E)ndbit .. */
+			data[0] = MP_BEGIN_FRAG | MP_END_FRAG | ((mp_seqno >> 8) & 0xf);	/* (B)egin & (E)ndbit .. */
 			data[1] = mp_seqno & 0xff;
 			data[2] = proto;	/* PID compression */
 		} else {
-			unsigned char *data = skb_push(skb, 5);
+			unsigned char *data = isdn_ppp_skb_push(&skb, 5);
+			if(!data)
+				return 0;
 			data[0] = MP_BEGIN_FRAG | MP_END_FRAG;	/* (B)egin & (E)ndbit .. */
 			data[1] = (mp_seqno >> 16) & 0xff;	/* sequence number: 24bit */
 			data[2] = (mp_seqno >> 8) & 0xff;
@@ -1365,17 +1426,29 @@
 		proto = PPP_MP; /* MP Protocol, 0x003d */
 	}
 #endif
+
+	/*
+	 * 'link' compression 
+	 */
+	skb = isdn_ppp_compress(skb,&proto,ipt,ipts,1);
+
 	if( (ipt->pppcfg & SC_COMP_PROT) && (proto <= 0xff) ) {
-		unsigned char *data = skb_push(skb,1);
+		unsigned char *data = isdn_ppp_skb_push(&skb,1);
+		if(!data)
+			return 0;
 		data[0] = proto & 0xff;
 	}
 	else {
-		unsigned char *data = skb_push(skb,2);
+		unsigned char *data = isdn_ppp_skb_push(&skb,2);
+		if(!data)
+			return 0;
 		data[0] = (proto >> 8) & 0xff;
 		data[1] = proto & 0xff;
 	}
 	if(!(ipt->pppcfg & SC_COMP_AC)) {
-		unsigned char *data = skb_push(skb,2);
+		unsigned char *data = isdn_ppp_skb_push(&skb,2);
+		if(!data)
+			return 0;
 		data[0] = 0xff;    /* All Stations */
 		data[1] = 0x03;    /* Unnumbered information */
 	}
@@ -1406,10 +1479,8 @@
 	p->ib.sq = NULL;
 	while (q) {
 		struct sqqueue *qn = q->next;
-		if (q->skb) {
-			SET_SKB_FREE(q->skb);
+		if (q->skb)
 			dev_kfree_skb(q->skb);
-		}
 		kfree(q);
 		q = qn;
 	}
@@ -1424,7 +1495,6 @@
 
 	while (q) {
 		struct mpqueue *ql = q->next;
-		SET_SKB_FREE(q->skb);
 		dev_kfree_skb(q->skb);
 		kfree(q);
 		q = ql;
@@ -1599,7 +1669,6 @@
 	if (!(*skb)) {
 		while (q) {
 			struct mpqueue *ql = q->next;
-			SET_SKB_FREE(q->skb);
 			dev_kfree_skb(q->skb);
 			kfree(q);
 			q = ql;
@@ -1612,7 +1681,6 @@
 		struct mpqueue *ql = q->next;
 		memcpy((*skb)->data + cnt, q->skb->data, q->skb->len);
 		cnt += q->skb->len;
-		SET_SKB_FREE(q->skb);
 		dev_kfree_skb(q->skb);
 		kfree(q);
 		q = ql;
@@ -1632,13 +1700,15 @@
 	struct sqqueue *q;
 
 	while ((q = net_dev->ib.sq) && (q->sqno_start == net_dev->ib.next_num || q->sqno_end <= min_sqno)) {
+		int proto;
 		if (q->sqno_start != net_dev->ib.next_num) {
 			printk(KERN_DEBUG "ippp: MP, stepping over missing frame: %ld\n", net_dev->ib.next_num);
 #ifdef CONFIG_ISDN_PPP_VJ
-			slhc_toss(ippp_table[net_dev->local.ppp_slot]->slcomp);
+			slhc_toss(ippp_table[net_dev->local->ppp_slot]->slcomp);
 #endif
 		}
-		isdn_ppp_push_higher(net_dev, lp, q->skb, -1);
+		proto = isdn_ppp_strip_proto(q->skb);
+		isdn_ppp_push_higher(net_dev, lp, q->skb, proto);
 		net_dev->ib.sq = q->next;
 		net_dev->ib.next_num = q->sqno_end + 1;
 		kfree(q);
@@ -1663,32 +1733,30 @@
 
 	struct mpqueue *ql,
 	*q = dev->mp_last;
-	while (q) {
-		if (q->sqno < min_sqno) {
-			if (q->BEbyte & MP_END_FRAG) {
-				printk(KERN_DEBUG "ippp: freeing stale packet!\n");
-				if ((dev->mp_last = q->next))
-					q->next->last = NULL;
-				while (q) {
-					ql = q->last;
-					SET_SKB_FREE(q->skb);
-					dev_kfree_skb(q->skb);
-					kfree(q);
+	while(q && (q->sqno < min_sqno) ) {
+		if ( (q->BEbyte & MP_END_FRAG) || 
+			 (q->next && (q->next->sqno <= min_sqno) && (q->next->BEbyte & MP_BEGIN_FRAG)) ) {
+			printk(KERN_DEBUG "ippp: freeing stale packet(s), min_sq: %ld!\n",min_sqno);
+			if ((dev->mp_last = q->next))
+				q->next->last = NULL;
+			while (q) {
+				ql = q->last;
+				printk(KERN_DEBUG "ippp, freeing packet with sqno: %ld\n",q->sqno);
+				dev_kfree_skb(q->skb);
+				kfree(q);
 #ifdef CONFIG_ISDN_PPP_VJ
-					toss = 1;
+				toss = 1;
 #endif
-					q = ql;
-				}
-				q = dev->mp_last;
-			} else
-				q = q->next;
+				q = ql;
+			}
+			q = dev->mp_last;
 		} else
-			break;
+			q = q->next;
 	}
 #ifdef CONFIG_ISDN_PPP_VJ
 	/* did we free a stale frame ? */
 	if (toss)
-		slhc_toss(ippp_table[dev->local.ppp_slot]->slcomp);
+		slhc_toss(ippp_table[dev->local->ppp_slot]->slcomp);
 #endif
 }
 
@@ -1708,7 +1776,7 @@
 	*qn;
 
 	while (net_dev) {
-		isdn_net_local *lp = &net_dev->local;
+		isdn_net_local *lp = net_dev->local;
 		if (net_dev->ib.modify || lp->master) {	/* interface locked or slave? */
 			net_dev = net_dev->next;
 			continue;
@@ -1728,7 +1796,8 @@
 				net_dev->ib.next_num = q->sqno_end + 1;
 				q->next = NULL;
 				for (; ql;) {
-					isdn_ppp_push_higher(net_dev, lp, ql->skb, -1);
+					int proto = isdn_ppp_strip_proto(ql->skb);
+					isdn_ppp_push_higher(net_dev, lp, ql->skb, proto);
 					qn = ql->next;
 					kfree(ql);
 					ql = qn;
@@ -1804,7 +1873,7 @@
 		case SIOCGPPPVER:
 			r = (char *) ifr->ifr_ifru.ifru_data;
 			len = strlen(PPP_VERSION) + 1;
-		        error = copy_to_user(r, PPP_VERSION, len);
+			error = copy_to_user(r, PPP_VERSION, len);
 			break;
 		case SIOCGPPPSTATS:
 			error = isdn_ppp_dev_ioctl_stats(lp->ppp_slot, ifr, dev);
@@ -1853,7 +1922,7 @@
 
 	if (!(ndev = isdn_net_findif(name)))
 		return 1;
-	lp = &ndev->local;
+	lp = ndev->local;
 	if (!(lp->flags & ISDN_NET_CONNECTED))
 		return 5;
 
@@ -1884,7 +1953,7 @@
 
 	if (!(ndev = isdn_net_findif(name)))
 		return 1;
-	lp = &ndev->local;
+	lp = ndev->local;
 	if (!(lp->flags & ISDN_NET_CONNECTED))
 		return 5;
 
@@ -1905,6 +1974,128 @@
 #endif
 }
 
+/*
+ * PPP compression stuff
+ */
+static struct sk_buff *isdn_ppp_decompress(struct sk_buff *skb,struct ippp_struct *is,struct ippp_struct *master)
+{
+#if 1
+	printk(KERN_ERR "compression not included!\n");
+	dev_kfree_skb(skb);
+	return NULL;
+#else
+	if(!master) {
+		/* 
+		 * single link compression 
+		 */
+		if(!is->link_compressor) {
+			printk(KERN_ERR "ippp: no (link) compressor defined!\n");
+			dev_kfree_skb(skb);
+			return NULL;
+		}
+		if(!is->link_decomp_stat) {
+			printk(KERN_DEBUG "ippp: initialize link compressor\n");
+		}
+/*
+		-> decompress link
+*/
+    }
+	else {
+		/*
+		 * 'normal' or bundle-compression 
+		 */
+		if(!master->compressor) {
+			printk(KERN_ERR "ippp: no (link) compressor defined!\n");
+			dev_kfree_skb(skb);
+			return NULL;
+		}
+		if(!master->decomp_stat) {
+#if 0
+			master->decomp_stat = (master->compressor->decomp_alloc)( .. );
+#endif
+			printk(KERN_DEBUG "ippp: initialize compressor\n");
+		}
+	}
+	
+	return skb;
+#endif
+}
+
+/*
+ * compress a frame 
+ *   type=0: normal/bundle compression
+ *       =1: link compression
+ * returns original skb if we haven't compressed the frame
+ * and a new skb pointer if we've done it
+ */
+static struct sk_buff *isdn_ppp_compress(struct sk_buff *skb_in,int *proto,
+	struct ippp_struct *is,struct ippp_struct *master,int type)
+{
+#if 1	
+	return skb_in;
+#else
+    int ret;
+    int new_proto;
+    struct isdn_ppp_compressor *compressor;
+    void *stat;
+    struct sk_buff *skb_out;
+
+	if(type) { /* type=1 => Link compression */
+		compressor = is->link_compressor;
+		stat = is->link_comp_stat;
+		new_proto = PPP_LINK_COMP;
+	}
+	else {
+		if(!master) {
+			compressor = is->compressor;
+			stat = is->comp_stat;
+		}
+		else {
+			compressor = master->compressor;
+			stat = master->comp_stat;
+		}
+		new_proto = PPP_COMP;
+	}
+
+	if(!compressor) {
+		printk(KERN_ERR "No compressor set!\n");
+		return skb_in;
+	}
+	if(!stat) {
+		/* init here ? */
+		return skb_in;
+	}
+
+	skb_out = dev_alloc_skb(skb_in->len);
+	if(!skb_out)
+		return skb_in;
+
+	ret = (compressor->compress)(stat,skb_in,skb_out,*proto);
+	if(!ret) {
+		dev_kfree_skb(skb_out);
+		return skb_in;
+	}
+	
+	dev_kfree_skb(skb_in);
+	*proto = new_proto;
+	return skb_out;
+#endif
+
+}
+
+/*
+ * we received a CCP frame .. 
+ * not a clean solution, but we SHOULD handle a few cased in the kernel
+ */
+static void isdn_ppp_receive_ccp(isdn_net_dev *net_dev, isdn_net_local *lp,
+	 struct sk_buff *skb)
+{
+#if 0
+	printk(KERN_DEBUG "isdn_ppp_receive_cpp: %02x %02x %02x %02x %02x %02x %02x %02x\n",
+		skb->data[0],skb->data[1],skb->data[2],skb->data[3],
+		skb->data[4],skb->data[5],skb->data[6],skb->data[7] );
+#endif
+}
 
 int isdn_ppp_register_compressor(struct isdn_ppp_compressor *ipc)
 {
@@ -1937,6 +2128,7 @@
 		if(ipc->num == num) {
 			return 0;	
 			is->compressor = ipc;
+			is->link_compressor = ipc;
 		}
 		ipc = ipc->next;
 	}
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/isdn_ppp.h linux/drivers/isdn/isdn_ppp.h
--- v2.1.91/linux/drivers/isdn/isdn_ppp.h	Thu Feb 27 10:57:30 1997
+++ linux/drivers/isdn/isdn_ppp.h	Wed Apr  1 16:21:03 1998
@@ -1,4 +1,4 @@
-/* $Id: isdn_ppp.h,v 1.9 1997/02/11 18:32:59 fritz Exp $
+/* $Id: isdn_ppp.h,v 1.12 1998/01/31 22:07:48 keil Exp $
 
  * header for Linux ISDN subsystem, functions for synchronous PPP (linklevel).
  *
@@ -19,6 +19,21 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_ppp.h,v $
+ * Revision 1.12  1998/01/31 22:07:48  keil
+ * changes for newer kernels
+ *
+ * Revision 1.11  1997/10/01 09:20:44  fritz
+ * Removed old compatibility stuff for 2.0.X kernels.
+ * From now on, this code is for 2.1.X ONLY!
+ * Old stuff is still in the separate branch.
+ *
+ * Revision 1.10  1997/06/17 13:06:00  hipp
+ * Applied Eric's underflow-patches (slightly modified)
+ * more compression changes (but disabled at the moment)
+ * changed one copy_to_user() to run with enabled IRQs
+ * a few MP changes
+ * changed 'proto' handling in the isdn_ppp receive code
+ *
  * Revision 1.9  1997/02/11 18:32:59  fritz
  * Bugfix in isdn_ppp_free_mpqueue().
  *
@@ -63,11 +78,7 @@
 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);
-#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 unsigned int isdn_ppp_poll(struct file *, struct poll_table_struct *);
 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 *);
@@ -78,3 +89,7 @@
 #define IPPP_CLOSEWAIT	0x04
 #define IPPP_NOBLOCK	0x08
 #define IPPP_ASSIGNED	0x10
+
+#define IPPP_MAX_HEADER 10
+
+
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/isdn_syms.c linux/drivers/isdn/isdn_syms.c
--- v2.1.91/linux/drivers/isdn/isdn_syms.c	Mon Feb 23 18:12:04 1998
+++ linux/drivers/isdn/isdn_syms.c	Wed Dec 31 16:00:00 1969
@@ -1,54 +0,0 @@
-/* $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/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.91/linux/drivers/isdn/isdn_tty.c linux/drivers/isdn/isdn_tty.c
--- v2.1.91/linux/drivers/isdn/isdn_tty.c	Thu Feb 12 20:56:06 1998
+++ linux/drivers/isdn/isdn_tty.c	Wed Apr  1 16:21:03 1998
@@ -1,4 +1,4 @@
-/* $Id: isdn_tty.c,v 1.41 1997/05/27 15:17:31 fritz Exp $
+/* $Id: isdn_tty.c,v 1.47 1998/02/22 19:44:14 fritz Exp $
 
  * Linux ISDN subsystem, tty functions and AT-command emulator (linklevel).
  *
@@ -20,6 +20,35 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_tty.c,v $
+ * Revision 1.47  1998/02/22 19:44:14  fritz
+ * Bugfixes and improvements regarding V.110, V.110 now running.
+ *
+ * Revision 1.46  1998/02/20 17:23:08  fritz
+ * Changes for recent kernels.
+ * Merged in contributions by Thomas Pfeiffer (V.110 T.70+ Extended FAX stuff)
+ * Added symbolic constants for Modem-Registers.
+ *
+ * Revision 1.45  1998/01/31 22:07:49  keil
+ * changes for newer kernels
+ *
+ * Revision 1.44  1998/01/31 19:30:02  calle
+ * Merged changes from and for 2.1.82, not tested only compiled ...
+ *
+ * Revision 1.43  1997/10/09 21:29:04  fritz
+ * New HL<->LL interface:
+ *   New BSENT callback with nr. of bytes included.
+ *   Sending without ACK.
+ *   New L1 error status (not yet in use).
+ *   Cleaned up obsolete structures.
+ * Implemented Cisco-SLARP.
+ * Changed local net-interface data to be dynamically allocated.
+ * Removed old 2.0 compatibility stuff.
+ *
+ * Revision 1.42  1997/10/01 09:20:49  fritz
+ * Removed old compatibility stuff for 2.0.X kernels.
+ * From now on, this code is for 2.1.X ONLY!
+ * Old stuff is still in the separate branch.
+ *
  * Revision 1.41  1997/05/27 15:17:31  fritz
  * Added changes for recent 2.1.x kernels:
  *   changed return type of isdn_close
@@ -199,6 +228,8 @@
 #define VBUFX (VBUF/16)
 #endif
 
+#define FIX_FILE_TRANSFER
+
 /* Prototypes */
 
 static int isdn_tty_edit_at(const char *, int, modem_info *, int);
@@ -223,12 +254,63 @@
 static int si2bit[8] =
 {4, 1, 4, 4, 4, 4, 4, 4};
 
-char *isdn_tty_revision = "$Revision: 1.41 $";
+char *isdn_tty_revision = "$Revision: 1.47 $";
 
 #define DLE 0x10
 #define ETX 0x03
 #define DC4 0x14
 
+/*
+ * Definition of some special Registers of AT-Emulator
+ */
+#define REG_RINGATA   0
+#define REG_RINGCNT   1
+#define REG_ESC       2
+#define REG_CR        3
+#define REG_LF        4
+#define REG_BS        5
+
+#define REG_RESP     12
+#define BIT_RESP      1
+#define REG_RESPNUM  12
+#define BIT_RESPNUM   2
+#define REG_ECHO     12
+#define BIT_ECHO      4
+#define REG_DCD      12
+#define BIT_DCD       8
+#define REG_CTS      12
+#define BIT_CTS      16
+#define REG_DTRR     12
+#define BIT_DTRR     32
+#define REG_DSR      12
+#define BIT_DSR      64
+#define REG_CPPP     12
+#define BIT_CPPP    128
+
+#define REG_DELXMT   13
+#define BIT_DELXMT    1
+#define REG_T70      13
+#define BIT_T70       2
+#define BIT_T70_EXT  32
+#define REG_DTRHUP   13
+#define BIT_DTRHUP    4
+#define REG_RESPXT   13
+#define BIT_RESPXT    8
+#define REG_CIDONCE  13
+#define BIT_CIDONCE  16
+#define REG_RUNG     13
+#define BIT_RUNG     64
+
+#define REG_L2PROT   14
+#define REG_L3PROT   15
+#define REG_PSIZE    16
+#define REG_WSIZE    17
+#define REG_SI1      18
+#define REG_SI2      19
+#define REG_SI1I     20
+#define REG_PLAN     21
+#define REG_SCREEN   22
+
 /* isdn_tty_try_read() is called from within isdn_tty_rcv_skb()
  * to stuff incoming data directly into a tty's flip-buffer. This
  * is done to speed up tty-receiving if the receive-queue is empty.
@@ -272,10 +354,9 @@
 #ifdef CONFIG_ISDN_AUDIO
 					}
 #endif
-					if (info->emu.mdmreg[12] & 128)
+					if (info->emu.mdmreg[REG_CPPP] & BIT_CPPP)
 						tty->flip.flag_buf_ptr[len - 1] = 0xff;
 					queue_task(&tty->flip.tqueue, &tq_timer);
-					SET_SKB_FREE(skb);
 					kfree_skb(skb);
 					return 1;
 				}
@@ -319,7 +400,7 @@
 									   tty->flip.char_buf_ptr,
 									   tty->flip.flag_buf_ptr, c, 0);
 							/* CISCO AsyncPPP Hack */
-							if (!(info->emu.mdmreg[12] & 128))
+							if (!(info->emu.mdmreg[REG_CPPP] & BIT_CPPP))
 								memset(tty->flip.flag_buf_ptr, 0, r);
 							tty->flip.count += r;
 							tty->flip.flag_buf_ptr += r;
@@ -371,19 +452,26 @@
 #endif
 		) {
 		/* If Modem not listening, drop data */
-		SET_SKB_FREE(skb);
 		kfree_skb(skb);
 		return 1;
 	}
-	if (info->emu.mdmreg[13] & 2)
-		/* T.70 decoding: Simply throw away the T.70 header (4 bytes) */
-		if ((skb->data[0] == 1) && ((skb->data[1] == 0) || (skb->data[1] == 1)))
-			skb_pull(skb, 4);
+	if (info->emu.mdmreg[REG_T70] & BIT_T70) {
+		if (info->emu.mdmreg[REG_T70] & BIT_T70_EXT) {
+			/* T.70 decoding: throw away the T.70 header (2 or 4 bytes)   */
+			if (skb->data[0] == 3) /* pure data packet -> 4 byte headers  */
+				skb_pull(skb, 4);
+			else
+				if (skb->data[0] == 1) /* keepalive packet -> 2 byte hdr  */
+					skb_pull(skb, 2);
+		} else
+			/* T.70 decoding: Simply throw away the T.70 header (4 bytes) */
+			if ((skb->data[0] == 1) && ((skb->data[1] == 0) || (skb->data[1] == 1)))
+				skb_pull(skb, 4);
+	}
 #ifdef CONFIG_ISDN_AUDIO
 	if (skb_headroom(skb) < sizeof(isdn_audio_skb)) {
 		printk(KERN_WARNING
 		       "isdn_audio: insufficient skb_headroom, dropping\n");
-		SET_SKB_FREE(skb);
 		kfree_skb(skb);
 		return 1;
 	}
@@ -454,16 +542,12 @@
 	save_flags(flags);
 	cli();
 	if (skb_queue_len(&info->xmit_queue))
-		while ((skb = skb_dequeue(&info->xmit_queue))) {
-			SET_SKB_FREE(skb);
+		while ((skb = skb_dequeue(&info->xmit_queue)))
 			kfree_skb(skb);
-		}
 #ifdef CONFIG_ISDN_AUDIO
 	if (skb_queue_len(&info->dtmf_queue))
-		while ((skb = skb_dequeue(&info->dtmf_queue))) {
-			SET_SKB_FREE(skb);
+		while ((skb = skb_dequeue(&info->dtmf_queue)))
 			kfree_skb(skb);
-		}
 #endif
 	restore_flags(flags);
 }
@@ -479,7 +563,7 @@
 		return;
 	len = skb->len;
 	if ((slen = isdn_writebuf_skb_stub(info->isdn_driver,
-					   info->isdn_channel, skb)) == len) {
+					   info->isdn_channel, 1, skb)) == len) {
 		struct tty_struct *tty = info->tty;
 		info->send_outstanding++;
 		info->msr |= UART_MSR_CTS;
@@ -492,7 +576,6 @@
 	}
 	if (slen < 0) {
 		/* Error: no channel, already shutdown, or wrong parameter */
-		SET_SKB_FREE(skb);
 		dev_kfree_skb(skb);
 		return;
 	}
@@ -590,7 +673,7 @@
 
 	while (c--) {
 		if (from_user)
-			GET_USER(ch, buf);
+			get_user(ch, buf);
 		else
 			ch = *buf;
 		if ((ch != 0x11) && (ch != 0x13))
@@ -640,7 +723,7 @@
 		restore_flags(flags);
 		return;
 	}
-	if ((info->emu.mdmreg[12] & 0x10) != 0)
+	if ((info->emu.mdmreg[REG_CTS] & BIT_CTS) != 0)
 		info->msr &= ~UART_MSR_CTS;
 	info->lsr &= ~UART_LSR_TEMT;
 	if (info->isdn_driver < 0) {
@@ -710,10 +793,13 @@
 		}
 	}
 #endif                          /* CONFIG_ISDN_AUDIO */
-	SET_SKB_FREE(skb);
-	if (info->emu.mdmreg[13] & 2)
+	if (info->emu.mdmreg[REG_T70] & BIT_T70) {
 		/* Add T.70 simplified header */
-		memcpy(skb_push(skb, 4), "\1\0\1\0", 4);
+		if (info->emu.mdmreg[REG_T70] & BIT_T70_EXT)
+			memcpy(skb_push(skb, 2), "\1\0", 2);
+		else
+			memcpy(skb_push(skb, 4), "\1\0\1\0", 4);
+	}
 	skb_queue_tail(&info->xmit_queue, skb);
 }
 
@@ -761,27 +847,27 @@
 {
 	int usg = ISDN_USAGE_MODEM;
 	int si = 7;
-	int l2 = m->mdmreg[14];
+	int l2 = m->mdmreg[REG_L2PROT];
 	isdn_ctrl cmd;
 	ulong flags;
 	int i;
 	int j;
 
 	for (j = 7; j >= 0; j--)
-		if (m->mdmreg[18] & (1 << j)) {
+		if (m->mdmreg[REG_SI1] & (1 << j)) {
 			si = bit2si[j];
 			break;
 		}
 #ifdef CONFIG_ISDN_AUDIO
 	if (si == 1) {
-		l2 = 4;
+		l2 = ISDN_PROTO_L2_TRANS;
 		usg = ISDN_USAGE_VOICE;
 	}
 #endif
-	m->mdmreg[20] = si2bit[si];
+	m->mdmreg[REG_SI1I] = si2bit[si];
 	save_flags(flags);
 	cli();
-	i = isdn_get_free_channel(usg, l2, m->mdmreg[15], -1, -1);
+	i = isdn_get_free_channel(usg, l2, m->mdmreg[REG_L3PROT], -1, -1);
 	if (i < 0) {
 		restore_flags(flags);
 		isdn_tty_modem_result(6, info);
@@ -798,32 +884,32 @@
 		cmd.driver = info->isdn_driver;
 		cmd.arg = info->isdn_channel;
 		cmd.command = ISDN_CMD_CLREAZ;
-		dev->drv[info->isdn_driver]->interface->command(&cmd);
+		isdn_command(&cmd);
 		strcpy(cmd.parm.num, isdn_map_eaz2msn(m->msn, info->isdn_driver));
 		cmd.driver = info->isdn_driver;
 		cmd.command = ISDN_CMD_SETEAZ;
-		dev->drv[info->isdn_driver]->interface->command(&cmd);
+		isdn_command(&cmd);
 		cmd.driver = info->isdn_driver;
 		cmd.command = ISDN_CMD_SETL2;
 		info->last_l2 = l2;
 		cmd.arg = info->isdn_channel + (l2 << 8);
-		dev->drv[info->isdn_driver]->interface->command(&cmd);
+		isdn_command(&cmd);
 		cmd.driver = info->isdn_driver;
 		cmd.command = ISDN_CMD_SETL3;
-		cmd.arg = info->isdn_channel + (m->mdmreg[15] << 8);
-		dev->drv[info->isdn_driver]->interface->command(&cmd);
+		cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8);
+		isdn_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.parm.setup.si2 = m->mdmreg[REG_SI2];
 		cmd.command = ISDN_CMD_DIAL;
 		info->dialing = 1;
 		strcpy(dev->num[i], n);
 		isdn_info_update();
-		dev->drv[info->isdn_driver]->interface->command(&cmd);
+		isdn_command(&cmd);
 	}
 }
 
@@ -865,6 +951,9 @@
 		info->adpcmr = NULL;
 	}
 #endif
+	if ((info->msr & UART_MSR_RI) &&
+		(info->emu.mdmreg[REG_RUNG] & BIT_RUNG))
+		isdn_tty_modem_result(12, info);
 	info->msr &= ~(UART_MSR_DCD | UART_MSR_RI);
 	info->lsr |= UART_LSR_TEMT;
 	if (info->isdn_driver >= 0) {
@@ -872,11 +961,11 @@
 			cmd.driver = info->isdn_driver;
 			cmd.command = ISDN_CMD_HANGUP;
 			cmd.arg = info->isdn_channel;
-			dev->drv[info->isdn_driver]->interface->command(&cmd);
+			isdn_command(&cmd);
 		}
 		isdn_all_eaz(info->isdn_driver, info->isdn_channel);
-		info->emu.mdmreg[1] = 0;
-		usage = (info->emu.mdmreg[20] == 1) ?
+		info->emu.mdmreg[REG_RINGCNT] = 0;
+		usage = (info->emu.mdmreg[REG_SI1I] == 1) ?
 		    ISDN_USAGE_VOICE : ISDN_USAGE_MODEM;
 		isdn_free_channel(info->isdn_driver, info->isdn_channel,
 				  usage);
@@ -937,7 +1026,7 @@
 		isdn_tty_modem_ncarrier(info);
 	} else {
 		info->mcr &= ~UART_MCR_DTR;
-		if (info->emu.mdmreg[13] & 4) {
+		if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) {
 #ifdef ISDN_DEBUG_MODEM_HUP
 			printk(KERN_DEBUG "Mhup in changespeed\n");
 #endif
@@ -1020,7 +1109,7 @@
 	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) {
+		if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) {
 			isdn_tty_modem_reset_regs(info, 0);
 #ifdef ISDN_DEBUG_MODEM_HUP
 			printk(KERN_DEBUG "Mhup in isdn_tty_shutdown\n");
@@ -1047,8 +1136,8 @@
 static int
 isdn_tty_write(struct tty_struct *tty, int from_user, const u_char * buf, int count)
 {
-	int c,
-	 total = 0;
+	int c;
+	int total = 0;
 	ulong flags;
 	modem_info *info = (modem_info *) tty->driver_data;
 
@@ -1074,7 +1163,7 @@
 #ifdef CONFIG_ISDN_AUDIO
 			if (!info->vonline)
 #endif
-				isdn_tty_check_esc(buf, m->mdmreg[2], c,
+				isdn_tty_check_esc(buf, m->mdmreg[REG_ESC], c,
 						   &(m->pluscount),
 						   &(m->lastplus),
 						   from_user);
@@ -1116,7 +1205,7 @@
 			} else
 #endif
 				info->xmit_count += c;
-			if (m->mdmreg[13] & 1) {
+			if (m->mdmreg[REG_DELXMT] & BIT_DELXMT) {
 				isdn_tty_senddown(info);
 				isdn_tty_tint(info);
 			}
@@ -1304,7 +1393,7 @@
 	uint arg;
 	int pre_dtr;
 
-	GET_USER(arg, (uint *) value);
+	get_user(arg, (uint *) value);
 	switch (cmd) {
 		case TIOCMBIS:
 #ifdef ISDN_DEBUG_MODEM_IOCTL
@@ -1327,7 +1416,7 @@
 			}
 			if (arg & TIOCM_DTR) {
 				info->mcr &= ~UART_MCR_DTR;
-				if (info->emu.mdmreg[13] & 4) {
+				if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) {
 					isdn_tty_modem_reset_regs(info, 0);
 #ifdef ISDN_DEBUG_MODEM_HUP
 					printk(KERN_DEBUG "Mhup in TIOCMBIC\n");
@@ -1348,7 +1437,7 @@
 			       | ((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) {
+					if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) {
 						isdn_tty_modem_reset_regs(info, 0);
 #ifdef ISDN_DEBUG_MODEM_HUP
 						printk(KERN_DEBUG "Mhup in TIOCMSET\n");
@@ -1414,7 +1503,7 @@
 			error = verify_area(VERIFY_READ, (void *) arg, sizeof(long));
 			if (error)
 				return error;
-			GET_USER(arg, (ulong *) arg);
+			get_user(arg, (ulong *) arg);
 			tty->termios->c_cflag =
 			    ((tty->termios->c_cflag & ~CLOCAL) |
 			     (arg ? CLOCAL : 0));
@@ -1443,7 +1532,6 @@
 				return error;
 			else
 				return isdn_tty_get_lsr_info(info, (uint *) arg);
-
 		default:
 #ifdef ISDN_DEBUG_MODEM_IOCTL
 			printk(KERN_DEBUG "UNKNOWN ioctl 0x%08x on ttyi%d\n", cmd, info->line);
@@ -1823,10 +1911,10 @@
 isdn_tty_modem_reset_regs(modem_info * info, int force)
 {
 	atemu *m = &info->emu;
-	if ((m->mdmreg[12] & 32) || force) {
+	if ((m->mdmreg[REG_DTRR] & BIT_DTRR) || force) {
 		memcpy(m->mdmreg, m->profile, ISDN_MODEM_ANZREG);
 		memcpy(m->msn, m->pmsn, ISDN_MSNLEN);
-		info->xmit_size = m->mdmreg[16] * 16;
+		info->xmit_size = m->mdmreg[REG_PSIZE] * 16;
 	}
 #ifdef CONFIG_ISDN_AUDIO
 	isdn_tty_modem_reset_vpar(m);
@@ -1881,6 +1969,7 @@
 	m->tty_modem.stop = NULL;
 	m->tty_modem.start = NULL;
 	m->tty_modem.hangup = isdn_tty_hangup;
+	m->tty_modem.driver_name = "isdn_tty";
 	/*
 	 * The callout device is just like normal device except for
 	 * major number and the subtype code.
@@ -1905,7 +1994,7 @@
 		sprintf(info->last_num, "none");
 		info->last_dir = 0;
 		info->last_lhup = 1;
-		info->last_l2 = 0;
+		info->last_l2 = -1;
 		info->last_si = 0;
 		isdn_tty_reset_profile(&info->emu);
 		isdn_tty_modem_reset_regs(info, 1);
@@ -1927,7 +2016,7 @@
 #ifdef CONFIG_ISDN_AUDIO
 		skb_queue_head_init(&info->dtmf_queue);
 #endif
-		if (!(info->xmit_buf = kmalloc(ISDN_SERIAL_XMIT_SIZE + 5, GFP_KERNEL))) {
+		if (!(info->xmit_buf = kmalloc(ISDN_SERIAL_XMIT_MAX + 5, GFP_KERNEL))) {
 			printk(KERN_ERR "Could not allocate modem xmit-buffer\n");
 			return -3;
 		}
@@ -1977,12 +2066,12 @@
 #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]);
+		       info->emu.mdmreg[REG_SI1], info->emu.mdmreg[REG_SI2]);
 #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[REG_SI1] & si2bit[si1]) &&  /* SI1 is matching */
+		    (info->emu.mdmreg[REG_SI2] == si2)) {	  /* SI2 is matching */
 			idx = isdn_dc2minor(di, ch);
 #ifdef ISDN_DEBUG_MODEM_ICALL
 			printk(KERN_DEBUG "m_fi: match1\n");
@@ -1990,7 +2079,10 @@
 			       info->flags, info->isdn_driver, info->isdn_channel,
 			       dev->usage[idx]);
 #endif
-			if ((info->flags & ISDN_ASYNC_NORMAL_ACTIVE) &&
+			if (
+#ifndef FIX_FILE_TRANSFER
+				(info->flags & ISDN_ASYNC_NORMAL_ACTIVE) &&
+#endif
 			    (info->isdn_driver == -1) &&
 			    (info->isdn_channel == -1) &&
 			    (USG_NONE(dev->usage[idx]))) {
@@ -2001,9 +2093,9 @@
 				dev->usage[idx] &= ISDN_USAGE_EXCLUSIVE;
 				dev->usage[idx] |= (si1 == 1) ? ISDN_USAGE_VOICE : ISDN_USAGE_MODEM;
 				strcpy(dev->num[idx], nr);
-				info->emu.mdmreg[20] = si2bit[si1];
-				info->emu.mdmreg[21] = setup.plan;
-				info->emu.mdmreg[22] = setup.screen;
+				info->emu.mdmreg[REG_SI1I] = si2bit[si1];
+				info->emu.mdmreg[REG_PLAN] = setup.plan;
+				info->emu.mdmreg[REG_SCREEN] = setup.screen;
 				isdn_info_update();
 				restore_flags(flags);
 				printk(KERN_INFO "isdn_tty: call from %s, -> RING on ttyI%d\n", nr,
@@ -2029,12 +2121,20 @@
 {
 	int mi;
 	modem_info *info;
+	char *e;
 
 	if (i < 0)
 		return 0;
 	if ((mi = dev->m_idx[i]) >= 0) {
 		info = &dev->mdm.info[mi];
 		switch (c->command) {
+                        case ISDN_STAT_CINF:
+                                printk(KERN_DEBUG "CHARGEINFO on ttyI%d: %ld %s\n", info->line, c->arg, c->parm.num);
+                                info->emu.charge = (unsigned) simple_strtoul(c->parm.num, &e, 10);
+                                if (e == c->parm.num)
+					info->emu.charge = 0;
+				
+                                break;			
 			case ISDN_STAT_BSENT:
 #ifdef ISDN_TTY_STAT_DEBUG
 				printk(KERN_DEBUG "tty_STAT_BSENT ttyI%d\n", info->line);
@@ -2093,6 +2193,7 @@
 				 */
 				if (TTY_IS_ACTIVE(info)) {
 					info->msr |= UART_MSR_DCD;
+					info->emu.charge = 0;
 					if (info->dialing) {
 						info->dialing = 0;
 						info->last_dir = 1;
@@ -2184,13 +2285,13 @@
 	for (p = msg; *p; p++) {
 		switch (*p) {
 			case '\r':
-				c = m->mdmreg[3];
+				c = m->mdmreg[REG_CR];
 				break;
 			case '\n':
-				c = m->mdmreg[4];
+				c = m->mdmreg[REG_LF];
 				break;
 			case '\b':
-				c = m->mdmreg[5];
+				c = m->mdmreg[REG_BS];
 				break;
 			default:
 				c = *p;
@@ -2293,14 +2394,14 @@
 	static char *msg[] =
 	{"OK", "CONNECT", "RING", "NO CARRIER", "ERROR",
 	 "CONNECT 64000", "NO DIALTONE", "BUSY", "NO ANSWER",
-	 "RINGING", "NO MSN/EAZ", "VCON"};
+	 "RINGING", "NO MSN/EAZ", "VCON", "RUNG"};
 	ulong flags;
 	char s[10];
 
 	switch (code) {
 		case 2:
-			m->mdmreg[1]++;	/* RING */
-			if (m->mdmreg[1] == m->mdmreg[0])
+			m->mdmreg[REG_RINGCNT]++;	/* RING */
+			if (m->mdmreg[REG_RINGCNT] == m->mdmreg[REG_RINGATA])
 				/* Automatically accept incoming call */
 				isdn_tty_cmd_ATA(info);
 			break;
@@ -2313,7 +2414,7 @@
 #endif
 			save_flags(flags);
 			cli();
-			m->mdmreg[1] = 0;
+			m->mdmreg[REG_RINGCNT] = 0;
 			del_timer(&info->nc_timer);
 			info->ncarrier = 0;
 			if ((info->flags & ISDN_ASYNC_CLOSING) || (!info->tty)) {
@@ -2356,15 +2457,19 @@
 				info->online = 1;
 			break;
 	}
-	if (m->mdmreg[12] & 1) {
+	if (m->mdmreg[REG_RESP] & BIT_RESP) {
 		/* Show results */
 		isdn_tty_at_cout("\r\n", info);
-		if (m->mdmreg[12] & 2) {
+		if (m->mdmreg[REG_RESPNUM] & BIT_RESPNUM) {
 			/* Show numeric results */
 			sprintf(s, "%d", code);
 			isdn_tty_at_cout(s, info);
 		} else {
-			if ((code == 2) && (!(m->mdmreg[13] & 16))) {
+			if ((code == 2) &&
+				(m->mdmreg[REG_RUNG] & BIT_RUNG) &&
+				(m->mdmreg[REG_RINGCNT] > 1))
+				return;
+			if ((code == 2) && (!(m->mdmreg[REG_CIDONCE] & BIT_CIDONCE))) {
 				isdn_tty_at_cout("CALLER NUMBER: ", info);
 				isdn_tty_at_cout(dev->num[info->drv_index], info);
 				isdn_tty_at_cout("\r\n", info);
@@ -2373,7 +2478,8 @@
 			switch (code) {
 				case 2:
 					/* Print CID only once, _after_ 1.st RING */
-					if ((m->mdmreg[13] & 16) && (m->mdmreg[1] == 1)) {
+					if ((m->mdmreg[REG_CIDONCE] & BIT_CIDONCE) &&
+					    (m->mdmreg[REG_RINGCNT] == 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);
@@ -2383,18 +2489,39 @@
 				case 6:
 				case 7:
 				case 8:
-					m->mdmreg[1] = 0;
+					m->mdmreg[REG_RINGCNT] = 0;
 					/* Append Cause-Message if enabled */
-					if (m->mdmreg[13] & 8) {
+					if (m->mdmreg[REG_RESPXT] & BIT_RESPXT) {
 						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)
+					switch (m->mdmreg[REG_L2PROT]) {
+						case ISDN_PROTO_L2_X75I:
+						case ISDN_PROTO_L2_X75UI:
+						case ISDN_PROTO_L2_X75BUI:
+							isdn_tty_at_cout("/X.75", info);
+							break;
+						case ISDN_PROTO_L2_HDLC:
+							isdn_tty_at_cout("/HDLC", info);
+							break;
+						case ISDN_PROTO_L2_V11096:
+							isdn_tty_at_cout("/V110/9600", info);
+							break;
+						case ISDN_PROTO_L2_V11019:
+							isdn_tty_at_cout("/V110/19200", info);
+							break;
+						case ISDN_PROTO_L2_V11038:
+							isdn_tty_at_cout("/V110/38400", info);
+							break;
+					}
+					if (m->mdmreg[REG_T70] & BIT_T70) {
 						isdn_tty_at_cout("/T.70", info);
+						if (m->mdmreg[REG_T70] & BIT_T70_EXT)
+							isdn_tty_at_cout("+", info);
+					}
 					break;
 			}
 		}
@@ -2479,16 +2606,25 @@
 	isdn_tty_at_cout("    Layer-2 Protocol: ", info);
 	switch (info->last_l2) {
 		case ISDN_PROTO_L2_X75I:
-			isdn_tty_at_cout("x75i", info);
+			isdn_tty_at_cout("X.75i", info);
 			break;
 		case ISDN_PROTO_L2_X75UI:
-			isdn_tty_at_cout("x75ui", info);
+			isdn_tty_at_cout("X.75ui", info);
 			break;
 		case ISDN_PROTO_L2_X75BUI:
-			isdn_tty_at_cout("x75bui", info);
+			isdn_tty_at_cout("X.75bui", info);
 			break;
 		case ISDN_PROTO_L2_HDLC:
-			isdn_tty_at_cout("hdlc", info);
+			isdn_tty_at_cout("HDLC", info);
+			break;
+		case ISDN_PROTO_L2_V11096:
+			isdn_tty_at_cout("V.110 9600 Baud", info);
+			break;
+		case ISDN_PROTO_L2_V11019:
+			isdn_tty_at_cout("V.110 19200 Baud", info);
+			break;
+		case ISDN_PROTO_L2_V11038:
+			isdn_tty_at_cout("V.110 38400 Baud", info);
 			break;
 		case ISDN_PROTO_L2_TRANS:
 			isdn_tty_at_cout("transparent", info);
@@ -2497,7 +2633,12 @@
 			isdn_tty_at_cout("unknown", info);
 			break;
 	}
-	isdn_tty_at_cout((m->mdmreg[13] & 2) ? "/t.70\r\n" : "\r\n", info);
+	if (m->mdmreg[REG_T70] & BIT_T70) {
+		isdn_tty_at_cout("/T.70", info);
+		if (m->mdmreg[REG_T70] & BIT_T70_EXT)
+			isdn_tty_at_cout("+", info);
+	}
+	isdn_tty_at_cout("\r\n", info);
 	isdn_tty_at_cout("    Service:          ", info);
 	switch (info->last_si) {
 		case 1:
@@ -2535,30 +2676,36 @@
 			/* &B - Set Buffersize */
 			p[0]++;
 			i = isdn_getnum(p);
-			if ((i < 0) || (i > ISDN_SERIAL_XMIT_SIZE))
+			if ((i < 0) || (i > ISDN_SERIAL_XMIT_MAX))
 				PARSE_ERROR1;
 #ifdef CONFIG_ISDN_AUDIO
-			if ((m->mdmreg[18] & 1) && (i > VBUF))
+			if ((m->mdmreg[REG_SI1] & 1) && (i > VBUF))
 				PARSE_ERROR1;
 #endif
-			m->mdmreg[16] = i / 16;
-			info->xmit_size = m->mdmreg[16] * 16;
+			m->mdmreg[REG_PSIZE] = i / 16;
+			info->xmit_size = m->mdmreg[REG_PSIZE] * 16;
+			switch (m->mdmreg[REG_L2PROT]) {
+				case ISDN_PROTO_L2_V11096:
+				case ISDN_PROTO_L2_V11019:
+				case ISDN_PROTO_L2_V11038:
+					info->xmit_size /= 10;		
+			}
 			break;
 		case 'D':
 			/* &D - Set DCD-Low-behavior */
 			p[0]++;
 			switch (isdn_getnum(p)) {
 				case 0:
-					m->mdmreg[13] &= ~4;
-					m->mdmreg[12] &= ~32;
+					m->mdmreg[REG_DTRHUP] &= ~BIT_DTRHUP;
+					m->mdmreg[REG_DTRR] &= ~BIT_DTRR;
 					break;
 				case 2:
-					m->mdmreg[13] |= 4;
-					m->mdmreg[12] &= ~32;
+					m->mdmreg[REG_DTRHUP] |= BIT_DTRHUP;
+					m->mdmreg[REG_DTRR] &= ~BIT_DTRR;
 					break;
 				case 3:
-					m->mdmreg[13] |= 4;
-					m->mdmreg[12] |= 32;
+					m->mdmreg[REG_DTRHUP] |= BIT_DTRHUP;
+					m->mdmreg[REG_DTRR] |= BIT_DTRR;
 					break;
 				default:
 					PARSE_ERROR1
@@ -2575,12 +2722,46 @@
 			isdn_tty_reset_profile(m);
 			isdn_tty_modem_reset_regs(info, 1);
 			break;
+		case 'R':
+			/* &R - Set V.110 bitrate adaption */
+			p[0]++;
+			i = isdn_getnum(p);
+			switch (i) {
+				case 0:
+					/* Switch off V.110, back to X.75 */
+					m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I;
+					m->mdmreg[REG_SI2] = 0;
+					info->xmit_size = m->mdmreg[REG_PSIZE] * 16;
+					break;
+				case 9600:
+					m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_V11096;
+					m->mdmreg[REG_SI2] = 197;
+					info->xmit_size = m->mdmreg[REG_PSIZE] * 16 / 10;
+					break;
+				case 19200:
+					m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_V11019;
+					m->mdmreg[REG_SI2] = 199;
+					info->xmit_size = m->mdmreg[REG_PSIZE] * 16 / 10;
+					break;
+				case 38400:
+					m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_V11038;
+					m->mdmreg[REG_SI2] = 198; /* no existing standard for this */
+					info->xmit_size = m->mdmreg[REG_PSIZE] * 16 / 10;
+					break;
+				default:
+					PARSE_ERROR1;
+			}
+			/* Switch off T.70 */
+			m->mdmreg[REG_T70] &= ~(BIT_T70 | BIT_T70_EXT);
+			/* Set Service 7 */
+			m->mdmreg[REG_SI1] |= 4;
+			break;
 		case 'S':
 			/* &S - Set Windowsize */
 			p[0]++;
 			i = isdn_getnum(p);
 			if ((i > 0) && (i < 9))
-				m->mdmreg[17] = i;
+				m->mdmreg[REG_WSIZE] = i;
 			else
 				PARSE_ERROR1;
 			break;
@@ -2610,19 +2791,27 @@
 			}
 			break;
 		case 'X':
-			/* &X - Switch to BTX-Mode */
+			/* &X - Switch to BTX-Mode and T.70 */
 			p[0]++;
 			switch (isdn_getnum(p)) {
 				case 0:
-					m->mdmreg[13] &= ~2;
-					info->xmit_size = m->mdmreg[16] * 16;
+					m->mdmreg[REG_T70] &= ~(BIT_T70 | BIT_T70_EXT);
+					info->xmit_size = m->mdmreg[REG_PSIZE] * 16;
 					break;
 				case 1:
-					m->mdmreg[13] |= 2;
-					m->mdmreg[14] = 0;
+					m->mdmreg[REG_T70] |= BIT_T70;
+					m->mdmreg[REG_T70] &= ~BIT_T70_EXT;
+					m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I;
+					info->xmit_size = 112;
+					m->mdmreg[REG_SI1] = 4;
+					m->mdmreg[REG_SI2] = 0;
+					break;
+				case 2:
+					m->mdmreg[REG_T70] |= (BIT_T70 | BIT_T70_EXT);
+					m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I;
 					info->xmit_size = 112;
-					m->mdmreg[18] = 4;
-					m->mdmreg[19] = 0;
+					m->mdmreg[REG_SI1] = 4;
+					m->mdmreg[REG_SI2] = 0;
 					break;
 				default:
 					PARSE_ERROR1;
@@ -2639,22 +2828,28 @@
 {
 	/* Some plausibility checks */
 	switch (mreg) {
-		case 14:
-			if (mval > ISDN_PROTO_L2_TRANS)
+		case REG_L2PROT:
+			if (mval > ISDN_PROTO_L2_MAX)
 				return 1;
 			break;
-		case 16:
-			if ((mval * 16) > ISDN_SERIAL_XMIT_SIZE)
+		case REG_PSIZE:
+			if ((mval * 16) > ISDN_SERIAL_XMIT_MAX)
 				return 1;
 #ifdef CONFIG_ISDN_AUDIO
-			if ((m->mdmreg[18] & 1) && (mval > VBUFX))
+			if ((m->mdmreg[REG_SI1] & 1) && (mval > VBUFX))
 				return 1;
 #endif
 			info->xmit_size = mval * 16;
+			switch (m->mdmreg[REG_L2PROT]) {
+				case ISDN_PROTO_L2_V11096:
+				case ISDN_PROTO_L2_V11019:
+				case ISDN_PROTO_L2_V11038:
+					info->xmit_size /= 10;		
+			}
 			break;
-		case 20:
-		case 21:
-		case 22:
+		case REG_SI1I:
+		case REG_PLAN:
+		case REG_SCREEN:
 			/* readonly registers */
 			return 1;
 	}
@@ -2741,31 +2936,31 @@
 		/* Accept incoming call */
 		info->last_dir = 0;
 		strcpy(info->last_num, dev->num[info->drv_index]);
-		m->mdmreg[1] = 0;
+		m->mdmreg[REG_RINGCNT] = 0;
 		info->msr &= ~UART_MSR_RI;
-		l2 = m->mdmreg[14];
+		l2 = m->mdmreg[REG_L2PROT];
 #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;
+		if ((m->mdmreg[REG_SI1] & m->mdmreg[REG_SI1I]) != m->mdmreg[REG_SI1]) {
+			if (m->mdmreg[REG_SI1I] == 1)
+				l2 = ISDN_PROTO_L2_TRANS;
 			else
-				l2 = 0;
+				l2 = ISDN_PROTO_L2_X75I;
 		}
 #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);
+		isdn_command(&cmd);
 		cmd.driver = info->isdn_driver;
 		cmd.command = ISDN_CMD_SETL3;
-		cmd.arg = info->isdn_channel + (m->mdmreg[15] << 8);
-		dev->drv[info->isdn_driver]->interface->command(&cmd);
+		cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8);
+		isdn_command(&cmd);
 		cmd.driver = info->isdn_driver;
 		cmd.arg = info->isdn_channel;
 		cmd.command = ISDN_CMD_ACCEPTD;
-		dev->drv[info->isdn_driver]->interface->command(&cmd);
+		isdn_command(&cmd);
 	} else
 		isdn_tty_modem_result(8, info);
 }
@@ -2787,7 +2982,7 @@
 			case '?':
 				p[0]++;
 				sprintf(rs, "\r\n%d",
-					(m->mdmreg[18] & 1) ? 8 : 0);
+					(m->mdmreg[REG_SI1] & 1) ? 8 : 0);
 				isdn_tty_at_cout(rs, info);
 				break;
 			case '=':
@@ -2795,18 +2990,22 @@
 				switch (*p[0]) {
 					case '0':
 						p[0]++;
-						m->mdmreg[18] = 4;
+						m->mdmreg[REG_SI1] = 4;
 						info->xmit_size =
-						    m->mdmreg[16] * 16;
+						    m->mdmreg[REG_PSIZE] * 16;
+						break;
+					case '2':
+						printk(KERN_DEBUG "isdn_tty: FCLASS=2\n");
+						p[0]++;
 						break;
 					case '8':
 						p[0]++;
-						m->mdmreg[18] = 5;
+						m->mdmreg[REG_SI1] = 5;
 						info->xmit_size = VBUF;
 						break;
 					case '?':
 						p[0]++;
-						isdn_tty_at_cout("\r\n0,8",
+						isdn_tty_at_cout("\r\n0,2,8",
 								 info);
 						break;
 					default:
@@ -2824,7 +3023,7 @@
 			case '?':
 				p[0]++;
 				sprintf(rs, "\r\n%d",
-					m->mdmreg[0]);
+					m->mdmreg[REG_RINGATA]);
 				isdn_tty_at_cout(rs, info);
 				break;
 			case '=':
@@ -2832,13 +3031,100 @@
 				par = isdn_getnum(p);
 				if ((par < 0) || (par > 255))
 					PARSE_ERROR1;
-				m->mdmreg[0] = par;
+				m->mdmreg[REG_RINGATA] = par;
 				break;
 			default:
 				PARSE_ERROR1;
 		}
 		return 0;
 	}
+        if (!strncmp(p[0], "TBC=", 4)) { /* UNKLAR !! */
+                p[0] += 4;
+                printk(KERN_DEBUG "isdn_tty: Fax FTBC=%c\n", *p[0]);
+                switch (*p[0]) {
+                        case '0':
+                                p[0]++;
+                                break;
+                        default:
+                                PARSE_ERROR1;
+                }
+                return 0;
+        }
+        if (!strncmp(p[0], "BOR=", 4)) { /* UNKLAR !! */
+                p[0] += 4;
+                printk(KERN_DEBUG "isdn_tty: Fax FBOR=%c\n", *p[0]);
+                switch (*p[0]) {
+                        case '0':
+                                p[0]++;
+                                break;
+                        default:
+                                PARSE_ERROR1;
+                }
+                return 0;
+        }
+        if (!strncmp(p[0], "DCC=", 4)) { /* SETUP irgendwie !! */
+		int i, val[]={0,0,0,0,0,0,0,0};
+
+                p[0] += 4;
+                if (*p[0] == '?') {
+			isdn_tty_at_cout("\r\n(0,1),(0-5),(0-2),(0-2),(0,1),(0),(0),(0-7)",info);
+			p[0]++;
+                } else {
+			for (i=0; (*p[0]>='0') && (*p[0]<='9'); i++) {
+				val[i] = *p[0] - 48;
+				p[0]++;
+				if (*p[0] == ',')
+					p[0]++;
+			}
+			printk(KERN_DEBUG "isdn_tty: Fax Setup values=%d,%d,%d,%d,%d,%d,%d,%d\n",
+			       val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7]);
+                }
+                return 0;
+        }
+        if (!strncmp(p[0], "LID=", 4)) { /* set sender ID !! */
+		char senderID[80];
+		int i;
+
+                p[0] += 4;
+                if (*p[0] =='"')
+			p[0]++;
+                for(i=0; (*p[0]) && (*p[0] != '"'); i++)
+			senderID[i] = *p[0]++;
+                senderID[i] = 0;
+                if (*p[0] =='"')
+			p[0]++;
+                printk(KERN_DEBUG "isdn_tty: Fax sender=>%s<\n", senderID);
+                return 0;
+        }
+        if (!strncmp(p[0], "MFR?", 4)) {
+                p[0] += 4;
+                printk(KERN_DEBUG "isdn_tty: FMFR?\n");
+                isdn_tty_at_cout("\r\nISDNfax", info);
+                return 0;
+        }
+        if (!strncmp(p[0], "MDL?", 4)) {
+                p[0] += 4;
+                printk(KERN_DEBUG "isdn_tty: FMDL?\n");
+                isdn_tty_at_cout("\r\nAVM-B1", info);
+                return 0;
+        }
+        if (!strncmp(p[0], "AP=?", 4)) {
+                p[0] += 4;
+                printk(KERN_DEBUG "isdn_tty: FAP=?\n");
+                return 0;
+        }
+        if (!strncmp(p[0], "PHCTO=", 6)) {
+		/* beim trace mit dem zyxel folgt der wert 30 ;*/
+                p[0] += 6;
+                printk(KERN_DEBUG "isdn_tty: FPHCTO=%s\n", p[0]);
+                return 0;
+        }
+        if (!strncmp(p[0], "CR=", 3)) {
+                p[0] += 3;
+                printk(KERN_DEBUG "isdn_tty: FCR=%s\n", p[0]);
+                return 0;
+        }
+        printk(KERN_DEBUG "isdn_tty: unknown token=>AT+F%s<\n", p[0]);
 	PARSE_ERROR1;
 }
 
@@ -3110,10 +3396,10 @@
 				p++;
 				switch (isdn_getnum(&p)) {
 					case 0:
-						m->mdmreg[12] &= ~4;
+						m->mdmreg[REG_ECHO] &= ~BIT_ECHO;
 						break;
 					case 1:
-						m->mdmreg[12] |= 4;
+						m->mdmreg[REG_ECHO] |= BIT_ECHO;
 						break;
 					default:
 						PARSE_ERROR;
@@ -3149,6 +3435,11 @@
 						p++;
 						isdn_tty_report(info);
 						break;
+					case '3':
+                                                p++;
+                                                sprintf(ds, "\r\n%d", info->emu.charge);
+                                                isdn_tty_at_cout(ds, info);
+                                                break;
 					default:
 				}
 				break;
@@ -3166,10 +3457,10 @@
 				p++;
 				switch (isdn_getnum(&p)) {
 					case 0:
-						m->mdmreg[12] |= 1;
+						m->mdmreg[REG_RESP] |= BIT_RESP;
 						break;
 					case 1:
-						m->mdmreg[12] &= ~1;
+						m->mdmreg[REG_RESP] &= ~BIT_RESP;
 						break;
 					default:
 						PARSE_ERROR;
@@ -3186,10 +3477,10 @@
 				p++;
 				switch (isdn_getnum(&p)) {
 					case 0:
-						m->mdmreg[12] |= 2;
+						m->mdmreg[REG_RESP] |= BIT_RESPNUM;
 						break;
 					case 1:
-						m->mdmreg[12] &= ~2;
+						m->mdmreg[REG_RESP] &= ~BIT_RESPNUM;
 						break;
 					default:
 						PARSE_ERROR;
@@ -3210,7 +3501,7 @@
 							return;
 						break;
 					case 'V':
-						if (!(m->mdmreg[18] & 1))
+						if (!(m->mdmreg[REG_SI1] & 1))
 							PARSE_ERROR;
 						p++;
 						if (isdn_tty_cmd_PLUSV(&p, info))
@@ -3261,14 +3552,14 @@
 
 	for (cnt = count; cnt > 0; p++, cnt--) {
 		if (user)
-			GET_USER(c, p);
+			get_user(c, p);
 		else
 			c = *p;
 		total++;
-		if (c == m->mdmreg[3] || c == m->mdmreg[4]) {
+		if (c == m->mdmreg[REG_CR] || c == m->mdmreg[REG_LF]) {
 			/* Separator (CR oder LF) */
 			m->mdmcmd[m->mdmcmdl] = 0;
-			if (m->mdmreg[12] & 4) {
+			if (m->mdmreg[REG_ECHO] & BIT_ECHO) {
 				eb[0] = c;
 				eb[1] = 0;
 				isdn_tty_at_cout(eb, info);
@@ -3278,18 +3569,18 @@
 			m->mdmcmdl = 0;
 			continue;
 		}
-		if (c == m->mdmreg[5] && m->mdmreg[5] < 128) {
+		if (c == m->mdmreg[REG_BS] && m->mdmreg[REG_BS] < 128) {
 			/* Backspace-Funktion */
 			if ((m->mdmcmdl > 2) || (!m->mdmcmdl)) {
 				if (m->mdmcmdl)
 					m->mdmcmdl--;
-				if (m->mdmreg[12] & 4)
+				if (m->mdmreg[REG_ECHO] & BIT_ECHO)
 					isdn_tty_at_cout("\b", info);
 			}
 			continue;
 		}
 		if (cmdchar(c)) {
-			if (m->mdmreg[12] & 4) {
+			if (m->mdmreg[REG_ECHO] & BIT_ECHO) {
 				eb[0] = c;
 				eb[1] = 0;
 				isdn_tty_at_cout(eb, info);
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/isdn_v110.c linux/drivers/isdn/isdn_v110.c
--- v2.1.91/linux/drivers/isdn/isdn_v110.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/isdn_v110.c	Wed Apr  1 16:21:03 1998
@@ -0,0 +1,645 @@
+/* $Id: isdn_v110.c,v 1.2 1998/02/22 19:44:25 fritz Exp $
+
+ * Linux ISDN subsystem, V.110 related functions (linklevel).
+ *
+ * Copyright by Thomas Pfeiffer (pfeiffer@pds.de)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Log: isdn_v110.c,v $
+ * Revision 1.2  1998/02/22 19:44:25  fritz
+ * Bugfixes and improvements regarding V.110, V.110 now running.
+ *
+ * Revision 1.1  1998/02/20 17:32:09  fritz
+ * First checkin (not yet completely functionable).
+ *
+ */
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+
+#include <linux/isdn.h>
+#include "isdn_v110.h"
+
+#undef ISDN_V110_DEBUG
+
+char *isdn_v110_revision = "$Revision: 1.2 $";
+
+#define V110_38400 255
+#define V110_19200  15
+#define V110_9600    3
+
+/* Die folgenden Daten sind fertig kodierte Matrizen, jeweils
+   als online und offline matrix für 9600, 19200 und 38400
+ */
+static unsigned char V110_OnMatrix_9600[] =
+{0xfc, 0xfc, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff,
+ 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd,
+ 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff,
+ 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd};
+
+static unsigned char V110_OffMatrix_9600[] =
+{0xfc, 0xfc, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+static unsigned char V110_OnMatrix_19200[] =
+{0xf0, 0xf0, 0xff, 0xf7, 0xff, 0xf7, 0xff, 0xf7, 0xff, 0xf7,
+ 0xfd, 0xff, 0xff, 0xf7, 0xff, 0xf7, 0xff, 0xf7, 0xff, 0xf7};
+
+static unsigned char V110_OffMatrix_19200[] =
+{0xf0, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+static unsigned char V110_OnMatrix_38400[] =
+{0x00, 0x7f, 0x7f, 0x7f, 0x7f, 0xfd, 0x7f, 0x7f, 0x7f, 0x7f};
+
+static unsigned char V110_OffMatrix_38400[] =
+{0x00, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff};
+
+
+/* FlipBits dreht die Reihenfolge von jeweils keylen bits in einem byte um.
+   Aus der Bitreihenfolge 76543210 werden bei keylen=4 die bits 45670123,
+   bei keylen=2 die bits 67452301. Dies ist notwendig, weil die reihenfolge
+   auf der isdn-leitung falsch herum ist.
+ */
+
+static __inline unsigned char
+FlipBits(unsigned char c, int keylen)
+{
+	unsigned char b = c;
+	unsigned char bit = 128;
+	int i;
+	int j;
+	int hunks = (8 / keylen);
+
+	c = 0;
+	for (i = 0; i < hunks; i++) {
+		for (j = 0; j < keylen; j++) {
+			if (b & (bit >> j))
+				c |= bit >> (keylen - j - 1);
+		}
+		bit >>= keylen;
+	}
+	return c;
+}
+
+
+/* isdn_v110_open allocates and initializes private V.110 data
+ * structures and returns a pointer to these.
+ */
+static isdn_v110_stream *
+isdn_v110_open(unsigned char key, int hdrlen, int maxsize)
+{
+	int i;
+	isdn_v110_stream *v;
+
+	if ((v = kmalloc(sizeof(isdn_v110_stream), GFP_KERNEL)) == NULL)
+		return NULL;
+	memset(v, 0, sizeof(isdn_v110_stream));
+	v->key = key;
+	v->nbits = 0;
+	for (i = 0; key & (1 << i); i++)
+		v->nbits++;
+
+	v->nbytes = 8 / v->nbits;
+	v->decodelen = 0;
+
+	switch (key) {
+		case V110_38400:
+			v->OnlineFrame = V110_OnMatrix_38400;
+			v->OfflineFrame = V110_OffMatrix_38400;
+			break;
+		case V110_19200:
+			v->OnlineFrame = V110_OnMatrix_19200;
+			v->OfflineFrame = V110_OffMatrix_19200;
+			break;
+		default:
+			v->OnlineFrame = V110_OnMatrix_9600;
+			v->OfflineFrame = V110_OffMatrix_9600;
+			break;
+	}
+	v->framelen = v->nbytes * 10;
+	v->SyncInit = 5;
+	v->introducer = 0;
+	v->dbit = 1;
+	v->b = 0;
+	v->skbres = hdrlen;
+	v->maxsize = maxsize - hdrlen;
+	if ((v->encodebuf = kmalloc(maxsize, GFP_KERNEL)) == NULL) {
+		kfree(v);
+		return NULL;
+	}
+	return v;
+}
+
+/* isdn_v110_close frees private V.110 data structures */
+static void
+isdn_v110_close(isdn_v110_stream * v)
+{
+	if (v == NULL)
+		return;
+#ifdef ISDN_V110_DEBUG
+	printk(KERN_DEBUG "v110 close\n");
+#if 0
+	printk(KERN_DEBUG "isdn_v110_close: nbytes=%d\n", v->nbytes);
+	printk(KERN_DEBUG "isdn_v110_close: nbits=%d\n", v->nbits);
+	printk(KERN_DEBUG "isdn_v110_close: key=%d\n", v->key);
+	printk(KERN_DEBUG "isdn_v110_close: SyncInit=%d\n", v->SyncInit);
+	printk(KERN_DEBUG "isdn_v110:close: decodelen=%d\n", v->decodelen);
+	printk(KERN_DEBUG "isdn_v110_close: framelen=%d\n", v->framelen);
+#endif
+#endif
+	kfree(v->encodebuf);
+	kfree(v);
+}
+
+
+/* ValidHeaderBytes prüft, wieviele bytes in v->decodebuf gültig sind */
+
+static int
+ValidHeaderBytes(isdn_v110_stream * v)
+{
+	int i;
+	for (i = 0; (i < v->decodelen) && (i < v->nbytes); i++)
+		if ((v->decodebuf[i] & v->key) != 0)
+			break;
+	return i;
+}
+
+/* SyncHeader schiebt den decodebuf pointer auf den nächsten gültigen header */
+
+static void
+SyncHeader(isdn_v110_stream * v)
+{
+	unsigned char *rbuf = v->decodebuf;
+	int len = v->decodelen;
+
+	if (len == 0)
+		return;
+	for (rbuf++, len--; len > 0; len--, rbuf++)	/* such den SyncHeader in buf ! */
+		if ((*rbuf & v->key) == 0)	/* erstes byte gefunden ?       */
+			break;  /* jupp!                        */
+	if (len)
+		memcpy(v->decodebuf, rbuf, len);
+
+	v->decodelen = len;
+#ifdef ISDN_V110_DEBUG
+	printk(KERN_DEBUG "isdn_v110: Header resync\n");
+#endif
+}
+
+/* DecodeMatrix takes n (n>=1) matrices (v110 frames, 10 bytes) where
+   len is the number of matrix-lines. len must be a multiple of 10, i.e.
+   only complete matices must be given.
+   From these, netto data is extracted and returned in buf. The return-value
+   is the bytecount of the decoded data.
+ */
+static int
+DecodeMatrix(isdn_v110_stream * v, unsigned char *m, int len, unsigned char *buf)
+{
+	int line = 0;
+	int buflen = 0;
+	int mbit = 64;
+	int introducer = v->introducer;
+	int dbit = v->dbit;
+	unsigned char b = v->b;
+
+	while (line < len) {    /* sind schon alle matrizenzeilen abgearbeitet? */
+		if ((line % 10) == 0) {	/* die 0. zeile der matrix ist immer null ! */
+			if (m[line] != 0x00) {	/* nicht 0 ? dann fehler! */
+#ifdef ISDN_V110_DEBUG
+				printk(KERN_DEBUG "isdn_v110: DecodeMatrix, V110 Bad Header\n");
+#endif
+
+/*
+  dann einen return zu machen, ist auch irgendwie nicht das richtige! :-(
+  v->introducer = 0; v->dbit = 1; v->b = 0;
+  return buflen;                                                                                                     anzahl schon erzeugter daten zurückgeben!
+  */
+			}
+			line++; /* sonst die nächste matrixzeile nehmen */
+			continue;
+		} else if ((line % 10) == 5) {	/* in zeile 5 stehen nur e-bits ! */
+			if ((m[line] & 0x70) != 0x30) {	/* 011 muß am anfang stehen! */
+#ifdef ISDN_V110_DEBUG
+				printk(KERN_DEBUG "isdn_v110: DecodeMatrix, V110 Bad 5th line\n");
+#endif
+/* dann einen return zu machen, ist auch irgendwie nicht das richtige! :-(
+   v->introducer = 0; v->dbit = 1; v->b = 0;
+   return buflen;
+ */
+			}
+			line++; /* alles klar, nächste zeile */
+			continue;
+		} else if (!introducer) {	/* every byte starts with 10 (stopbit, startbit) */
+			introducer = (m[line] & mbit) ? 0 : 1;	/* aktuelles bit der matrix */
+		      next_byte:
+			if (mbit > 2) {	/* war es das letzte bit dieser matrixzeile ? */
+				mbit >>= 1;	/* nein, nimm das nächste in dieser zeile */
+				continue;
+			}       /* sonst links in der nächsten zeile anfangen */
+			mbit = 64;
+			line++;
+			continue;
+		} else {        /* sonst müssen wir ein datenbit setzen */
+			if (m[line] & mbit)	/* war das bit in der matrix gesetzt ? */
+				b |= dbit;	/* ja, dann setz es auch im datenbyte  */
+			else
+				b &= dbit - 1;	/* nein, lösch bit im datenbyte */
+			if (dbit < 128)	/* haben wir schon ein ganzes byte voll ? */
+				dbit <<= 1;	/* nein, auf zum nächsten datenbit */
+			else {  /* ein ganzes datenbyte ist voll */
+				buf[buflen++] = b;	/* byte in den output buffer kopieren */
+				introducer = b = 0;	/* Init der Introsequenz und des datenbytes */
+				dbit = 1;	/* als nächstes suchen wir das nullte bit */
+			}
+			goto next_byte;	/* suche das nächste bit in der matrix */
+		}
+	}
+	v->introducer = introducer;
+	v->dbit = dbit;
+	v->b = b;
+	return buflen;          /* return anzahl der bytes im output buffer */
+}
+
+/* DecodeStream erhält vom input stream V110 kodierte Daten, die zu den
+   V110 frames zusammengepackt werden müssen. Die Daten können an diese
+   Schnittstelle so übergeben werden, wie sie von der Leitung kommen, ohne
+   darauf achten zu müssen, das frames usw. eingehalten werden.
+ */
+struct sk_buff *
+isdn_v110_decode(isdn_v110_stream * v, struct sk_buff *skb)
+{
+	int i;
+	int j;
+	int len;
+	unsigned char *v110_buf;
+	unsigned char *rbuf;
+
+	if (!skb) {
+		printk(KERN_WARNING "isdn_v110_decode called with NULL skb!\n");
+		return NULL;
+	}
+	rbuf = skb->data;
+	len = skb->len;
+	if (v == NULL) {
+		/* invalid handle, no chance to proceed */
+		printk(KERN_WARNING "isdn_v110_decode called with NULL stream!\n");
+		dev_kfree_skb(skb);
+		return NULL;
+	}
+	if (v->decodelen == 0)  /* cache empty?               */
+		for (; len > 0; len--, rbuf++)	/* scan for SyncHeader in buf */
+			if ((*rbuf & v->key) == 0)
+				break;	/* found first byte           */
+	if (len == 0) {
+		dev_kfree_skb(skb);
+		return NULL;
+	}
+	/* copy new data to decode-buffer */
+	memcpy(&(v->decodebuf[v->decodelen]), rbuf, len);
+	v->decodelen += len;
+      ReSync:
+	if (v->decodelen < v->nbytes) {	/* got a new header ? */
+		dev_kfree_skb(skb);
+		return NULL;    /* no, try later      */
+	}
+	if (ValidHeaderBytes(v) != v->nbytes) {	/* ist es ein ungültiger header ? */
+		SyncHeader(v);  /* nein, such einen header */
+		goto ReSync;
+	}
+	len = (v->decodelen - (v->decodelen % (10 * v->nbytes))) / v->nbytes;
+	if ((v110_buf = kmalloc(len, GFP_ATOMIC)) == NULL) {
+		printk(KERN_WARNING "isdn_v110_decode: Couldn't allocate v110_buf\n");
+		dev_kfree_skb(skb);
+		return NULL;
+	}
+	for (i = 0; i < len; i++) {
+		v110_buf[i] = 0;
+		for (j = 0; j < v->nbytes; j++)
+			v110_buf[i] |= (v->decodebuf[(i * v->nbytes) + j] & v->key) << (8 - ((j + 1) * v->nbits));
+		v110_buf[i] = FlipBits(v110_buf[i], v->nbits);
+	}
+	v->decodelen = (v->decodelen % (10 * v->nbytes));
+	memcpy(v->decodebuf, &(v->decodebuf[len * v->nbytes]), v->decodelen);
+
+	skb_trim(skb, DecodeMatrix(v, v110_buf, len, skb->data));
+	kfree(v110_buf);
+	if (skb->len)
+		return skb;
+	else {
+		kfree_skb(skb);
+		return NULL;
+	}
+}
+
+/* EncodeMatrix takes input data in buf, len is the bytecount.
+   Data is encoded into v110 frames in m. Return value is the number of
+   matrix-lines generated.
+ */
+static int
+EncodeMatrix(unsigned char *buf, int len, unsigned char *m, int mlen)
+{
+	int line = 0;
+	int i = 0;
+	int mbit = 128;
+	int dbit = 1;
+	int introducer = 3;
+	int ibit[] = {0, 1, 1};
+
+	while ((i < len) && (line < mlen)) {	/* solange noch input da ist */
+		switch (line % 10) {	/* in welcher matrixzeile sind wir ? */
+			case 0:
+				m[line++] = 0x00;	/* zeile 0 ist immer 0 */
+				mbit = 128;	/* und es geht mit dem 7. bit weiter */
+				break;
+			case 5:
+				m[line++] = 0xbf;	/* zeile 5 ist immer 10111111 */
+				mbit = 128;	/* und es geht mit dem 7. bit weiter */
+				break;
+		}
+		if (line >= mlen) {
+			printk(KERN_WARNING "isdn_v110 (EncodeMatrix): buffer full!\n");
+			return line;
+		}
+	next_bit:
+		switch (mbit) { /* ganz linkes oder rechtes bit ? */
+			case 1:
+				line++;	/* ganz rechts ! dann in die nächste */
+				if (line >= mlen) {
+					printk(KERN_WARNING "isdn_v110 (EncodeMatrix): buffer full!\n");
+					return line;
+				}
+			case 128:
+				m[line] = 128;	/* ganz links byte auf 1000000 setzen */
+				mbit = 64;	/* aktuelles bit in der matrixzeile */
+				continue;
+		}
+		if (introducer) {	/* 110 sequenz setzen ? */
+			introducer--;	/* ein digit weniger setzen */
+			m[line] |= ibit[introducer] ? mbit : 0;	/* entsprechendes bit setzen */
+			mbit >>= 1;	/* bit der matrixzeile >> 1 */
+			goto next_bit;	/* und dort weiter machen */
+		}               /* else datenbits in die matrix packen! */
+		m[line] |= (buf[i] & dbit) ? mbit : 0;	/* datenbit in matrix setzen */
+		if (dbit == 128) {	/* war es das letzte datenbit ? */
+			dbit = 1;	/* dann mach beim nächsten weiter */
+			i++;    /* nächste datenbyte des input buffers */
+			if (i < len)	/* war es schon das letzte ? */
+				introducer = 3;	/* nein, schreib den introducer 110 */
+			else {  /* war das letzte datenbyte ! */
+				m[line] |= (mbit - 1) & 0xfe;	/* setz restliche bits der zeile auf 1 */
+				break;
+			}
+		} else          /* nicht das letzte datenbit */
+			dbit <<= 1;	/* dann gehe zum nächsten datenbit */
+		mbit >>= 1;     /* und setz bit der matrix weiter */
+		goto next_bit;
+
+	}
+	/* evtl. noch restliche zeilen in der matrix generieren... */
+	if ((line) && ((line + 10) < mlen))
+		switch (++line % 10) {
+			case 1:
+				m[line++] = 0xfe;
+			case 2:
+				m[line++] = 0xfe;
+			case 3:
+				m[line++] = 0xfe;
+			case 4:
+				m[line++] = 0xfe;
+			case 5:
+				m[line++] = 0xbf;
+			case 6:
+				m[line++] = 0xfe;
+			case 7:
+				m[line++] = 0xfe;
+			case 8:
+				m[line++] = 0xfe;
+			case 9:
+				m[line++] = 0xfe;
+		}
+	return line;            /* soviele matrixzeilen sind es */
+}
+
+/*
+ * Build a sync frame.
+ */
+static struct sk_buff *
+isdn_v110_sync(isdn_v110_stream *v)
+{
+	struct sk_buff *skb;
+
+	if (v == NULL) {
+		/* invalid handle, no chance to proceed */
+		printk(KERN_WARNING "isdn_v110_sync called with NULL stream!\n");
+		return NULL;
+	}
+	if ((skb = dev_alloc_skb(v->framelen + v->skbres))) {
+		skb_reserve(skb, v->skbres);
+		memcpy(skb_put(skb, v->framelen), v->OfflineFrame, v->framelen);
+	}
+	return skb;
+}
+
+/*
+ * Build an idle frame.
+ */
+static struct sk_buff *
+isdn_v110_idle(isdn_v110_stream *v)
+{
+	struct sk_buff *skb;
+
+	if (v == NULL) {
+		/* invalid handle, no chance to proceed */
+		printk(KERN_WARNING "isdn_v110_sync called with NULL stream!\n");
+		return NULL;
+	}
+	if ((skb = dev_alloc_skb(v->framelen + v->skbres))) {
+		skb_reserve(skb, v->skbres);
+		memcpy(skb_put(skb, v->framelen), v->OnlineFrame, v->framelen);
+	}
+	return skb;
+}
+
+struct sk_buff *
+isdn_v110_encode(isdn_v110_stream * v, struct sk_buff *skb)
+{
+	int i;
+	int j;
+	int rlen;
+	int mlen;
+	int olen;
+	int size;
+	int sval1;
+	int sval2;
+	int nframes;
+	unsigned char *v110buf;
+	unsigned char *rbuf;
+	struct sk_buff *nskb;
+
+	if (v == NULL) {
+		/* invalid handle, no chance to proceed */
+		printk(KERN_WARNING "isdn_v110_encode called with NULL stream!\n");
+		return NULL;
+	}
+	if (!skb) {
+		/* invalid skb, no chance to proceed */
+		printk(KERN_WARNING "isdn_v110_encode called with NULL skb!\n");
+		return NULL;
+	}
+	rlen = skb->len;
+	nframes = (rlen + 3) / 4;
+	v110buf = v->encodebuf;
+	if ((nframes * 40) > v->maxsize) {
+		size = v->maxsize;
+		rlen = v->maxsize / 40;
+	} else
+		size = nframes * 40;
+	if (!(nskb = dev_alloc_skb(size + v->skbres + sizeof(int)))) {
+		printk(KERN_WARNING "isdn_v110_encode: Couldn't alloc skb\n");
+		return NULL;
+	}
+	skb_reserve(nskb, v->skbres + sizeof(int));
+	if (skb->len == 0) {
+		memcpy(skb_put(nskb, v->framelen), v->OnlineFrame, v->framelen);
+		*((int *)skb_push(nskb, sizeof(int))) = 0;
+		return nskb;
+	}
+	mlen = EncodeMatrix(skb->data, rlen, v110buf, size);
+	/* jetzt noch jeweils 2 oder 4 bits auf den output stream verteilen! */
+	rbuf = skb_put(nskb, size);
+	olen = 0;
+	sval1 = 8 - v->nbits;
+	sval2 = v->key << sval1;
+	for (i = 0; i < mlen; i++) {
+		v110buf[i] = FlipBits(v110buf[i], v->nbits);
+		for (j = 0; j < v->nbytes; j++) {
+			if (size--)
+				*rbuf++ = ~v->key | (((v110buf[i] << (j * v->nbits)) & sval2) >> sval1);
+			else {
+				printk(KERN_WARNING "isdn_v110_encode: buffers full!\n");
+				goto buffer_full;
+			}
+			olen++;
+		}
+	}
+buffer_full:
+	skb_trim(nskb, olen);
+	*((int *)skb_push(nskb, sizeof(int))) = rlen;
+	return nskb;
+}
+
+int
+isdn_v110_stat_callback(int idx, isdn_ctrl * c)
+{
+	isdn_v110_stream *v = NULL;
+	int i;
+	int ret;
+
+	if (idx < 0)
+		return 0;
+	switch (c->command) {
+		case ISDN_STAT_BSENT:
+                        /* Keep the send-queue of the driver filled
+			 * with frames:
+			 * If number of outstanding frames < 3,
+			 * send down an Idle-Frame (or an Sync-Frame, if
+			 * v->SyncInit != 0). 
+			 */
+			if (!(v = dev->v110[idx]))
+				return 0;
+			atomic_inc(&dev->v110use[idx]);
+			if (v->skbidle > 0) {
+				v->skbidle--;
+				ret = 1;
+			} else {
+				if (v->skbuser > 0)
+					v->skbuser--;
+				ret = 0;
+			}
+			for (i = v->skbuser + v->skbidle; i < 2; i++) {
+				struct sk_buff *skb;
+				if (v->SyncInit > 0)
+					skb = isdn_v110_sync(v);
+				else
+					skb = isdn_v110_idle(v);
+				if (skb) {
+					if (dev->drv[c->driver]->interface->writebuf_skb(c->driver, c->arg, 1, skb) <= 0) {
+						dev_kfree_skb(skb);
+						break;
+					} else {
+						if (v->SyncInit)
+							v->SyncInit--;
+						v->skbidle++;
+					}
+				} else
+					break;
+			}
+			atomic_dec(&dev->v110use[idx]);
+			return ret;
+		case ISDN_STAT_DHUP:
+		case ISDN_STAT_BHUP:
+			while (1) {
+				atomic_inc(&dev->v110use[idx]);
+				if (atomic_dec_and_test(&dev->v110use[idx])) {
+					isdn_v110_close(dev->v110[idx]);
+					dev->v110[idx] = NULL;
+					break;
+				}
+				sti();
+			}
+			break;
+		case ISDN_STAT_BCONN:
+			if (dev->v110emu[idx] && (dev->v110[idx] == NULL)) {
+				int hdrlen = dev->drv[c->driver]->interface->hl_hdrlen;
+				int maxsize = dev->drv[c->driver]->interface->maxbufsize;
+				atomic_inc(&dev->v110use[idx]);
+				switch (dev->v110emu[idx]) {
+					case ISDN_PROTO_L2_V11096:
+						dev->v110[idx] = isdn_v110_open(V110_9600, hdrlen, maxsize);
+						break;
+					case ISDN_PROTO_L2_V11019:
+						dev->v110[idx] = isdn_v110_open(V110_19200, hdrlen, maxsize);
+						break;
+					case ISDN_PROTO_L2_V11038:
+						dev->v110[idx] = isdn_v110_open(V110_38400, hdrlen, maxsize);
+						break;
+					default:
+				}
+				if ((v = dev->v110[idx])) {
+					while (v->SyncInit) {
+						struct sk_buff *skb = isdn_v110_sync(v);
+						if (dev->drv[c->driver]->interface->writebuf_skb(c->driver, c->arg, 1, skb) <= 0) {
+							dev_kfree_skb(skb);
+							/* Unable to send, try later */
+							break;
+						}
+						v->SyncInit--;
+						v->skbidle++;
+					}
+				} else
+					printk(KERN_WARNING "isdn_v110: Couldn't open stream for chan %d\n", idx);
+				atomic_dec(&dev->v110use[idx]);
+			}
+			break;
+		default:
+			return 0;
+	}
+	return 0;
+}
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/isdn_v110.h linux/drivers/isdn/isdn_v110.h
--- v2.1.91/linux/drivers/isdn/isdn_v110.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/isdn_v110.h	Wed Apr  1 16:21:03 1998
@@ -0,0 +1,45 @@
+/* $Id: isdn_v110.h,v 1.1 1998/02/20 17:32:11 fritz Exp $
+
+ * Linux ISDN subsystem, V.110 related functions (linklevel).
+ *
+ * Copyright by Thomas Pfeiffer (pfeiffer@pds.de)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Log: isdn_v110.h,v $
+ * Revision 1.1  1998/02/20 17:32:11  fritz
+ * First checkin (not yet completely functionable).
+ *
+ */
+#ifndef _isdn_v110_h_
+#define _isdn_v110_h_
+
+/* isdn_v110_encode erhält len Nettodaten in buf, kodiert diese und liefert
+   das Ergebnis wieder in buf. Wieviele Bytes kodiert wurden wird als
+   return zurück gegeben. Diese Daten können dann 1:1 auf die Leitung
+   gegeben werden.
+*/
+extern struct sk_buff *isdn_v110_encode(isdn_v110_stream *, struct sk_buff *);
+
+/* isdn_v110_decode erhält vom input stream V110 kodierte Daten, die zu den
+   V110 frames zusammengepackt werden müssen. Die Daten können an diese
+   Schnittstelle so übergeben werden, wie sie von der Leitung kommen, ohne
+   darauf achten zu müssen, das frames usw. eingehalten werden.
+ */
+extern struct sk_buff *isdn_v110_decode(isdn_v110_stream *, struct sk_buff *);
+
+extern int isdn_v110_stat_callback(int, isdn_ctrl *);
+
+#endif
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/isdn_x25iface.c linux/drivers/isdn/isdn_x25iface.c
--- v2.1.91/linux/drivers/isdn/isdn_x25iface.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/isdn_x25iface.c	Wed Apr  1 16:21:03 1998
@@ -0,0 +1,340 @@
+/* $Id: isdn_x25iface.c,v 1.3 1998/02/20 17:25:20 fritz Exp $
+ * stuff needed to support the Linux X.25 PLP code on top of devices that
+ * can provide a lab_b service using the concap_proto mechanism.
+ * This module supports a network interface wich provides lapb_sematics
+ * -- as defined in ../../Documentation/networking/x25-iface.txt -- to
+ * the upper layer and assumes that the lower layer provides a reliable
+ * data link service by means of the the concap_device_ops callbacks.
+ *
+ * Only protocol specific stuff goes here. Device specific stuff
+ * goes to another -- device related -- concap_proto support source file.
+ *
+ * $Log: isdn_x25iface.c,v $
+ * Revision 1.3  1998/02/20 17:25:20  fritz
+ * Changes for recent kernels.
+ *
+ * Revision 1.2  1998/01/31 22:49:22  keil
+ * correct comments
+ *
+ * Revision 1.1  1998/01/31 22:27:58  keil
+ * New files from Henner Eisen for X.25 support
+ *
+ */
+
+/* #include <linux/isdn.h> */
+#include <linux/netdevice.h>
+#include <linux/concap.h>
+#include <linux/wanrouter.h>
+#include "isdn_x25iface.h"
+
+/* for debugging messages not to cause an oops when device pointer is NULL*/
+#define MY_DEVNAME(dev)  ( (dev) ? (dev)->name : "DEVICE UNSPECIFIED" )
+
+
+typedef struct isdn_x25iface_proto_data {
+	int magic;
+	enum wan_states state;
+	/* Private stuff, not to be accessed via proto_data. We provide the
+	   other storage for the concap_proto instance here as well,
+	   enabling us to allocate both with just one kmalloc(): */ 
+	struct concap_proto priv;
+} ix25_pdata_t;
+
+
+
+/* is now in header file (extern): struct concap_proto * isdn_x25iface_proto_new(void); */
+void isdn_x25iface_proto_del( struct concap_proto * );
+int isdn_x25iface_proto_close( struct concap_proto * );
+int isdn_x25iface_proto_restart( struct concap_proto *,
+				 struct device *,
+				 struct concap_device_ops *);
+int isdn_x25iface_xmit( struct concap_proto *, struct sk_buff * );
+int isdn_x25iface_receive( struct concap_proto *, struct sk_buff * );
+int isdn_x25iface_connect_ind( struct concap_proto * );
+int isdn_x25iface_disconn_ind( struct concap_proto * );
+
+
+static struct concap_proto_ops ix25_pops = {
+	&isdn_x25iface_proto_new,
+	&isdn_x25iface_proto_del,
+	&isdn_x25iface_proto_restart,
+	&isdn_x25iface_proto_close,
+	&isdn_x25iface_xmit,
+	&isdn_x25iface_receive,
+	&isdn_x25iface_connect_ind,
+	&isdn_x25iface_disconn_ind
+};
+
+/* error message helper fuction */
+static void illegal_state_warn( unsigned state, unsigned char firstbyte) 
+{
+	printk( KERN_WARNING "isdn_x25iface: firstbyte %x illegal in"
+		"current state %d\n",firstbyte, state );
+}
+
+/* check protocol data field for consistency */
+static int pdata_is_bad( ix25_pdata_t * pda ){
+
+	if( pda  &&  pda -> magic == ISDN_X25IFACE_MAGIC ) return 0;
+	printk( KERN_WARNING
+		"isdn_x25iface_xxx: illegal pointer to proto data\n" );
+	return 1;
+}
+
+/* create a new x25 interface protocol instance
+ */
+struct concap_proto * isdn_x25iface_proto_new()
+{
+	ix25_pdata_t * tmp = kmalloc(sizeof(ix25_pdata_t),GFP_KERNEL);
+	IX25DEBUG("isdn_x25iface_proto_new\n");
+	if( tmp ){
+		tmp -> magic = ISDN_X25IFACE_MAGIC;
+		tmp -> state = WAN_UNCONFIGURED;
+		/* private data space used to hold the concap_proto data.
+		   Only to be accessed via the returned pointer */
+		tmp -> priv.dops       = NULL;
+		tmp -> priv.net_dev    = NULL;
+		tmp -> priv.pops       = &ix25_pops;
+		tmp -> priv.flags      = 0;
+		tmp -> priv.proto_data = tmp;
+		return( &(tmp -> priv) );
+	}
+	return NULL;
+};
+
+/* close the x25iface encapsulation protocol 
+ */
+int isdn_x25iface_proto_close(struct concap_proto *cprot){
+
+	ix25_pdata_t *tmp;
+        int ret = 0;
+	ulong flags;
+
+	if( ! cprot ){
+		printk( KERN_ERR "isdn_x25iface_proto_close: "
+			"invalid concap_proto pointer\n" );
+		return -1;
+	}
+	IX25DEBUG( "isdn_x25iface_proto_close %s \n", MY_DEVNAME(cprot -> net_dev) );
+	save_flags(flags);
+	cli();  /* avoid races with incoming events calling pops methods while
+		 cprot members are inconsistent */  
+	cprot -> dops    = NULL;
+	cprot -> net_dev = NULL;
+	tmp = cprot -> proto_data;
+	if( pdata_is_bad( tmp ) ){
+		ret = -1;
+	} else {
+		tmp -> state = WAN_UNCONFIGURED;
+	}
+	restore_flags(flags);
+
+	return ret;
+}
+
+/* Delete the x25iface encapsulation protocol instance
+ */
+void isdn_x25iface_proto_del(struct concap_proto *cprot){
+
+	ix25_pdata_t * tmp;
+ 
+	IX25DEBUG( "isdn_x25iface_proto_del \n" );
+	if( ! cprot ){
+		printk( KERN_ERR "isdn_x25iface_proto_del: "
+			"concap_proto pointer is NULL\n" );
+		return;
+	}
+	tmp = cprot -> proto_data;
+	if( tmp == NULL ){ 
+		printk( KERN_ERR "isdn_x25iface_proto_del: inconsistent "
+			"proto_data pointer (maybe already deleted?)\n"); 
+		return;
+	}
+	/* close if the protocol is still open */
+	if( cprot -> dops ) isdn_x25iface_proto_close(cprot);
+	/* freeing the storage should be sufficient now. But some additional
+	   settings might help to catch wild pointer bugs */
+	tmp -> magic = 0;
+	cprot -> proto_data = NULL;
+
+	kfree( tmp );
+	return;
+}
+
+/* (re-)initialize the data structures for x25iface encapsulation
+ */
+int isdn_x25iface_proto_restart(struct concap_proto *cprot,
+				struct device *ndev, 
+				struct concap_device_ops *dops)
+{
+	ix25_pdata_t * pda = cprot -> proto_data ;
+	ulong flags;
+
+	IX25DEBUG( "isdn_x25iface_proto_restart %s \n", MY_DEVNAME(ndev) );
+
+	if ( pdata_is_bad( pda ) ) return -1;
+
+	if( !( dops  && dops -> data_req && dops -> connect_req 
+	       && dops -> disconn_req )  ){
+		printk( KERN_WARNING "isdn_x25iface_restart: required dops"
+			" missing\n" );
+		isdn_x25iface_proto_close(cprot);
+		return -1;
+	}
+	save_flags(flags);
+	cli();  /* avoid races with incoming events calling pops methods while
+		 cprot members are inconsistent */  
+	cprot -> net_dev = ndev;
+	cprot -> pops = &ix25_pops;
+	cprot -> dops = dops;
+	pda -> state = WAN_DISCONNECTED;
+	restore_flags(flags);
+	return 0;
+}
+
+/* deliver a dl_data frame received from i4l HL driver to the network layer 
+ */
+int isdn_x25iface_receive(struct concap_proto *cprot, struct sk_buff *skb)
+{
+  	IX25DEBUG( "isdn_x25iface_receive %s \n", MY_DEVNAME(cprot->net_dev) );
+	if ( ( (ix25_pdata_t*) (cprot->proto_data) ) 
+	     -> state == WAN_CONNECTED ){
+		skb -> dev = cprot -> net_dev;
+		skb -> protocol = htons(ETH_P_X25);
+		skb -> pkt_type = PACKET_HOST;
+		if( skb_push(skb, 1)){
+			skb -> data[0]=0x00;
+			skb -> mac.raw = skb -> data;
+			netif_rx(skb);
+			return 0;
+		}
+	}
+	printk(KERN_WARNING "isdn_x25iface_receive %s: not connected, skb dropped\n", MY_DEVNAME(cprot->net_dev) );
+	dev_kfree_skb(skb);
+	return -1;
+}
+
+/* a connection set up is indicated by lower layer 
+ */
+int isdn_x25iface_connect_ind(struct concap_proto *cprot)
+{
+	struct sk_buff * skb = dev_alloc_skb(1);
+	enum wan_states *state_p 
+	  = &( ( (ix25_pdata_t*) (cprot->proto_data) ) -> state);
+	IX25DEBUG( "isdn_x25iface_connect_ind %s \n"
+		   , MY_DEVNAME(cprot->net_dev) );
+	if( *state_p == WAN_UNCONFIGURED ){ 
+		printk(KERN_WARNING 
+		       "isdn_x25iface_connect_ind while unconfigured %s\n"
+		       , MY_DEVNAME(cprot->net_dev) );
+		return -1;
+	}
+	*state_p = WAN_CONNECTED;
+	if( skb ){
+		*( skb_put(skb, 1) ) = 0x01;
+		skb -> mac.raw = skb -> data;
+		skb -> dev  = cprot -> net_dev;
+		skb -> protocol = htons(ETH_P_X25);
+		skb -> pkt_type = PACKET_HOST;
+		netif_rx(skb);
+		return 0;
+	} else {
+		printk(KERN_WARNING "isdn_x25iface_connect_ind: "
+		       " out of memory -- disconnecting\n");
+		cprot -> dops -> disconn_req(cprot);
+		return -1;
+	}
+}
+	
+/* a disconnect is indicated by lower layer 
+ */
+int isdn_x25iface_disconn_ind(struct concap_proto *cprot)
+{
+	struct sk_buff *skb;
+	enum wan_states *state_p 
+	  = &( ( (ix25_pdata_t*) (cprot->proto_data) ) -> state);
+	IX25DEBUG( "isdn_x25iface_disconn_ind %s \n", MY_DEVNAME(cprot -> net_dev) );
+	if( *state_p == WAN_UNCONFIGURED ){ 
+		printk(KERN_WARNING 
+		       "isdn_x25iface_disconn_ind while unconfigured\n");
+		return -1;
+	}
+	if(! cprot -> net_dev) return -1;
+	*state_p = WAN_DISCONNECTED;
+	skb = dev_alloc_skb(1);
+	if( skb ){
+		*( skb_put(skb, 1) ) = 0x02;
+		skb -> mac.raw = skb -> data;
+		skb -> dev  = cprot -> net_dev;
+		skb -> protocol = htons(ETH_P_X25);
+		skb -> pkt_type = PACKET_HOST;
+		netif_rx(skb);
+		return 0;
+	} else {
+		printk(KERN_WARNING "isdn_x25iface_disconn_ind:"
+		       " out of memory\n");
+		return -1;
+	}
+}
+
+/* process a frame handed over to us from linux network layer. First byte
+   semantics as defined in ../../Documentation/networking/x25-iface.txt 
+   */
+int isdn_x25iface_xmit(struct concap_proto *cprot, struct sk_buff *skb)
+{
+	unsigned char firstbyte = skb->data[0];
+	unsigned *state = 
+		&( ( (ix25_pdata_t*) (cprot -> proto_data) ) -> state  );
+	int ret = 0;
+	IX25DEBUG( "isdn_x25iface_xmit: %s first=%x state=%d \n", MY_DEVNAME(cprot -> net_dev), firstbyte, *state );
+	switch ( firstbyte ){
+	case 0x00: /* dl_data request */
+		if( *state == WAN_CONNECTED ){
+			skb_pull(skb, 1);
+			cprot -> net_dev -> trans_start = jiffies;
+			ret = ( cprot -> dops -> data_req(cprot, skb) );
+			/* prepare for future retransmissions */
+			if( ret ) skb_push(skb,1);
+			return ret;
+		}
+		illegal_state_warn( *state, firstbyte ); 
+		break;
+	case 0x01: /* dl_connect request */
+		if( *state == WAN_DISCONNECTED ){
+			*state = WAN_CONNECTING;
+		        cprot -> dops -> connect_req(cprot);
+		} else {
+			illegal_state_warn( *state, firstbyte );
+		}
+		break;
+	case 0x02: /* dl_disconnect request */
+		switch ( *state ){
+		case WAN_DISCONNECTED: 
+			/* Should not happen. However, give upper layer a
+			   chance to recover from inconstistency  but don't
+			   trust the lower layer sending the disconn_confirm
+			   when already disconnected */
+			printk(KERN_WARNING "isdn_x25iface_xmit: disconnect "
+			       " requested while disconnected\n" );
+			isdn_x25iface_disconn_ind(cprot);
+			break; /* prevent infinite loops */
+		case WAN_CONNECTING:
+		case WAN_CONNECTED:
+			*state = WAN_DISCONNECTED;
+			cprot -> dops -> disconn_req(cprot);
+			break;
+		default:
+			illegal_state_warn( *state, firstbyte );
+		}
+		break;
+	case 0x03: /* changing lapb parameters requested */
+		printk(KERN_WARNING "isdn_x25iface_xmit: setting of lapb"
+		       " options not yet supported\n");
+		break;
+	default:
+		printk(KERN_WARNING "isdn_x25iface_xmit: frame with illegal"
+		       " first byte %x ignored:\n", firstbyte);
+	}
+	dev_kfree_skb(skb);
+	return 0;
+}
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/isdn_x25iface.h linux/drivers/isdn/isdn_x25iface.h
--- v2.1.91/linux/drivers/isdn/isdn_x25iface.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/isdn_x25iface.h	Wed Apr  1 16:21:03 1998
@@ -0,0 +1,32 @@
+/* $Id: isdn_x25iface.h,v 1.2 1998/01/31 22:49:23 keil Exp $
+ */
+#ifndef _LINUX_ISDN_X25IFACE_H
+#define _LINUX_ISDN_X25IFACE_H
+
+#define ISDN_X25IFACE_MAGIC 0x1e75a2b9
+/* #define DEBUG_ISDN_X25 if you want isdn_x25 debugging messages */
+#ifdef DEBUG_ISDN_X25
+#   define IX25DEBUG(fmt,args...) printk(KERN_DEBUG fmt , ## args)
+#else
+#   define IX25DEBUG(fmt,args...)
+#endif
+
+#include <linux/skbuff.h>
+#include <linux/wanrouter.h>
+#include <linux/isdn.h>
+#include <linux/concap.h>
+
+extern struct concap_proto_ops * isdn_x25iface_concap_proto_ops_pt;
+extern struct concap_proto     * isdn_x25iface_proto_new(void);
+
+
+
+#endif
+
+
+
+
+
+
+
+
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/isdnloop/Makefile linux/drivers/isdn/isdnloop/Makefile
--- v2.1.91/linux/drivers/isdn/isdnloop/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/isdnloop/Makefile	Wed Apr  1 16:21:03 1998
@@ -0,0 +1,11 @@
+L_OBJS :=
+M_OBJS :=
+
+ifeq ($(CONFIG_ISDN_DRV_LOOP),y)
+  L_OBJS += isdnloop.o
+else
+  M_OBJS += isdnloop.o
+endif
+
+include $(TOPDIR)/Rules.make
+
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/isdnloop/isdnloop.c linux/drivers/isdn/isdnloop/isdnloop.c
--- v2.1.91/linux/drivers/isdn/isdnloop/isdnloop.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/isdnloop/isdnloop.c	Wed Apr  1 16:21:03 1998
@@ -0,0 +1,1619 @@
+/* $Id: isdnloop.c,v 1.4 1998/02/24 21:39:05 he Exp $
+
+ * ISDN low-level module implementing a dummy loop driver.
+ *
+ * Copyright 1997 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)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Log: isdnloop.c,v $
+ * Revision 1.4  1998/02/24 21:39:05  he
+ * L2_PROT_X25DTE / DCE
+ * additional state 17 and new internal signal messages "BCON_I"
+ * (for reliable connect confirmation primitive as needed by x.25 upper layer)
+ * Changes for new LL-HL interface
+ *
+ * Revision 1.3  1998/02/20 17:33:30  fritz
+ * Changes for recent kernels.
+ *
+ * Revision 1.2  1997/10/01 09:22:03  fritz
+ * Removed old compatibility stuff for 2.0.X kernels.
+ * From now on, this code is for 2.1.X ONLY!
+ * Old stuff is still in the separate branch.
+ *
+ * Revision 1.1  1997/03/24 23:02:04  fritz
+ * Added isdnloop driver.
+ *
+ */
+
+#include "isdnloop.h"
+
+static char
+*revision = "$Revision: 1.4 $";
+
+static int isdnloop_addcard(char *);
+
+/*
+ * Free queue completely.
+ *
+ * Parameter:
+ *   card    = pointer to card struct
+ *   channel = channel number
+ */
+static void
+isdnloop_free_queue(isdnloop_card * card, int channel)
+{
+	struct sk_buff_head *queue = &card->bqueue[channel];
+	struct sk_buff *skb;
+
+	while ((skb = skb_dequeue(queue)))
+		dev_kfree_skb(skb);
+	card->sndcount[channel] = 0;
+}
+
+/*
+ * Send B-Channel data to another virtual card.
+ * This routine is called via timer-callback from isdnloop_pollbchan().
+ *
+ * Parameter:
+ *   card = pointer to card struct.
+ *   ch   = channel number (0-based)
+ */
+static void
+isdnloop_bchan_send(isdnloop_card * card, int ch)
+{
+	isdnloop_card *rcard = card->rcard[ch];
+	int rch = card->rch[ch], len, ack;
+	struct sk_buff *skb;
+	isdn_ctrl cmd;
+
+	while (card->sndcount[ch]) {
+		if ((skb = skb_dequeue(&card->bqueue[ch]))) {
+			len = skb->len;
+			card->sndcount[ch] -= len;
+			ack = *(skb->head); /* used as scratch area */
+			cmd.driver = card->myid;
+			cmd.arg = ch;
+			if (rcard){
+				rcard->interface.rcvcallb_skb(rcard->myid, rch, skb);
+			} else {
+				printk(KERN_WARNING "isdnloop: no rcard, skb dropped\n");
+				dev_kfree_skb(skb);
+
+				cmd.command = ISDN_STAT_L1ERR;
+				cmd.parm.errcode = ISDN_STAT_L1ERR_SEND;
+				card->interface.statcallb(&cmd); 
+			};
+			cmd.command = ISDN_STAT_BSENT;
+			cmd.parm.length = len;
+			if ( ack ) card->interface.statcallb(&cmd);
+		} else
+			card->sndcount[ch] = 0;
+	}
+}
+
+/*
+ * Send/Receive Data to/from the B-Channel.
+ * This routine is called via timer-callback.
+ * It schedules itself while any B-Channel is open.
+ *
+ * Parameter:
+ *   data = pointer to card struct, set by kernel timer.data
+ */
+static void
+isdnloop_pollbchan(unsigned long data)
+{
+	isdnloop_card *card = (isdnloop_card *) data;
+	unsigned long flags;
+
+	if (card->flags & ISDNLOOP_FLAGS_B1ACTIVE)
+		isdnloop_bchan_send(card, 0);
+	if (card->flags & ISDNLOOP_FLAGS_B2ACTIVE)
+		isdnloop_bchan_send(card, 1);
+	if (card->flags & (ISDNLOOP_FLAGS_B1ACTIVE | ISDNLOOP_FLAGS_B2ACTIVE)) {
+		/* schedule b-channel polling again */
+		save_flags(flags);
+		cli();
+		card->rb_timer.expires = jiffies + ISDNLOOP_TIMER_BCREAD;
+		add_timer(&card->rb_timer);
+		card->flags |= ISDNLOOP_FLAGS_RBTIMER;
+		restore_flags(flags);
+	} else
+		card->flags &= ~ISDNLOOP_FLAGS_RBTIMER;
+}
+
+/*
+ * Parse ICN-type setup string and fill fields of setup-struct
+ * with parsed data.
+ *
+ * Parameter:
+ *   setup = setup string, format: [caller-id],si1,si2,[called-id]
+ *   cmd   = pointer to struct to be filled.
+ */
+static void
+isdnloop_parse_setup(char *setup, isdn_ctrl * cmd)
+{
+	char *t = setup;
+	char *s = strpbrk(t, ",");
+
+	*s++ = '\0';
+	strncpy(cmd->parm.setup.phone, t, sizeof(cmd->parm.setup.phone));
+	s = strpbrk(t = s, ",");
+	*s++ = '\0';
+	if (!strlen(t))
+		cmd->parm.setup.si1 = 0;
+	else
+		cmd->parm.setup.si1 = simple_strtoul(t, NULL, 10);
+	s = strpbrk(t = s, ",");
+	*s++ = '\0';
+	if (!strlen(t))
+		cmd->parm.setup.si2 = 0;
+	else
+		cmd->parm.setup.si2 =
+		    simple_strtoul(t, NULL, 10);
+	strncpy(cmd->parm.setup.eazmsn, s, sizeof(cmd->parm.setup.eazmsn));
+	cmd->parm.setup.plan = 0;
+	cmd->parm.setup.screen = 0;
+}
+
+typedef struct isdnloop_stat {
+	char *statstr;
+	int command;
+	int action;
+} isdnloop_stat;
+/* *INDENT-OFF* */
+static isdnloop_stat isdnloop_stat_table[] =
+{
+	{"BCON_",          ISDN_STAT_BCONN, 1}, /* B-Channel connected        */
+	{"BDIS_",          ISDN_STAT_BHUP,  2}, /* B-Channel disconnected     */
+	{"DCON_",          ISDN_STAT_DCONN, 0}, /* D-Channel connected        */
+	{"DDIS_",          ISDN_STAT_DHUP,  0}, /* D-Channel disconnected     */
+	{"DCAL_I",         ISDN_STAT_ICALL, 3}, /* Incoming call dialup-line  */
+	{"DSCA_I",         ISDN_STAT_ICALL, 3}, /* Incoming call 1TR6-SPV     */
+	{"FCALL",          ISDN_STAT_ICALL, 4}, /* Leased line connection up  */
+	{"CIF",            ISDN_STAT_CINF,  5}, /* Charge-info, 1TR6-type     */
+	{"AOC",            ISDN_STAT_CINF,  6}, /* Charge-info, DSS1-type     */
+	{"CAU",            ISDN_STAT_CAUSE, 7}, /* Cause code                 */
+	{"TEI OK",         ISDN_STAT_RUN,   0}, /* Card connected to wallplug */
+	{"NO D-CHAN",      ISDN_STAT_NODCH, 0}, /* No D-channel available     */
+	{"E_L1: ACT FAIL", ISDN_STAT_BHUP,  8}, /* Layer-1 activation failed  */
+	{"E_L2: DATA LIN", ISDN_STAT_BHUP,  8}, /* Layer-2 data link lost     */
+	{"E_L1: ACTIVATION FAILED",
+			   ISDN_STAT_BHUP,  8},         /* Layer-1 activation failed  */
+	{NULL, 0, -1}
+};
+/* *INDENT-ON* */
+
+
+/*
+ * Parse Status message-strings from virtual card.
+ * Depending on status, call statcallb for sending messages to upper
+ * levels. Also set/reset B-Channel active-flags.
+ *
+ * Parameter:
+ *   status  = status string to parse.
+ *   channel = channel where message comes from.
+ *   card    = card where message comes from.
+ */
+static void
+isdnloop_parse_status(u_char * status, int channel, isdnloop_card * card)
+{
+	isdnloop_stat *s = isdnloop_stat_table;
+	int action = -1;
+	isdn_ctrl cmd;
+
+	while (s->statstr) {
+		if (!strncmp(status, s->statstr, strlen(s->statstr))) {
+			cmd.command = s->command;
+			action = s->action;
+			break;
+		}
+		s++;
+	}
+	if (action == -1)
+		return;
+	cmd.driver = card->myid;
+	cmd.arg = channel;
+	switch (action) {
+		case 1:
+			/* BCON_x */
+			card->flags |= (channel) ?
+			    ISDNLOOP_FLAGS_B2ACTIVE : ISDNLOOP_FLAGS_B1ACTIVE;
+			break;
+		case 2:
+			/* BDIS_x */
+			card->flags &= ~((channel) ?
+					 ISDNLOOP_FLAGS_B2ACTIVE : ISDNLOOP_FLAGS_B1ACTIVE);
+			isdnloop_free_queue(card, channel);
+			break;
+		case 3:
+			/* DCAL_I and DSCA_I */
+			isdnloop_parse_setup(status + 6, &cmd);
+			break;
+		case 4:
+			/* FCALL */
+			sprintf(cmd.parm.setup.phone, "LEASED%d", card->myid);
+			sprintf(cmd.parm.setup.eazmsn, "%d", channel + 1);
+			cmd.parm.setup.si1 = 7;
+			cmd.parm.setup.si2 = 0;
+			cmd.parm.setup.plan = 0;
+			cmd.parm.setup.screen = 0;
+			break;
+		case 5:
+			/* CIF */
+			strncpy(cmd.parm.num, status + 3, sizeof(cmd.parm.num) - 1);
+			break;
+		case 6:
+			/* AOC */
+			sprintf(cmd.parm.num, "%d",
+			     (int) simple_strtoul(status + 7, NULL, 16));
+			break;
+		case 7:
+			/* CAU */
+			status += 3;
+			if (strlen(status) == 4)
+				sprintf(cmd.parm.num, "%s%c%c",
+				     status + 2, *status, *(status + 1));
+			else
+				strncpy(cmd.parm.num, status + 1, sizeof(cmd.parm.num) - 1);
+			break;
+		case 8:
+			/* Misc Errors on L1 and L2 */
+			card->flags &= ~ISDNLOOP_FLAGS_B1ACTIVE;
+			isdnloop_free_queue(card, 0);
+			cmd.arg = 0;
+			cmd.driver = card->myid;
+			card->interface.statcallb(&cmd);
+			cmd.command = ISDN_STAT_DHUP;
+			cmd.arg = 0;
+			cmd.driver = card->myid;
+			card->interface.statcallb(&cmd);
+			cmd.command = ISDN_STAT_BHUP;
+			card->flags &= ~ISDNLOOP_FLAGS_B2ACTIVE;
+			isdnloop_free_queue(card, 1);
+			cmd.arg = 1;
+			cmd.driver = card->myid;
+			card->interface.statcallb(&cmd);
+			cmd.command = ISDN_STAT_DHUP;
+			cmd.arg = 1;
+			cmd.driver = card->myid;
+			break;
+	}
+	card->interface.statcallb(&cmd);
+}
+
+/*
+ * Store a cwcharacter into ringbuffer for reading from /dev/isdnctrl
+ *
+ * Parameter:
+ *   card = pointer to card struct.
+ *   c    = char to store.
+ */
+static void
+isdnloop_putmsg(isdnloop_card * card, unsigned char c)
+{
+	ulong flags;
+
+	save_flags(flags);
+	cli();
+	*card->msg_buf_write++ = (c == 0xff) ? '\n' : c;
+	if (card->msg_buf_write == card->msg_buf_read) {
+		if (++card->msg_buf_read > card->msg_buf_end)
+			card->msg_buf_read = card->msg_buf;
+	}
+	if (card->msg_buf_write > card->msg_buf_end)
+		card->msg_buf_write = card->msg_buf;
+	restore_flags(flags);
+}
+
+/*
+ * Poll a virtual cards message queue.
+ * If there are new status-replies from the card, copy them to
+ * ringbuffer for reading on /dev/isdnctrl and call
+ * isdnloop_parse_status() for processing them. Watch for special
+ * Firmware bootmessage and parse it, to get the D-Channel protocol.
+ * If there are B-Channels open, initiate a timer-callback to
+ * isdnloop_pollbchan().
+ * This routine is called periodically via timer interrupt.
+ *
+ * Parameter:
+ *   data = pointer to card struct
+ */
+static void
+isdnloop_polldchan(unsigned long data)
+{
+	isdnloop_card *card = (isdnloop_card *) data;
+	struct sk_buff *skb;
+	int avail;
+	int left;
+	u_char c;
+	int ch;
+	int flags;
+	u_char *p;
+	isdn_ctrl cmd;
+
+	if ((skb = skb_dequeue(&card->dqueue)))
+		avail = skb->len;
+	else
+		avail = 0;
+	for (left = avail; left > 0; left--) {
+		c = *skb->data;
+		skb_pull(skb, 1);
+		isdnloop_putmsg(card, c);
+		card->imsg[card->iptr] = c;
+		if (card->iptr < 59)
+			card->iptr++;
+		if (!skb->len) {
+			avail++;
+			isdnloop_putmsg(card, '\n');
+			card->imsg[card->iptr] = 0;
+			card->iptr = 0;
+			if (card->imsg[0] == '0' && card->imsg[1] >= '0' &&
+			  card->imsg[1] <= '2' && card->imsg[2] == ';') {
+				ch = (card->imsg[1] - '0') - 1;
+				p = &card->imsg[3];
+				isdnloop_parse_status(p, ch, card);
+			} else {
+				p = card->imsg;
+				if (!strncmp(p, "DRV1.", 5)) {
+					printk(KERN_INFO "isdnloop: (%s) %s\n", CID, p);
+					if (!strncmp(p + 7, "TC", 2)) {
+						card->ptype = ISDN_PTYPE_1TR6;
+						card->interface.features |= ISDN_FEATURE_P_1TR6;
+						printk(KERN_INFO
+						       "isdnloop: (%s) 1TR6-Protocol loaded and running\n", CID);
+					}
+					if (!strncmp(p + 7, "EC", 2)) {
+						card->ptype = ISDN_PTYPE_EURO;
+						card->interface.features |= ISDN_FEATURE_P_EURO;
+						printk(KERN_INFO
+						       "isdnloop: (%s) Euro-Protocol loaded and running\n", CID);
+					}
+					continue;
+
+				}
+			}
+		}
+	}
+	if (avail) {
+		cmd.command = ISDN_STAT_STAVAIL;
+		cmd.driver = card->myid;
+		cmd.arg = avail;
+		card->interface.statcallb(&cmd);
+	}
+	if (card->flags & (ISDNLOOP_FLAGS_B1ACTIVE | ISDNLOOP_FLAGS_B2ACTIVE))
+		if (!(card->flags & ISDNLOOP_FLAGS_RBTIMER)) {
+			/* schedule b-channel polling */
+			card->flags |= ISDNLOOP_FLAGS_RBTIMER;
+			save_flags(flags);
+			cli();
+			del_timer(&card->rb_timer);
+			card->rb_timer.function = isdnloop_pollbchan;
+			card->rb_timer.data = (unsigned long) card;
+			card->rb_timer.expires = jiffies + ISDNLOOP_TIMER_BCREAD;
+			add_timer(&card->rb_timer);
+			restore_flags(flags);
+		}
+	/* schedule again */
+	save_flags(flags);
+	cli();
+	card->st_timer.expires = jiffies + ISDNLOOP_TIMER_DCREAD;
+	add_timer(&card->st_timer);
+	restore_flags(flags);
+}
+
+/*
+ * Append a packet to the transmit buffer-queue.
+ *
+ * Parameter:
+ *   channel = Number of B-channel
+ *   skb     = packet to send.
+ *   card    = pointer to card-struct
+ * Return:
+ *   Number of bytes transferred, -E??? on error
+ */
+static int
+isdnloop_sendbuf(int channel, struct sk_buff *skb, isdnloop_card * card)
+{
+	int len = skb->len;
+	unsigned long flags;
+	struct sk_buff *nskb;
+
+	if (len > 4000) {
+		printk(KERN_WARNING
+		       "isdnloop: Send packet too large\n");
+		return -EINVAL;
+	}
+	if (len) {
+		if (!(card->flags & (channel) ? ISDNLOOP_FLAGS_B2ACTIVE : ISDNLOOP_FLAGS_B1ACTIVE))
+			return 0;
+		if (card->sndcount[channel] > ISDNLOOP_MAX_SQUEUE)
+			return 0;
+		save_flags(flags);
+		cli();
+		nskb = skb_clone(skb, GFP_ATOMIC);
+		if (nskb) {
+			skb_queue_tail(&card->bqueue[channel], nskb);
+			dev_kfree_skb(skb);
+		} else
+			len = 0;
+		card->sndcount[channel] += len;
+		restore_flags(flags);
+	}
+	return len;
+}
+
+/*
+ * Read the messages from the card's ringbuffer
+ *
+ * Parameter:
+ *   buf  = pointer to buffer.
+ *   len  = number of bytes to read.
+ *   user = flag, 1: called from userlevel 0: called from kernel.
+ *   card = pointer to card struct.
+ * Return:
+ *   number of bytes actually transferred.
+ */
+static int
+isdnloop_readstatus(u_char * buf, int len, int user, isdnloop_card * card)
+{
+	int count;
+	u_char *p;
+
+	for (p = buf, count = 0; count < len; p++, count++) {
+		if (card->msg_buf_read == card->msg_buf_write)
+			return count;
+		if (user)
+			put_user(*card->msg_buf_read++, p);
+		else
+			*p = *card->msg_buf_read++;
+		if (card->msg_buf_read > card->msg_buf_end)
+			card->msg_buf_read = card->msg_buf;
+	}
+	return count;
+}
+
+/*
+ * Simulate a card's response by appending it to the cards
+ * message queue.
+ *
+ * Parameter:
+ *   card = pointer to card struct.
+ *   s    = pointer to message-string.
+ *   ch   = channel: 0 = generic messages, 1 and 2 = D-channel messages.
+ * Return:
+ *   0 on success, 1 on memory squeeze.
+ */
+static int
+isdnloop_fake(isdnloop_card * card, char *s, int ch)
+{
+	struct sk_buff *skb;
+	int len = strlen(s) + ((ch >= 0) ? 3 : 0);
+
+	if (!(skb = dev_alloc_skb(len))) {
+		printk(KERN_WARNING "isdnloop: Out of memory in isdnloop_fake\n");
+		return 1;
+	}
+	if (ch >= 0)
+		sprintf(skb_put(skb, 3), "%02d;", ch);
+	memcpy(skb_put(skb, strlen(s)), s, strlen(s));
+	skb_queue_tail(&card->dqueue, skb);
+	return 0;
+}
+/* *INDENT-OFF* */
+static isdnloop_stat isdnloop_cmd_table[] =
+{
+	{"BCON_R",         0,  1},	/* B-Channel connect        */
+	{"BCON_I",         0, 17},	/* B-Channel connect ind    */
+	{"BDIS_R",         0,  2},	/* B-Channel disconnect     */
+	{"DDIS_R",         0,  3},	/* D-Channel disconnect     */
+	{"DCON_R",         0, 16},	/* D-Channel connect        */
+	{"DSCA_R",         0,  4},	/* Dial 1TR6-SPV     */
+	{"DCAL_R",         0,  5},	/* Dial */
+	{"EAZC",           0,  6},	/* Clear EAZ listener */
+	{"EAZ",            0,  7},	/* Set EAZ listener */
+	{"SEEAZ",          0,  8},	/* Get EAZ listener */
+	{"MSN",            0,  9},	/* Set/Clear MSN listener */
+	{"MSALL",          0, 10},	/* Set multi MSN listeners */
+	{"SETSIL",         0, 11},	/* Set SI list     */
+	{"SEESIL",         0, 12},	/* Get SI list     */
+	{"SILC",           0, 13},	/* Clear SI list     */
+	{"LOCK",           0, -1},	/* LOCK channel     */
+	{"UNLOCK",         0, -1},	/* UNLOCK channel     */
+	{"FV2ON",          1, 14},	/* Leased mode on               */
+	{"FV2OFF",         1, 15},	/* Leased mode off              */
+	{NULL, 0, -1}
+};
+/* *INDENT-ON* */
+
+
+/*
+ * Simulate an error-response from a card.
+ *
+ * Parameter:
+ *   card = pointer to card struct.
+ */
+static void
+isdnloop_fake_err(isdnloop_card * card)
+{
+	char buf[60];
+
+	sprintf(buf, "E%s", card->omsg);
+	isdnloop_fake(card, buf, -1);
+	isdnloop_fake(card, "NAK", -1);
+}
+
+static u_char ctable_eu[] =
+{0x00, 0x11, 0x01, 0x12};
+static u_char ctable_1t[] =
+{0x00, 0x3b, 0x01, 0x3a};
+
+/*
+ * Assemble a simplified cause message depending on the
+ * D-channel protocol used.
+ *
+ * Parameter:
+ *   card = pointer to card struct.
+ *   loc  = location: 0 = local, 1 = remote.
+ *   cau  = cause: 1 = busy, 2 = nonexistent callerid, 3 = no user responding.
+ * Return:
+ *   Pointer to buffer containing the assembled message.
+ */
+static char *
+isdnloop_unicause(isdnloop_card * card, int loc, int cau)
+{
+	static char buf[6];
+
+	switch (card->ptype) {
+		case ISDN_PTYPE_EURO:
+			sprintf(buf, "E%02X%02X", (loc) ? 4 : 2, ctable_eu[cau]);
+			break;
+		case ISDN_PTYPE_1TR6:
+			sprintf(buf, "%02X44", ctable_1t[cau]);
+			break;
+		default:
+			return ("0000");
+	}
+	return (buf);
+}
+
+/*
+ * Release a virtual connection. Called from timer interrupt, when
+ * called party did not respond.
+ *
+ * Parameter:
+ *   card = pointer to card struct.
+ *   ch   = channel (0-based)
+ */
+static void
+isdnloop_atimeout(isdnloop_card * card, int ch)
+{
+	unsigned long flags;
+	char buf[60];
+
+	save_flags(flags);
+	cli();
+	if (card->rcard) {
+		isdnloop_fake(card->rcard[ch], "DDIS_I", card->rch[ch] + 1);
+		card->rcard[ch]->rcard[card->rch[ch]] = NULL;
+		card->rcard[ch] = NULL;
+	}
+	isdnloop_fake(card, "DDIS_I", ch + 1);
+	/* No user responding */
+	sprintf(buf, "CAU%s", isdnloop_unicause(card, 1, 3));
+	isdnloop_fake(card, buf, ch + 1);
+	restore_flags(flags);
+}
+
+/*
+ * Wrapper for isdnloop_atimeout().
+ */
+static void
+isdnloop_atimeout0(unsigned long data)
+{
+	isdnloop_card *card = (isdnloop_card *) data;
+	isdnloop_atimeout(card, 0);
+}
+
+/*
+ * Wrapper for isdnloop_atimeout().
+ */
+static void
+isdnloop_atimeout1(unsigned long data)
+{
+	isdnloop_card *card = (isdnloop_card *) data;
+	isdnloop_atimeout(card, 1);
+}
+
+/*
+ * Install a watchdog for a user, not responding.
+ *
+ * Parameter:
+ *   card = pointer to card struct.
+ *   ch   = channel to watch for.
+ */
+static void
+isdnloop_start_ctimer(isdnloop_card * card, int ch)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	init_timer(&card->c_timer[ch]);
+	card->c_timer[ch].expires = jiffies + ISDNLOOP_TIMER_ALERTWAIT;
+	if (ch)
+		card->c_timer[ch].function = isdnloop_atimeout1;
+	else
+		card->c_timer[ch].function = isdnloop_atimeout0;
+	card->c_timer[ch].data = (unsigned long) card;
+	add_timer(&card->c_timer[ch]);
+	restore_flags(flags);
+}
+
+/*
+ * Kill a pending channel watchdog.
+ *
+ * Parameter:
+ *   card = pointer to card struct.
+ *   ch   = channel (0-based).
+ */
+static void
+isdnloop_kill_ctimer(isdnloop_card * card, int ch)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	del_timer(&card->c_timer[ch]);
+	restore_flags(flags);
+}
+
+static u_char si2bit[] =
+{0, 1, 0, 0, 0, 2, 0, 4, 0, 0};
+static u_char bit2si[] =
+{1, 5, 7};
+
+/*
+ * Try finding a listener for an outgoing call.
+ *
+ * Parameter:
+ *   card = pointer to calling card.
+ *   p    = pointer to ICN-type setup-string.
+ *   lch  = channel of calling card.
+ *   cmd  = pointer to struct to be filled when parsing setup.
+ * Return:
+ *   0 = found match, alerting should happen.
+ *   1 = found matching number but it is busy.
+ *   2 = no matching listener.
+ *   3 = found matching number but SI does not match.
+ */
+static int
+isdnloop_try_call(isdnloop_card * card, char *p, int lch, isdn_ctrl * cmd)
+{
+	isdnloop_card *cc = cards;
+	unsigned long flags;
+	int ch;
+	int num_match;
+	int i;
+	char *e;
+	char nbuf[32];
+
+	isdnloop_parse_setup(p, cmd);
+	while (cc) {
+		for (ch = 0; ch < 2; ch++) {
+			/* Exclude ourself */
+			if ((cc == card) && (ch == lch))
+				continue;
+			num_match = 0;
+			switch (cc->ptype) {
+				case ISDN_PTYPE_EURO:
+					for (i = 0; i < 3; i++)
+						if (!(strcmp(cc->s0num[i], cmd->parm.setup.phone)))
+							num_match = 1;
+					break;
+				case ISDN_PTYPE_1TR6:
+					e = cc->eazlist[ch];
+					while (*e) {
+						sprintf(nbuf, "%s%c", cc->s0num[0], *e);
+						if (!(strcmp(nbuf, cmd->parm.setup.phone)))
+							num_match = 1;
+						e++;
+					}
+			}
+			if (num_match) {
+				save_flags(flags);
+				cli();
+				/* channel idle? */
+				if (!(cc->rcard[ch])) {
+					/* Check SI */
+					if (!(si2bit[cmd->parm.setup.si1] & cc->sil[ch])) {
+						restore_flags(flags);
+						return 3;
+					}
+					/* ch is idle, si and number matches */
+					cc->rcard[ch] = card;
+					cc->rch[ch] = lch;
+					card->rcard[lch] = cc;
+					card->rch[lch] = ch;
+					restore_flags(flags);
+					return 0;
+				} else {
+					restore_flags(flags);
+					/* num matches, but busy */
+					if (ch == 1)
+						return 1;
+				}
+			}
+		}
+		cc = cc->next;
+	}
+	return 2;
+}
+
+/*
+ * Depending on D-channel protocol and caller/called, modify
+ * phone number.
+ *
+ * Parameter:
+ *   card   = pointer to card struct.
+ *   phone  = pointer phone number.
+ *   caller = flag: 1 = caller, 0 = called.
+ * Return:
+ *   pointer to new phone number.
+ */
+static char *
+isdnloop_vstphone(isdnloop_card * card, char *phone, int caller)
+{
+	int i;
+	static char nphone[30];
+
+	switch (card->ptype) {
+		case ISDN_PTYPE_EURO:
+			if (caller) {
+				for (i = 0; i < 2; i++)
+					if (!(strcmp(card->s0num[i], phone)))
+						return (phone);
+				return (card->s0num[0]);
+			}
+			return (phone);
+			break;
+		case ISDN_PTYPE_1TR6:
+			if (caller) {
+				sprintf(nphone, "%s%c", card->s0num[0], phone[0]);
+				return (nphone);
+			} else
+				return (&phone[strlen(phone) - 1]);
+			break;
+	}
+	return ("\0");
+}
+
+/*
+ * Parse an ICN-type command string sent to the 'card'.
+ * Perform misc. actions depending on the command.
+ *
+ * Parameter:
+ *   card = pointer to card struct.
+ */
+static void
+isdnloop_parse_cmd(isdnloop_card * card)
+{
+	char *p = card->omsg;
+	isdn_ctrl cmd;
+	char buf[60];
+	isdnloop_stat *s = isdnloop_cmd_table;
+	int action = -1;
+	int i;
+	int ch;
+
+	if ((card->omsg[0] != '0') && (card->omsg[2] != ';')) {
+		isdnloop_fake_err(card);
+		return;
+	}
+	ch = card->omsg[1] - '0';
+	if ((ch < 0) || (ch > 2)) {
+		isdnloop_fake_err(card);
+		return;
+	}
+	p += 3;
+	while (s->statstr) {
+		if (!strncmp(p, s->statstr, strlen(s->statstr))) {
+			action = s->action;
+			if (s->command && (ch != 0)) {
+				isdnloop_fake_err(card);
+				return;
+			}
+			break;
+		}
+		s++;
+	}
+	if (action == -1)
+		return;
+	switch (action) {
+		case 1:
+			/* 0x;BCON_R */
+			if (card->rcard[ch - 1]) {
+				isdnloop_fake(card->rcard[ch - 1], "BCON_I",
+					      card->rch[ch - 1] + 1);
+			}
+			break;
+		case 17:
+			/* 0x;BCON_I */
+			if (card->rcard[ch - 1]) {
+				isdnloop_fake(card->rcard[ch - 1], "BCON_C",
+					      card->rch[ch - 1] + 1);
+			}
+			break;
+		case 2:
+			/* 0x;BDIS_R */
+			isdnloop_fake(card, "BDIS_C", ch);
+			if (card->rcard[ch - 1]) {
+				isdnloop_fake(card->rcard[ch - 1], "BDIS_I",
+					      card->rch[ch - 1] + 1);
+			}
+			break;
+		case 16:
+			/* 0x;DCON_R */
+			isdnloop_kill_ctimer(card, ch - 1);
+			if (card->rcard[ch - 1]) {
+				isdnloop_kill_ctimer(card->rcard[ch - 1], card->rch[ch - 1]);
+				isdnloop_fake(card->rcard[ch - 1], "DCON_C",
+					      card->rch[ch - 1] + 1);
+				isdnloop_fake(card, "DCON_C", ch);
+			}
+			break;
+		case 3:
+			/* 0x;DDIS_R */
+			isdnloop_kill_ctimer(card, ch - 1);
+			if (card->rcard[ch - 1]) {
+				isdnloop_kill_ctimer(card->rcard[ch - 1], card->rch[ch - 1]);
+				isdnloop_fake(card->rcard[ch - 1], "DDIS_I",
+					      card->rch[ch - 1] + 1);
+				card->rcard[ch - 1] = NULL;
+			}
+			isdnloop_fake(card, "DDIS_C", ch);
+			break;
+		case 4:
+			/* 0x;DSCA_Rdd,yy,zz,oo */
+			if (card->ptype != ISDN_PTYPE_1TR6) {
+				isdnloop_fake_err(card);
+				return;
+			}
+			/* Fall through */
+		case 5:
+			/* 0x;DCAL_Rdd,yy,zz,oo */
+			p += 6;
+			switch (isdnloop_try_call(card, p, ch - 1, &cmd)) {
+				case 0:
+					/* Alerting */
+					sprintf(buf, "D%s_I%s,%02d,%02d,%s",
+					   (action == 4) ? "SCA" : "CAL",
+						isdnloop_vstphone(card, cmd.parm.setup.eazmsn, 1),
+						cmd.parm.setup.si1,
+						cmd.parm.setup.si2,
+					isdnloop_vstphone(card->rcard[ch],
+					       cmd.parm.setup.phone, 0));
+					isdnloop_fake(card->rcard[ch - 1], buf, card->rch[ch - 1] + 1);
+					/* Fall through */
+				case 3:
+					/* si1 does not match, dont alert but start timer */
+					isdnloop_start_ctimer(card, ch - 1);
+					break;
+				case 1:
+					/* Remote busy */
+					isdnloop_fake(card, "DDIS_I", ch);
+					sprintf(buf, "CAU%s", isdnloop_unicause(card, 1, 1));
+					isdnloop_fake(card, buf, ch);
+					break;
+				case 2:
+					/* No such user */
+					isdnloop_fake(card, "DDIS_I", ch);
+					sprintf(buf, "CAU%s", isdnloop_unicause(card, 1, 2));
+					isdnloop_fake(card, buf, ch);
+					break;
+			}
+			break;
+		case 6:
+			/* 0x;EAZC */
+			card->eazlist[ch - 1][0] = '\0';
+			break;
+		case 7:
+			/* 0x;EAZ */
+			p += 3;
+			strcpy(card->eazlist[ch - 1], p);
+			break;
+		case 8:
+			/* 0x;SEEAZ */
+			sprintf(buf, "EAZ-LIST: %s", card->eazlist[ch - 1]);
+			isdnloop_fake(card, buf, ch + 1);
+			break;
+		case 9:
+			/* 0x;MSN */
+			break;
+		case 10:
+			/* 0x;MSNALL */
+			break;
+		case 11:
+			/* 0x;SETSIL */
+			p += 6;
+			i = 0;
+			while (strchr("0157", *p)) {
+				if (i)
+					card->sil[ch - 1] |= si2bit[*p - '0'];
+				i = (*p++ == '0');
+			}
+			if (*p)
+				isdnloop_fake_err(card);
+			break;
+		case 12:
+			/* 0x;SEESIL */
+			sprintf(buf, "SIN-LIST: ");
+			p = buf + 10;
+			for (i = 0; i < 3; i++)
+				if (card->sil[ch - 1] & (1 << i))
+					p += sprintf(p, "%02d", bit2si[i]);
+			isdnloop_fake(card, buf, ch + 1);
+			break;
+		case 13:
+			/* 0x;SILC */
+			card->sil[ch - 1] = 0;
+			break;
+		case 14:
+			/* 00;FV2ON */
+			break;
+		case 15:
+			/* 00;FV2OFF */
+			break;
+	}
+}
+
+/*
+ * Put command-strings into the of the 'card'. In reality, execute them
+ * right in place by calling isdnloop_parse_cmd(). Also copy every
+ * command to the read message ringbuffer, preceeding it with a '>'.
+ * These mesagges can be read at /dev/isdnctrl.
+ *
+ * Parameter:
+ *   buf  = pointer to command buffer.
+ *   len  = length of buffer data.
+ *   user = flag: 1 = called form userlevel, 0 called from kernel.
+ *   card = pointer to card struct.
+ * Return:
+ *   number of bytes transfered (currently always equals len).
+ */
+static int
+isdnloop_writecmd(const u_char * buf, int len, int user, isdnloop_card * card)
+{
+	int xcount = 0;
+	int ocount = 1;
+	isdn_ctrl cmd;
+
+	while (len) {
+		int count = MIN(255, len);
+		u_char *p;
+		u_char msg[0x100];
+
+		if (user)
+			copy_from_user(msg, buf, count);
+		else
+			memcpy(msg, buf, count);
+		isdnloop_putmsg(card, '>');
+		for (p = msg; count > 0; count--, p++) {
+			len--;
+			xcount++;
+			isdnloop_putmsg(card, *p);
+			card->omsg[card->optr] = *p;
+			if (*p == '\n') {
+				card->omsg[card->optr] = '\0';
+				card->optr = 0;
+				isdnloop_parse_cmd(card);
+				if (len) {
+					isdnloop_putmsg(card, '>');
+					ocount++;
+				}
+			} else {
+				if (card->optr < 59)
+					card->optr++;
+			}
+			ocount++;
+		}
+	}
+	cmd.command = ISDN_STAT_STAVAIL;
+	cmd.driver = card->myid;
+	cmd.arg = ocount;
+	card->interface.statcallb(&cmd);
+	return xcount;
+}
+
+/*
+ * Delete card's pending timers, send STOP to linklevel
+ */
+static void
+isdnloop_stopcard(isdnloop_card * card)
+{
+	unsigned long flags;
+	isdn_ctrl cmd;
+
+	save_flags(flags);
+	cli();
+	if (card->flags & ISDNLOOP_FLAGS_RUNNING) {
+		card->flags &= ~ISDNLOOP_FLAGS_RUNNING;
+		del_timer(&card->st_timer);
+		del_timer(&card->rb_timer);
+		del_timer(&card->c_timer[0]);
+		del_timer(&card->c_timer[1]);
+		cmd.command = ISDN_STAT_STOP;
+		cmd.driver = card->myid;
+		card->interface.statcallb(&cmd);
+	}
+	restore_flags(flags);
+}
+
+/*
+ * Stop all cards before unload.
+ */
+static void
+isdnloop_stopallcards(void)
+{
+	isdnloop_card *p = cards;
+
+	while (p) {
+		isdnloop_stopcard(p);
+		p = p->next;
+	}
+}
+
+/*
+ * Start a 'card'. Simulate card's boot message and set the phone
+ * number(s) of the virtual 'S0-Interface'. Install D-channel
+ * poll timer.
+ *
+ * Parameter:
+ *   card  = pointer to card struct.
+ *   sdefp = pointer to struct holding ioctl parameters.
+ * Return:
+ *   0 on success, -E??? otherwise.
+ */
+static int
+isdnloop_start(isdnloop_card * card, isdnloop_sdef * sdefp)
+{
+	unsigned long flags;
+	isdnloop_sdef sdef;
+	int i;
+
+	if (card->flags & ISDNLOOP_FLAGS_RUNNING)
+		return -EBUSY;
+	copy_from_user((char *) &sdef, (char *) sdefp, sizeof(sdef));
+	save_flags(flags);
+	cli();
+	switch (sdef.ptype) {
+		case ISDN_PTYPE_EURO:
+			if (isdnloop_fake(card, "DRV1.23EC-Q.931-CAPI-CNS-BASIS-20.02.96",
+					  -1)) {
+				restore_flags(flags);
+				return -ENOMEM;
+			}
+			card->sil[0] = card->sil[1] = 4;
+			if (isdnloop_fake(card, "TEI OK", 0)) {
+				restore_flags(flags);
+				return -ENOMEM;
+			}
+			for (i = 0; i < 3; i++)
+				strcpy(card->s0num[i], sdef.num[i]);
+			break;
+		case ISDN_PTYPE_1TR6:
+			if (isdnloop_fake(card, "DRV1.04TC-1TR6-CAPI-CNS-BASIS-29.11.95",
+					  -1)) {
+				restore_flags(flags);
+				return -ENOMEM;
+			}
+			card->sil[0] = card->sil[1] = 4;
+			if (isdnloop_fake(card, "TEI OK", 0)) {
+				restore_flags(flags);
+				return -ENOMEM;
+			}
+			strcpy(card->s0num[0], sdef.num[0]);
+			card->s0num[1][0] = '\0';
+			card->s0num[2][0] = '\0';
+			break;
+		default:
+			restore_flags(flags);
+			printk(KERN_WARNING "isdnloop: Illegal D-channel protocol %d\n",
+			       sdef.ptype);
+			return -EINVAL;
+	}
+	init_timer(&card->st_timer);
+	card->st_timer.expires = jiffies + ISDNLOOP_TIMER_DCREAD;
+	card->st_timer.function = isdnloop_polldchan;
+	card->st_timer.data = (unsigned long) card;
+	add_timer(&card->st_timer);
+	card->flags |= ISDNLOOP_FLAGS_RUNNING;
+	restore_flags(flags);
+	return 0;
+}
+
+/*
+ * Main handler for commands sent by linklevel.
+ */
+static int
+isdnloop_command(isdn_ctrl * c, isdnloop_card * card)
+{
+	ulong a;
+	int i;
+	char cbuf[60];
+	isdn_ctrl cmd;
+	isdnloop_cdef cdef;
+
+	switch (c->command) {
+		case ISDN_CMD_IOCTL:
+			memcpy(&a, c->parm.num, sizeof(ulong));
+			switch (c->arg) {
+				case ISDNLOOP_IOCTL_DEBUGVAR:
+					return (ulong) card;
+				case ISDNLOOP_IOCTL_STARTUP:
+					if ((i = verify_area(VERIFY_READ, (void *) a, sizeof(isdnloop_sdef))))
+						return i;
+					return (isdnloop_start(card, (isdnloop_sdef *) a));
+					break;
+				case ISDNLOOP_IOCTL_ADDCARD:
+					if ((i = verify_area(VERIFY_READ, (void *) a, sizeof(isdnloop_cdef))))
+						return i;
+					copy_from_user((char *) &cdef, (char *) a, sizeof(cdef));
+					return (isdnloop_addcard(cdef.id1));
+					break;
+				case ISDNLOOP_IOCTL_LEASEDCFG:
+					if (a) {
+						if (!card->leased) {
+							card->leased = 1;
+							while (card->ptype == ISDN_PTYPE_UNKNOWN) {
+								current->timeout = jiffies + 10;
+								schedule();
+							}
+							current->timeout = jiffies + 10;
+							schedule();
+							sprintf(cbuf, "00;FV2ON\n01;EAZ1\n02;EAZ2\n");
+							i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card);
+							printk(KERN_INFO
+							       "isdnloop: (%s) Leased-line mode enabled\n",
+							       CID);
+							cmd.command = ISDN_STAT_RUN;
+							cmd.driver = card->myid;
+							cmd.arg = 0;
+							card->interface.statcallb(&cmd);
+						}
+					} else {
+						if (card->leased) {
+							card->leased = 0;
+							sprintf(cbuf, "00;FV2OFF\n");
+							i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card);
+							printk(KERN_INFO
+							       "isdnloop: (%s) Leased-line mode disabled\n",
+							       CID);
+							cmd.command = ISDN_STAT_RUN;
+							cmd.driver = card->myid;
+							cmd.arg = 0;
+							card->interface.statcallb(&cmd);
+						}
+					}
+					return 0;
+				default:
+					return -EINVAL;
+			}
+			break;
+		case ISDN_CMD_DIAL:
+			if (!card->flags & ISDNLOOP_FLAGS_RUNNING)
+				return -ENODEV;
+			if (card->leased)
+				break;
+			if ((c->arg & 255) < ISDNLOOP_BCH) {
+				char *p;
+				char dial[50];
+				char dcode[4];
+
+				a = c->arg;
+				p = c->parm.setup.phone;
+				if (*p == 's' || *p == 'S') {
+					/* Dial for SPV */
+					p++;
+					strcpy(dcode, "SCA");
+				} else
+					/* Normal Dial */
+					strcpy(dcode, "CAL");
+				strcpy(dial, p);
+				sprintf(cbuf, "%02d;D%s_R%s,%02d,%02d,%s\n", (int) (a + 1),
+					dcode, dial, c->parm.setup.si1,
+				c->parm.setup.si2, c->parm.setup.eazmsn);
+				i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card);
+			}
+			break;
+		case ISDN_CMD_ACCEPTD:
+			if (!card->flags & ISDNLOOP_FLAGS_RUNNING)
+				return -ENODEV;
+			if (c->arg < ISDNLOOP_BCH) {
+				a = c->arg + 1;
+				cbuf[0] = 0;
+				switch (card->l2_proto[a - 1]) {
+					case ISDN_PROTO_L2_X75I:
+						sprintf(cbuf, "%02d;BX75\n", (int) a);
+						break;
+#ifdef CONFIG_ISDN_X25
+					case ISDN_PROTO_L2_X25DTE:
+						sprintf(cbuf, "%02d;BX2T\n", (int) a);
+						break;
+					case ISDN_PROTO_L2_X25DCE:
+						sprintf(cbuf, "%02d;BX2C\n", (int) a);
+						break;
+#endif
+					case ISDN_PROTO_L2_HDLC:
+						sprintf(cbuf, "%02d;BTRA\n", (int) a);
+						break;
+				}
+				if (strlen(cbuf))
+					i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card);
+				sprintf(cbuf, "%02d;DCON_R\n", (int) a);
+				i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card);
+			}
+			break;
+		case ISDN_CMD_ACCEPTB:
+			if (!card->flags & ISDNLOOP_FLAGS_RUNNING)
+				return -ENODEV;
+			if (c->arg < ISDNLOOP_BCH) {
+				a = c->arg + 1;
+				switch (card->l2_proto[a - 1]) {
+					case ISDN_PROTO_L2_X75I:
+						sprintf(cbuf, "%02d;BCON_R,BX75\n", (int) a);
+						break;
+#ifdef CONFIG_ISDN_X25
+					case ISDN_PROTO_L2_X25DTE:
+						sprintf(cbuf, "%02d;BCON_R,BX2T\n", (int) a);
+						break;
+					case ISDN_PROTO_L2_X25DCE:
+						sprintf(cbuf, "%02d;BCON_R,BX2C\n", (int) a);
+						break;
+#endif
+					case ISDN_PROTO_L2_HDLC:
+						sprintf(cbuf, "%02d;BCON_R,BTRA\n", (int) a);
+						break;
+					default:
+						sprintf(cbuf, "%02d;BCON_R\n", (int) a);
+				}
+				printk(KERN_DEBUG "isdnloop writecmd '%s'\n", cbuf);
+				i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card);
+				break;
+		case ISDN_CMD_HANGUP:
+				if (!card->flags & ISDNLOOP_FLAGS_RUNNING)
+					return -ENODEV;
+				if (c->arg < ISDNLOOP_BCH) {
+					a = c->arg + 1;
+					sprintf(cbuf, "%02d;BDIS_R\n%02d;DDIS_R\n", (int) a, (int) a);
+					i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card);
+				}
+				break;
+		case ISDN_CMD_SETEAZ:
+				if (!card->flags & ISDNLOOP_FLAGS_RUNNING)
+					return -ENODEV;
+				if (card->leased)
+					break;
+				if (c->arg < ISDNLOOP_BCH) {
+					a = c->arg + 1;
+					if (card->ptype == ISDN_PTYPE_EURO) {
+						sprintf(cbuf, "%02d;MS%s%s\n", (int) a,
+							c->parm.num[0] ? "N" : "ALL", c->parm.num);
+					} else
+						sprintf(cbuf, "%02d;EAZ%s\n", (int) a,
+							c->parm.num[0] ? c->parm.num : "0123456789");
+					i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card);
+				}
+				break;
+		case ISDN_CMD_CLREAZ:
+				if (!card->flags & ISDNLOOP_FLAGS_RUNNING)
+					return -ENODEV;
+				if (card->leased)
+					break;
+				if (c->arg < ISDNLOOP_BCH) {
+					a = c->arg + 1;
+					if (card->ptype == ISDN_PTYPE_EURO)
+						sprintf(cbuf, "%02d;MSNC\n", (int) a);
+					else
+						sprintf(cbuf, "%02d;EAZC\n", (int) a);
+					i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card);
+				}
+				break;
+		case ISDN_CMD_SETL2:
+				if (!card->flags & ISDNLOOP_FLAGS_RUNNING)
+					return -ENODEV;
+				if ((c->arg & 255) < ISDNLOOP_BCH) {
+					a = c->arg;
+					switch (a >> 8) {
+						case ISDN_PROTO_L2_X75I:
+							sprintf(cbuf, "%02d;BX75\n", (int) (a & 255) + 1);
+							break;
+#ifdef CONFIG_ISDN_X25
+						case ISDN_PROTO_L2_X25DTE:
+							sprintf(cbuf, "%02d;BX2T\n", (int) (a & 255) + 1);
+							break;
+						case ISDN_PROTO_L2_X25DCE:
+							sprintf(cbuf, "%02d;BX2C\n", (int) (a & 255) + 1);
+							break;
+#endif
+						case ISDN_PROTO_L2_HDLC:
+							sprintf(cbuf, "%02d;BTRA\n", (int) (a & 255) + 1);
+							break;
+						default:
+							return -EINVAL;
+					}
+					i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card);
+					card->l2_proto[a & 255] = (a >> 8);
+				}
+				break;
+		case ISDN_CMD_GETL2:
+				if (!card->flags & ISDNLOOP_FLAGS_RUNNING)
+					return -ENODEV;
+				if ((c->arg & 255) < ISDNLOOP_BCH)
+					return card->l2_proto[c->arg & 255];
+				else
+					return -ENODEV;
+		case ISDN_CMD_SETL3:
+				if (!card->flags & ISDNLOOP_FLAGS_RUNNING)
+					return -ENODEV;
+				return 0;
+		case ISDN_CMD_GETL3:
+				if (!card->flags & ISDNLOOP_FLAGS_RUNNING)
+					return -ENODEV;
+				if ((c->arg & 255) < ISDNLOOP_BCH)
+					return ISDN_PROTO_L3_TRANS;
+				else
+					return -ENODEV;
+		case ISDN_CMD_GETEAZ:
+				if (!card->flags & ISDNLOOP_FLAGS_RUNNING)
+					return -ENODEV;
+				break;
+		case ISDN_CMD_SETSIL:
+				if (!card->flags & ISDNLOOP_FLAGS_RUNNING)
+					return -ENODEV;
+				break;
+		case ISDN_CMD_GETSIL:
+				if (!card->flags & ISDNLOOP_FLAGS_RUNNING)
+					return -ENODEV;
+				break;
+		case ISDN_CMD_LOCK:
+				MOD_INC_USE_COUNT;
+				break;
+		case ISDN_CMD_UNLOCK:
+				MOD_DEC_USE_COUNT;
+				break;
+		default:
+				return -EINVAL;
+			}
+	}
+	return 0;
+}
+
+/*
+ * Find card with given driverId
+ */
+static inline isdnloop_card *
+isdnloop_findcard(int driverid)
+{
+	isdnloop_card *p = cards;
+
+	while (p) {
+		if (p->myid == driverid)
+			return p;
+		p = p->next;
+	}
+	return (isdnloop_card *) 0;
+}
+
+/*
+ * Wrapper functions for interface to linklevel
+ */
+static int
+if_command(isdn_ctrl * c)
+{
+	isdnloop_card *card = isdnloop_findcard(c->driver);
+
+	if (card)
+		return (isdnloop_command(c, card));
+	printk(KERN_ERR
+	       "isdnloop: if_command called with invalid driverId!\n");
+	return -ENODEV;
+}
+
+static int
+if_writecmd(const u_char * buf, int len, int user, int id, int channel)
+{
+	isdnloop_card *card = isdnloop_findcard(id);
+
+	if (card) {
+		if (!card->flags & ISDNLOOP_FLAGS_RUNNING)
+			return -ENODEV;
+		return (isdnloop_writecmd(buf, len, user, card));
+	}
+	printk(KERN_ERR
+	       "isdnloop: if_writecmd called with invalid driverId!\n");
+	return -ENODEV;
+}
+
+static int
+if_readstatus(u_char * buf, int len, int user, int id, int channel)
+{
+	isdnloop_card *card = isdnloop_findcard(id);
+
+	if (card) {
+		if (!card->flags & ISDNLOOP_FLAGS_RUNNING)
+			return -ENODEV;
+		return (isdnloop_readstatus(buf, len, user, card));
+	}
+	printk(KERN_ERR
+	       "isdnloop: if_readstatus called with invalid driverId!\n");
+	return -ENODEV;
+}
+
+static int
+if_sendbuf(int id, int channel, int ack, struct sk_buff *skb)
+{
+	isdnloop_card *card = isdnloop_findcard(id);
+
+	if (card) {
+		if (!card->flags & ISDNLOOP_FLAGS_RUNNING)
+			return -ENODEV;
+		/* ack request stored in skb scratch area */
+		*(skb->head) = ack;
+		return (isdnloop_sendbuf(channel, skb, card));
+	}
+	printk(KERN_ERR
+	       "isdnloop: 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 isdnloop_card *
+isdnloop_initcard(char *id)
+{
+	isdnloop_card *card;
+	int i;
+
+	if (!(card = (isdnloop_card *) kmalloc(sizeof(isdnloop_card), GFP_KERNEL))) {
+		printk(KERN_WARNING
+		 "isdnloop: (%s) Could not allocate card-struct.\n", id);
+		return (isdnloop_card *) 0;
+	}
+	memset((char *) card, 0, sizeof(isdnloop_card));
+	card->interface.channels = ISDNLOOP_BCH;
+	card->interface.hl_hdrlen  = 1; /* scratch area for storing ack flag*/ 
+	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 |
+#ifdef CONFIG_ISDN_X25
+	    ISDN_FEATURE_L2_X25DTE |
+	    ISDN_FEATURE_L2_X25DCE |
+#endif
+	    ISDN_FEATURE_L2_HDLC |
+	    ISDN_FEATURE_L3_TRANS |
+	    ISDN_FEATURE_P_UNKNOWN;
+	card->ptype = ISDN_PTYPE_UNKNOWN;
+	strncpy(card->interface.id, id, sizeof(card->interface.id) - 1);
+	card->msg_buf_write = card->msg_buf;
+	card->msg_buf_read = card->msg_buf;
+	card->msg_buf_end = &card->msg_buf[sizeof(card->msg_buf) - 1];
+	for (i = 0; i < ISDNLOOP_BCH; i++) {
+		card->l2_proto[i] = ISDN_PROTO_L2_X75I;
+		skb_queue_head_init(&card->bqueue[i]);
+	}
+	skb_queue_head_init(&card->dqueue);
+	card->next = cards;
+	cards = card;
+	if (!register_isdn(&card->interface)) {
+		cards = cards->next;
+		printk(KERN_WARNING
+		       "isdnloop: Unable to register %s\n", id);
+		kfree(card);
+		return (isdnloop_card *) 0;
+	}
+	card->myid = card->interface.channels;
+	return card;
+}
+
+static int
+isdnloop_addcard(char *id1)
+{
+	ulong flags;
+	isdnloop_card *card;
+
+	save_flags(flags);
+	cli();
+	if (!(card = isdnloop_initcard(id1))) {
+		restore_flags(flags);
+		return -EIO;
+	}
+	restore_flags(flags);
+	printk(KERN_INFO
+	       "isdnloop: (%s) virtual card added\n",
+	       card->interface.id);
+	return 0;
+}
+
+#ifdef MODULE
+#define isdnloop_init init_module
+#else
+void
+isdnloop_setup(char *str, int *ints)
+{
+	static char sid[20];
+
+	if (strlen(str)) {
+		strcpy(sid, str);
+		isdnloop_id = sid;
+	}
+}
+#endif
+
+int
+isdnloop_init(void)
+{
+	char *p;
+	char rev[10];
+
+	/* No symbols to export, hide all symbols */
+	EXPORT_NO_SYMBOLS;
+
+	if ((p = strchr(revision, ':'))) {
+		strcpy(rev, p + 1);
+		p = strchr(rev, '$');
+		*p = 0;
+	} else
+		strcpy(rev, " ??? ");
+	printk(KERN_NOTICE "isdnloop-ISDN-driver Rev%s\n", rev);
+	return (isdnloop_addcard(isdnloop_id));
+}
+
+#ifdef MODULE
+void
+cleanup_module(void)
+{
+	isdn_ctrl cmd;
+	isdnloop_card *card = cards;
+	isdnloop_card *last;
+	int i;
+
+	isdnloop_stopallcards();
+	while (card) {
+		cmd.command = ISDN_STAT_UNLOAD;
+		cmd.driver = card->myid;
+		card->interface.statcallb(&cmd);
+		for (i = 0; i < ISDNLOOP_BCH; i++)
+			isdnloop_free_queue(card, i);
+		card = card->next;
+	}
+	card = cards;
+	while (card) {
+		struct sk_buff *skb;
+
+		last = card;
+		while ((skb = skb_dequeue(&card->dqueue)))
+			dev_kfree_skb(skb);
+		card = card->next;
+		kfree(last);
+	}
+	printk(KERN_NOTICE "isdnloop-ISDN-driver unloaded\n");
+}
+#endif
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/isdnloop/isdnloop.h linux/drivers/isdn/isdnloop/isdnloop.h
--- v2.1.91/linux/drivers/isdn/isdnloop/isdnloop.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/isdnloop/isdnloop.h	Wed Apr  1 16:21:03 1998
@@ -0,0 +1,145 @@
+/* $Id: isdnloop.h,v 1.2 1997/10/01 09:22:07 fritz Exp $
+
+ * Loopback lowlevel module for testing of linklevel.
+ *
+ * Copyright 1997 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)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Log: isdnloop.h,v $
+ * Revision 1.2  1997/10/01 09:22:07  fritz
+ * Removed old compatibility stuff for 2.0.X kernels.
+ * From now on, this code is for 2.1.X ONLY!
+ * Old stuff is still in the separate branch.
+ *
+ * Revision 1.1  1997/03/24 23:02:05  fritz
+ * Added isdnloop driver.
+ *
+ */
+
+#ifndef isdnloop_h
+#define isdnloop_h
+
+#define ISDNLOOP_IOCTL_DEBUGVAR  0
+#define ISDNLOOP_IOCTL_ADDCARD   1
+#define ISDNLOOP_IOCTL_LEASEDCFG 2
+#define ISDNLOOP_IOCTL_STARTUP   3
+
+/* Struct for adding new cards */
+typedef struct isdnloop_cdef {
+	char id1[10];
+} isdnloop_cdef;
+
+/* Struct for configuring cards */
+typedef struct isdnloop_sdef {
+	int ptype;
+	char num[3][20];
+} isdnloop_sdef;
+
+#if defined(__KERNEL__) || defined(__DEBUGVAR__)
+
+#ifdef __KERNEL__
+/* Kernel includes */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/major.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/ioport.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/isdnif.h>
+
+#endif                          /* __KERNEL__ */
+
+#define ISDNLOOP_FLAGS_B1ACTIVE 1	/* B-Channel-1 is open           */
+#define ISDNLOOP_FLAGS_B2ACTIVE 2	/* B-Channel-2 is open           */
+#define ISDNLOOP_FLAGS_RUNNING  4	/* Cards driver activated        */
+#define ISDNLOOP_FLAGS_RBTIMER  8	/* scheduling of B-Channel-poll  */
+#define ISDNLOOP_TIMER_BCREAD 1 /* B-Channel poll-cycle          */
+#define ISDNLOOP_TIMER_DCREAD (HZ/2)	/* D-Channel poll-cycle          */
+#define ISDNLOOP_TIMER_ALERTWAIT (10*HZ)	/* Alert timeout                 */
+#define ISDNLOOP_MAX_SQUEUE 65536	/* Max. outstanding send-data    */
+#define ISDNLOOP_BCH 2          /* channels per card             */
+
+/*
+ * Per card driver data
+ */
+typedef struct isdnloop_card {
+	struct isdnloop_card *next;	/* Pointer to next device struct    */
+	struct isdnloop_card
+	*rcard[ISDNLOOP_BCH];   /* Pointer to 'remote' card         */
+	int rch[ISDNLOOP_BCH];  /* 'remote' channel                 */
+	int myid;               /* Driver-Nr. assigned by linklevel */
+	int leased;             /* Flag: This Adapter is connected  */
+	/*       to a leased line           */
+	int sil[ISDNLOOP_BCH];  /* SI's to listen for               */
+	char eazlist[ISDNLOOP_BCH][11];
+	/* EAZ's to listen for              */
+	char s0num[3][20];      /* 1TR6 base-number or MSN's        */
+	unsigned short flags;   /* Statusflags                      */
+	int ptype;              /* Protocol type (1TR6 or Euro)     */
+	struct timer_list st_timer;	/* Timer for Status-Polls           */
+	struct timer_list rb_timer;	/* Timer for B-Channel-Polls        */
+	struct timer_list
+	 c_timer[ISDNLOOP_BCH]; /* Timer for Alerting               */
+	int l2_proto[ISDNLOOP_BCH];	/* Current layer-2-protocol         */
+	isdn_if interface;      /* Interface to upper layer         */
+	int iptr;               /* Index to imsg-buffer             */
+	char imsg[60];          /* Internal buf for status-parsing  */
+	int optr;               /* Index to omsg-buffer             */
+	char omsg[60];          /* Internal buf for cmd-parsing     */
+	char msg_buf[2048];     /* Buffer for status-messages       */
+	char *msg_buf_write;    /* Writepointer for statusbuffer    */
+	char *msg_buf_read;     /* Readpointer for statusbuffer     */
+	char *msg_buf_end;      /* Pointer to end of statusbuffer   */
+	int sndcount[ISDNLOOP_BCH];	/* Byte-counters for B-Ch.-send     */
+	struct sk_buff_head
+	 bqueue[ISDNLOOP_BCH];  /* B-Channel queues                 */
+	struct sk_buff_head dqueue;	/* D-Channel queue                  */
+} isdnloop_card;
+
+/*
+ * Main driver data
+ */
+#ifdef __KERNEL__
+static isdnloop_card *cards = (isdnloop_card *) 0;
+static char *isdnloop_id = "\0";
+
+#ifdef MODULE
+MODULE_AUTHOR("Fritz Elfert");
+MODULE_PARM(isdnloop_id, "s");
+MODULE_PARM_DESC(isdnloop_id, "ID-String of first card");
+#endif
+
+#endif                          /* __KERNEL__ */
+
+/* Utility-Macros */
+
+#define CID (card->interface.id)
+#define MIN(a,b) ((a<b)?a:b)
+#define MAX(a,b) ((a>b)?a:b)
+
+#endif                          /* defined(__KERNEL__) || defined(__DEBUGVAR__) */
+#endif                          /* isdnloop_h */
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/pcbit/capi.c linux/drivers/isdn/pcbit/capi.c
--- v2.1.91/linux/drivers/isdn/pcbit/capi.c	Thu Feb 27 10:57:30 1997
+++ linux/drivers/isdn/pcbit/capi.c	Wed Apr  1 16:21:03 1998
@@ -147,9 +147,6 @@
 		return -1;
 	}
 
-        SET_SKB_FREE((*skb));
-
-
         *((ushort*) skb_put(*skb, 2) ) = chan->callref;  
         *(skb_put(*skb, 1)) = 0x01;  /* ACCEPT_CALL */
         *(skb_put(*skb, 1)) = 0;
@@ -170,8 +167,6 @@
 		return -1;
 	}
 
-        SET_SKB_FREE((*skb));
-
         *((ushort*) skb_put(*skb, 2) ) = chan->callref;  
 
 #ifdef DEBUG
@@ -200,8 +195,6 @@
 		return -1;
 	}
 
-        SET_SKB_FREE((*skb));
-
         *((ushort*) skb_put(*skb, 2) ) = chan->callref;  
 
         return 2;
@@ -222,8 +215,6 @@
 		return -1;
 	}
 
-        SET_SKB_FREE((*skb));
-  
         *((ushort*) skb_put(*skb, 2) ) = chan->callref;  
 
         /* Layer2 protocol */
@@ -285,8 +276,6 @@
 		return -1;
 	}
 
-        SET_SKB_FREE((*skb));
-
         *((ushort*) skb_put(*skb, 2) ) = chan->callref;  
 
         
@@ -338,8 +327,6 @@
 		return -1;
 	}
 
-        SET_SKB_FREE((*skb));
-
         *((ushort*) skb_put(*skb, 2) ) = chan->callref;  
 
         *(skb_put(*skb, 1)) = chan->layer2link;
@@ -357,8 +344,6 @@
 		return -1;
 	}
 
-        SET_SKB_FREE((*skb));
-
         *((ushort*) skb_put(*skb, 2) ) = callref;  
 
         *(skb_put(*skb, 1)) = 2;                  /* Cause.Length = 2; */
@@ -381,8 +366,6 @@
 		printk(KERN_WARNING "capi_disc_resp: alloc_skb failed\n");
 		return -1;
 	}
-
-        SET_SKB_FREE((*skb));
 
         *((ushort*) skb_put(*skb, 2)) = chan->callref;  
 
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/pcbit/drv.c linux/drivers/isdn/pcbit/drv.c
--- v2.1.91/linux/drivers/isdn/pcbit/drv.c	Thu Feb 12 20:56:07 1998
+++ linux/drivers/isdn/pcbit/drv.c	Wed Apr  1 16:21:03 1998
@@ -61,7 +61,7 @@
 
 int pcbit_command(isdn_ctrl* ctl);
 int pcbit_stat(u_char* buf, int len, int user, int, int);
-int pcbit_xmit(int driver, int chan, struct sk_buff *skb);
+int pcbit_xmit(int driver, int chan, int ack, struct sk_buff *skb);
 int pcbit_writecmd(const u_char*, int, int, int, int);
 
 static int set_protocol_running(struct pcbit_dev * dev);
@@ -164,7 +164,6 @@
 			    ISDN_FEATURE_L2_HDLC | ISDN_FEATURE_L2_TRANS );
 
 	dev_if->writebuf_skb = pcbit_xmit;
-	dev_if->writebuf  = NULL;
 	dev_if->hl_hdrlen = 10;
 
 	dev_if->maxbufsize = MAXBUFSIZE;
@@ -330,7 +329,7 @@
 }
 #endif
 
-int pcbit_xmit(int driver, int chnum, struct sk_buff *skb)
+int pcbit_xmit(int driver, int chnum, int ack, struct sk_buff *skb)
 {
 	ushort hdrlen;
 	int refnum, len;
@@ -730,8 +729,6 @@
 		break;
 #endif
 	}
-
-	SET_SKB_FREE(skb);
 
 	kfree_skb(skb);
 
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/pcbit/layer2.c linux/drivers/isdn/pcbit/layer2.c
--- v2.1.91/linux/drivers/isdn/pcbit/layer2.c	Thu Feb 12 20:56:07 1998
+++ linux/drivers/isdn/pcbit/layer2.c	Wed Apr  1 16:21:03 1998
@@ -380,10 +380,8 @@
 			return;
 #else
 			/* discard previous queued frame */
-			if (dev->read_frame->skb) {
-				SET_SKB_FREE(dev->read_frame->skb);
+			if (dev->read_frame->skb)
 				kfree_skb(dev->read_frame->skb);
-			}
 			kfree(dev->read_frame);
 			dev->read_frame = NULL;
 #endif
@@ -648,10 +646,8 @@
 	dev->w_busy = dev->r_busy = 1;
 
 	if (dev->read_frame) {
-		if (dev->read_frame->skb) {
-			SET_SKB_FREE(dev->read_frame->skb);
+		if (dev->read_frame->skb)
 			kfree_skb(dev->read_frame->skb);
-		}
 		kfree(dev->read_frame);
 		dev->read_frame = NULL;
 	}
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/pcbit/module.c linux/drivers/isdn/pcbit/module.c
--- v2.1.91/linux/drivers/isdn/pcbit/module.c	Fri Jan 30 11:28:07 1998
+++ linux/drivers/isdn/pcbit/module.c	Wed Apr  1 16:21:03 1998
@@ -35,10 +35,8 @@
 extern int pcbit_init_dev(int board, int mem_base, int irq);
 
 #ifdef MODULE
-#if (LINUX_VERSION_CODE > 0x020111)
 MODULE_PARM(mem, "1-" __MODULE_STRING(MAX_PCBIT_CARDS) "i");
 MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_PCBIT_CARDS) "i");
-#endif
 #define pcbit_init init_module
 #endif
 
@@ -87,11 +85,7 @@
 	}
 
 	/* No symbols to export, hide all symbols */
-#if (LINUX_VERSION_CODE < 0x020111)
-	register_symtab(NULL);
-#else
 	EXPORT_NO_SYMBOLS;
-#endif
 
 	return 0;
 }
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/sc/debug.c linux/drivers/isdn/sc/debug.c
--- v2.1.91/linux/drivers/isdn/sc/debug.c	Thu Feb 27 10:57:30 1997
+++ linux/drivers/isdn/sc/debug.c	Wed Apr  1 16:21:04 1998
@@ -1,5 +1,5 @@
 /*
- *  $Id: debug.c,v 1.2 1996/11/20 17:49:50 fritz Exp $
+ *  $Id: debug.c,v 1.3 1997/10/01 09:22:20 fritz Exp $
  *  Copyright (C) 1996  SpellCaster Telecommunications Inc.
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -29,13 +29,8 @@
 
 #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
+#define REQUEST_IRQ(a,b,c,d,e) request_irq(a,b,c,d,e)
+#define FREE_IRQ(a,b) free_irq(a,b)
 
 inline char *strcpy(char *, const char *);
 
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/sc/event.c linux/drivers/isdn/sc/event.c
--- v2.1.91/linux/drivers/isdn/sc/event.c	Thu Feb 27 10:57:30 1997
+++ linux/drivers/isdn/sc/event.c	Wed Apr  1 16:21:04 1998
@@ -1,5 +1,5 @@
 /*
- *  $Id: event.c,v 1.3 1997/02/11 22:53:41 fritz Exp $
+ *  $Id: event.c,v 1.4 1997/10/09 22:30:58 fritz Exp $
  *  Copyright (C) 1996  SpellCaster Telecommunications Inc.
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -62,10 +62,16 @@
 	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);
+		switch (event) {
+			case ISDN_STAT_BSENT:
+				memcpy(&cmd.parm.length, Data, sizeof(cmd.parm.length));
+				break;
+			case ISDN_STAT_ICALL:
+				memcpy(&cmd.parm.setup, Data, sizeof(cmd.parm.setup));
+				break;
+			default:
+				strcpy(cmd.parm.num, Data);
+		}
 	}
 
 	cmd.command = event;
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/sc/hardware.h linux/drivers/isdn/sc/hardware.h
--- v2.1.91/linux/drivers/isdn/sc/hardware.h	Fri Jan 30 11:28:07 1998
+++ linux/drivers/isdn/sc/hardware.h	Wed Apr  1 16:21:04 1998
@@ -16,6 +16,11 @@
 					   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 */
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/sc/init.c linux/drivers/isdn/sc/init.c
--- v2.1.91/linux/drivers/isdn/sc/init.c	Fri Jan 30 11:28:07 1998
+++ linux/drivers/isdn/sc/init.c	Wed Apr  1 16:21:04 1998
@@ -20,7 +20,7 @@
 #define MAX_IRQS	10
 
 extern void interrupt_handler(int, void *, struct pt_regs *);
-extern int sndpkt(int, int, struct sk_buff *);
+extern int sndpkt(int, int, int, struct sk_buff *);
 extern int command(isdn_ctrl *);
 extern int indicate_status(int, int, ulong, char*);
 extern int reset(int);
@@ -38,12 +38,10 @@
 }
 
 #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
 /*
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/sc/interrupt.c linux/drivers/isdn/sc/interrupt.c
--- v2.1.91/linux/drivers/isdn/sc/interrupt.c	Sun Jan  4 10:55:08 1998
+++ linux/drivers/isdn/sc/interrupt.c	Wed Apr  1 16:21:04 1998
@@ -1,5 +1,5 @@
 /*
- *  $Id: interrupt.c,v 1.3 1997/02/11 22:53:43 fritz Exp $
+ *  $Id: interrupt.c,v 1.4 1998/01/31 22:10:52 keil Exp $
  *  Copyright (C) 1996  SpellCaster Telecommunications Inc.
  *
  *  This program is free software; you can redistribute it and/or modify
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/sc/message.c linux/drivers/isdn/sc/message.c
--- v2.1.91/linux/drivers/isdn/sc/message.c	Sun Jan  4 10:55:08 1998
+++ linux/drivers/isdn/sc/message.c	Wed Apr  1 16:21:04 1998
@@ -1,5 +1,5 @@
 /*
- *  $Id: message.c,v 1.2 1996/11/20 17:49:54 fritz Exp $
+ *  $Id: message.c,v 1.3 1998/01/31 22:10:55 keil Exp $
  *  Copyright (C) 1996  SpellCaster Telecommunications Inc.
  *
  *  message.c - functions for sending and receiving control messages
@@ -33,7 +33,6 @@
 #include "hardware.h"
 #include "message.h"
 #include "card.h"
-#include <asm/io.h>
 
 extern board *adapter[];
 extern unsigned int cinst;
@@ -203,7 +202,7 @@
 	 * wait for an empty slot in the queue
 	 */
 	while (!(inb(adapter[card]->ioport[FIFO_STATUS]) & WF_NOT_FULL))
-		__SLOW_DOWN_IO;
+		udelay(1);
 
 	/*
 	 * Disable interrupts and map in shared memory
diff -u --recursive --new-file v2.1.91/linux/drivers/isdn/sc/packet.c linux/drivers/isdn/sc/packet.c
--- v2.1.91/linux/drivers/isdn/sc/packet.c	Thu Feb 12 20:56:07 1998
+++ linux/drivers/isdn/sc/packet.c	Wed Apr  1 16:21:04 1998
@@ -1,5 +1,5 @@
 /*
- *  $Id: packet.c,v 1.2 1996/11/20 17:49:55 fritz Exp $
+ *  $Id: packet.c,v 1.4 1998/02/12 23:08:50 keil Exp $
  *  Copyright (C) 1996  SpellCaster Telecommunications Inc.
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -36,7 +36,7 @@
 extern unsigned int cinst;
 
 extern int get_card_from_id(int);
-extern int indicate_status(int, int,ulong,char*);
+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,
@@ -47,6 +47,7 @@
 	LLData	ReqLnkWrite;
 	int status;
 	int card;
+	unsigned long len;
 
 	card = get_card_from_id(devId);
 
@@ -89,6 +90,7 @@
 
 	status = sendmessage(card, CEPID, ceReqTypeLnk, ceReqClass1, ceReqLnkWrite,
 				channel+1, sizeof(LLData), (unsigned int*)&ReqLnkWrite);
+	len = data->len;
 	if(status) {
 		pr_debug("%s: Failed to send packet, status = %d\n", adapter[card]->devicename, status);
 		return -1;
@@ -101,9 +103,9 @@
 			adapter[card]->channel[channel].next_sendbuf;
 			pr_debug("%s: Packet sent successfully\n", adapter[card]->devicename);
 		dev_kfree_skb(data);
-		indicate_status(card,ISDN_STAT_BSENT,channel,NULL);
+		indicate_status(card,ISDN_STAT_BSENT,channel, (char *)&len);
 	}
-	return data->len;
+	return len;
 }
 
 void rcvpkt(int card, RspMessage *rcvmsg)
diff -u --recursive --new-file v2.1.91/linux/drivers/misc/parport_pc.c linux/drivers/misc/parport_pc.c
--- v2.1.91/linux/drivers/misc/parport_pc.c	Thu Mar 26 15:57:03 1998
+++ linux/drivers/misc/parport_pc.c	Tue Mar 31 16:17:40 1998
@@ -482,8 +482,9 @@
  */
 static int parport_ECR_present(struct parport *pb)
 {
-	unsigned char r, octr = parport_pc_read_control(pb), 
-	  oecr = parport_pc_read_econtrol(pb);
+	unsigned char r, octr = parport_pc_read_control(pb);
+	unsigned char oecr = parport_pc_read_econtrol(pb);
+	unsigned char tmp;
 
 	r = parport_pc_read_control(pb);	
 	if ((parport_pc_read_econtrol(pb) & 0x3) == (r & 0x3)) {
@@ -500,12 +501,14 @@
 		return 0;
 
 	parport_pc_write_econtrol(pb, 0x34);
-	if (parport_pc_read_econtrol(pb) != 0x35)
-		return 0;
+	tmp = parport_pc_read_econtrol(pb);
 
 	parport_pc_write_econtrol(pb, oecr);
 	parport_pc_write_control(pb, octr);
 	
+	if (tmp != 0x35)
+		return 0;
+
 	return PARPORT_MODE_PCECR;
 }
 
@@ -735,6 +738,7 @@
 {
 	int irqs;
 	unsigned char octr = parport_pc_read_control(pb);
+	unsigned char oecr = parport_pc_read_econtrol(pb);
 
 #ifndef ADVANCED_DETECT
 	return PARPORT_IRQ_NONE;
@@ -758,6 +762,7 @@
 	udelay(20);
 
 	pb->irq = close_intr_election(irqs);
+	parport_pc_write_econtrol(pb, oecr);
 	parport_pc_write_control(pb, octr);
 	return pb->irq;
 }
@@ -766,6 +771,7 @@
 {
 	int irqs;
 	unsigned char octr = parport_pc_read_control(pb);
+	unsigned char oecr = parport_pc_read_econtrol(pb);
 
 #ifndef ADVANCED_DETECT
 	return PARPORT_IRQ_NONE;
@@ -794,6 +800,7 @@
 	if (pb->irq <= 0)
 		pb->irq = PARPORT_IRQ_NONE;	/* No interrupt detected */
 	
+	parport_pc_write_econtrol(pb, oecr);
 	parport_pc_write_control(pb, octr);
 	return pb->irq;
 }
@@ -815,7 +822,7 @@
 			
 	if (pb->irq == PARPORT_IRQ_NONE && 
 	    (pb->modes & PARPORT_MODE_PCECPEPP)) {
-		int oecr = parport_pc_read_econtrol(pb);
+		unsigned char oecr = parport_pc_read_econtrol(pb);
 		parport_pc_write_econtrol(pb, 0x80);
 		pb->irq = irq_probe_EPP(pb);
 		parport_pc_write_econtrol(pb, oecr);
diff -u --recursive --new-file v2.1.91/linux/drivers/net/de4x5.c linux/drivers/net/de4x5.c
--- v2.1.91/linux/drivers/net/de4x5.c	Tue Mar 17 22:18:14 1998
+++ linux/drivers/net/de4x5.c	Mon Mar 30 00:21:40 1998
@@ -1094,14 +1094,14 @@
     
     dev->base_addr = iobase;
     if (lp->bus == EISA) {
-	printk("%s: %s at 0x%04lx (EISA slot %ld)", 
+	printk("%s: %s at 0x%04lx (EISA slot %ld),\n", 
 	       dev->name, name, iobase, ((iobase>>12)&0x0f));
     } else {                                 /* PCI port address */
-	printk("%s: %s at 0x%04lx (PCI bus %d, device %d)", dev->name, name,
+	printk("%s: %s at 0x%04lx (PCI bus %d, device %d),\n", dev->name, name,
 	       iobase, lp->bus_num, lp->device);
     }
     
-    printk(", h/w address ");
+    printk("      h/w address ");
     status = get_hw_addr(dev);
     for (i = 0; i < ETH_ALEN - 1; i++) {     /* get the ethernet addr. */
 	printk("%2.2x:", dev->dev_addr[i]);
@@ -2092,9 +2092,7 @@
 	 index++) {
 	dev_num = PCI_SLOT(dev_fn);
 	if ((pbus || dnum) && ((pbus != pb) || (dnum != dev_num))) continue;
-	for (pdev = pci_devices; pdev; pdev = pdev->next) {
-	    if ((pdev->bus->number==pb) && (pdev->devfn==dev_fn)) break;
-	}
+	pdev = pci_find_dev(pb, dev_fn);
 
 	vendor = pdev->vendor;
 	device = pdev->device << 8;
@@ -2193,9 +2191,7 @@
 
 	if (lp->bus_num != pb) return;
 	dev_num = PCI_SLOT(dev_fn);
-	for (pdev = pci_devices; pdev; pdev = pdev->next) {
-	    if ((pdev->bus->number == pb) && (pdev->devfn == dev_fn)) break;
-	}
+	pdev = pci_find_dev(pb, dev_fn);
 
 	vendor = pdev->vendor;
 	device = pdev->device << 8;
@@ -5737,10 +5733,7 @@
     for (i=0; 
 	 (pcibios_find_class(class, i, &pb, &dev_fn)!= PCIBIOS_DEVICE_NOT_FOUND);
 	 i++) {
-	for (pdev = pci_devices; pdev; pdev = pdev->next) {
-	    if ((pdev->bus->number==pb) && (pdev->devfn==dev_fn)) break;
-	}
-
+	pdev = pci_find_dev(pb, dev_fn);
 	vendor = pdev->vendor;
 	device = pdev->device << 8;
 	if (is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x) j++;
diff -u --recursive --new-file v2.1.91/linux/drivers/net/ppp.c linux/drivers/net/ppp.c
--- v2.1.91/linux/drivers/net/ppp.c	Tue Mar 17 22:18:14 1998
+++ linux/drivers/net/ppp.c	Fri Mar 27 17:45:16 1998
@@ -8,7 +8,7 @@
  *  Dynamic PPP devices by Jim Freeman <jfree@caldera.com>.
  *  ppp_tty_receive ``noisy-raise-bug'' fixed by Ove Ewerlid <ewerlid@syscon.uu.se>
  *
- *  ==FILEVERSION 980123==
+ *  ==FILEVERSION 980319==
  *
  *  NOTE TO MAINTAINERS:
  *     If you modify this file at all, please set the number above to the
@@ -1326,6 +1326,10 @@
 			(*ppp->sc_rcomp->incomp) (ppp->sc_rc_state,
 						  data, count);
 		}
+	} else if (proto == PPP_COMP && (ppp->flags & SC_DEBUG)) {
+		printk(KERN_DEBUG "ppp: frame not decompressed: "
+		       "flags=%x, count=%d, sc_rc_state=%p\n",
+		       ppp->flags, count, ppp->sc_rc_state);
 	}
 /*
  * Process the uncompressed frame.
@@ -1659,6 +1663,9 @@
 		}
 		break;
 	}
+	if (ppp->flags & SC_DEBUG)
+		printk(KERN_DEBUG "ppp_proto_ccp: %s code %d, flags=%x\n",
+		       (rcvd? "rcvd": "sent"), CCP_CODE(dp), ppp->flags);
 	restore_flags(flags);
 }
 
@@ -1977,16 +1984,15 @@
 	    (control == PPP_UI)			&&
 	    (proto != PPP_LCP)			&&
 	    (proto != PPP_CCP)) {
-		new_data = kmalloc (ppp->mtu, GFP_ATOMIC);
+		new_data = kmalloc (ppp->mtu + PPP_HDRLEN, GFP_ATOMIC);
 		if (new_data == NULL) {
-			if (ppp->flags & SC_DEBUG)
-				printk (KERN_ERR
-					"ppp_dev_xmit_frame: no memory\n");
+			printk (KERN_ERR "ppp_dev_xmit_frame: no memory\n");
 			return 1;
 		}
 
 		new_count = (*ppp->sc_xcomp->compress)
-		    (ppp->sc_xc_state, data, new_data, count, ppp->mtu);
+		    (ppp->sc_xc_state, data, new_data, count,
+		     ppp->mtu + PPP_HDRLEN);
 
 		if (new_count > 0 && (ppp->flags & SC_CCP_UP)) {
 			ppp_dev_xmit_lower (ppp, buf, new_data, new_count, 0);
@@ -2153,7 +2159,7 @@
 }
 
 /*
- * Process the BSD compression IOCTL event for the tty device.
+ * Process the set-compression ioctl.
  */
 
 static int
@@ -2187,7 +2193,7 @@
 
 	save_flags(flags);
 	cli();
-	ppp->flags &= ~(SC_COMP_RUN | SC_DECOMP_RUN);
+	ppp->flags &= ~(data.transmit? SC_COMP_RUN: SC_DECOMP_RUN);
 	restore_flags(flags);
 
 	cp = find_compressor (ccp_option[0]);
@@ -2257,8 +2263,9 @@
                unsigned int param2, unsigned long param3)
 {
 	struct ppp *ppp = tty2ppp (tty);
-	register int temp_i = 0;
+	register int temp_i = 0, oldflags;
 	int error = 0;
+	unsigned long flags;
 /*
  * Verify the status of the PPP device.
  */
@@ -2308,16 +2315,18 @@
 		if (error != 0)
 			break;
 		temp_i &= SC_MASK;
-		temp_i |= (ppp->flags & ~SC_MASK);
 
-		if ((ppp->flags & SC_CCP_OPEN) &&
-		    (temp_i & SC_CCP_OPEN) == 0)
-			ppp_ccp_closed (ppp);
+		if ((ppp->flags & SC_CCP_OPEN) && (temp_i & SC_CCP_OPEN) == 0)
+			ppp_ccp_closed(ppp);
 
-		if ((ppp->flags | temp_i) & SC_DEBUG)
+		save_flags(flags);
+		cli();
+		oldflags = ppp->flags;
+		ppp->flags = temp_i |= (ppp->flags & ~SC_MASK);
+		restore_flags(flags);
+		if ((oldflags | temp_i) & SC_DEBUG)
 			printk (KERN_INFO
 				"ppp_tty_ioctl: set flags to %x\n", temp_i);
-		ppp->flags = temp_i;
 		break;
 /*
  * Set the compression mode
diff -u --recursive --new-file v2.1.91/linux/drivers/net/ppp_deflate.c linux/drivers/net/ppp_deflate.c
--- v2.1.91/linux/drivers/net/ppp_deflate.c	Fri Jan 30 11:28:07 1998
+++ linux/drivers/net/ppp_deflate.c	Fri Mar 27 17:45:16 1998
@@ -1,5 +1,5 @@
 /*
- *  ==FILEVERSION 971001==
+ *  ==FILEVERSION 980319==
  *
  * ppp_deflate.c - interface the zlib procedures for Deflate compression
  * and decompression (as used by gzip) to the PPP code.
@@ -188,20 +188,21 @@
 	struct ppp_deflate_state *state;
 	int w_size;
 
-	MOD_INC_USE_COUNT;
-	if (opt_len != CILEN_DEFLATE || options[0] != CI_DEFLATE
+	if (opt_len != CILEN_DEFLATE
+	    || (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT)
 	    || options[1] != CILEN_DEFLATE
 	    || DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL
 	    || options[3] != DEFLATE_CHK_SEQUENCE)
-		goto out_fail;
+		return NULL;
 	w_size = DEFLATE_SIZE(options[2]);
 	if (w_size < DEFLATE_MIN_SIZE || w_size > DEFLATE_MAX_SIZE)
-		goto out_fail;
+		return NULL;
 
 	state = (struct ppp_deflate_state *) kmalloc(sizeof(*state), GFP_KERNEL);
 	if (state == NULL)
-		goto out_fail;
+		return NULL;
 
+	MOD_INC_USE_COUNT;
 	memset (state, 0, sizeof (struct ppp_deflate_state));
 	state->strm.next_in = NULL;
 	state->strm.zalloc  = zalloc_init;
@@ -217,7 +218,6 @@
 
 out_free:
 	z_comp_free(state);
-out_fail:
 	MOD_DEC_USE_COUNT;
 	return NULL;
 }
@@ -230,7 +230,8 @@
 {
 	struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
 
-	if (opt_len < CILEN_DEFLATE || options[0] != CI_DEFLATE
+	if (opt_len < CILEN_DEFLATE
+	    || (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT)
 	    || options[1] != CILEN_DEFLATE
 	    || DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL
 	    || DEFLATE_SIZE(options[2]) != state->w_size
@@ -264,7 +265,7 @@
     int isize, osize;
 {
 	struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
-	int r, proto, off, olen;
+	int r, proto, off, olen, oavail;
 	unsigned char *wptr;
 
 	/*
@@ -291,9 +292,10 @@
 	wptr += PPP_HDRLEN;
 	wptr[0] = state->seqno >> 8;
 	wptr[1] = state->seqno;
-	wptr += 2;
+	wptr += DEFLATE_OVHD;
+	olen = PPP_HDRLEN + DEFLATE_OVHD;
 	state->strm.next_out = wptr;
-	state->strm.avail_out = osize - (PPP_HDRLEN + 2);
+	state->strm.avail_out = oavail = osize - olen;
 	++state->seqno;
 
 	off = (proto > 0xff) ? 2 : 3;	/* skip 1st proto byte if 0 */
@@ -301,25 +303,23 @@
 	state->strm.next_in = rptr;
 	state->strm.avail_in = (isize - off);
 
-	olen = 0;
 	for (;;) {
 		r = deflate(&state->strm, Z_PACKET_FLUSH);
 		if (r != Z_OK) {
 			if (state->debug)
-				printk(KERN_DEBUG "z_compress: deflate returned %d (%s)\n",
-				       r, (state->strm.msg? state->strm.msg: ""));
+				printk(KERN_ERR
+				       "z_compress: deflate returned %d\n", r);
 			break;
 		}
 		if (state->strm.avail_out == 0) {
-			olen += osize;
+			olen += oavail;
 			state->strm.next_out = NULL;
-			state->strm.avail_out = 1000000;
+			state->strm.avail_out = oavail = 1000000;
 		} else {
 			break;		/* all done */
 		}
 	}
-	if (olen < osize)
-		olen += osize - state->strm.avail_out;
+	olen += oavail - state->strm.avail_out;
 
 	/*
 	 * See if we managed to reduce the size of the packet.
@@ -372,19 +372,21 @@
 	struct ppp_deflate_state *state;
 	int w_size;
 
-	MOD_INC_USE_COUNT;
-	if (opt_len != CILEN_DEFLATE || options[0] != CI_DEFLATE
+	if (opt_len != CILEN_DEFLATE
+	    || (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT)
 	    || options[1] != CILEN_DEFLATE
 	    || DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL
 	    || options[3] != DEFLATE_CHK_SEQUENCE)
-		goto out_fail;
+		return NULL;
 	w_size = DEFLATE_SIZE(options[2]);
 	if (w_size < DEFLATE_MIN_SIZE || w_size > DEFLATE_MAX_SIZE)
-		goto out_fail;
+		return NULL;
 
 	state = (struct ppp_deflate_state *) kmalloc(sizeof(*state), GFP_KERNEL);
 	if (state == NULL)
-		goto out_fail;
+		return NULL;
+
+	MOD_INC_USE_COUNT;
 	memset (state, 0, sizeof (struct ppp_deflate_state));
 	state->w_size        = w_size;
 	state->strm.next_out = NULL;
@@ -398,7 +400,6 @@
 
 out_free:
 	z_decomp_free(state);
-out_fail:
 	MOD_DEC_USE_COUNT;
 	return NULL;
 }
@@ -411,7 +412,8 @@
 {
 	struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
 
-	if (opt_len < CILEN_DEFLATE || options[0] != CI_DEFLATE
+	if (opt_len < CILEN_DEFLATE
+	    || (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT)
 	    || options[1] != CILEN_DEFLATE
 	    || DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL
 	    || DEFLATE_SIZE(options[2]) != state->w_size
@@ -543,8 +545,12 @@
 		}
 	}
 
-	if (decode_proto)
+	if (decode_proto) {
+		if (state->debug)
+			printk(KERN_DEBUG "z_decompress%d: didn't get proto\n",
+			       state->unit);
 		return DECOMP_ERROR;
+	}
 
 	olen = osize + overflow - state->strm.avail_out;
 	state->stats.unc_bytes += olen;
@@ -634,6 +640,23 @@
 	z_comp_stats,		/* decomp_stat */
 };
 
+struct compressor ppp_deflate_draft = {
+	CI_DEFLATE_DRAFT,	/* compress_proto */
+	z_comp_alloc,		/* comp_alloc */
+	z_comp_free,		/* comp_free */
+	z_comp_init,		/* comp_init */
+	z_comp_reset,		/* comp_reset */
+	z_compress,		/* compress */
+	z_comp_stats,		/* comp_stat */
+	z_decomp_alloc,		/* decomp_alloc */
+	z_decomp_free,		/* decomp_free */
+	z_decomp_init,		/* decomp_init */
+	z_decomp_reset,		/* decomp_reset */
+	z_decompress,		/* decompress */
+	z_incomp,		/* incomp */
+	z_comp_stats,		/* decomp_stat */
+};
+
 #ifdef MODULE
 /*************************************************************
  * Module support routines
@@ -646,6 +669,7 @@
         if (answer == 0)
                 printk (KERN_INFO
 			"PPP Deflate Compression module registered\n");
+	ppp_register_compressor(&ppp_deflate_draft);
         return answer;
 }
      
@@ -655,7 +679,9 @@
 	if (MOD_IN_USE)
 		printk (KERN_INFO
 			"Deflate Compression module busy, remove delayed\n");
-	else
+	else {
 	        ppp_unregister_compressor (&ppp_deflate);
+	        ppp_unregister_compressor (&ppp_deflate_draft);
+	}
 }
 #endif
diff -u --recursive --new-file v2.1.91/linux/drivers/net/smc-mca.c linux/drivers/net/smc-mca.c
--- v2.1.91/linux/drivers/net/smc-mca.c	Mon Feb 23 18:12:06 1998
+++ linux/drivers/net/smc-mca.c	Wed Apr  1 14:37:14 1998
@@ -306,9 +306,9 @@
 
 #define MAX_ULTRAMCA_CARDS  4   /* Max number of Ultra cards per module */
 #define NAMELEN     8   /* # of chars for storing dev->name */
-static char namelist[NAMELEN * MAX_ULTRA_CARDS] = { 0, };
+static char namelist[NAMELEN * MAX_ULTRAMCA_CARDS] = { 0, };
 
-static struct device dev_ultra[MAX_ULTRA_CARDS] =
+static struct device dev_ultra[MAX_ULTRAMCA_CARDS] =
 {
 	{
 		NULL,       /* assign a chunk of namelist[] below */
@@ -318,11 +318,11 @@
 	},
 };
 
-static int io[MAX_ULTRA_CARDS] = { 0, };
-static int irq[MAX_ULTRA_CARDS]  = { 0, };
+static int io[MAX_ULTRAMCA_CARDS] = { 0, };
+static int irq[MAX_ULTRAMCA_CARDS]  = { 0, };
 
-MODULE_PARM(io, "1-" __MODULE_STRING(MAX_ULTRA_CARDS) "i");
-MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_ULTRA_CARDS) "i");
+MODULE_PARM(io, "1-" __MODULE_STRING(MAX_ULTRAMCA_CARDS) "i");
+MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_ULTRAMCA_CARDS) "i");
 
 /* This is set up so that only a single autoprobe takes place per call.
 ISA device autoprobes on a running machine are not recommended. */
@@ -331,7 +331,7 @@
 {
 	int this_dev, found = 0;
 
-	for (this_dev = 0; this_dev < MAX_ULTRA_CARDS; this_dev++)
+	for (this_dev = 0; this_dev < MAX_ULTRAMCA_CARDS; this_dev++)
 	{
 		struct device *dev = &dev_ultra[this_dev];
 		dev->name = namelist+(NAMELEN*this_dev);
@@ -360,7 +360,7 @@
 {
 	int this_dev;
 
-	for (this_dev = 0; this_dev < MAX_ULTRA_CARDS; this_dev++)
+	for (this_dev = 0; this_dev < MAX_ULTRAMCA_CARDS; this_dev++)
 	{
 		struct device *dev = &dev_ultra[this_dev];
         	if (dev->priv != NULL)
diff -u --recursive --new-file v2.1.91/linux/drivers/pci/oldproc.c linux/drivers/pci/oldproc.c
--- v2.1.91/linux/drivers/pci/oldproc.c	Tue Mar 10 10:03:32 1998
+++ linux/drivers/pci/oldproc.c	Mon Mar 30 00:21:40 1998
@@ -213,6 +213,7 @@
 	DEVICE( CYRIX,		CYRIX_5530_VIDEO,"5530 Kahlua Video"),
 	DEVICE( LEADTEK,	LEADTEK_805,	"S3 805"),
 	DEVICE( CONTAQ,		CONTAQ_82C599,	"82C599"),
+	DEVICE( CONTAQ,		CONTAQ_82C693,	"82C693"),
 	DEVICE( OLICOM,		OLICOM_OC3136,	"OC-3136/3137"),
 	DEVICE( OLICOM,		OLICOM_OC2315,	"OC-2315"),
 	DEVICE( OLICOM,		OLICOM_OC2325,	"OC-2325"),
diff -u --recursive --new-file v2.1.91/linux/drivers/pci/pci.c linux/drivers/pci/pci.c
--- v2.1.91/linux/drivers/pci/pci.c	Thu Mar 26 15:57:03 1998
+++ linux/drivers/pci/pci.c	Mon Mar 30 00:21:40 1998
@@ -273,3 +273,13 @@
 
 	return mem_start;
 }
+
+struct pci_dev *pci_find_dev(unsigned char bus, unsigned char devfn)
+{
+	struct pci_dev *pdev;
+	for (pdev = pci_devices; pdev; pdev = pdev->next) {
+		if ((pdev->bus->number==bus) && (pdev->devfn==devfn)) break;
+
+	}
+	return pdev;
+}
diff -u --recursive --new-file v2.1.91/linux/drivers/scsi/BusLogic.c linux/drivers/scsi/BusLogic.c
--- v2.1.91/linux/drivers/scsi/BusLogic.c	Tue Feb 17 13:12:46 1998
+++ linux/drivers/scsi/BusLogic.c	Sun Mar 29 10:00:00 1998
@@ -26,8 +26,8 @@
 */
 
 
-#define BusLogic_DriverVersion		"2.0.11"
-#define BusLogic_DriverDate		"31 January 1998"
+#define BusLogic_DriverVersion		"2.0.12"
+#define BusLogic_DriverDate		"29 March 1998"
 
 
 #include <linux/version.h>
@@ -1020,6 +1020,38 @@
 	   : check_region(0x134, BusLogic_MultiMasterAddressCount) == 0))
 	BusLogic_AppendProbeAddressISA(0x134);
     }
+  /*
+    Iterate over the older non-compliant MultiMaster PCI Host Adapters,
+    noting the PCI bus location and assigned IRQ Channel.
+  */
+  Index = 0;
+  while (pcibios_find_device(PCI_VENDOR_ID_BUSLOGIC,
+			     PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER_NC,
+			     Index++, &Bus, &DeviceFunction) == 0)
+    if (pcibios_read_config_dword(Bus, DeviceFunction,
+				  PCI_BASE_ADDRESS_0, &BaseAddress0) == 0 &&
+	pcibios_read_config_byte(Bus, DeviceFunction,
+				 PCI_INTERRUPT_LINE, &IRQ_Channel) == 0)
+      {
+	unsigned char Device = DeviceFunction >> 3;
+	IO_Address = BaseAddress0 & PCI_BASE_ADDRESS_IO_MASK;
+	if (IO_Address == 0 || IRQ_Channel == 0 || IRQ_Channel >= NR_IRQS)
+	  continue;
+	for (i = 0; i < BusLogic_ProbeInfoCount; i++)
+	  {
+	    BusLogic_ProbeInfo_T *ProbeInfo = &BusLogic_ProbeInfoList[i];
+	    if (ProbeInfo->IO_Address == IO_Address &&
+		ProbeInfo->HostAdapterType == BusLogic_MultiMaster)
+	      {
+		ProbeInfo->HostAdapterBusType = BusLogic_PCI_Bus;
+		ProbeInfo->PCI_Address = 0;
+		ProbeInfo->Bus = Bus;
+		ProbeInfo->Device = Device;
+		ProbeInfo->IRQ_Channel = IRQ_Channel;
+		break;
+	      }
+	  }
+      }
   return PCIMultiMasterCount;
 }
 
@@ -1976,6 +2008,12 @@
   if (HostAdapter->BounceBuffersRequired)
     HostAdapter->UntaggedQueueDepth = BusLogic_UntaggedQueueDepthBB;
   else HostAdapter->UntaggedQueueDepth = BusLogic_UntaggedQueueDepth;
+  if (HostAdapter->DriverOptions != NULL)
+    HostAdapter->CommonQueueDepth =
+      HostAdapter->DriverOptions->CommonQueueDepth;
+  if (HostAdapter->CommonQueueDepth > 0 &&
+      HostAdapter->CommonQueueDepth < HostAdapter->UntaggedQueueDepth)
+    HostAdapter->UntaggedQueueDepth = HostAdapter->CommonQueueDepth;
   /*
     Tagged Queuing is only allowed if Disconnect/Reconnect is permitted.
     Therefore, mask the Tagged Queuing Permitted Default bits with the
@@ -2682,17 +2720,26 @@
   for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
     if (HostAdapter->TargetFlags[TargetID].TargetExists)
       {
-	int QueueDepth = HostAdapter->UntaggedQueueDepth;
+	int QueueDepth = HostAdapter->QueueDepth[TargetID];
 	if (HostAdapter->TargetFlags[TargetID].TaggedQueuingSupported &&
 	    (HostAdapter->TaggedQueuingPermitted & (1 << TargetID)))
 	  {
-	    QueueDepth = HostAdapter->QueueDepth[TargetID];
 	    TaggedDeviceCount++;
 	    if (QueueDepth == 0) AutomaticTaggedDeviceCount++;
 	  }
-	else UntaggedDeviceCount++;
-	HostAdapter->QueueDepth[TargetID] = QueueDepth;
+	else
+	  {
+	    UntaggedDeviceCount++;
+	    if (QueueDepth == 0 ||
+		QueueDepth > HostAdapter->UntaggedQueueDepth)
+	      {
+		QueueDepth = HostAdapter->UntaggedQueueDepth;
+		HostAdapter->QueueDepth[TargetID] = QueueDepth;
+	      }
+	  }
 	AllocatedQueueDepth += QueueDepth;
+	if (QueueDepth == 1)
+	  HostAdapter->TaggedQueuingPermitted &= ~(1 << TargetID);
       }
   HostAdapter->TargetDeviceCount = TaggedDeviceCount + UntaggedDeviceCount;
   if (AutomaticTaggedDeviceCount > 0)
@@ -4567,14 +4614,18 @@
   QueueDepth:<integer>
 
     The "QueueDepth:" or QD:" option specifies the Queue Depth to use for all
-    Target Devices that support Tagged Queuing.  If no Queue Depth option is
-    provided, the Queue Depth will be determined automatically based on the
-    Host Adapter's Total Queue Depth and the number, type, speed, and
+    Target Devices that support Tagged Queuing, as well as the maximum Queue
+    Depth for devices that do not support Tagged Queuing.  If no Queue Depth
+    option is provided, the Queue Depth will be determined automatically based
+    on the Host Adapter's Total Queue Depth and the number, type, speed, and
     capabilities of the detected Target Devices.  For Host Adapters that
     require ISA Bounce Buffers, the Queue Depth is automatically set by default
-    to BusLogic_QueueDepthBounceBuffers to avoid excessive preallocation of DMA
-    Bounce Buffer memory.  Target Devices that do not support Tagged Queuing
-    always use a Queue Depth of BusLogic_UntaggedQueueDepth.
+    to BusLogic_TaggedQueueDepthBB or BusLogic_UntaggedQueueDepthBB to avoid
+    excessive preallocation of DMA Bounce Buffer memory.  Target Devices that
+    do not support Tagged Queuing always have their Queue Depth set to
+    BusLogic_UntaggedQueueDepth or BusLogic_UntaggedQueueDepthBB, unless a
+    lower Queue Depth option is provided.  A Queue Depth of 1 automatically
+    disables Tagged Queuing.
 
   QueueDepth:[<integer>,<integer>...]
 
@@ -4826,6 +4877,7 @@
 				 NULL, QueueDepth);
 		  return;
 		}
+	      DriverOptions->CommonQueueDepth = QueueDepth;
 	      for (TargetID = 0;
 		   TargetID < BusLogic_MaxTargetDevices;
 		   TargetID++)
diff -u --recursive --new-file v2.1.91/linux/drivers/scsi/BusLogic.h linux/drivers/scsi/BusLogic.h
--- v2.1.91/linux/drivers/scsi/BusLogic.h	Tue Feb 17 13:12:46 1998
+++ linux/drivers/scsi/BusLogic.h	Wed Apr  1 17:31:32 1998
@@ -1252,6 +1252,7 @@
   unsigned short TaggedQueuingPermittedMask;
   unsigned short BusSettleTime;
   BusLogic_LocalOptions_T LocalOptions;
+  unsigned char CommonQueueDepth;
   unsigned char QueueDepth[BusLogic_MaxTargetDevices];
   BusLogic_ErrorRecoveryStrategy_T
     ErrorRecoveryStrategy[BusLogic_MaxTargetDevices];
@@ -1412,6 +1413,7 @@
   unsigned short DriverQueueDepth;
   unsigned short HostAdapterQueueDepth;
   unsigned short UntaggedQueueDepth;
+  unsigned short CommonQueueDepth;
   unsigned short BusSettleTime;
   unsigned short SynchronousPermitted;
   unsigned short FastPermitted;
diff -u --recursive --new-file v2.1.91/linux/drivers/scsi/NCR5380.c linux/drivers/scsi/NCR5380.c
--- v2.1.91/linux/drivers/scsi/NCR5380.c	Fri Jan 30 11:28:08 1998
+++ linux/drivers/scsi/NCR5380.c	Mon Mar 30 00:21:40 1998
@@ -491,11 +491,11 @@
  */
 #ifndef USLEEP_SLEEP
 /* 20 ms (reasonable hard disk speed) */
-#define USLEEP_SLEEP 2
+#define USLEEP_SLEEP (20*HZ/1000)
 #endif
 /* 300 RPM (floppy speed) */
 #ifndef USLEEP_POLL
-#define USLEEP_POLL 20
+#define USLEEP_POLL (200*HZ/1000)
 #endif
 
 static struct Scsi_Host * expires_first = NULL;
@@ -644,7 +644,7 @@
 	    == 0)) 
 	    trying_irqs |= mask;
 
-    timeout = jiffies + 25;
+    timeout = jiffies + (250*HZ/1000);
     probe_irq = IRQ_NONE;
 
 /*
@@ -820,7 +820,7 @@
 SPRINTF("PAS16 release=%d", PAS16_PUBLIC_RELEASE);
 #endif
 
-   SPRINTF("\nBase Addr: 0x%05X    ", (int)instance->base);
+   SPRINTF("\nBase Addr: 0x%05lX    ", (long)instance->base);
    SPRINTF("io_port: %04x      ", (int)instance->io_port);
    if (instance->irq == IRQ_NONE)
       SPRINTF("IRQ: None.\n");
@@ -1003,7 +1003,7 @@
 	case 5:
 	    printk("scsi%d: SCSI bus busy, waiting up to five seconds\n",
 		instance->host_no);
-	    timeout = jiffies + 500;
+	    timeout = jiffies + 5*HZ;
 	    while (jiffies < timeout && (NCR5380_read(STATUS_REG) & SR_BSY));
 	    break;
 	case 2:
@@ -1619,7 +1619,7 @@
      * selection.
      */
 
-    timeout = jiffies + 25; 
+    timeout = jiffies + (250*HZ/1000); 
 
     /* 
      * XXX very interesting - we're seeing a bounce where the BSY we 
diff -u --recursive --new-file v2.1.91/linux/drivers/scsi/NCR5380.h linux/drivers/scsi/NCR5380.h
--- v2.1.91/linux/drivers/scsi/NCR5380.h	Mon Apr 29 07:14:19 1996
+++ linux/drivers/scsi/NCR5380.h	Mon Mar 30 00:21:40 1998
@@ -314,29 +314,33 @@
 static int NCR5380_transfer_pio (struct Scsi_Host *instance,
 	unsigned char *phase, int *count, unsigned char **data);
 
-#if (defined(REAL_DMA) || defined(REAL_DMA_POLL)) && defined(i386)
-static __inline__ int NCR5380_i386_dma_setup (struct Scsi_Host *instance,
+#if (defined(REAL_DMA) || defined(REAL_DMA_POLL))
+
+#if defined(i386) || defined(__alpha__)
+
+static __inline__ int NCR5380_pc_dma_setup (struct Scsi_Host *instance,
 	unsigned char *ptr, unsigned int count, unsigned char mode) {
     unsigned limit;
+    unsigned long bus_addr = virt_to_bus(ptr);
 
     if (instance->dma_channel <=3) {
 	if (count > 65536)
 	    count = 65536;
-	limit = 65536 - (((unsigned) ptr) & 0xFFFF);
+	limit = 65536 - (bus_addr & 0xFFFF);
     } else {
 	if (count > 65536 * 2) 
 	    count = 65536 * 2;
-	limit = 65536* 2 - (((unsigned) ptr) & 0x1FFFF);
+	limit = 65536* 2 - (bus_addr & 0x1FFFF);
     }
 
     if (count > limit) count = limit;
 
-    if ((count & 1) || (((unsigned) ptr) & 1))
+    if ((count & 1) || (bus_addr & 1))
 	panic ("scsi%d : attempted unaligned DMA transfer\n", instance->host_no);
     cli();
     disable_dma(instance->dma_channel);
     clear_dma_ff(instance->dma_channel);
-    set_dma_addr(instance->dma_channel, (unsigned int) ptr);
+    set_dma_addr(instance->dma_channel, bus_addr);
     set_dma_count(instance->dma_channel, count);
     set_dma_mode(instance->dma_channel, mode);
     enable_dma(instance->dma_channel);
@@ -344,17 +348,17 @@
     return count;
 }
 
-static __inline__ int NCR5380_i386_dma_write_setup (struct Scsi_Host *instance,
+static __inline__ int NCR5380_pc_dma_write_setup (struct Scsi_Host *instance,
     unsigned char *src, unsigned int count) {
-    return NCR5380_i386_dma_setup (instance, src, count, DMA_MODE_WRITE);
+    return NCR5380_pc_dma_setup (instance, src, count, DMA_MODE_WRITE);
 }
 
-static __inline__ int NCR5380_i386_dma_read_setup (struct Scsi_Host *instance,
+static __inline__ int NCR5380_pc_dma_read_setup (struct Scsi_Host *instance,
     unsigned char *src, unsigned int count) {
-    return NCR5380_i386_dma_setup (instance, src, count, DMA_MODE_READ);
+    return NCR5380_pc_dma_setup (instance, src, count, DMA_MODE_READ);
 }
 
-static __inline__ int NCR5380_i386_dma_residual (struct Scsi_Host *instance) {
+static __inline__ int NCR5380_pc_dma_residual (struct Scsi_Host *instance) {
     register int tmp;
     cli();
     clear_dma_ff(instance->dma_channel);
@@ -362,7 +366,8 @@
     sti();
     return tmp;
 }
-#endif /* defined(REAL_DMA) && defined(i386)  */
+#endif /* defined(i386) || defined(__alpha__) */
+#endif /* defined(REAL_DMA)  */
 #endif __KERNEL_
 #endif /* ndef ASM */
 #endif /* NCR5380_H */
diff -u --recursive --new-file v2.1.91/linux/drivers/scsi/NCR53c406a.c linux/drivers/scsi/NCR53c406a.c
--- v2.1.91/linux/drivers/scsi/NCR53c406a.c	Fri Jan 23 18:10:32 1998
+++ linux/drivers/scsi/NCR53c406a.c	Mon Mar 30 00:21:40 1998
@@ -226,7 +226,8 @@
 #endif USE_BIOS
 		       
 /* possible i/o port addresses */
-static unsigned short ports[] = { 0x230, 0x330 };
+static unsigned short ports[] =
+  { 0x230, 0x330, 0x280, 0x290, 0x330, 0x340, 0x300, 0x310, 0x348, 0x350 };
 #define PORT_COUNT (sizeof( ports ) / sizeof( unsigned short ))
 
 /* possible interrupt channels */
diff -u --recursive --new-file v2.1.91/linux/drivers/scsi/README.BusLogic linux/drivers/scsi/README.BusLogic
--- v2.1.91/linux/drivers/scsi/README.BusLogic	Tue Feb 17 13:12:47 1998
+++ linux/drivers/scsi/README.BusLogic	Sun Mar 29 10:00:00 1998
@@ -1,10 +1,10 @@
 	   BusLogic MultiMaster and FlashPoint SCSI Driver for Linux
 
-			 Version 2.0.11 for Linux 2.0
+			 Version 2.0.12 for Linux 2.0
 
 			      PRODUCTION RELEASE
 
-				31 January 1998
+				 29 March 1998
 
 			       Leonard N. Zubkoff
 			       Dandelion Digital
@@ -429,14 +429,18 @@
 QueueDepth:<integer>
 
   The "QueueDepth:" or QD:" option specifies the Queue Depth to use for all
-  Target Devices that support Tagged Queuing.  If no Queue Depth option is
-  provided, the Queue Depth will be determined automatically based on the
-  Host Adapter's Total Queue Depth and the number, type, speed, and
+  Target Devices that support Tagged Queuing, as well as the maximum Queue
+  Depth for devices that do not support Tagged Queuing.  If no Queue Depth
+  option is provided, the Queue Depth will be determined automatically based
+  on the Host Adapter's Total Queue Depth and the number, type, speed, and
   capabilities of the detected Target Devices.  For Host Adapters that
   require ISA Bounce Buffers, the Queue Depth is automatically set by default
-  to BusLogic_QueueDepthBounceBuffers to avoid excessive preallocation of DMA
-  Bounce Buffer memory.  Target Devices that do not support Tagged Queuing
-  always use a Queue Depth of BusLogic_UntaggedQueueDepth.
+  to BusLogic_TaggedQueueDepthBB or BusLogic_UntaggedQueueDepthBB to avoid
+  excessive preallocation of DMA Bounce Buffer memory.  Target Devices that
+  do not support Tagged Queuing always have their Queue Depth set to
+  BusLogic_UntaggedQueueDepth or BusLogic_UntaggedQueueDepthBB, unless a
+  lower Queue Depth option is provided.  A Queue Depth of 1 automatically
+  disables Tagged Queuing.
 
 QueueDepth:[<integer>,<integer>...]
 
@@ -587,7 +591,7 @@
 replacing "/usr/src" with wherever you keep your Linux kernel source tree:
 
   cd /usr/src
-  tar -xvzf BusLogic-2.0.11.tar.gz
+  tar -xvzf BusLogic-2.0.12.tar.gz
   mv README.* LICENSE.* BusLogic.[ch] FlashPoint.c linux/drivers/scsi
   patch -p < BusLogic.patch
   cd linux
diff -u --recursive --new-file v2.1.91/linux/drivers/scsi/hosts.h linux/drivers/scsi/hosts.h
--- v2.1.91/linux/drivers/scsi/hosts.h	Mon Feb 23 18:12:07 1998
+++ linux/drivers/scsi/hosts.h	Wed Apr  1 17:31:32 1998
@@ -332,7 +332,7 @@
 
     /* These parameters should be set by the detect routine */
     unsigned char *base;
-    unsigned int  io_port;
+    unsigned long io_port;
     unsigned char n_io_port;
     unsigned char irq;
     unsigned char dma_channel;
@@ -374,7 +374,7 @@
     /*
      * We should ensure that this is aligned, both for better performance
      * and also because some compilers (m68k) don't automatically force
-     * alignment to a 4-byte boundary.
+     * alignment to a long boundary.
      */
     unsigned long hostdata[0]  /* Used for storage of host specific stuff */
         __attribute__ ((aligned (sizeof(unsigned long))));
diff -u --recursive --new-file v2.1.91/linux/drivers/scsi/ncr53c8xx.c linux/drivers/scsi/ncr53c8xx.c
--- v2.1.91/linux/drivers/scsi/ncr53c8xx.c	Tue Mar 17 22:18:14 1998
+++ linux/drivers/scsi/ncr53c8xx.c	Mon Mar 30 00:21:40 1998
@@ -666,12 +666,12 @@
 typedef struct {
 	int	bus;
 	u_char	device_fn;
-	u_int	base;
-	u_int	base_2;
-	u_int	io_port;
+	u_long	base;
+	u_long	base_2;
+	u_long	io_port;
 	int	irq;
 /* port and reg fields to use INB, OUTB macros */
-	u_int	port;
+	u_long	port;
 	volatile struct	ncr_reg	*reg;
 } ncr_slot;
 
@@ -1834,7 +1834,7 @@
 	/*
 	**	address of the ncr control registers in io space
 	*/
-	u_int		port;
+	u_long		port;
 
 	/*
 	**	irq level
@@ -4388,7 +4388,7 @@
 	u_long flags = 0;
 	ncr_nvram *nvram = device->nvram;
 
-printf(KERN_INFO "ncr53c%s-%d: rev=0x%02x, base=0x%x, io_port=0x%x, irq=%d\n",
+printf(KERN_INFO "ncr53c%s-%d: rev=0x%02x, base=0x%lx, io_port=0x%lx, irq=%d\n",
 	device->chip.name, unit, device->chip.revision_id, device->slot.base,
 	device->slot.io_port, device->slot.irq);
 
@@ -9296,7 +9296,10 @@
 	ushort vendor_id, device_id, command;
 	uchar cache_line_size, latency_timer;
 	uchar irq, revision;
-#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,90)
+	ulong base, base_2, io_port;
+	struct pci_dev *pdev;
+#elif LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
 	uint base, base_2, io_port; 
 #else
 	ulong base, base_2; 
@@ -9309,7 +9312,7 @@
 	ncr_chip *chip;
 
 	printk(KERN_INFO "ncr53c8xx: at PCI bus %d, device %d, function %d\n",
-		bus, (int) (device_fn & 0xf8) >> 3, (int) device_fn & 7);
+		bus, PCI_SLOT(device_fn), PCI_FUNC(device_fn));
 	/*
 	 * Read info from the PCI config space.
 	 * pcibios_read_config_xxx() functions are assumed to be used for 
@@ -9323,6 +9326,13 @@
 					PCI_DEVICE_ID, &device_id);
 	(void) pcibios_read_config_word(bus, device_fn,
 					PCI_COMMAND, &command);
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,90)
+	pdev = pci_find_dev(bus, device_fn);
+	io_port = pdev->base_address[0];
+	base    = pdev->base_address[1];
+	base_2  = pdev->base_address[2];
+	irq     = pdev->irq;
+#else
 	(void) pcibios_read_config_dword(bus, device_fn,
 					PCI_BASE_ADDRESS_0, &io_port);	
 	(void) pcibios_read_config_dword(bus, device_fn,
@@ -9330,9 +9340,10 @@
 	(void) pcibios_read_config_dword(bus, device_fn,
 					PCI_BASE_ADDRESS_2, &base_2);
 	(void) pcibios_read_config_byte(bus, device_fn,
-					PCI_CLASS_REVISION,&revision);	
-	(void) pcibios_read_config_byte(bus, device_fn,
 					PCI_INTERRUPT_LINE, &irq);
+#endif
+	(void) pcibios_read_config_byte(bus, device_fn,
+					PCI_CLASS_REVISION, &revision);	
 	(void) pcibios_read_config_byte(bus, device_fn,
 					PCI_CACHE_LINE_SIZE, &cache_line_size);
 	(void) pcibios_read_config_byte(bus, device_fn,
@@ -9448,7 +9459,7 @@
 	/*
 	 * Try to fix up PCI config according to wished features.
 	 */
-#if defined(__i386__) && !defined(MODULE)
+#if defined(__i386) && !defined(MODULE)
 	if ((driver_setup.pci_fix_up & 1) &&
 	    (chip->features & FE_CLSE) && cache_line_size == 0) {
 #if LINUX_VERSION_CODE < LinuxVersionCode(2,1,75)
diff -u --recursive --new-file v2.1.91/linux/drivers/scsi/qlogicisp.c linux/drivers/scsi/qlogicisp.c
--- v2.1.91/linux/drivers/scsi/qlogicisp.c	Sun Feb  2 05:34:32 1997
+++ linux/drivers/scsi/qlogicisp.c	Mon Mar 30 00:21:40 1998
@@ -55,6 +55,8 @@
 
 #define DEFAULT_LOOP_COUNT	1000000
 
+#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
+
 /* End Configuration section *************************************************/
 
 #include <linux/module.h>
@@ -606,7 +608,7 @@
 		}
 
 		if (check_region(host->io_port, 0xff)) {
-			printk("qlogicisp : i/o region 0x%04x-0x%04x already "
+			printk("qlogicisp : i/o region 0x%lx-0x%lx already "
 			       "in use\n",
 			       host->io_port, host->io_port + 0xff);
 			free_irq(host->irq, NULL);
@@ -658,7 +660,7 @@
 
 	hostdata = (struct isp1020_hostdata *) host->hostdata;
 	sprintf(buf,
-		"QLogic ISP1020 SCSI on PCI bus %d device %d irq %d base 0x%x",
+		"QLogic ISP1020 SCSI on PCI bus %d device %d irq %d base 0x%lx",
 		hostdata->bus, (hostdata->device_fn & 0xf8) >> 3, host->irq,
 		host->io_port);
 
@@ -1166,10 +1168,13 @@
 
 static int isp1020_init(struct Scsi_Host *sh)
 {
-	u_int io_base;
+	u_long io_base = 0;
 	struct isp1020_hostdata *hostdata;
 	u_char bus, device_fn, revision, irq;
 	u_short vendor_id, device_id, command;
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,90)
+	struct pci_dev *pdev;
+#endif
 
 	ENTER("isp1020_init");
 
@@ -1182,16 +1187,24 @@
 					PCI_DEVICE_ID, &device_id)
             || pcibios_read_config_word(bus, device_fn,
 					PCI_COMMAND, &command)
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,1,90)
             || pcibios_read_config_dword(bus, device_fn,
 					 PCI_BASE_ADDRESS_0, &io_base)
 	    || pcibios_read_config_byte(bus, device_fn,
-					PCI_CLASS_REVISION, &revision)
+					PCI_INTERRUPT_LINE, &irq)
+#endif
             || pcibios_read_config_byte(bus, device_fn,
-					PCI_INTERRUPT_LINE, &irq))
+					PCI_CLASS_REVISION, &revision))
 	{
 		printk("qlogicisp : error reading PCI configuration\n");
 		return 1;
 	}
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,90)
+	pdev = pci_find_dev(bus, device_fn);
+	io_base = pdev->base_address[0];
+	irq     = pdev->irq;
+#endif
 
 	if (vendor_id != PCI_VENDOR_ID_QLOGIC) {
 		printk("qlogicisp : 0x%04x is not QLogic vendor ID\n",
diff -u --recursive --new-file v2.1.91/linux/drivers/scsi/sg.c linux/drivers/scsi/sg.c
--- v2.1.91/linux/drivers/scsi/sg.c	Tue Mar 10 10:03:33 1998
+++ linux/drivers/scsi/sg.c	Tue Mar 31 10:09:24 1998
@@ -28,6 +28,8 @@
 #include <scsi/scsi_ioctl.h>
 #include <scsi/sg.h>
 
+int sg_big_buff = SG_BIG_BUFF;		/* for now, sg_big_buff is read-only through sysctl */
+
 static int sg_init(void);
 static int sg_attach(Scsi_Device *);
 static int sg_detect(Scsi_Device *);
diff -u --recursive --new-file v2.1.91/linux/drivers/scsi/wd7000.c linux/drivers/scsi/wd7000.c
--- v2.1.91/linux/drivers/scsi/wd7000.c	Tue Mar 17 22:18:14 1998
+++ linux/drivers/scsi/wd7000.c	Mon Mar 30 14:06:37 1998
@@ -513,7 +513,7 @@
 typedef struct icbUnsMask {	/* I'm totally guessing here */
     unchar op;
     volatile unchar mask[14];	/* mask bits                 */
-#ifdef 0
+#if 0
     unchar rsvd[12];		/* reserved                  */
 #endif
     volatile unchar vue;	/* vendor-unique error code  */
diff -u --recursive --new-file v2.1.91/linux/fs/binfmt_aout.c linux/fs/binfmt_aout.c
--- v2.1.91/linux/fs/binfmt_aout.c	Thu Mar 26 15:57:04 1998
+++ linux/fs/binfmt_aout.c	Wed Apr  1 16:26:33 1998
@@ -362,8 +362,7 @@
 
 	current->mm->rss = 0;
 	current->mm->mmap = NULL;
-	current->suid = current->euid = current->fsuid = bprm->e_uid;
-	current->sgid = current->egid = current->fsgid = bprm->e_gid;
+	compute_creds(bprm);
  	current->flags &= ~PF_FORKNOEXEC;
 #ifdef __sparc__
 	if (N_MAGIC(ex) == NMAGIC) {
diff -u --recursive --new-file v2.1.91/linux/fs/binfmt_elf.c linux/fs/binfmt_elf.c
--- v2.1.91/linux/fs/binfmt_elf.c	Tue Mar 10 10:03:33 1998
+++ linux/fs/binfmt_elf.c	Wed Apr  1 16:26:34 1998
@@ -705,8 +705,7 @@
 #ifndef VM_STACK_FLAGS
 	current->executable = dget(bprm->dentry);
 #endif
-	current->suid = current->euid = current->fsuid = bprm->e_uid;
-	current->sgid = current->egid = current->fsgid = bprm->e_gid;
+	compute_creds(bprm);
 	current->flags &= ~PF_FORKNOEXEC;
 	bprm->p = (unsigned long)
 	  create_elf_tables((char *)bprm->p,
diff -u --recursive --new-file v2.1.91/linux/fs/buffer.c linux/fs/buffer.c
--- v2.1.91/linux/fs/buffer.c	Thu Mar 26 15:57:04 1998
+++ linux/fs/buffer.c	Wed Apr  1 17:20:49 1998
@@ -1141,6 +1141,9 @@
 	return NULL;
 }
 
+/*
+ * Note: the caller should wake up the buffer_wait list if needed.
+ */
 static void put_unused_buffer_head(struct buffer_head * bh)
 {
 	if (nr_unused_buffer_heads >= MAX_UNUSED_BUFFERS) {
@@ -1153,9 +1156,6 @@
 	nr_unused_buffer_heads++;
 	bh->b_next_free = unused_list;
 	unused_list = bh;
-	if (!waitqueue_active(&buffer_wait))
-		return;
-	wake_up(&buffer_wait);
 }
 
 /* 
@@ -1165,18 +1165,26 @@
  * fields after the final unlock.  So, the device driver puts them on
  * the reuse_list instead once IO completes, and we recover these to
  * the unused_list here.
+ *
+ * Note that we don't do a wakeup here, but return a flag indicating
+ * whether we got any buffer heads. A task ready to sleep can check
+ * the returned value, and any tasks already sleeping will have been
+ * awakened when the buffer heads were added to the reuse list.
  */
-static inline void recover_reusable_buffer_heads(void)
+static inline int recover_reusable_buffer_heads(void)
 {
-	struct buffer_head *head;
-
-	head = xchg(&reuse_list, NULL);
+	struct buffer_head *head = xchg(&reuse_list, NULL);
+	int found = 0;
 	
-	while (head) {
-		struct buffer_head *bh = head;
-		head = head->b_next_free;
-		put_unused_buffer_head(bh);
+	if (head) {
+		do {
+			struct buffer_head *bh = head;
+			head = head->b_next_free;
+			put_unused_buffer_head(bh);
+		} while (head);
+		found = 1;
 	}
+	return found;
 }
 
 /*
@@ -1275,11 +1283,15 @@
  * In case anything failed, we just free everything we got.
  */
 no_grow:
-	bh = head;
-	while (bh) {
-		head = bh;
-		bh = bh->b_this_page;
-		put_unused_buffer_head(head);
+	if (head) {
+		do {
+			bh = head;
+			head = head->b_this_page;
+			put_unused_buffer_head(bh);
+		} while (head);
+
+		/* Wake up any waiters ... */
+		wake_up(&buffer_wait);
 	}
 
 	/*
@@ -1305,8 +1317,8 @@
 	 */
 	add_wait_queue(&buffer_wait, &wait);
 	current->state = TASK_UNINTERRUPTIBLE;
-	recover_reusable_buffer_heads();
-	schedule();
+	if (!recover_reusable_buffer_heads())
+		schedule();
 	remove_wait_queue(&buffer_wait, &wait);
 	current->state = TASK_RUNNING;
 	goto try_again;
@@ -1333,14 +1345,24 @@
  */
 static inline void free_async_buffers (struct buffer_head * bh)
 {
-	struct buffer_head * tmp;
+	struct buffer_head *tmp, *tail;
 
-	tmp = bh;
-	do {
-		tmp->b_next_free = xchg(&reuse_list, NULL);
-		reuse_list = tmp;
-		tmp = tmp->b_this_page;
-	} while (tmp != bh);
+	/*
+	 * Link all the buffers into the b_next_free list,
+	 * so we only have to do one xchg() operation ...
+	 */
+	tail = bh;
+	while ((tmp = tail->b_this_page) != bh) {
+		tail->b_next_free = tmp;
+		tail = tmp;
+	};
+
+	/* Update the reuse list */
+	tail->b_next_free = xchg(&reuse_list, NULL);
+	reuse_list = bh;
+
+	/* Wake up any waiters ... */
+	wake_up(&buffer_wait);
 }
 
 static void end_buffer_io_async(struct buffer_head * bh, int uptodate)
@@ -1390,7 +1412,6 @@
 	clear_bit(PG_locked, &page->flags);
 	wake_up(&page->wait);
 	after_unlock_page(page);
-	wake_up(&buffer_wait);
 	return;
 
 still_busy:
@@ -1636,6 +1657,7 @@
 			return 0;
 		tmp = tmp->b_this_page;
 	} while (tmp != bh);
+
 	tmp = bh;
 	do {
 		p = tmp;
@@ -1649,6 +1671,9 @@
 		remove_from_queues(p);
 		put_unused_buffer_head(p);
 	} while (tmp != bh);
+	/* Wake up anyone waiting for buffer heads */
+	wake_up(&buffer_wait);
+
 	buffermem -= PAGE_SIZE;
 	mem_map[MAP_NR(page)].buffers = NULL;
 	free_page(page);
diff -u --recursive --new-file v2.1.91/linux/fs/dcache.c linux/fs/dcache.c
--- v2.1.91/linux/fs/dcache.c	Wed Feb  4 11:36:01 1998
+++ linux/fs/dcache.c	Tue Mar 31 13:07:39 1998
@@ -19,6 +19,8 @@
 #include <linux/malloc.h>
 #include <linux/init.h>
 
+#include <asm/uaccess.h>
+
 #define DCACHE_PARANOIA 1
 /* #define DCACHE_DEBUG 1 */
 
@@ -669,7 +671,7 @@
 	d_instantiate(entry, inode);
 }
 
-#define switch(x,y) do { \
+#define do_switch(x,y) do { \
 	__typeof__ (x) __tmp = x; \
 	x = y; y = __tmp; } while (0)
 
@@ -705,10 +707,10 @@
 	list_del(&target->d_child);
 
 	/* Switch the parents and the names.. */
-	switch(dentry->d_parent, target->d_parent);
-	switch(dentry->d_name.name, target->d_name.name);
-	switch(dentry->d_name.len, target->d_name.len);
-	switch(dentry->d_name.hash, target->d_name.hash);
+	do_switch(dentry->d_parent, target->d_parent);
+	do_switch(dentry->d_name.name, target->d_name.name);
+	do_switch(dentry->d_name.len, target->d_name.len);
+	do_switch(dentry->d_name.hash, target->d_name.hash);
 	list_add(&target->d_child, &target->d_parent->d_subdirs);
 	list_add(&dentry->d_child, &dentry->d_parent->d_subdirs);
 }
@@ -755,6 +757,41 @@
 		dentry = parent;
 	}
 	return retval;
+}
+
+/*
+ * NOTE! The user-level library version returns a
+ * character pointer. The kernel system call just
+ * returns the length of the buffer filled (which
+ * includes the ending '\0' character), or a negative
+ * error value. So libc would do something like
+ *
+ *	char *getcwd(char * buf, size_t size)
+ *	{
+ *		int retval;
+ *
+ *		retval = sys_getcwd(buf, size);
+ *		if (retval >= 0)
+ *			return buf;
+ *		errno = -retval;
+ *		return NULL;
+ *	}
+ */
+asmlinkage int sys_getcwd(char *buf, unsigned long size)
+{
+	int error;
+	unsigned long len;
+	char * page = (char *) __get_free_page(GFP_USER);
+	char * cwd = d_path(current->fs->pwd, page, PAGE_SIZE);
+
+	error = -ERANGE;
+	len = PAGE_SIZE + page - cwd;
+	if (len <= size) {
+		error = len;
+		if (copy_to_user(buf, cwd, len))
+			error = -EFAULT;
+	}
+	return error;
 }
 
 /*
diff -u --recursive --new-file v2.1.91/linux/fs/exec.c linux/fs/exec.c
--- v2.1.91/linux/fs/exec.c	Thu Mar 26 15:57:04 1998
+++ linux/fs/exec.c	Wed Apr  1 16:26:34 1998
@@ -605,6 +605,45 @@
 			id_change = 1;
 	}
 
+	/* We don't have VFS support for capabilities yet */
+	cap_clear(bprm->cap_inheritable);
+	cap_clear(bprm->cap_permitted);
+	cap_clear(bprm->cap_effective);
+
+	/*  To support inheritance of root-permissions and suid-root
+         *  executables under compatibility mode, we raise the
+         *  effective and inherited bitmasks of the executable file
+         *  (translation: we set the executable "capability dumb" and
+         *  set the allowed set to maximum). We don't set any forced
+         *  bits.
+         *
+         *  If only the real uid is 0, we only raise the inheritable
+         *  bitmask of the executable file (translation: we set the
+         *  allowed set to maximum and the application to "capability
+         *  smart"). 
+         */
+
+	if (!issecure(SECURE_NOROOT)) {
+		if (bprm->e_uid == 0 || current->uid == 0)
+			cap_set_full(bprm->cap_inheritable);
+		if (bprm->e_uid == 0) 
+			cap_set_full(bprm->cap_effective);
+	}
+
+        /* We use a conservative definition of suid for capabilities.
+         * The process is suid if the permitted set is not a subset of
+         * the current permitted set after the exec call.
+         *         new permitted set = forced | (allowed & inherited)
+         *                       pP' = fP     | (fI      & pI)
+         */
+
+        if ((bprm->cap_permitted.cap |
+	     (current->cap_inheritable.cap &
+	      bprm->cap_inheritable.cap)) &
+	    ~current->cap_permitted.cap) {
+		id_change = 1;
+	}
+
 	if (id_change) {
 		/* We can't suid-execute if we're sharing parts of the executable */
 		/* or if we're being traced (or if suid execs are not allowed)    */
@@ -622,6 +661,45 @@
 	memset(bprm->buf,0,sizeof(bprm->buf));
 	return read_exec(bprm->dentry,0,bprm->buf,128,1);
 }
+
+/*
+ * This function is used to produce the new IDs and capabilities
+ * from the old ones and the file's capabilities.
+ *
+ * The formula used for evolving capabilities is:
+ *
+ *       pI' = pI
+ *       pP' = fP | (fI & pI)
+ *       pE' = pP' & fE          [NB. fE is 0 or ~0]
+ *
+ * I=Inheritable, P=Permitted, E=Effective // p=process, f=file
+ * ' indicates post-exec().
+ */
+
+void compute_creds(struct linux_binprm *bprm) 
+{
+	int new_permitted = bprm->cap_permitted.cap |
+		(bprm->cap_inheritable.cap & current->cap_inheritable.cap);
+
+	current->cap_permitted.cap = new_permitted;
+	current->cap_effective.cap = new_permitted & bprm->cap_effective.cap;
+	
+        /* XXX - Audit candidate */
+        if (!cap_isclear(current->cap_effective)) {
+                printk(KERN_NOTICE
+		       "raising capabilities on `%s'(pid=%d) [%04x]:%lu\n",
+		       current->comm, current->pid,
+		       kdev_t_to_nr(bprm->dentry->d_inode->i_dev), 
+		       bprm->dentry->d_inode->i_ino);
+        }
+	
+        current->suid = current->euid = current->fsuid = bprm->e_uid;
+        current->sgid = current->egid = current->fsgid = bprm->e_gid;
+        if (current->euid != current->uid || current->egid != current->gid ||
+	    !cap_isclear(current->cap_permitted))
+                current->dumpable = 0;
+}
+
 
 void remove_arg_zero(struct linux_binprm *bprm)
 {
diff -u --recursive --new-file v2.1.91/linux/fs/ext2/file.c linux/fs/ext2/file.c
--- v2.1.91/linux/fs/ext2/file.c	Fri Jan 30 15:50:57 1998
+++ linux/fs/ext2/file.c	Fri Mar 27 10:11:38 1998
@@ -219,6 +219,11 @@
 		count -= c;
 		mark_buffer_uptodate(bh, 1);
 		mark_buffer_dirty(bh, 0);
+
+		/* Mark the buffer untouched if we'll move on to the next one.. */
+		if (!(pos & (sb->s_blocksize-1)))
+			clear_bit(BH_Touched, &bh->b_state);
+
 		if (filp->f_flags & O_SYNC)
 			bufferlist[buffercount++] = bh;
 		else
diff -u --recursive --new-file v2.1.91/linux/fs/ext2/inode.c linux/fs/ext2/inode.c
--- v2.1.91/linux/fs/ext2/inode.c	Thu Feb 12 20:56:12 1998
+++ linux/fs/ext2/inode.c	Wed Apr  1 16:26:34 1998
@@ -648,7 +648,7 @@
 	     (ATTR_FLAG_APPEND | ATTR_FLAG_IMMUTABLE)) ^
 	    (inode->u.ext2_i.i_flags &
 	     (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL))) {
-		if (securelevel > 0 || !fsuser())
+		if (!fsuser())
 			goto out;
 	} else if ((current->fsuid != inode->i_uid) && !fsuser())
 		goto out;
diff -u --recursive --new-file v2.1.91/linux/fs/ext2/ioctl.c linux/fs/ext2/ioctl.c
--- v2.1.91/linux/fs/ext2/ioctl.c	Tue Jul  8 16:07:59 1997
+++ linux/fs/ext2/ioctl.c	Wed Apr  1 16:26:34 1998
@@ -37,7 +37,7 @@
 		    (inode->u.ext2_i.i_flags &
 		     (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL))) {
 			/* This test looks nicer. Thanks to Pauline Middelink */
-			if (!fsuser() || securelevel > 0)
+			if (!fsuser())
 				return -EPERM;
 		} else
 			if ((current->fsuid != inode->i_uid) && !fsuser())
diff -u --recursive --new-file v2.1.91/linux/fs/ncpfs/ncplib_kernel.c linux/fs/ncpfs/ncplib_kernel.c
--- v2.1.91/linux/fs/ncpfs/ncplib_kernel.c	Tue Mar 10 10:03:34 1998
+++ linux/fs/ncpfs/ncplib_kernel.c	Mon Mar 30 16:40:37 1998
@@ -147,6 +147,9 @@
 	int size, int options, int *ret_size, int *ret_options) {
 	int result;
 
+	/* there is minimum */
+	if (size < 512) size = 512;
+
 	ncp_init_request(server);
 	ncp_add_word(server, htons(size));
 	ncp_add_byte(server, options);
@@ -157,7 +160,10 @@
 		return result;
 	}
 
-	*ret_size = min(ntohs(ncp_reply_word(server, 0)), size);
+	/* NCP over UDP returns 0 (!!!) */
+	result = ntohs(ncp_reply_word(server, 0));
+	if (result >= 512) size=min(result, size);
+	*ret_size = size;
 	*ret_options = ncp_reply_byte(server, 4);
 
 	ncp_unlock_server(server);
diff -u --recursive --new-file v2.1.91/linux/fs/ncpfs/ncpsign_kernel.c linux/fs/ncpfs/ncpsign_kernel.c
--- v2.1.91/linux/fs/ncpfs/ncpsign_kernel.c	Tue Mar 17 22:18:15 1998
+++ linux/fs/ncpfs/ncpsign_kernel.c	Mon Mar 30 16:40:37 1998
@@ -25,24 +25,24 @@
 #define PVAL(buf,pos) ((unsigned)BVAL(buf,pos))
 #define BSET(buf,pos,val) (BVAL(buf,pos) = (val))
 
-static inline word
+static inline __u16
 WVAL_LH(__u8 * buf, int pos)
 {
 	return PVAL(buf, pos) | PVAL(buf, pos + 1) << 8;
 }
-static inline dword
+static inline __u32
 DVAL_LH(__u8 * buf, int pos)
 {
 	return WVAL_LH(buf, pos) | WVAL_LH(buf, pos + 2) << 16;
 }
 static inline void
-WSET_LH(__u8 * buf, int pos, word val)
+WSET_LH(__u8 * buf, int pos, __u16 val)
 {
 	BSET(buf, pos, val & 0xff);
 	BSET(buf, pos + 1, val >> 8);
 }
 static inline void
-DSET_LH(__u8 * buf, int pos, dword val)
+DSET_LH(__u8 * buf, int pos, __u32 val)
 {
 	WSET_LH(buf, pos, val & 0xffff);
 	WSET_LH(buf, pos + 2, val >> 16);
diff -u --recursive --new-file v2.1.91/linux/fs/ntfs/types.h linux/fs/ntfs/types.h
--- v2.1.91/linux/fs/ntfs/types.h	Fri Dec 19 15:24:21 1997
+++ linux/fs/ntfs/types.h	Mon Mar 30 00:21:40 1998
@@ -14,7 +14,7 @@
 #include <linux/fs.h>
 #endif
 
-#if defined(i386) || defined(__i386__)
+#if defined(i386) || defined(__i386__) || defined(__alpha__)
 
 /* unsigned integral types */
 #ifndef NTFS_INTEGRAL_TYPES
diff -u --recursive --new-file v2.1.91/linux/fs/proc/fd.c linux/fs/proc/fd.c
--- v2.1.91/linux/fs/proc/fd.c	Thu Mar 26 15:57:05 1998
+++ linux/fs/proc/fd.c	Wed Apr  1 16:23:13 1998
@@ -50,7 +50,7 @@
 	NULL,			/* writepage */
 	NULL,			/* bmap */
 	NULL,			/* truncate */
-	NULL			/* permission */
+	proc_permission		/* permission */
 };
 
 /*
diff -u --recursive --new-file v2.1.91/linux/fs/proc/inode.c linux/fs/proc/inode.c
--- v2.1.91/linux/fs/proc/inode.c	Thu Mar 26 15:57:05 1998
+++ linux/fs/proc/inode.c	Wed Apr  1 17:32:39 1998
@@ -129,6 +129,108 @@
 	return 1;
 }
 
+/*
+ * The standard rules, copied from fs/namei.c:permission().
+ */
+static int standard_permission(struct inode *inode, int mask)
+{
+	int mode = inode->i_mode;
+
+	if ((mask & S_IWOTH) && IS_RDONLY(inode) &&
+	    (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
+		return -EROFS; /* Nobody gets write access to a read-only fs */
+	else if ((mask & S_IWOTH) && IS_IMMUTABLE(inode))
+		return -EACCES; /* Nobody gets write access to an immutable file */
+	else if (current->fsuid == inode->i_uid)
+		mode >>= 6;
+	else if (in_group_p(inode->i_gid))
+		mode >>= 3;
+	if (((mode & mask & 0007) == mask) || fsuser())
+		return 0;
+	return -EACCES;
+}
+
+/* 
+ * Set up permission rules for processes looking at other processes.
+ * You're not allowed to see a process unless it has the same or more
+ * restricted root than your own.  This prevents a chrooted processes
+ * from escaping through the /proc entries of less restricted
+ * processes, and thus allows /proc to be safely mounted in a chrooted
+ * area.
+ *
+ * Note that root (uid 0) doesn't get permission for this either,
+ * since chroot is stronger than root.
+ *
+ * XXX TODO: use the dentry mechanism to make off-limits procs simply
+ * invisible rather than denied?  Does each namespace root get its own
+ * dentry tree?
+ *
+ * This also applies the default permissions checks, as it only adds
+ * restrictions.
+ *
+ * Jeremy Fitzhardinge <jeremy@zip.com.au>
+ */
+int proc_permission(struct inode *inode, int mask)
+{
+	struct task_struct *p;
+	unsigned long ino = inode->i_ino;
+	unsigned long pid;
+	struct dentry *de, *base;
+
+	if (standard_permission(inode, mask) != 0)
+		return -EACCES;
+
+	/* 
+	 * Find the root of the processes being examined (if any).
+	 * XXX Surely there's a better way of doing this?
+	 */
+	if (ino >= PROC_OPENPROM_FIRST && 
+	    ino <  PROC_OPENPROM_FIRST + PROC_NOPENPROM)
+		return 0;		/* already allowed */
+
+	pid = ino >> 16;
+	if (pid == 0)
+		return 0;		/* already allowed */
+	
+	de = NULL;
+	base = current->fs->root;
+
+	read_lock(&tasklist_lock);
+	p = find_task_by_pid(pid);
+
+	if (p != NULL)
+		de = p->fs->root;
+	read_unlock(&tasklist_lock);
+
+	if (p == NULL)
+		return -EACCES;		/* ENOENT? */
+
+	if (de == NULL)
+	{
+		/* kswapd and bdflush don't have proper root or cwd... */
+		return -EACCES;
+	}
+	
+	/* XXX locking? */
+	for(;;)
+	{
+		struct dentry *parent;
+
+		if (de == base)
+			return 0;	/* already allowed */
+
+		de = de->d_covers;
+		parent = de->d_parent;
+
+		if (de == parent)
+			break;
+
+		de = parent;
+	}
+
+	return -EACCES;			/* incompatible roots */
+}
+
 struct inode * proc_get_inode(struct super_block * sb, int ino,
 				struct proc_dir_entry * de)
 {
diff -u --recursive --new-file v2.1.91/linux/fs/proc/link.c linux/fs/proc/link.c
--- v2.1.91/linux/fs/proc/link.c	Thu Mar 26 15:57:05 1998
+++ linux/fs/proc/link.c	Wed Apr  1 16:23:13 1998
@@ -58,7 +58,7 @@
 	NULL,			/* writepage */
 	NULL,			/* bmap */
 	NULL,			/* truncate */
-	NULL			/* permission */
+	proc_permission		/* permission */
 };
 
 static struct dentry * proc_follow_link(struct dentry *dentry,
diff -u --recursive --new-file v2.1.91/linux/fs/proc/mem.c linux/fs/proc/mem.c
--- v2.1.91/linux/fs/proc/mem.c	Tue Mar 17 22:18:15 1998
+++ linux/fs/proc/mem.c	Wed Apr  1 16:23:13 1998
@@ -9,6 +9,7 @@
 #include <linux/sched.h>
 #include <linux/kernel.h>
 #include <linux/mm.h>
+#include <linux/proc_fs.h>
 
 #include <asm/page.h>
 #include <asm/uaccess.h>
@@ -336,5 +337,5 @@
 	NULL,			/* writepage */
 	NULL,			/* bmap */
 	NULL,			/* truncate */
-	NULL			/* permission */
+	proc_permission		/* permission */
 };
diff -u --recursive --new-file v2.1.91/linux/include/asm-alpha/apecs.h linux/include/asm-alpha/apecs.h
--- v2.1.91/linux/include/asm-alpha/apecs.h	Mon Jan 12 14:51:14 1998
+++ linux/include/asm-alpha/apecs.h	Mon Mar 30 00:21:40 1998
@@ -77,8 +77,18 @@
 #else /* CONFIG_ALPHA_XL */
 
 /* these are for normal APECS family machines, AVANTI/MUSTANG/EB64/PC64 */
+#ifdef CONFIG_ALPHA_SRM_SETUP
+/* if we are using the SRM PCI setup, we'll need to use variables instead */
+#define APECS_DMA_WIN_BASE_DEFAULT	(1024*1024*1024)
+#define APECS_DMA_WIN_SIZE_DEFAULT	(1024*1024*1024)
+
+extern unsigned int APECS_DMA_WIN_BASE;
+extern unsigned int APECS_DMA_WIN_SIZE;
+
+#else /* SRM_SETUP */
 #define APECS_DMA_WIN_BASE	(1024*1024*1024)
 #define APECS_DMA_WIN_SIZE	(1024*1024*1024)
+#endif /* SRM_SETUP */
 
 #endif /* CONFIG_ALPHA_XL */
 
diff -u --recursive --new-file v2.1.91/linux/include/asm-alpha/bitops.h linux/include/asm-alpha/bitops.h
--- v2.1.91/linux/include/asm-alpha/bitops.h	Tue May 13 22:41:15 1997
+++ linux/include/asm-alpha/bitops.h	Mon Mar 30 00:21:40 1998
@@ -17,7 +17,7 @@
  * bit 0 is the LSB of addr; bit 64 is the LSB of (addr+1).
  */
 
-extern __inline__ void set_bit(unsigned long nr, void * addr)
+extern __inline__ void set_bit(unsigned long nr, volatile void * addr)
 {
 	unsigned long oldbit;
 	unsigned long temp;
@@ -38,7 +38,7 @@
 	:"Ir" (1UL << (nr & 31)), "m" (*m));
 }
 
-extern __inline__ void clear_bit(unsigned long nr, void * addr)
+extern __inline__ void clear_bit(unsigned long nr, volatile void * addr)
 {
 	unsigned long oldbit;
 	unsigned long temp;
@@ -59,7 +59,7 @@
 	:"Ir" (1UL << (nr & 31)), "m" (*m));
 }
 
-extern __inline__ void change_bit(unsigned long nr, void * addr)
+extern __inline__ void change_bit(unsigned long nr, volatile void * addr)
 {
 	unsigned long temp;
 	unsigned int * m = ((unsigned int *) addr) + (nr >> 5);
@@ -76,7 +76,8 @@
 	:"Ir" (1UL << (nr & 31)), "m" (*m));
 }
 
-extern __inline__ unsigned long test_and_set_bit(unsigned long nr, void * addr)
+extern __inline__ unsigned long test_and_set_bit(unsigned long nr,
+						 volatile void * addr)
 {
 	unsigned long oldbit;
 	unsigned long temp;
@@ -99,7 +100,8 @@
 	return oldbit != 0;
 }
 
-extern __inline__ unsigned long test_and_clear_bit(unsigned long nr, void * addr)
+extern __inline__ unsigned long test_and_clear_bit(unsigned long nr,
+						   volatile void * addr)
 {
 	unsigned long oldbit;
 	unsigned long temp;
@@ -122,7 +124,8 @@
 	return oldbit != 0;
 }
 
-extern __inline__ unsigned long test_and_change_bit(unsigned long nr, void * addr)
+extern __inline__ unsigned long test_and_change_bit(unsigned long nr,
+						    volatile void * addr)
 {
 	unsigned long oldbit;
 	unsigned long temp;
@@ -143,7 +146,7 @@
 	return oldbit != 0;
 }
 
-extern __inline__ unsigned long test_bit(int nr, const void * addr)
+extern __inline__ unsigned long test_bit(int nr, volatile void * addr)
 {
 	return 1UL & (((const int *) addr)[nr >> 5] >> (nr & 31));
 }
diff -u --recursive --new-file v2.1.91/linux/include/asm-alpha/cia.h linux/include/asm-alpha/cia.h
--- v2.1.91/linux/include/asm-alpha/cia.h	Mon Oct  7 02:40:22 1996
+++ linux/include/asm-alpha/cia.h	Mon Mar 30 00:21:40 1998
@@ -74,11 +74,23 @@
 
 #define BYTE_ENABLE_SHIFT 5
 #define TRANSFER_LENGTH_SHIFT 3
-#define MEM_SP1_MASK 0x1fffffff  /* Mem sparse space 1 mask is 29 bits */
 
+#define MEM_R1_MASK 0x1fffffff  /* SPARSE Mem region 1 mask is 29 bits */
+#define MEM_R2_MASK 0x07ffffff  /* SPARSE Mem region 2 mask is 27 bits */
+#define MEM_R3_MASK 0x03ffffff  /* SPARSE Mem region 3 mask is 26 bits */
+
+#ifdef CONFIG_ALPHA_SRM_SETUP
+/* if we are using the SRM PCI setup, we'll need to use variables instead */
+#define CIA_DMA_WIN_BASE_DEFAULT    (1024*1024*1024)
+#define CIA_DMA_WIN_SIZE_DEFAULT    (1024*1024*1024)
 
-#define CIA_DMA_WIN_BASE	(1024UL*1024UL*1024UL)
+extern unsigned int CIA_DMA_WIN_BASE;
+extern unsigned int CIA_DMA_WIN_SIZE;
+
+#else /* SRM_SETUP */
+#define CIA_DMA_WIN_BASE            (1024*1024*1024)
 #define CIA_DMA_WIN_SIZE	(1024*1024*1024)
+#endif /* SRM_SETUP */
 
 /*
  * 21171-CA Control and Status Registers (p4-1)
@@ -86,6 +98,7 @@
 #define CIA_IOC_CIA_REV               (IDENT_ADDR + 0x8740000080UL)
 #define CIA_IOC_PCI_LAT               (IDENT_ADDR + 0x87400000C0UL)
 #define CIA_IOC_CIA_CTRL              (IDENT_ADDR + 0x8740000100UL)
+#define CIA_IOC_CIA_CNFG              (IDENT_ADDR + 0x8740000140UL)
 #define CIA_IOC_HAE_MEM               (IDENT_ADDR + 0x8740000400UL)
 #define CIA_IOC_HAE_IO                (IDENT_ADDR + 0x8740000440UL)
 #define CIA_IOC_CFG                   (IDENT_ADDR + 0x8740000480UL)
@@ -119,18 +132,25 @@
 #define CIA_IOC_PCI_ERR3              (IDENT_ADDR + 0x8740008880UL)
 
 /*
- * 2117A-CA PCI Address Translation Registers.   I've only defined
- * the first window fully as that's the only one that we're currently using.
- * The other window bases are needed to disable the windows.
+ * 2117A-CA PCI Address Translation Registers.
  */
 #define CIA_IOC_PCI_TBIA              (IDENT_ADDR + 0x8760000100UL)
+
 #define CIA_IOC_PCI_W0_BASE           (IDENT_ADDR + 0x8760000400UL)
 #define CIA_IOC_PCI_W0_MASK           (IDENT_ADDR + 0x8760000440UL)
 #define CIA_IOC_PCI_T0_BASE           (IDENT_ADDR + 0x8760000480UL)
 
 #define CIA_IOC_PCI_W1_BASE           (IDENT_ADDR + 0x8760000500UL)
+#define CIA_IOC_PCI_W1_MASK           (IDENT_ADDR + 0x8760000540UL)
+#define CIA_IOC_PCI_T1_BASE           (IDENT_ADDR + 0x8760000580UL)
+
 #define CIA_IOC_PCI_W2_BASE           (IDENT_ADDR + 0x8760000600UL)
+#define CIA_IOC_PCI_W2_MASK           (IDENT_ADDR + 0x8760000640UL)
+#define CIA_IOC_PCI_T2_BASE           (IDENT_ADDR + 0x8760000680UL)
+
 #define CIA_IOC_PCI_W3_BASE           (IDENT_ADDR + 0x8760000700UL)
+#define CIA_IOC_PCI_W3_MASK           (IDENT_ADDR + 0x8760000740UL)
+#define CIA_IOC_PCI_T3_BASE           (IDENT_ADDR + 0x8760000780UL)
 
 /*
  * 21171-CA System configuration registers (p4-3)
@@ -155,6 +175,8 @@
 #define CIA_CONF		        (IDENT_ADDR + 0x8700000000UL)
 #define CIA_IO				(IDENT_ADDR + 0x8580000000UL)
 #define CIA_SPARSE_MEM			(IDENT_ADDR + 0x8000000000UL)
+#define CIA_SPARSE_MEM_R2		(IDENT_ADDR + 0x8400000000UL)
+#define CIA_SPARSE_MEM_R3		(IDENT_ADDR + 0x8500000000UL)
 #define CIA_DENSE_MEM		        (IDENT_ADDR + 0x8600000000UL)
 
 /*
@@ -296,13 +318,125 @@
  * 
  */
 
+#ifdef CONFIG_ALPHA_SRM_SETUP
+
+extern unsigned long cia_sm_base_r1, cia_sm_base_r2, cia_sm_base_r3;
+
+extern inline unsigned long __readb(unsigned long addr)
+{
+	unsigned long result, shift, work;
+
+	if ((addr >= cia_sm_base_r1) &&
+	    (addr <= (cia_sm_base_r1 + MEM_R1_MASK)))
+	  work = (((addr & MEM_R1_MASK) << 5) + CIA_SPARSE_MEM + 0x00);
+	else
+	if ((addr >= cia_sm_base_r2) &&
+	    (addr <= (cia_sm_base_r2 + MEM_R2_MASK)))
+	  work = (((addr & MEM_R2_MASK) << 5) + CIA_SPARSE_MEM_R2 + 0x00);
+	else
+	if ((addr >= cia_sm_base_r3) &&
+	    (addr <= (cia_sm_base_r3 + MEM_R3_MASK)))
+	  work = (((addr & MEM_R3_MASK) << 5) + CIA_SPARSE_MEM_R3 + 0x00);
+	else
+	{
+#if 0
+	  printk("__readb: address 0x%lx not covered by HAE\n", addr);
+#endif
+	  return 0x0ffUL;
+	}
+	shift = (addr & 0x3) << 3;
+	result = *(vuip) work;
+	result >>= shift;
+	return 0x0ffUL & result;
+}
+
+extern inline unsigned long __readw(unsigned long addr)
+{
+	unsigned long result, shift, work;
+
+	if ((addr >= cia_sm_base_r1) &&
+	    (addr <= (cia_sm_base_r1 + MEM_R1_MASK)))
+	  work = (((addr & MEM_R1_MASK) << 5) + CIA_SPARSE_MEM + 0x08);
+	else
+	if ((addr >= cia_sm_base_r2) &&
+	    (addr <= (cia_sm_base_r2 + MEM_R2_MASK)))
+	  work = (((addr & MEM_R2_MASK) << 5) + CIA_SPARSE_MEM_R2 + 0x08);
+	else
+	if ((addr >= cia_sm_base_r3) &&
+	    (addr <= (cia_sm_base_r3 + MEM_R3_MASK)))
+	  work = (((addr & MEM_R3_MASK) << 5) + CIA_SPARSE_MEM_R3 + 0x08);
+	else
+	{
+#if 0
+	  printk("__readw: address 0x%lx not covered by HAE\n", addr);
+#endif
+	  return 0x0ffffUL;
+	}
+	shift = (addr & 0x3) << 3;
+	result = *(vuip) work;
+	result >>= shift;
+	return 0x0ffffUL & result;
+}
+
+extern inline void __writeb(unsigned char b, unsigned long addr)
+{
+	unsigned long work;
+
+	if ((addr >= cia_sm_base_r1) &&
+	    (addr <= (cia_sm_base_r1 + MEM_R1_MASK)))
+	  work = (((addr & MEM_R1_MASK) << 5) + CIA_SPARSE_MEM + 0x00);
+	else
+	if ((addr >= cia_sm_base_r2) &&
+	    (addr <= (cia_sm_base_r2 + MEM_R2_MASK)))
+	  work = (((addr & MEM_R2_MASK) << 5) + CIA_SPARSE_MEM_R2 + 0x00);
+	else
+	if ((addr >= cia_sm_base_r3) &&
+	    (addr <= (cia_sm_base_r3 + MEM_R3_MASK)))
+	  work = (((addr & MEM_R3_MASK) << 5) + CIA_SPARSE_MEM_R3 + 0x00);
+	else
+	{
+#if 0
+	  printk("__writeb: address 0x%lx not covered by HAE\n", addr);
+#endif
+	  return;
+	}
+	*(vuip) work = b * 0x01010101;
+}
+
+extern inline void __writew(unsigned short b, unsigned long addr)
+{
+	unsigned long work;
+
+	if ((addr >= cia_sm_base_r1) &&
+	    (addr <= (cia_sm_base_r1 + MEM_R1_MASK)))
+	  work = (((addr & MEM_R1_MASK) << 5) + CIA_SPARSE_MEM + 0x00);
+	else
+	if ((addr >= cia_sm_base_r2) &&
+	    (addr <= (cia_sm_base_r2 + MEM_R2_MASK)))
+	  work = (((addr & MEM_R2_MASK) << 5) + CIA_SPARSE_MEM_R2 + 0x00);
+	else
+	if ((addr >= cia_sm_base_r3) &&
+	    (addr <= (cia_sm_base_r3 + MEM_R3_MASK)))
+	  work = (((addr & MEM_R3_MASK) << 5) + CIA_SPARSE_MEM_R3 + 0x00);
+	else
+	{
+#if 0
+	  printk("__writew: address 0x%lx not covered by HAE\n", addr);
+#endif
+	  return;
+	}
+	*(vuip) work = b * 0x00010001;
+}
+
+#else /* SRM_SETUP */
+
 extern inline unsigned long __readb(unsigned long addr)
 {
 	unsigned long result, shift, msb;
 
 	shift = (addr & 0x3) * 8 ;
 	msb = addr & 0xE0000000 ;
-	addr &= MEM_SP1_MASK ;
+	addr &= MEM_R1_MASK ;
 	if (msb != hae.cache) {
 	  set_hae(msb);
 	}
@@ -317,7 +451,7 @@
 
 	shift = (addr & 0x3) * 8;
 	msb = addr & 0xE0000000 ;
-	addr &= MEM_SP1_MASK ;
+	addr &= MEM_R1_MASK ;
 	if (msb != hae.cache) {
 	  set_hae(msb);
 	}
@@ -326,17 +460,12 @@
 	return 0xffffUL & result;
 }
 
-extern inline unsigned long __readl(unsigned long addr)
-{
-	return *(vuip) (addr + CIA_DENSE_MEM);
-}
-
 extern inline void __writeb(unsigned char b, unsigned long addr)
 {
         unsigned long msb ; 
 
 	msb = addr & 0xE0000000 ;
-	addr &= MEM_SP1_MASK ;
+	addr &= MEM_R1_MASK ;
 	if (msb != hae.cache) {
 	  set_hae(msb);
 	}
@@ -348,11 +477,18 @@
         unsigned long msb ; 
 
 	msb = addr & 0xE0000000 ;
-	addr &= MEM_SP1_MASK ;
+	addr &= MEM_R1_MASK ;
 	if (msb != hae.cache) {
 	  set_hae(msb);
 	}
 	*(vuip) ((addr << 5) + CIA_SPARSE_MEM + 0x08) = b * 0x00010001;
+}
+
+#endif /* SRM_SETUP */
+
+extern inline unsigned long __readl(unsigned long addr)
+{
+	return *(vuip) (addr + CIA_DENSE_MEM);
 }
 
 extern inline void __writel(unsigned int b, unsigned long addr)
diff -u --recursive --new-file v2.1.91/linux/include/asm-alpha/hardirq.h linux/include/asm-alpha/hardirq.h
--- v2.1.91/linux/include/asm-alpha/hardirq.h	Wed Feb  4 11:36:01 1998
+++ linux/include/asm-alpha/hardirq.h	Mon Mar 30 00:21:40 1998
@@ -17,7 +17,65 @@
 
 #else
 
-#error No habla alpha SMP
+/* initially just a straight copy if the i386 code */
+
+#include <asm/atomic.h>
+#include <asm/spinlock.h>
+#include <asm/system.h>
+#include <asm/smp.h>
+
+extern unsigned char global_irq_holder;
+extern spinlock_t global_irq_lock;
+extern atomic_t global_irq_count;
+
+static inline void release_irqlock(int cpu)
+{
+	/* if we didn't own the irq lock, just ignore.. */
+	if (global_irq_holder == (unsigned char) cpu) {
+		global_irq_holder = NO_PROC_ID;
+		spin_unlock(&global_irq_lock);
+        }
+}
+
+/* Ordering of the counter bumps is _deadly_ important. */
+static inline void hardirq_enter(int cpu)
+{
+	++local_irq_count[cpu];
+        atomic_inc(&global_irq_count);
+}
+
+static inline void hardirq_exit(int cpu)
+{
+	atomic_dec(&global_irq_count);
+        --local_irq_count[cpu];
+}
+
+static inline int hardirq_trylock(int cpu)
+{
+	unsigned long flags;
+	int ret = 1;
+
+        __save_and_cli(flags);
+        if ((atomic_add_return(1, &global_irq_count) != 1) ||
+	    (global_irq_lock.lock != 0)) {
+		atomic_dec(&global_irq_count);
+                __restore_flags(flags);
+                ret = 0;
+        } else {
+		++local_irq_count[cpu];
+                __sti();
+        }
+        return ret;
+}
+
+#define hardirq_endlock(cpu) \
+	do { \
+	       __cli(); \
+		hardirq_exit(cpu); \
+		__sti(); \
+	} while (0)
+
+extern void synchronize_irq(void);
 
 #endif /* __SMP__ */
 #endif /* _ALPHA_HARDIRQ_H */
diff -u --recursive --new-file v2.1.91/linux/include/asm-alpha/hwrpb.h linux/include/asm-alpha/hwrpb.h
--- v2.1.91/linux/include/asm-alpha/hwrpb.h	Tue Mar 10 10:03:34 1998
+++ linux/include/asm-alpha/hwrpb.h	Mon Mar 30 00:21:40 1998
@@ -16,7 +16,7 @@
 #define EV56_CPU		7	/* EV5.6 (21164)	*/
 #define EV6_CPU			8	/* EV6 (21164)		*/
 #define PCA56_CPU		9	/* PCA56 (21164PC)	*/
-#define PCA57_CPU		10	/* PCA57 (??)		*/
+#define PCA57_CPU		10	/* PCA57 (21164??)	*/
 
 /*
  * DEC system types for Alpha systems.  Found in HWRPB.
@@ -34,14 +34,12 @@
 #define ST_DEC_AXPPCI_33	 11	/* NoName system type	*/
 #define ST_DEC_TLASER		 12	/* Turbolaser systype	*/
 #define ST_DEC_2100_A50		 13	/* Avanti systype	*/
-#define ST_DEC_MUSTANG		 14	/* Mustang systype	*/
 #define ST_DEC_ALCOR		 15	/* Alcor (EV5) systype	*/
 #define ST_DEC_1000		 17	/* Mikasa systype	*/
+#define ST_DEC_EB64		 18	/* EB64 systype		*/
 #define ST_DEC_EB66		 19	/* EB66 systype		*/
 #define ST_DEC_EB64P		 20	/* EB64+ systype	*/
-#define ST_DEC_EB66P		-19	/* EB66 systype		*/
-#define ST_DEC_EBPC64		-20	/* Cabriolet (AlphaPC64) systype */
-#define ST_DEC_BURNS		 21	/* Laptop systype	*/
+#define ST_DEC_BURNS		 21	/* laptop systype	*/
 #define ST_DEC_RAWHIDE		 22	/* Rawhide systype	*/
 #define ST_DEC_K2		 23	/* K2 systype		*/
 #define ST_DEC_LYNX		 24	/* Lynx systype		*/
@@ -49,7 +47,7 @@
 #define ST_DEC_EB164		 26	/* EB164 systype	*/
 #define ST_DEC_NORITAKE		 27	/* Noritake systype	*/
 #define ST_DEC_CORTEX		 28	/* Cortex systype	*/
-#define ST_DEC_MIATA		 30	/* MIATA systype        */
+#define ST_DEC_MIATA		 30	/* Miata systype        */
 #define ST_DEC_XXM		 31	/* XXM systype		*/
 #define ST_DEC_TAKARA		 32	/* Takara systype	*/
 #define ST_DEC_YUKON		 33	/* Yukon systype	*/
@@ -61,7 +59,6 @@
 #define ST_UNOFFICIAL_BIAS	100
 #define ST_DTI_RUFFIAN		101	/* RUFFIAN systype	*/
 
-
 struct pcb_struct {
 	unsigned long ksp;
 	unsigned long usp;
@@ -139,6 +136,12 @@
 	struct memclust_struct cluster[0];
 };
 
+struct dsr_struct {
+	long smm;			/* SMM nubber used by LMF       */
+	unsigned long  lurt_off;	/* offset to LURT table         */
+	unsigned long  sysname_off;	/* offset to sysname char count */
+};
+
 struct hwrpb_struct {
 	unsigned long phys_addr;	/* check: physical address of the hwrpb */
 	unsigned long id;		/* check: "HWRPB\0\0\0" */
@@ -178,7 +181,7 @@
 	unsigned long chksum;
 	unsigned long rxrdy;
 	unsigned long txrdy;
-	unsigned long dsrdbt_offset;	/* "Dynamic System Recognition Data Block Table" Whee */
+	unsigned long dsr_offset;	/* "Dynamic System Recognition Data Block Table" */
 };
 
 #endif
diff -u --recursive --new-file v2.1.91/linux/include/asm-alpha/io.h linux/include/asm-alpha/io.h
--- v2.1.91/linux/include/asm-alpha/io.h	Mon Jan 12 14:51:14 1998
+++ linux/include/asm-alpha/io.h	Mon Mar 30 00:21:40 1998
@@ -25,7 +25,11 @@
 /*
  * Virtual -> physical identity mapping starts at this offset
  */
+#ifdef USE_48_BIT_KSEG
+#define IDENT_ADDR	(0xffff800000000000UL)
+#else
 #define IDENT_ADDR	(0xfffffc0000000000UL)
+#endif
 
 #ifdef __KERNEL__
 
@@ -38,11 +42,11 @@
  */
 extern inline void set_hae(unsigned long new_hae)
 {
-	unsigned long ipl;
-	ipl = swpipl(7);
+	unsigned long ipl = swpipl(7);
 	hae.cache = new_hae;
 	*hae.reg = new_hae;
 	mb();
+	new_hae = *hae.reg; /* read to make sure it was written */
 	setipl(ipl);
 }
 
@@ -84,6 +88,10 @@
 # include <asm/t2.h>		/* get chip-specific definitions */
 #elif defined(CONFIG_ALPHA_PYXIS)
 # include <asm/pyxis.h>		/* get chip-specific definitions */
+#elif defined(CONFIG_ALPHA_TSUNAMI)
+# include <asm/tsunami.h>	/* get chip-specific definitions */
+#elif defined(CONFIG_ALPHA_MCPCIA)
+# include <asm/mcpcia.h>	/* get chip-specific definitions */
 #else
 # include <asm/jensen.h>
 #endif
diff -u --recursive --new-file v2.1.91/linux/include/asm-alpha/irq.h linux/include/asm-alpha/irq.h
--- v2.1.91/linux/include/asm-alpha/irq.h	Tue Mar 10 10:03:34 1998
+++ linux/include/asm-alpha/irq.h	Mon Mar 30 00:21:40 1998
@@ -10,21 +10,46 @@
 #include <linux/linkage.h>
 #include <linux/config.h>
 
-#if defined(CONFIG_ALPHA_CABRIOLET) || defined(CONFIG_ALPHA_EB66P) || \
-	defined(CONFIG_ALPHA_EB164) || defined(CONFIG_ALPHA_PC164) || \
+#if   defined(CONFIG_ALPHA_CABRIOLET) || \
+      defined(CONFIG_ALPHA_EB66P)     || \
+      defined(CONFIG_ALPHA_EB164)     || \
+      defined(CONFIG_ALPHA_PC164)     || \
 	defined(CONFIG_ALPHA_LX164)
-# define NR_IRQS	33
-#elif defined(CONFIG_ALPHA_EB66) || defined(CONFIG_ALPHA_EB64P) || \
+
+# define NR_IRQS	35
+
+#elif defined(CONFIG_ALPHA_EB66)      || \
+      defined(CONFIG_ALPHA_EB64P)     || \
 	defined(CONFIG_ALPHA_MIKASA)
+
 # define NR_IRQS	32
-#elif defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT) || \
-	defined(CONFIG_ALPHA_MIATA) || defined(CONFIG_ALPHA_NORITAKE) || \
-	defined(CONFIG_ALPHA_RUFFIAN)
+
+#elif defined(CONFIG_ALPHA_ALCOR)     || \
+      defined(CONFIG_ALPHA_XLT)       || \
+      defined(CONFIG_ALPHA_MIATA)     || \
+      defined(CONFIG_ALPHA_RUFFIAN)   || \
+      defined(CONFIG_ALPHA_NORITAKE)
+
 # define NR_IRQS	48
-#elif defined(CONFIG_ALPHA_SABLE) || defined(CONFIG_ALPHA_SX164)
+
+#elif defined(CONFIG_ALPHA_SABLE)     || \
+      defined(CONFIG_ALPHA_SX164)
+
 # define NR_IRQS	40
-#else
+
+#elif defined(CONFIG_ALPHA_DP264) || \
+      defined(CONFIG_ALPHA_RAWHIDE)
+
+# define NR_IRQS	64
+
+#elif defined(CONFIG_ALPHA_TAKARA)
+
+# define NR_IRQS	20
+
+#else /* everyone else */
+
 # define NR_IRQS	16
+
 #endif
 
 
diff -u --recursive --new-file v2.1.91/linux/include/asm-alpha/lca.h linux/include/asm-alpha/lca.h
--- v2.1.91/linux/include/asm-alpha/lca.h	Mon Jan 12 14:51:14 1998
+++ linux/include/asm-alpha/lca.h	Mon Mar 30 00:21:40 1998
@@ -54,8 +54,18 @@
 
 #include <asm/system.h>
 
+#ifdef CONFIG_ALPHA_SRM_SETUP
+/* if we are using the SRM PCI setup, we'll need to use variables instead */
+#define LCA_DMA_WIN_BASE_DEFAULT    (1024*1024*1024)
+#define LCA_DMA_WIN_SIZE_DEFAULT    (1024*1024*1024)
+
+extern unsigned int LCA_DMA_WIN_BASE;
+extern unsigned int LCA_DMA_WIN_SIZE;
+
+#else /* SRM_SETUP */
 #define LCA_DMA_WIN_BASE	(1024*1024*1024)
 #define LCA_DMA_WIN_SIZE	(1024*1024*1024)
+#endif /* SRM_SETUP */
 
 /*
  * Memory Controller registers:
diff -u --recursive --new-file v2.1.91/linux/include/asm-alpha/mcpcia.h linux/include/asm-alpha/mcpcia.h
--- v2.1.91/linux/include/asm-alpha/mcpcia.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-alpha/mcpcia.h	Mon Mar 30 00:21:40 1998
@@ -0,0 +1,668 @@
+#ifndef __ALPHA_MCPCIA__H__
+#define __ALPHA_MCPCIA__H__
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+
+/*
+ * MCPCIA is the internal name for a core logic chipset which provides
+ * PCI access for the RAWHIDE family of systems.
+ *
+ * This file is based on:
+ *
+ * RAWHIDE System Programmer's Manual
+ * 16-May-96
+ * Rev. 1.4
+ *
+ */
+
+/*------------------------------------------------------------------------**
+**                                                                        **
+**  I/O procedures                                                   **
+**                                                                        **
+**      inport[b|w|t|l], outport[b|w|t|l] 8:16:24:32 IO xfers             **
+**	inportbxt: 8 bits only                                            **
+**      inport:    alias of inportw                                       **
+**      outport:   alias of outportw                                      **
+**                                                                        **
+**      inmem[b|w|t|l], outmem[b|w|t|l] 8:16:24:32 ISA memory xfers       **
+**	inmembxt: 8 bits only                                             **
+**      inmem:    alias of inmemw                                         **
+**      outmem:   alias of outmemw                                        **
+**                                                                        **
+**------------------------------------------------------------------------*/
+
+
+/* MCPCIA ADDRESS BIT DEFINITIONS
+ *
+ *  3 3 3 3|3 3 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 
+ *  9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |1| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |0|0|0|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
+ *  |                                                                        \_/ \_/
+ *  |                                                                         |   |
+ *  +-- IO space, not cached.                                   Byte Enable --+   |
+ *                                                              Transfer Length --+
+ *
+ *
+ *
+ *   Byte      Transfer
+ *   Enable    Length    Transfer  Byte    Address
+ *   adr<6:5>  adr<4:3>  Length    Enable  Adder
+ *   ---------------------------------------------
+ *      00        00      Byte      1110   0x000
+ *      01        00      Byte      1101   0x020
+ *      10        00      Byte      1011   0x040
+ *      11        00      Byte      0111   0x060
+ *
+ *      00        01      Word      1100   0x008
+ *      01        01      Word      1001   0x028 <= Not supported in this code.
+ *      10        01      Word      0011   0x048
+ *
+ *      00        10      Tribyte   1000   0x010
+ *      01        10      Tribyte   0001   0x030
+ *
+ *      10        11      Longword  0000   0x058
+ *
+ *      Note that byte enables are asserted low.
+ *
+ */
+
+#define BYTE_ENABLE_SHIFT 5
+#define TRANSFER_LENGTH_SHIFT 3
+
+#define MEM_R1_MASK 0x1fffffff  /* SPARSE Mem region 1 mask is 29 bits */
+#define MEM_R2_MASK 0x07ffffff  /* SPARSE Mem region 2 mask is 27 bits */
+#define MEM_R3_MASK 0x03ffffff  /* SPARSE Mem region 3 mask is 26 bits */
+
+#ifdef CONFIG_ALPHA_SRM_SETUP
+/* if we are using the SRM PCI setup, we'll need to use variables instead */
+#define MCPCIA_DMA_WIN_BASE_DEFAULT    (2*1024*1024*1024U)
+#define MCPCIA_DMA_WIN_SIZE_DEFAULT    (2*1024*1024*1024U)
+
+extern unsigned int MCPCIA_DMA_WIN_BASE;
+extern unsigned int MCPCIA_DMA_WIN_SIZE;
+
+#else /* SRM_SETUP */
+#define MCPCIA_DMA_WIN_BASE	(2*1024*1024*1024UL)
+#define MCPCIA_DMA_WIN_SIZE	(2*1024*1024*1024UL)
+#endif /* SRM_SETUP */
+
+#define HOSE(h) (((unsigned long)(h)) << 33)
+/*
+ *  General Registers
+ */
+#define MCPCIA_REV(h)		(IDENT_ADDR + 0xf9e0000000UL + HOSE(h))
+#define MCPCIA_WHOAMI(h)	(IDENT_ADDR + 0xf9e0000040UL + HOSE(h))
+#define MCPCIA_PCI_LAT(h)	(IDENT_ADDR + 0xf9e0000080UL + HOSE(h))
+#define MCPCIA_CAP_CTRL(h)	(IDENT_ADDR + 0xf9e0000100UL + HOSE(h))
+#define MCPCIA_HAE_MEM(h)	(IDENT_ADDR + 0xf9e0000400UL + HOSE(h))
+#define MCPCIA_HAE_IO(h)	(IDENT_ADDR + 0xf9e0000440UL + HOSE(h))
+#if 0
+#define MCPCIA_IACK_SC(h)	(IDENT_ADDR + 0xf9e0000480UL + HOSE(h))
+#endif
+#define MCPCIA_HAE_DENSE(h)	(IDENT_ADDR + 0xf9e00004c0UL + HOSE(h))
+
+/*
+ * Interrupt Control registers
+ */
+#define MCPCIA_INT_CTL(h)	(IDENT_ADDR + 0xf9e0000500UL + HOSE(h))
+#define MCPCIA_INT_REQ(h)	(IDENT_ADDR + 0xf9e0000540UL + HOSE(h))
+#define MCPCIA_INT_TARG(h)	(IDENT_ADDR + 0xf9e0000580UL + HOSE(h))
+#define MCPCIA_INT_ADR(h)	(IDENT_ADDR + 0xf9e00005c0UL + HOSE(h))
+#define MCPCIA_INT_ADR_EXT(h)	(IDENT_ADDR + 0xf9e0000600UL + HOSE(h))
+#define MCPCIA_INT_MASK0(h)	(IDENT_ADDR + 0xf9e0000640UL + HOSE(h))
+#define MCPCIA_INT_MASK1(h)	(IDENT_ADDR + 0xf9e0000680UL + HOSE(h))
+#define MCPCIA_INT_ACK0(h)	(IDENT_ADDR + 0xf9f0003f00UL + HOSE(h))
+#define MCPCIA_INT_ACK1(h)	(IDENT_ADDR + 0xf9e0003f40UL + HOSE(h))
+
+/*
+ * Performance Monitor registers
+ */
+#define MCPCIA_PERF_MONITOR(h)	(IDENT_ADDR + 0xf9e0000300UL + HOSE(h))
+#define MCPCIA_PERF_CONTROL(h)	(IDENT_ADDR + 0xf9e0000340UL + HOSE(h))
+
+/*
+ * Diagnostic Registers
+ */
+#define MCPCIA_CAP_DIAG(h)	(IDENT_ADDR + 0xf9e0000700UL + HOSE(h))
+#define MCPCIA_TOP_OF_MEM(h)	(IDENT_ADDR + 0xf9e00007c0UL + HOSE(h))
+
+/*
+ * Error registers
+ */
+#define MCPCIA_CAP_ERR(h)	(IDENT_ADDR + 0xf9e0000880UL + HOSE(h))
+#define MCPCIA_PCI_ERR1(h)	(IDENT_ADDR + 0xf9e0001040UL + HOSE(h))
+
+/*
+ * PCI Address Translation Registers.
+ */
+#define MCPCIA_SG_TBIA(h)	(IDENT_ADDR + 0xf9e0001300UL + HOSE(h))
+#define MCPCIA_HBASE(h)		(IDENT_ADDR + 0xf9e0001340UL + HOSE(h))
+
+#define MCPCIA_W0_BASE(h)	(IDENT_ADDR + 0xf9e0001400UL + HOSE(h))
+#define MCPCIA_W0_MASK(h)	(IDENT_ADDR + 0xf9e0001440UL + HOSE(h))
+#define MCPCIA_T0_BASE(h)	(IDENT_ADDR + 0xf9e0001480UL + HOSE(h))
+
+#define MCPCIA_W1_BASE(h)	(IDENT_ADDR + 0xf9e0001500UL + HOSE(h))
+#define MCPCIA_W1_MASK(h)	(IDENT_ADDR + 0xf9e0001540UL + HOSE(h))
+#define MCPCIA_T1_BASE(h)	(IDENT_ADDR + 0xf9e0001580UL + HOSE(h))
+
+#define MCPCIA_W2_BASE(h)	(IDENT_ADDR + 0xf9e0001600UL + HOSE(h))
+#define MCPCIA_W2_MASK(h)	(IDENT_ADDR + 0xf9e0001640UL + HOSE(h))
+#define MCPCIA_T2_BASE(h)	(IDENT_ADDR + 0xf9e0001680UL + HOSE(h))
+
+#define MCPCIA_W3_BASE(h)	(IDENT_ADDR + 0xf9e0001700UL + HOSE(h))
+#define MCPCIA_W3_MASK(h)	(IDENT_ADDR + 0xf9e0001740UL + HOSE(h))
+#define MCPCIA_T3_BASE(h)	(IDENT_ADDR + 0xf9e0001780UL + HOSE(h))
+
+/*
+ * Memory spaces:
+ */
+#define MCPCIA_CONF(h)		(IDENT_ADDR + 0xf9c0000000UL + HOSE(h))
+#define MCPCIA_IO(h)		(IDENT_ADDR + 0xf980000000UL + HOSE(h))
+#define MCPCIA_SPARSE(h)	(IDENT_ADDR + 0xf800000000UL + HOSE(h))
+#define MCPCIA_DENSE(h)		(IDENT_ADDR + 0xf900000000UL + HOSE(h))
+#define MCPCIA_IACK_SC(h)	(IDENT_ADDR + 0xf9f0003f00UL + HOSE(h))
+
+#define HAE_ADDRESS		MCPCIA_HAE_MEM(0)
+
+#ifdef __KERNEL__
+
+/*
+ * Translate physical memory address as seen on (PCI) bus into
+ * a kernel virtual address and vv.
+ */
+extern inline unsigned long virt_to_bus(void * address)
+{
+	return virt_to_phys(address) + MCPCIA_DMA_WIN_BASE;
+}
+
+extern inline void * bus_to_virt(unsigned long address)
+{
+	return phys_to_virt(address - MCPCIA_DMA_WIN_BASE);
+}
+
+/*
+ * I/O functions:
+ *
+ * MCPCIA, the RAWHIDE family PCI/memory support chipset for the EV5 (21164)
+ * and EV56 (21164a) processors, can use either a sparse address mapping
+ * scheme, or the so-called byte-word PCI address space, to get at PCI memory
+ * and I/O.
+ *
+ * Unfortunately, we can't use BWIO with EV5, so for now, we always use SPARSE.
+ */
+
+#define vuip	volatile unsigned int *
+
+#ifdef DISABLE_BWIO_ENABLED
+
+extern inline unsigned int __inb(unsigned long addr)
+{
+	register unsigned long result;
+
+	__asm__ __volatile__ (
+		 "ldbu %0,%1"
+		 : "=r" (result)
+		 : "m"  (*(unsigned char *)(addr+MCPCIA_BW_IO)));
+
+	return result;
+}
+
+extern inline void __outb(unsigned char b, unsigned long addr)
+{
+	__asm__ __volatile__ (
+		 "stb %1,%0\n\t"
+		 "mb"
+		 : : "m" (*(unsigned char *)(addr+MCPCIA_BW_IO)), "r" (b));
+}
+
+extern inline unsigned int __inw(unsigned long addr)
+{
+	register unsigned long result;
+
+	__asm__ __volatile__ (
+		 "ldwu %0,%1"
+		 : "=r" (result)
+		 : "m"  (*(unsigned short *)(addr+MCPCIA_BW_IO)));
+
+	return result;
+}
+
+extern inline void __outw(unsigned short b, unsigned long addr)
+{
+	__asm__ __volatile__ (
+		 "stw %1,%0\n\t"
+		 "mb"
+		 : : "m" (*(unsigned short *)(addr+MCPCIA_BW_IO)), "r" (b));
+}
+
+extern inline unsigned int __inl(unsigned long addr)
+{
+	register unsigned long result;
+
+	__asm__ __volatile__ (
+		 "ldl %0,%1"
+		 : "=r" (result)
+		 : "m"  (*(unsigned int *)(addr+MCPCIA_BW_IO)));
+
+	return result;
+}
+
+extern inline void __outl(unsigned int b, unsigned long addr)
+{
+	__asm__ __volatile__ (
+		 "stl %1,%0\n\t"
+		 "mb"
+		 : : "m" (*(unsigned int *)(addr+MCPCIA_BW_IO)), "r" (b));
+}
+
+#define inb(port) __inb((port))
+#define inw(port) __inw((port))
+#define inl(port) __inl((port))
+
+#define outb(x, port) __outb((x),(port))
+#define outw(x, port) __outw((x),(port))
+#define outl(x, port) __outl((x),(port))
+
+#else /* BWIO_ENABLED */
+
+extern inline unsigned int __inb(unsigned long in_addr)
+{
+	unsigned long addr = in_addr & 0xffffffffUL;
+	unsigned long hose = (in_addr >> 32) & 3;
+	long result = *(vuip) ((addr << 5) + MCPCIA_IO(hose) + 0x00);
+	result >>= (addr & 3) * 8;
+	return 0xffUL & result;
+}
+
+extern inline void __outb(unsigned char b, unsigned long in_addr)
+{
+	unsigned long addr = in_addr & 0xffffffffUL;
+	unsigned long hose = (in_addr >> 32) & 3;
+	unsigned int w;
+
+	asm ("insbl %2,%1,%0" : "r="(w) : "ri"(addr & 0x3), "r"(b));
+	*(vuip) ((addr << 5) + MCPCIA_IO(hose) + 0x00) = w;
+	mb();
+}
+
+extern inline unsigned int __inw(unsigned long in_addr)
+{
+	unsigned long addr = in_addr & 0xffffffffUL;
+	unsigned long hose = (in_addr >> 32) & 3;
+	long result = *(vuip) ((addr << 5) + MCPCIA_IO(hose) + 0x08);
+	result >>= (addr & 3) * 8;
+	return 0xffffUL & result;
+}
+
+extern inline void __outw(unsigned short b, unsigned long in_addr)
+{
+	unsigned long addr = in_addr & 0xffffffffUL;
+	unsigned long hose = (in_addr >> 32) & 3;
+	unsigned int w;
+
+	asm ("inswl %2,%1,%0" : "r="(w) : "ri"(addr & 0x3), "r"(b));
+	*(vuip) ((addr << 5) + MCPCIA_IO(hose) + 0x08) = w;
+	mb();
+}
+
+extern inline unsigned int __inl(unsigned long in_addr)
+{
+	unsigned long addr = in_addr & 0xffffffffUL;
+	unsigned long hose = (in_addr >> 32) & 3;
+	return *(vuip) ((addr << 5) + MCPCIA_IO(hose) + 0x18);
+}
+
+extern inline void __outl(unsigned int b, unsigned long in_addr)
+{
+	unsigned long addr = in_addr & 0xffffffffUL;
+	unsigned long hose = (in_addr >> 32) & 3;
+	*(vuip) ((addr << 5) + MCPCIA_IO(hose) + 0x18) = b;
+	mb();
+}
+
+#define inb(port) \
+(__builtin_constant_p((port))?__inb(port):_inb(port))
+
+#define outb(x, port) \
+(__builtin_constant_p((port))?__outb((x),(port)):_outb((x),(port)))
+
+#endif /* BWIO_ENABLED */
+
+
+/*
+ * Memory functions.  64-bit and 32-bit accesses are done through
+ * dense memory space, everything else through sparse space.
+ * 
+ * For reading and writing 8 and 16 bit quantities we need to 
+ * go through one of the three sparse address mapping regions
+ * and use the HAE_MEM CSR to provide some bits of the address.
+ * The following few routines use only sparse address region 1
+ * which gives 1Gbyte of accessible space which relates exactly
+ * to the amount of PCI memory mapping *into* system address space.
+ * See p 6-17 of the specification but it looks something like this:
+ *
+ * 21164 Address:
+ * 
+ *          3         2         1
+ * 9876543210987654321098765432109876543210
+ * 1ZZZZ0.PCI.QW.Address............BBLL                 
+ *
+ * ZZ = SBZ
+ * BB = Byte offset
+ * LL = Transfer length
+ *
+ * PCI Address:
+ *
+ * 3         2         1
+ * 10987654321098765432109876543210
+ * HHH....PCI.QW.Address........ 00
+ *
+ * HHH = 31:29 HAE_MEM CSR
+ * 
+ */
+
+#ifdef DISABLE_BWIO_ENABLED
+
+extern inline unsigned long __readb(unsigned long addr)
+{
+	register unsigned long result;
+
+	__asm__ __volatile__ (
+		 "ldbu %0,%1"
+		 : "=r" (result)
+		 : "m"  (*(unsigned char *)(addr+MCPCIA_BW_MEM)));
+
+	return result;
+}
+
+extern inline unsigned long __readw(unsigned long addr)
+{
+	register unsigned long result;
+
+	__asm__ __volatile__ (
+		 "ldwu %0,%1"
+		 : "=r" (result)
+		 : "m"  (*(unsigned short *)(addr+MCPCIA_BW_MEM)));
+
+	return result;
+}
+
+extern inline unsigned long __readl(unsigned long addr)
+{
+	register unsigned long result;
+
+	__asm__ __volatile__ (
+		 "ldl %0,%1"
+		 : "=r" (result)
+		 : "m"  (*(unsigned int *)(addr+MCPCIA_BW_MEM)));
+
+	return result;
+}
+
+extern inline void __writeb(unsigned char b, unsigned long addr)
+{
+	__asm__ __volatile__ (
+		 "stb %1,%0\n\t"
+		 "mb"
+		 : : "m" (*(unsigned char *)(addr+MCPCIA_BW_MEM)), "r" (b));
+}
+
+extern inline void __writew(unsigned short b, unsigned long addr)
+{
+	__asm__ __volatile__ (
+		 "stw %1,%0\n\t"
+		 "mb"
+		 : : "m" (*(unsigned short *)(addr+MCPCIA_BW_MEM)), "r" (b));
+}
+
+extern inline void __writel(unsigned int b, unsigned long addr)
+{
+	__asm__ __volatile__ (
+		 "stl %1,%0\n\t"
+		 "mb"
+		 : : "m" (*(unsigned int *)(addr+MCPCIA_BW_MEM)), "r" (b));
+}
+
+#define readb(addr) __readb((addr))
+#define readw(addr) __readw((addr))
+
+#define writeb(b, addr) __writeb((b),(addr))
+#define writew(b, addr) __writew((b),(addr))
+
+#else /* BWIO_ENABLED */
+
+#ifdef CONFIG_ALPHA_SRM_SETUP
+
+extern unsigned long mcpcia_sm_base_r1, mcpcia_sm_base_r2, mcpcia_sm_base_r3;
+
+extern inline unsigned long __readb(unsigned long addr)
+{
+	unsigned long result, shift, work;
+
+	if ((addr >= mcpcia_sm_base_r1) &&
+	    (addr <= (mcpcia_sm_base_r1 + MEM_R1_MASK)))
+	  work = (((addr & MEM_R1_MASK) << 5) + MCPCIA_SPARSE_MEM + 0x00);
+	else
+	if ((addr >= mcpcia_sm_base_r2) &&
+	    (addr <= (mcpcia_sm_base_r2 + MEM_R2_MASK)))
+	  work = (((addr & MEM_R2_MASK) << 5) + MCPCIA_SPARSE_MEM_R2 + 0x00);
+	else
+	if ((addr >= mcpcia_sm_base_r3) &&
+	    (addr <= (mcpcia_sm_base_r3 + MEM_R3_MASK)))
+	  work = (((addr & MEM_R3_MASK) << 5) + MCPCIA_SPARSE_MEM_R3 + 0x00);
+	else
+	{
+#if 0
+	  printk("__readb: address 0x%lx not covered by HAE\n", addr);
+#endif
+	  return 0x0ffUL;
+	}
+	shift = (addr & 0x3) << 3;
+	result = *(vuip) work;
+	result >>= shift;
+	return 0x0ffUL & result;
+}
+
+extern inline unsigned long __readw(unsigned long addr)
+{
+	unsigned long result, shift, work;
+
+	if ((addr >= mcpcia_sm_base_r1) &&
+	    (addr <= (mcpcia_sm_base_r1 + MEM_R1_MASK)))
+	  work = (((addr & MEM_R1_MASK) << 5) + MCPCIA_SPARSE_MEM + 0x08);
+	else
+	if ((addr >= mcpcia_sm_base_r2) &&
+	    (addr <= (mcpcia_sm_base_r2 + MEM_R2_MASK)))
+	  work = (((addr & MEM_R2_MASK) << 5) + MCPCIA_SPARSE_MEM_R2 + 0x08);
+	else
+	if ((addr >= mcpcia_sm_base_r3) &&
+	    (addr <= (mcpcia_sm_base_r3 + MEM_R3_MASK)))
+	  work = (((addr & MEM_R3_MASK) << 5) + MCPCIA_SPARSE_MEM_R3 + 0x08);
+	else
+	{
+#if 0
+	  printk("__readw: address 0x%lx not covered by HAE\n", addr);
+#endif
+	  return 0x0ffffUL;
+	}
+	shift = (addr & 0x3) << 3;
+	result = *(vuip) work;
+	result >>= shift;
+	return 0x0ffffUL & result;
+}
+
+extern inline void __writeb(unsigned char b, unsigned long addr)
+{
+	unsigned long work;
+
+	if ((addr >= mcpcia_sm_base_r1) &&
+	    (addr <= (mcpcia_sm_base_r1 + MEM_R1_MASK)))
+	  work = (((addr & MEM_R1_MASK) << 5) + MCPCIA_SPARSE_MEM + 0x00);
+	else
+	if ((addr >= mcpcia_sm_base_r2) &&
+	    (addr <= (mcpcia_sm_base_r2 + MEM_R2_MASK)))
+	  work = (((addr & MEM_R2_MASK) << 5) + MCPCIA_SPARSE_MEM_R2 + 0x00);
+	else
+	if ((addr >= mcpcia_sm_base_r3) &&
+	    (addr <= (mcpcia_sm_base_r3 + MEM_R3_MASK)))
+	  work = (((addr & MEM_R3_MASK) << 5) + MCPCIA_SPARSE_MEM_R3 + 0x00);
+	else
+	{
+#if 0
+	  printk("__writeb: address 0x%lx not covered by HAE\n", addr);
+#endif
+	  return;
+	}
+	*(vuip) work = b * 0x01010101;
+}
+
+extern inline void __writew(unsigned short b, unsigned long addr)
+{
+	unsigned long work;
+
+	if ((addr >= mcpcia_sm_base_r1) &&
+	    (addr <= (mcpcia_sm_base_r1 + MEM_R1_MASK)))
+	  work = (((addr & MEM_R1_MASK) << 5) + MCPCIA_SPARSE_MEM + 0x00);
+	else
+	if ((addr >= mcpcia_sm_base_r2) &&
+	    (addr <= (mcpcia_sm_base_r2 + MEM_R2_MASK)))
+	  work = (((addr & MEM_R2_MASK) << 5) + MCPCIA_SPARSE_MEM_R2 + 0x00);
+	else
+	if ((addr >= mcpcia_sm_base_r3) &&
+	    (addr <= (mcpcia_sm_base_r3 + MEM_R3_MASK)))
+	  work = (((addr & MEM_R3_MASK) << 5) + MCPCIA_SPARSE_MEM_R3 + 0x00);
+	else
+	{
+#if 0
+	  printk("__writew: address 0x%lx not covered by HAE\n", addr);
+#endif
+	  return;
+	}
+	*(vuip) work = b * 0x00010001;
+}
+
+#else /* SRM_SETUP */
+
+extern inline unsigned long __readb(unsigned long in_addr)
+{
+	unsigned long addr = in_addr & 0xffffffffUL;
+	unsigned long hose = (in_addr >> 32) & 3;
+	unsigned long result, shift, msb, work, temp;
+
+	shift = (addr & 0x3) << 3;
+	msb = addr & 0xE0000000UL;
+	temp = addr & MEM_R1_MASK;
+	if (msb != hae.cache) {
+	  set_hae(msb);
+	}
+	work = ((temp << 5) + MCPCIA_SPARSE(hose) + 0x00);
+	result = *(vuip) work;
+	result >>= shift;
+	return 0x0ffUL & result;
+}
+
+extern inline unsigned long __readw(unsigned long in_addr)
+{
+	unsigned long addr = in_addr & 0xffffffffUL;
+	unsigned long hose = (in_addr >> 32) & 3;
+	unsigned long result, shift, msb, work, temp;
+
+	shift = (addr & 0x3) << 3;
+	msb = addr & 0xE0000000UL;
+	temp = addr & MEM_R1_MASK ;
+	if (msb != hae.cache) {
+	  set_hae(msb);
+	}
+	work = ((temp << 5) + MCPCIA_SPARSE(hose) + 0x08);
+	result = *(vuip) work;
+	result >>= shift;
+	return 0x0ffffUL & result;
+}
+
+extern inline void __writeb(unsigned char b, unsigned long in_addr)
+{
+	unsigned long addr = in_addr & 0xffffffffUL;
+	unsigned long hose = (in_addr >> 32) & 3;
+        unsigned long msb; 
+
+	msb = addr & 0xE0000000;
+	addr &= MEM_R1_MASK;
+	if (msb != hae.cache) {
+	  set_hae(msb);
+	}
+	*(vuip) ((addr << 5) + MCPCIA_SPARSE(hose) + 0x00) = b * 0x01010101;
+}
+
+extern inline void __writew(unsigned short b, unsigned long in_addr)
+{
+	unsigned long addr = in_addr & 0xffffffffUL;
+	unsigned long hose = (in_addr >> 32) & 3;
+        unsigned long msb ; 
+
+	msb = addr & 0xE0000000 ;
+	addr &= MEM_R1_MASK ;
+	if (msb != hae.cache) {
+	  set_hae(msb);
+	}
+	*(vuip) ((addr << 5) + MCPCIA_SPARSE(hose) + 0x08) = b * 0x00010001;
+}
+#endif /* SRM_SETUP */
+
+extern inline unsigned long __readl(unsigned long in_addr)
+{
+	unsigned long addr = in_addr & 0xffffffffUL;
+	unsigned long hose = (in_addr >> 32) & 3;
+	return *(vuip) (addr + MCPCIA_DENSE(hose));
+}
+
+extern inline void __writel(unsigned int b, unsigned long in_addr)
+{
+	unsigned long addr = in_addr & 0xffffffffUL;
+	unsigned long hose = (in_addr >> 32) & 3;
+	*(vuip) (addr + MCPCIA_DENSE(hose)) = b;
+}
+
+#endif /* BWIO_ENABLED */
+
+#define readl(a)	__readl((unsigned long)(a))
+#define writel(v,a)	__writel((v),(unsigned long)(a))
+
+#undef vuip
+
+struct linux_hose_info {
+        struct pci_bus                  pci_bus;
+	struct linux_hose_info         *next;
+	unsigned long                   pci_io_space;
+	unsigned long                   pci_mem_space;
+	unsigned long                   pci_config_space;
+	unsigned long                   pci_sparse_space;
+        unsigned int                    pci_first_busno;
+        unsigned int                    pci_last_busno;
+	unsigned int                    pci_hose_index;
+};
+
+extern unsigned long mcpcia_init (unsigned long mem_start,
+				 unsigned long mem_end);
+extern unsigned long mcpcia_fixup (unsigned long mem_start,
+				 unsigned long mem_end);
+
+#endif /* __KERNEL__ */
+
+/*
+ * Data structure for handling MCPCIA machine checks:
+ */
+struct el_MCPCIA_uncorrected_frame_mcheck {
+        struct el_common header;
+        struct el_common_EV5_uncorrectable_mcheck procdata;
+};
+
+#define RTC_PORT(x)	(0x70 + (x))
+#define RTC_ADDR(x)	(0x80 | (x))
+#define RTC_ALWAYS_BCD	0
+
+#endif /* __ALPHA_MCPCIA__H__ */
diff -u --recursive --new-file v2.1.91/linux/include/asm-alpha/mmu_context.h linux/include/asm-alpha/mmu_context.h
--- v2.1.91/linux/include/asm-alpha/mmu_context.h	Mon Dec 30 03:03:12 1996
+++ linux/include/asm-alpha/mmu_context.h	Mon Mar 30 00:21:40 1998
@@ -39,28 +39,33 @@
 #define BROKEN_ASN 1
 #endif
 
+#ifdef __SMP__
+#define WIDTH_THIS_PROCESSOR	5
+/*
+ * last_asn[processor]:
+ * 63                                            0
+ * +-------------+----------------+--------------+
+ * | asn version | this processor | hardware asn |
+ * +-------------+----------------+--------------+
+ */
+extern unsigned long last_asn[];
+#define asn_cache last_asn[p->processor]
+
+#else
+#define WIDTH_THIS_PROCESSOR	0
+/*
+ * asn_cache:
+ * 63                                            0
+ * +------------------------------+--------------+
+ * |         asn version          | hardware asn |
+ * +------------------------------+--------------+
+ */
 extern unsigned long asn_cache;
+#endif /* __SMP__ */
 
-#define ASN_VERSION_SHIFT 16
-#define ASN_VERSION_MASK ((~0UL) << ASN_VERSION_SHIFT)
-#define ASN_FIRST_VERSION (1UL << ASN_VERSION_SHIFT)
-
-extern inline void get_new_mmu_context(struct task_struct *p,
-	struct mm_struct *mm,
-	unsigned long asn)
-{
-	/* check if it's legal.. */
-	if ((asn & ~ASN_VERSION_MASK) > MAX_ASN) {
-		/* start a new version, invalidate all old asn's */
-		tbiap(); imb();
-		asn = (asn & ASN_VERSION_MASK) + ASN_FIRST_VERSION;
-		if (!asn)
-			asn = ASN_FIRST_VERSION;
-	}
-	asn_cache = asn + 1;
-	mm->context = asn;			/* full version + asn */
-	p->tss.asn = asn & ~ASN_VERSION_MASK;	/* just asn */
-}
+#define WIDTH_HARDWARE_ASN	7
+#define ASN_FIRST_VERSION (1UL << (WIDTH_THIS_PROCESSOR + WIDTH_HARDWARE_ASN))
+#define HARDWARE_ASN_MASK ((1UL << WIDTH_HARDWARE_ASN) - 1)
 
 /*
  * NOTE! The way this is set up, the high bits of the "asn_cache" (and
@@ -73,6 +78,23 @@
  * force a new asn for any other processes the next time they want to
  * run.
  */
+extern inline void
+get_new_mmu_context(struct task_struct *p, struct mm_struct *mm)
+{
+	unsigned long asn = asn_cache;
+
+	if ((asn & HARDWARE_ASN_MASK) < MAX_ASN)
+		++asn;
+	else {
+		tbiap();
+		imb();
+		asn = (asn & ~HARDWARE_ASN_MASK) + ASN_FIRST_VERSION;
+	}
+	asn_cache = asn;
+	mm->context = asn;			/* full version + asn */
+	p->tss.asn = asn & HARDWARE_ASN_MASK;	/* just asn */
+}
+
 extern inline void get_mmu_context(struct task_struct *p)
 {
 #ifndef BROKEN_ASN
@@ -81,8 +103,8 @@
 	if (mm) {
 		unsigned long asn = asn_cache;
 		/* Check if our ASN is of an older version and thus invalid */
-		if ((mm->context ^ asn) & ASN_VERSION_MASK)
-			get_new_mmu_context(p, mm, asn);
+		if ((mm->context ^ asn) & ~HARDWARE_ASN_MASK)
+			get_new_mmu_context(p, mm);
 	}
 #endif
 }
@@ -91,3 +113,4 @@
 #define destroy_context(mm)	do { } while(0)
 
 #endif
+
diff -u --recursive --new-file v2.1.91/linux/include/asm-alpha/pgtable.h linux/include/asm-alpha/pgtable.h
--- v2.1.91/linux/include/asm-alpha/pgtable.h	Sat Sep  6 10:10:48 1997
+++ linux/include/asm-alpha/pgtable.h	Mon Mar 30 00:21:41 1998
@@ -8,6 +8,7 @@
  * This hopefully works with any standard alpha page-size, as defined
  * in <asm/page.h> (currently 8192).
  */
+#include <linux/config.h>
 
 #include <asm/system.h>
 #include <asm/mmu_context.h>
@@ -29,6 +30,9 @@
 {
 	__asm__ __volatile__(
 		"bis %0,%0,$16\n\t"
+#ifdef CONFIG_ALPHA_DP264
+		"zap $16,0xe0,$16\n\t"
+#endif /* DP264 */
 		"call_pal %1"
 		: /* no outputs */
 		: "r" (&task->tss), "i" (PAL_swpctx)
@@ -80,6 +84,7 @@
 	flush_tlb_current(current->mm);
 }
 
+#ifndef __SMP__
 /*
  * Flush everything (kernel mapping may also have
  * changed due to vmalloc/vfree)
@@ -128,6 +133,28 @@
 {
 	flush_tlb_mm(mm);
 }
+
+#else /* __SMP__ */
+
+/* ipi_msg_flush_tb is owned by the holder of the global kernel lock. */
+struct ipi_msg_flush_tb_struct {
+	volatile unsigned int flush_tb_mask;
+	union {
+		struct mm_struct *	flush_mm;
+		struct vm_area_struct *	flush_vma;
+	} p;
+	unsigned long flush_addr;
+  /*	unsigned long flush_end; */ /* not used by local_flush_tlb_range */
+};
+
+extern struct ipi_msg_flush_tb_struct ipi_msg_flush_tb;
+
+extern void flush_tlb_all(void);
+extern void flush_tlb_mm(struct mm_struct *);
+extern void flush_tlb_page(struct vm_area_struct *, unsigned long);
+extern void flush_tlb_range(struct mm_struct *, unsigned long, unsigned long);
+
+#endif /* __SMP__ */
 
 /* Certain architectures need to do special things when pte's
  * within a page table are directly modified.  Thus, the following
diff -u --recursive --new-file v2.1.91/linux/include/asm-alpha/pyxis.h linux/include/asm-alpha/pyxis.h
--- v2.1.91/linux/include/asm-alpha/pyxis.h	Tue Mar 10 10:03:34 1998
+++ linux/include/asm-alpha/pyxis.h	Mon Mar 30 00:21:41 1998
@@ -1,11 +1,11 @@
 #ifndef __ALPHA_PYXIS__H__
 #define __ALPHA_PYXIS__H__
 
-#include <linux/config.h> /* CONFIG_ALPHA_RUFFIAN. */
+#include <linux/config.h>
 #include <linux/types.h>
 
 /*
- * PYXIS is the internal name for a cor logic chipset which provides
+ * PYXIS is the internal name for a core logic chipset which provides
  * memory controller and PCI access for the 21164A chip based systems.
  *
  * This file is based on:
@@ -71,11 +71,23 @@
 
 #define BYTE_ENABLE_SHIFT 5
 #define TRANSFER_LENGTH_SHIFT 3
-#define MEM_SP1_MASK 0x1fffffff  /* Mem sparse space 1 mask is 29 bits */
 
+#define MEM_R1_MASK 0x1fffffff  /* SPARSE Mem region 1 mask is 29 bits */
+#define MEM_R2_MASK 0x07ffffff  /* SPARSE Mem region 2 mask is 27 bits */
+#define MEM_R3_MASK 0x03ffffff  /* SPARSE Mem region 3 mask is 26 bits */
+
+#ifdef CONFIG_ALPHA_SRM_SETUP
+/* if we are using the SRM PCI setup, we'll need to use variables instead */
+#define PYXIS_DMA_WIN_BASE_DEFAULT    (1024*1024*1024)
+#define PYXIS_DMA_WIN_SIZE_DEFAULT    (1024*1024*1024)
 
-#define PYXIS_DMA_WIN_BASE	(1024UL*1024UL*1024UL)
+extern unsigned int PYXIS_DMA_WIN_BASE;
+extern unsigned int PYXIS_DMA_WIN_SIZE;
+
+#else /* SRM_SETUP */
+#define PYXIS_DMA_WIN_BASE	(1024*1024*1024)
 #define PYXIS_DMA_WIN_SIZE	(1024*1024*1024)
+#endif /* SRM_SETUP */
 
 /*
  *  General Registers
@@ -97,13 +109,13 @@
 #define PYXIS_DIAG_CHECK		(IDENT_ADDR + 0x8740003000UL)
 
 /*
- * Performance Monitor registers (p4-3)
+ * Performance Monitor registers
  */
 #define PYXIS_PERF_MONITOR		(IDENT_ADDR + 0x8740004000UL)
 #define PYXIS_PERF_CONTROL		(IDENT_ADDR + 0x8740004040UL)
 
 /*
- * 21171-CA Error registers (p4-3)
+ * Error registers
  */
 #define PYXIS_ERR			(IDENT_ADDR + 0x8740008200UL)
 #define PYXIS_STAT			(IDENT_ADDR + 0x8740008240UL)
@@ -118,18 +130,25 @@
 #define PYXIS_PCI_ERR2			(IDENT_ADDR + 0x8740008880UL)
 
 /*
- *  PCI Address Translation Registers.   I've only defined
- * the first window fully as that's the only one that we're currently using.
- * The other window bases are needed to disable the windows.
+ * PCI Address Translation Registers.
  */
 #define PYXIS_TBIA			(IDENT_ADDR + 0x8760000100UL)
+
 #define PYXIS_W0_BASE			(IDENT_ADDR + 0x8760000400UL)
 #define PYXIS_W0_MASK			(IDENT_ADDR + 0x8760000440UL)
 #define PYXIS_T0_BASE			(IDENT_ADDR + 0x8760000480UL)
 
 #define PYXIS_W1_BASE			(IDENT_ADDR + 0x8760000500UL)
+#define PYXIS_W1_MASK			(IDENT_ADDR + 0x8760000540UL)
+#define PYXIS_T1_BASE			(IDENT_ADDR + 0x8760000580UL)
+
 #define PYXIS_W2_BASE			(IDENT_ADDR + 0x8760000600UL)
+#define PYXIS_W2_MASK			(IDENT_ADDR + 0x8760000640UL)
+#define PYXIS_T2_BASE			(IDENT_ADDR + 0x8760000680UL)
+
 #define PYXIS_W3_BASE			(IDENT_ADDR + 0x8760000700UL)
+#define PYXIS_W3_MASK			(IDENT_ADDR + 0x8760000740UL)
+#define PYXIS_T3_BASE			(IDENT_ADDR + 0x8760000780UL)
 
 /*
  * Memory Control registers
@@ -143,9 +162,19 @@
 #define PYXIS_CONF		        (IDENT_ADDR + 0x8700000000UL)
 #define PYXIS_IO			(IDENT_ADDR + 0x8580000000UL)
 #define PYXIS_SPARSE_MEM		(IDENT_ADDR + 0x8000000000UL)
+#define PYXIS_SPARSE_MEM_R2		(IDENT_ADDR + 0x8400000000UL)
+#define PYXIS_SPARSE_MEM_R3		(IDENT_ADDR + 0x8500000000UL)
 #define PYXIS_DENSE_MEM		        (IDENT_ADDR + 0x8600000000UL)
 
 /*
+ * Byte/Word PCI Memory Spaces:
+ */
+#define PYXIS_BW_MEM			(IDENT_ADDR + 0x8800000000UL)
+#define PYXIS_BW_IO			(IDENT_ADDR + 0x8900000000UL)
+#define PYXIS_BW_CFG_0			(IDENT_ADDR + 0x8a00000000UL)
+#define PYXIS_BW_CFG_1			(IDENT_ADDR + 0x8b00000000UL)
+
+/*
  * Interrupt Control registers
  */
 #define PYXIS_INT_REQ			(IDENT_ADDR + 0x87A0000000UL)
@@ -179,9 +208,8 @@
  * Translate physical memory address as seen on (PCI) bus into
  * a kernel virtual address and vv.
  */
-
 #if defined(CONFIG_ALPHA_RUFFIAN)
-/* Ruffian doesn't do 1G PCI window.  */
+/* Ruffian doesn't do 1G PCI window */
 
 extern inline unsigned long virt_to_bus(void * address)
 {
@@ -192,7 +220,7 @@
 {
 	return phys_to_virt(address);
 }
-#else
+#else /* RUFFIAN */
 extern inline unsigned long virt_to_bus(void * address)
 {
 	return virt_to_phys(address) + PYXIS_DMA_WIN_BASE;
@@ -207,13 +235,86 @@
 /*
  * I/O functions:
  *
- * PYXIS (the 2117x PCI/memory support chipset for the EV5 (21164)
- * series of processors uses a sparse address mapping scheme to
+ * PYXIS, the 21174 PCI/memory support chipset for the EV56 (21164A)
+ * and PCA56 (21164PC) processors, can use either a sparse address
+ * mapping scheme, or the so-called byte-word PCI address space, to
  * get at PCI memory and I/O.
  */
 
 #define vuip	volatile unsigned int *
 
+#ifdef BWIO_ENABLED
+
+extern inline unsigned int __inb(unsigned long addr)
+{
+	register unsigned long result;
+
+	__asm__ __volatile__ (
+		 "ldbu %0,%1"
+		 : "=r" (result)
+		 : "m"  (*(unsigned char *)(addr+PYXIS_BW_IO)));
+
+	return result;
+}
+
+extern inline void __outb(unsigned char b, unsigned long addr)
+{
+	__asm__ __volatile__ (
+		 "stb %1,%0\n\t"
+		 "mb"
+		 : : "m" (*(unsigned char *)(addr+PYXIS_BW_IO)), "r" (b));
+}
+
+extern inline unsigned int __inw(unsigned long addr)
+{
+	register unsigned long result;
+
+	__asm__ __volatile__ (
+		 "ldwu %0,%1"
+		 : "=r" (result)
+		 : "m"  (*(unsigned short *)(addr+PYXIS_BW_IO)));
+
+	return result;
+}
+
+extern inline void __outw(unsigned short b, unsigned long addr)
+{
+	__asm__ __volatile__ (
+		 "stw %1,%0\n\t"
+		 "mb"
+		 : : "m" (*(unsigned short *)(addr+PYXIS_BW_IO)), "r" (b));
+}
+
+extern inline unsigned int __inl(unsigned long addr)
+{
+	register unsigned long result;
+
+	__asm__ __volatile__ (
+		 "ldl %0,%1"
+		 : "=r" (result)
+		 : "m"  (*(unsigned int *)(addr+PYXIS_BW_IO)));
+
+	return result;
+}
+
+extern inline void __outl(unsigned int b, unsigned long addr)
+{
+	__asm__ __volatile__ (
+		 "stl %1,%0\n\t"
+		 "mb"
+		 : : "m" (*(unsigned int *)(addr+PYXIS_BW_IO)), "r" (b));
+}
+
+#define inb(port) __inb((port))
+#define inw(port) __inw((port))
+#define inl(port) __inl((port))
+
+#define outb(x, port) __outb((x),(port))
+#define outw(x, port) __outw((x),(port))
+#define outl(x, port) __outl((x),(port))
+
+#else /* BWIO_ENABLED */
+
 extern inline unsigned int __inb(unsigned long addr)
 {
 	long result = *(vuip) ((addr << 5) + PYXIS_IO + 0x00);
@@ -257,6 +358,14 @@
 	mb();
 }
 
+#define inb(port) \
+(__builtin_constant_p((port))?__inb(port):_inb(port))
+
+#define outb(x, port) \
+(__builtin_constant_p((port))?__outb((x),(port)):_outb((x),(port)))
+
+#endif /* BWIO_ENABLED */
+
 
 /*
  * Memory functions.  64-bit and 32-bit accesses are done through
@@ -290,25 +399,197 @@
  * 
  */
 
-extern inline void pyxis_set_hae(unsigned long new_hae)
+#ifdef BWIO_ENABLED
+
+extern inline unsigned long __readb(unsigned long addr)
+{
+	register unsigned long result;
+
+	__asm__ __volatile__ (
+		 "ldbu %0,%1"
+		 : "=r" (result)
+		 : "m"  (*(unsigned char *)(addr+PYXIS_BW_MEM)));
+
+	return result;
+}
+
+extern inline unsigned long __readw(unsigned long addr)
+{
+	register unsigned long result;
+
+	__asm__ __volatile__ (
+		 "ldwu %0,%1"
+		 : "=r" (result)
+		 : "m"  (*(unsigned short *)(addr+PYXIS_BW_MEM)));
+
+	return result;
+}
+
+extern inline unsigned long __readl(unsigned long addr)
+{
+	register unsigned long result;
+
+	__asm__ __volatile__ (
+		 "ldl %0,%1"
+		 : "=r" (result)
+		 : "m"  (*(unsigned int *)(addr+PYXIS_BW_MEM)));
+
+	return result;
+}
+
+extern inline void __writeb(unsigned char b, unsigned long addr)
 {
-        unsigned long ipl = swpipl(7);
-        hae.cache = new_hae;
-        *hae.reg = new_hae;
-        mb();
-	new_hae = *hae.reg; /* read it to be sure it got out */
-        setipl(ipl);
+	__asm__ __volatile__ (
+		 "stb %1,%0\n\t"
+		 "mb"
+		 : : "m" (*(unsigned char *)(addr+PYXIS_BW_MEM)), "r" (b));
 }
 
+extern inline void __writew(unsigned short b, unsigned long addr)
+{
+	__asm__ __volatile__ (
+		 "stw %1,%0\n\t"
+		 "mb"
+		 : : "m" (*(unsigned short *)(addr+PYXIS_BW_MEM)), "r" (b));
+}
+
+extern inline void __writel(unsigned int b, unsigned long addr)
+{
+	__asm__ __volatile__ (
+		 "stl %1,%0\n\t"
+		 "mb"
+		 : : "m" (*(unsigned int *)(addr+PYXIS_BW_MEM)), "r" (b));
+}
+
+#define readb(addr) __readb((addr))
+#define readw(addr) __readw((addr))
+
+#define writeb(b, addr) __writeb((b),(addr))
+#define writew(b, addr) __writew((b),(addr))
+
+#else /* BWIO_ENABLED */
+
+#ifdef CONFIG_ALPHA_SRM_SETUP
+
+extern unsigned long pyxis_sm_base_r1, pyxis_sm_base_r2, pyxis_sm_base_r3;
+
+extern inline unsigned long __readb(unsigned long addr)
+{
+	unsigned long result, shift, work;
+
+	if ((addr >= pyxis_sm_base_r1) &&
+	    (addr <= (pyxis_sm_base_r1 + MEM_R1_MASK)))
+	  work = (((addr & MEM_R1_MASK) << 5) + PYXIS_SPARSE_MEM + 0x00);
+	else
+	if ((addr >= pyxis_sm_base_r2) &&
+	    (addr <= (pyxis_sm_base_r2 + MEM_R2_MASK)))
+	  work = (((addr & MEM_R2_MASK) << 5) + PYXIS_SPARSE_MEM_R2 + 0x00);
+	else
+	if ((addr >= pyxis_sm_base_r3) &&
+	    (addr <= (pyxis_sm_base_r3 + MEM_R3_MASK)))
+	  work = (((addr & MEM_R3_MASK) << 5) + PYXIS_SPARSE_MEM_R3 + 0x00);
+	else
+	{
+#if 0
+	  printk("__readb: address 0x%lx not covered by HAE\n", addr);
+#endif
+	  return 0x0ffUL;
+	}
+	shift = (addr & 0x3) << 3;
+	result = *(vuip) work;
+	result >>= shift;
+	return 0x0ffUL & result;
+}
+
+extern inline unsigned long __readw(unsigned long addr)
+{
+	unsigned long result, shift, work;
+
+	if ((addr >= pyxis_sm_base_r1) &&
+	    (addr <= (pyxis_sm_base_r1 + MEM_R1_MASK)))
+	  work = (((addr & MEM_R1_MASK) << 5) + PYXIS_SPARSE_MEM + 0x08);
+	else
+	if ((addr >= pyxis_sm_base_r2) &&
+	    (addr <= (pyxis_sm_base_r2 + MEM_R2_MASK)))
+	  work = (((addr & MEM_R2_MASK) << 5) + PYXIS_SPARSE_MEM_R2 + 0x08);
+	else
+	if ((addr >= pyxis_sm_base_r3) &&
+	    (addr <= (pyxis_sm_base_r3 + MEM_R3_MASK)))
+	  work = (((addr & MEM_R3_MASK) << 5) + PYXIS_SPARSE_MEM_R3 + 0x08);
+	else
+	{
+#if 0
+	  printk("__readw: address 0x%lx not covered by HAE\n", addr);
+#endif
+	  return 0x0ffffUL;
+	}
+	shift = (addr & 0x3) << 3;
+	result = *(vuip) work;
+	result >>= shift;
+	return 0x0ffffUL & result;
+}
+
+extern inline void __writeb(unsigned char b, unsigned long addr)
+{
+	unsigned long work;
+
+	if ((addr >= pyxis_sm_base_r1) &&
+	    (addr <= (pyxis_sm_base_r1 + MEM_R1_MASK)))
+	  work = (((addr & MEM_R1_MASK) << 5) + PYXIS_SPARSE_MEM + 0x00);
+	else
+	if ((addr >= pyxis_sm_base_r2) &&
+	    (addr <= (pyxis_sm_base_r2 + MEM_R2_MASK)))
+	  work = (((addr & MEM_R2_MASK) << 5) + PYXIS_SPARSE_MEM_R2 + 0x00);
+	else
+	if ((addr >= pyxis_sm_base_r3) &&
+	    (addr <= (pyxis_sm_base_r3 + MEM_R3_MASK)))
+	  work = (((addr & MEM_R3_MASK) << 5) + PYXIS_SPARSE_MEM_R3 + 0x00);
+	else
+	{
+#if 0
+	  printk("__writeb: address 0x%lx not covered by HAE\n", addr);
+#endif
+	  return;
+	}
+	*(vuip) work = b * 0x01010101;
+}
+
+extern inline void __writew(unsigned short b, unsigned long addr)
+{
+	unsigned long work;
+
+	if ((addr >= pyxis_sm_base_r1) &&
+	    (addr <= (pyxis_sm_base_r1 + MEM_R1_MASK)))
+	  work = (((addr & MEM_R1_MASK) << 5) + PYXIS_SPARSE_MEM + 0x00);
+	else
+	if ((addr >= pyxis_sm_base_r2) &&
+	    (addr <= (pyxis_sm_base_r2 + MEM_R2_MASK)))
+	  work = (((addr & MEM_R2_MASK) << 5) + PYXIS_SPARSE_MEM_R2 + 0x00);
+	else
+	if ((addr >= pyxis_sm_base_r3) &&
+	    (addr <= (pyxis_sm_base_r3 + MEM_R3_MASK)))
+	  work = (((addr & MEM_R3_MASK) << 5) + PYXIS_SPARSE_MEM_R3 + 0x00);
+	else
+	{
+#if 0
+	  printk("__writew: address 0x%lx not covered by HAE\n", addr);
+#endif
+	  return;
+	}
+	*(vuip) work = b * 0x00010001;
+}
+
+#else /* SRM_SETUP */
+
 extern inline unsigned long __readb(unsigned long addr)
 {
 	unsigned long result, shift, msb, work, temp;
 
 	shift = (addr & 0x3) << 3;
 	msb = addr & 0xE0000000UL;
-	temp = addr & MEM_SP1_MASK ;
+	temp = addr & MEM_R1_MASK ;
 	if (msb != hae.cache) {
-	  pyxis_set_hae(msb);
+	  set_hae(msb);
 	}
 	work = ((temp << 5) + PYXIS_SPARSE_MEM + 0x00);
 	result = *(vuip) work;
@@ -322,9 +603,9 @@
 
 	shift = (addr & 0x3) << 3;
 	msb = addr & 0xE0000000UL;
-	temp = addr & MEM_SP1_MASK ;
+	temp = addr & MEM_R1_MASK ;
 	if (msb != hae.cache) {
-	  pyxis_set_hae(msb);
+	  set_hae(msb);
 	}
 	work = ((temp << 5) + PYXIS_SPARSE_MEM + 0x08);
 	result = *(vuip) work;
@@ -332,19 +613,14 @@
 	return 0x0ffffUL & result;
 }
 
-extern inline unsigned long __readl(unsigned long addr)
-{
-	return *(vuip) (addr + PYXIS_DENSE_MEM);
-}
-
 extern inline void __writeb(unsigned char b, unsigned long addr)
 {
         unsigned long msb ; 
 
 	msb = addr & 0xE0000000 ;
-	addr &= MEM_SP1_MASK ;
+	addr &= MEM_R1_MASK ;
 	if (msb != hae.cache) {
-	  pyxis_set_hae(msb);
+	  set_hae(msb);
 	}
 	*(vuip) ((addr << 5) + PYXIS_SPARSE_MEM + 0x00) = b * 0x01010101;
 }
@@ -354,23 +630,25 @@
         unsigned long msb ; 
 
 	msb = addr & 0xE0000000 ;
-	addr &= MEM_SP1_MASK ;
+	addr &= MEM_R1_MASK ;
 	if (msb != hae.cache) {
-	  pyxis_set_hae(msb);
+	  set_hae(msb);
 	}
 	*(vuip) ((addr << 5) + PYXIS_SPARSE_MEM + 0x08) = b * 0x00010001;
 }
+#endif /* SRM_SETUP */
+
+extern inline unsigned long __readl(unsigned long addr)
+{
+	return *(vuip) (addr + PYXIS_DENSE_MEM);
+}
 
 extern inline void __writel(unsigned int b, unsigned long addr)
 {
 	*(vuip) (addr + PYXIS_DENSE_MEM) = b;
 }
 
-#define inb(port) \
-(__builtin_constant_p((port))?__inb(port):_inb(port))
-
-#define outb(x, port) \
-(__builtin_constant_p((port))?__outb((x),(port)):_outb((x),(port)))
+#endif /* BWIO_ENABLED */
 
 #define readl(a)	__readl((unsigned long)(a))
 #define writel(v,a)	__writel((v),(unsigned long)(a))
diff -u --recursive --new-file v2.1.91/linux/include/asm-alpha/smp.h linux/include/asm-alpha/smp.h
--- v2.1.91/linux/include/asm-alpha/smp.h	Fri Jan 30 11:28:09 1998
+++ linux/include/asm-alpha/smp.h	Mon Mar 30 00:21:41 1998
@@ -3,6 +3,51 @@
 
 #define cpu_logical_map(cpu)	(cpu)
 
-/* We'll get here eventually.. */
+#ifdef __SMP__
+
+#include <linux/tasks.h>
+
+struct cpuinfo_alpha {
+	unsigned long loops_per_sec;
+	unsigned int next;
+};
+
+extern struct cpuinfo_alpha cpu_data[NR_CPUS];
+
+typedef volatile struct {
+  unsigned int kernel_flag; /* 4 bytes, please */
+  unsigned int akp; /* 4 bytes, please */
+  unsigned long pc;
+  unsigned int cpu;
+} klock_info_t;
+
+extern klock_info_t klock_info;
+
+#define KLOCK_HELD	0xff
+#define KLOCK_CLEAR	0x00
+
+extern int task_lock_depth;
+
+#define PROC_CHANGE_PENALTY     20
+
+extern __volatile__ int cpu_number_map[NR_CPUS];
+
+/* HACK: Cabrio WHAMI return value is bogus if more than 8 bits used.. :-( */
+#define hard_smp_processor_id() \
+({ \
+	register unsigned char __r0 __asm__("$0"); \
+	__asm__ __volatile__( \
+		"call_pal %0" \
+		: /* no output (bound to the template) */ \
+		:"i" (PAL_whami) \
+		:"$0", "$1", "$22", "$23", "$24", "$25", "memory"); \
+	__r0; \
+})
+
+#define smp_processor_id() hard_smp_processor_id()
+
+#endif /* __SMP__ */
+
+#define NO_PROC_ID	(-1)
 
 #endif
diff -u --recursive --new-file v2.1.91/linux/include/asm-alpha/smp_lock.h linux/include/asm-alpha/smp_lock.h
--- v2.1.91/linux/include/asm-alpha/smp_lock.h	Wed Apr 23 19:01:27 1997
+++ linux/include/asm-alpha/smp_lock.h	Mon Mar 30 00:21:41 1998
@@ -10,7 +10,110 @@
 
 #else
 
-#error "We do not support SMP on alpha yet"
+#include <asm/system.h>
+#include <asm/current.h>
+#include <asm/bitops.h>
+#include <asm/hardirq.h>
+
+#define kernel_lock_held() \
+  (klock_info.kernel_flag && (klock_info.akp == smp_processor_id()))
+
+/* Release global kernel lock and global interrupt lock */
+#define release_kernel_lock(task, cpu, depth)		\
+do {							\
+	if ((depth = (task)->lock_depth) != 0) {	\
+		__cli();				\
+		(task)->lock_depth = 0;			\
+		klock_info.akp = NO_PROC_ID;		\
+		klock_info.kernel_flag = 0;		\
+		mb();					\
+	}						\
+	release_irqlock(cpu);				\
+	__sti();					\
+} while (0)
+
+#if 1
+#define DEBUG_KERNEL_LOCK
+#else
+#undef DEBUG_KERNEL_LOCK
+#endif
+
+#ifdef DEBUG_KERNEL_LOCK
+extern void ___lock_kernel(klock_info_t *klip, int cpu, long ipl);
+#else /* DEBUG_KERNEL_LOCK */
+static inline void ___lock_kernel(klock_info_t *klip, int cpu, long ipl)
+{
+	long regx;
+
+	__asm__ __volatile__(
+	"1:	ldl_l	%1,%0;"
+	"	blbs	%1,6f;"
+	"	or	%1,1,%1;"
+	"	stl_c	%1,%0;"
+	"	beq	%1,6f;"
+	"4:	mb\n"
+	".section .text2,\"ax\"\n"
+	"6:	mov %4,$16;"
+	"	call_pal %3;"
+	"7:	ldl %1,%0;"
+	"	blbs %1,7b;"
+	"	bis $31,7,$16;"
+	"	call_pal %3;"
+	"	br 1b\n"
+	".previous"
+	: "=m,=m" (__dummy_lock(klip)), "=&r,=&r" (regx)
+	: "0,0" (__dummy_lock(klip)), "i,i" (PAL_swpipl), "i,r" (ipl)
+	: "$0", "$1", "$16", "$22", "$23", "$24", "$25", "memory"
+	);
+}
+#endif /* DEBUG_KERNEL_LOCK */
+
+#define reacquire_kernel_lock(task, cpu, depth)	\
+do {						\
+  if (depth) {					\
+    long ipl;					\
+    klock_info_t *klip = &klock_info;		\
+    __save_and_cli(ipl);			\
+    ___lock_kernel(klip, cpu, ipl);		\
+    klip->akp = cpu;				\
+    (task)->lock_depth = depth;			\
+    __restore_flags(ipl);			\
+  }						\
+} while (0)
+
+/* The following acquire and release the master kernel global lock,
+ * the idea is that the usage of this mechanmism becomes less and less
+ * as time goes on, to the point where they are no longer needed at all
+ * and can thus disappear.
+ */
+
+#define lock_kernel()				\
+if (current->lock_depth > 0) {			\
+     ++current->lock_depth;			\
+} else {					\
+  long ipl;					\
+  int cpu = smp_processor_id();			\
+  klock_info_t *klip = &klock_info;		\
+  __save_and_cli(ipl);				\
+  ___lock_kernel(klip, cpu, ipl);		\
+  klip->akp = cpu;				\
+  current->lock_depth = 1;			\
+  __restore_flags(ipl);				\
+}
+
+/* Release kernel global lock. */
+#define unlock_kernel()				\
+if (current->lock_depth > 1) {			\
+  --current->lock_depth;			\
+} else {					\
+  long ipl;					\
+  __save_and_cli(ipl);				\
+  klock_info.akp = NO_PROC_ID;			\
+  klock_info.kernel_flag = KLOCK_CLEAR;		\
+  mb();						\
+  current->lock_depth = 0;			\
+  __restore_flags(ipl);				\
+}  
 
 #endif /* __SMP__ */
 
diff -u --recursive --new-file v2.1.91/linux/include/asm-alpha/softirq.h linux/include/asm-alpha/softirq.h
--- v2.1.91/linux/include/asm-alpha/softirq.h	Wed Feb  4 11:36:01 1998
+++ linux/include/asm-alpha/softirq.h	Mon Mar 30 00:21:41 1998
@@ -1,10 +1,9 @@
 #ifndef _ALPHA_SOFTIRQ_H
 #define _ALPHA_SOFTIRQ_H
 
-/* The locking mechanism for base handlers, to prevent re-entrancy,
- * is entirely private to an implementation, it should not be
- * referenced at all outside of this file.
- */
+#include <asm/atomic.h>
+#include <asm/hardirq.h>
+
 extern unsigned int local_bh_count[NR_CPUS];
 
 #define get_active_bhs()	(bh_mask & bh_active)
@@ -42,10 +41,50 @@
 	set_bit(nr, &bh_active);
 }
 
+#ifdef __SMP__
+
 /*
- * start_bh_atomic/end_bh_atomic also nest
- * naturally by using a counter
+ * The locking mechanism for base handlers, to prevent re-entrancy,
+ * is entirely private to an implementation, it should not be
+ * referenced at all outside of this file.
  */
+extern atomic_t global_bh_lock;
+extern atomic_t global_bh_count;
+
+extern void synchronize_bh(void);
+
+static inline void start_bh_atomic(void)
+{
+	atomic_inc(&global_bh_lock);
+	synchronize_bh();
+}
+
+static inline void end_bh_atomic(void)
+{
+	atomic_dec(&global_bh_lock);
+}
+
+/* These are for the irq's testing the lock */
+static inline int softirq_trylock(int cpu)
+{
+	if (!test_and_set_bit(0,&global_bh_count)) {
+		if (atomic_read(&global_bh_lock) == 0) {
+			++local_bh_count[cpu];
+			return 1;
+		}
+		clear_bit(0,&global_bh_count);
+	}
+	return 0;
+}
+
+static inline void softirq_endlock(int cpu)
+{
+	local_bh_count[cpu]--;
+	clear_bit(0,&global_bh_count);
+}
+
+#else
+
 extern inline void start_bh_atomic(void)
 {
 	local_bh_count[smp_processor_id()]++;
@@ -58,19 +97,16 @@
 	local_bh_count[smp_processor_id()]--;
 }
 
-#ifndef __SMP__
-
 /* These are for the irq's testing the lock */
 #define softirq_trylock(cpu) \
   (local_bh_count[cpu] ? 0 : (local_bh_count[cpu] = 1))
+
 #define softirq_endlock(cpu) \
   (local_bh_count[cpu] = 0)
 
-#else
-
-#error FIXME
+#define synchronize_bh()	do { } while (0)
 
-#endif /* __SMP__ */
+#endif	/* SMP */
 
 /*
  * These use a mask count to correctly handle
diff -u --recursive --new-file v2.1.91/linux/include/asm-alpha/spinlock.h linux/include/asm-alpha/spinlock.h
--- v2.1.91/linux/include/asm-alpha/spinlock.h	Tue May 13 22:41:17 1997
+++ linux/include/asm-alpha/spinlock.h	Mon Mar 30 00:21:41 1998
@@ -45,7 +45,10 @@
 #define write_lock_irqsave(lock, flags)		do { (flags) = swpipl(7); } while (0)
 #define write_unlock_irqrestore(lock, flags)	setipl(flags)
 
-#else
+#else /* __SMP__ */
+
+#include <linux/kernel.h>
+#include <asm/current.h>
 
 /* Simple spin lock operations.  There are two variants, one clears IRQ's
  * on the local processor, one does not.
@@ -56,12 +59,15 @@
 typedef struct {
 	volatile unsigned long lock;
 	unsigned long previous;
+	unsigned long task;
 } spinlock_t;
 
 #define SPIN_LOCK_UNLOCKED { 0, 0 }
 
-#define spin_lock_init(lock)	do { (lock)->lock = 0; (lock)->previous = 0; } while(0)
-#define spin_unlock_wait(lock)	do { barrier(); } while(((volatile spinlock_t *)lock)->lock)
+#define spin_lock_init(x) \
+	do { (x)->lock = 0; (x)->previous = 0; } while(0)
+#define spin_unlock_wait(x) \
+	do { barrier(); } while(((volatile spinlock_t *)x)->lock)
 
 typedef struct { unsigned long a[100]; } __dummy_lock_t;
 #define __dummy_lock(lock) (*(__dummy_lock_t *)(lock))
@@ -73,40 +79,38 @@
 	:"=m" (__dummy_lock(lock)));
 }
 
+#if 1
+#define DEBUG_SPINLOCK
+#else
+#undef DEBUG_SPINLOCK
+#endif
+
+#ifdef DEBUG_SPINLOCK
+extern void spin_lock(spinlock_t * lock);
+#else
 static inline void spin_lock(spinlock_t * lock)
 {
-	__label__ l1;
 	long tmp;
-	long stuck = 0x100000000;
-l1:
+
 	/* Use sub-sections to put the actual loop at the end
 	   of this object file's text section so as to perfect
 	   branch prediction.  */
 	__asm__ __volatile__(
 	"1:	ldq_l	%0,%1\n"
-	"	subq	%2,1,%2\n"
 	"	blbs	%0,2f\n"
 	"	or	%0,1,%0\n"
 	"	stq_c	%0,%1\n"
-	"	beq	%0,3f\n"
+	"	beq	%0,2f\n"
 	"4:	mb\n"
 	".section .text2,\"ax\"\n"
 	"2:	ldq	%0,%1\n"
-	"	subq	%2,1,%2\n"
-	"3:	blt	%2,4b\n"
 	"	blbs	%0,2b\n"
 	"	br	1b\n"
 	".previous"
 	: "=r" (tmp),
-	  "=m" (__dummy_lock(lock)),
-	  "=r" (stuck)
-	: "2" (stuck));
-
-	if (stuck < 0)
-		printk("spinlock stuck at %p (%lx)\n",&&l1,lock->previous);
-	else
-		lock->previous = (unsigned long) &&l1;
+	  "=m" (__dummy_lock(lock)));
 }
+#endif /* DEBUG_SPINLOCK */
 
 #define spin_trylock(lock) (!test_and_set_bit(0,(lock)))
 
@@ -117,10 +121,124 @@
 	do { spin_unlock(lock); __sti(); } while (0)
 
 #define spin_lock_irqsave(lock, flags) \
-	do { flags = swpipl(7); spin_lock(lock); } while (0)
+	do { __save_and_cli(flags); spin_lock(lock); } while (0)
 
 #define spin_unlock_irqrestore(lock, flags) \
-	do { spin_unlock(lock); setipl(flags); } while (0)
+	do { spin_unlock(lock); __restore_flags(flags); } while (0)
+
+/***********************************************************/
+
+#if 1
+#define DEBUG_RWLOCK
+#else
+#undef DEBUG_RWLOCK
+#endif
+
+typedef struct { volatile int write_lock:1, read_counter:31; } rwlock_t;
+
+#define RW_LOCK_UNLOCKED { 0, 0 }
+
+#ifdef DEBUG_RWLOCK
+extern void write_lock(rwlock_t * lock);
+#else
+static inline void write_lock(rwlock_t * lock)
+{
+	long regx, regy;
+
+	__asm__ __volatile__(
+	"1:	ldl_l	%1,%0;"
+	"	blbs	%1,6f;"
+	"	or	%1,1,%2;"
+	"	stl_c	%2,%0;"
+	"	beq	%2,6f;"
+	"	blt	%1,8f;"
+	"4:	mb\n"
+	".section .text2,\"ax\"\n"
+	"6:	ldl	%1,%0;"
+	"	blbs	%1,6b;"
+	"	br	1b;"
+	"8:	ldl	%1,%0;"
+	"	blt	%1,8b;"
+	"9:	br	4b\n"
+	".previous"
+	: "=m" (__dummy_lock(lock)), "=&r" (regx), "=&r" (regy)
+	: "0" (__dummy_lock(lock))
+	);
+}
+#endif /* DEBUG_RWLOCK */
+
+static inline void write_unlock(rwlock_t * lock)
+{
+	__asm__ __volatile__("mb; stl $31,%0" : "=m" (__dummy_lock(lock)));
+}
+
+#ifdef DEBUG_RWLOCK
+extern void _read_lock(rwlock_t * lock);
+#else
+static inline void _read_lock(rwlock_t * lock)
+{
+	long regx;
+
+	__asm__ __volatile__(
+	"1:	ldl_l	%1,%0;"
+	"	blbs	%1,6f;"
+	"	subl	%1,2,%1;"
+	"	stl_c	%1,%0;"
+	"	beq	%1,6f;"
+	"4:	mb\n"
+	".section .text2,\"ax\"\n"
+	"6:	ldl	%1,%0;"
+	"	blbs	%1,6b;"
+	"	br	1b\n"
+	".previous"
+	: "=m" (__dummy_lock(lock)), "=&r" (regx)
+	: "0" (__dummy_lock(lock))
+	);
+}
+#endif /* DEBUG_RWLOCK */
+
+#define read_lock(lock) \
+do {	unsigned long flags; \
+	__save_and_cli(flags); \
+	_read_lock(lock); \
+	__restore_flags(flags); \
+} while(0)
+
+static inline void _read_unlock(rwlock_t * lock)
+{
+	long regx;
+	__asm__ __volatile__(
+	"1:	ldl_l	%1,%0;"
+	"	addl	%1,2,%1;"
+	"	stl_c	%1,%0;"
+	"	beq	%1,6f;"
+	".section .text2,\"ax\"\n"
+	"6:	br	1b\n"
+	".previous"
+	: "=m" (__dummy_lock(lock)), "=&r" (regx)
+	: "0" (__dummy_lock(lock)));
+}
+
+#define read_unlock(lock) \
+do {	unsigned long flags; \
+	__save_and_cli(flags); \
+	_read_unlock(lock); \
+	__restore_flags(flags); \
+} while(0)
+
+#define read_lock_irq(lock)	do { __cli(); _read_lock(lock); } while (0)
+#define read_unlock_irq(lock)	do { _read_unlock(lock); __sti(); } while (0)
+#define write_lock_irq(lock)	do { __cli(); write_lock(lock); } while (0)
+#define write_unlock_irq(lock)	do { write_unlock(lock); __sti(); } while (0)
+
+#define read_lock_irqsave(lock, flags)	\
+	do { __save_and_cli(flags); _read_lock(lock); } while (0)
+#define read_unlock_irqrestore(lock, flags) \
+	do { _read_unlock(lock); __restore_flags(flags); } while (0)
+#define write_lock_irqsave(lock, flags)	\
+	do { __save_and_cli(flags); write_lock(lock); } while (0)
+#define write_unlock_irqrestore(lock, flags) \
+	do { write_unlock(lock); __restore_flags(flags); } while (0)
 
 #endif /* SMP */
 #endif /* _ALPHA_SPINLOCK_H */
diff -u --recursive --new-file v2.1.91/linux/include/asm-alpha/system.h linux/include/asm-alpha/system.h
--- v2.1.91/linux/include/asm-alpha/system.h	Sun Nov 30 10:59:03 1997
+++ linux/include/asm-alpha/system.h	Mon Mar 30 00:21:41 1998
@@ -39,13 +39,54 @@
  */
 struct el_common {
 	unsigned int	size;		/* size in bytes of logout area */
-	int		sbz1	: 31;	/* should be zero */
-	char		retry	:  1;	/* retry flag */
+	int		sbz1	: 30;	/* should be zero */
+	int		err2	:  1;	/* second error */
+	int		retry	:  1;	/* retry flag */
 	unsigned int	proc_offset;	/* processor-specific offset */
 	unsigned int	sys_offset;	/* system-specific offset */
 	unsigned long	code;		/* machine check code */
 };
 
+/* Machine Check Frame for uncorrectable errors (Large format)
+ *      --- This is used to log uncorrectable errors such as
+ *          double bit ECC errors.
+ *      --- These errors are detected by both processor and systems.
+ */
+struct el_common_EV5_uncorrectable_mcheck {
+        unsigned long   shadow[8];        /* Shadow reg. 8-14, 25           */
+        unsigned long   paltemp[24];      /* PAL TEMP REGS.                 */
+        unsigned long   exc_addr;         /* Address of excepting instruction*/
+        unsigned long   exc_sum;          /* Summary of arithmetic traps.   */
+        unsigned long   exc_mask;         /* Exception mask (from exc_sum). */
+        unsigned long   pal_base;         /* Base address for PALcode.      */
+        unsigned long   isr;              /* Interrupt Status Reg.          */
+        unsigned long   icsr;             /* CURRENT SETUP OF EV5 IBOX      */
+        unsigned long   ic_perr_stat;     /* I-CACHE Reg. <11> set Data parity
+                                                         <12> set TAG parity*/
+        unsigned long   dc_perr_stat;     /* D-CACHE error Reg. Bits set to 1:
+                                                     <2> Data error in bank 0
+                                                     <3> Data error in bank 1
+                                                     <4> Tag error in bank 0
+                                                     <5> Tag error in bank 1 */
+        unsigned long   va;               /* Effective VA of fault or miss. */
+        unsigned long   mm_stat;          /* Holds the reason for D-stream 
+                                             fault or D-cache parity errors */
+        unsigned long   sc_addr;          /* Address that was being accessed
+                                             when EV5 detected Secondary cache
+                                             failure.                 */
+        unsigned long   sc_stat;          /* Helps determine if the error was
+                                             TAG/Data parity(Secondary Cache)*/
+        unsigned long   bc_tag_addr;      /* Contents of EV5 BC_TAG_ADDR    */
+        unsigned long   ei_addr;          /* Physical address of any transfer
+                                             that is logged in EV5 EI_STAT */
+        unsigned long   fill_syndrome;    /* For correcting ECC errors.     */
+        unsigned long   ei_stat;          /* Helps identify reason of any 
+                                             processor uncorrectable error
+                                             at its external interface.     */
+        unsigned long   ld_lock;          /* Contents of EV5 LD_LOCK register*/
+};
+
+
 extern void wrent(void *, unsigned long);
 extern void wrkgp(unsigned long);
 extern void wrusp(unsigned long);
@@ -96,6 +137,7 @@
 	r0; \
 })
 
+#ifdef THE_OLD_VERSION
 #define setipl(ipl) \
 do { \
 	register unsigned long __r16 __asm__("$16") = (ipl); \
@@ -117,6 +159,27 @@
 		:"$1", "$22", "$23", "$24", "$25", "memory"); \
 	__r0; \
 })
+#else
+#define setipl(ipl) \
+do { \
+	__asm__ __volatile__( \
+		"mov %0,$16; call_pal %1" \
+		: /* no output */ \
+		:"i,r" (ipl), "i,i" (PAL_swpipl) \
+		:"$0", "$1", "$16", "$22", "$23", "$24", "$25", "memory"); \
+} while (0)
+
+#define swpipl(ipl) \
+({ \
+	register unsigned long __r0 __asm__("$0"); \
+	__asm__ __volatile__( \
+		"mov %0,$16; call_pal %1" \
+		: /* no output (bound to the template) */ \
+		: "i,r" (ipl), "i,i" (PAL_swpipl) \
+		: "$0", "$1", "$16", "$22", "$23", "$24", "$25", "memory"); \
+	__r0; \
+})
+#endif
 
 #define __cli()			setipl(7)
 #define __sti()			setipl(0)
@@ -124,11 +187,36 @@
 #define __save_and_cli(flags)	do { (flags) = swpipl(7); } while (0)
 #define __restore_flags(flags)	setipl(flags)
 
+#ifdef __SMP__
+
+extern unsigned char global_irq_holder;
+
+#define save_flags(x) \
+do { \
+	(x) = ((global_irq_holder == (unsigned char) smp_processor_id()) \
+		? 1 \
+		: ((getipl() & 7) ? 2 : 0)); \
+} while (0)
+
+#define save_and_cli(flags)   do { save_flags(flags); cli(); } while(0)
+
+extern void __global_cli(void);
+extern void __global_sti(void);
+extern void __global_restore_flags(unsigned long flags);
+
+#define cli()                   __global_cli()
+#define sti()                   __global_sti()
+#define restore_flags(flags)    __global_restore_flags(flags)
+
+#else /* __SMP__ */
+
 #define cli()			setipl(7)
 #define sti()			setipl(0)
 #define save_flags(flags)	do { (flags) = getipl(); } while (0)
 #define save_and_cli(flags)	do { (flags) = swpipl(7); } while (0)
 #define restore_flags(flags)	setipl(flags)
+
+#endif /* __SMP__ */
 
 /*
  * TB routines..
diff -u --recursive --new-file v2.1.91/linux/include/asm-alpha/t2.h linux/include/asm-alpha/t2.h
--- v2.1.91/linux/include/asm-alpha/t2.h	Mon Jan 12 14:51:14 1998
+++ linux/include/asm-alpha/t2.h	Mon Mar 30 00:21:41 1998
@@ -1,6 +1,7 @@
 #ifndef __ALPHA_T2__H__
 #define __ALPHA_T2__H__
 
+#include <linux/config.h>
 #include <linux/types.h>
 
 /*
@@ -18,40 +19,56 @@
 
 #define BYTE_ENABLE_SHIFT 5
 #define TRANSFER_LENGTH_SHIFT 3
-#define MEM_SP1_MASK 0x1fffffff  /* Mem sparse space 1 mask is 29 bits */
+#define MEM_R1_MASK 0x03ffffff  /* Mem sparse space region 1 mask is 26 bits */
 
+#ifdef CONFIG_ALPHA_SRM_SETUP
+/* if we are using the SRM PCI setup, we'll need to use variables instead */
+#define T2_DMA_WIN_BASE_DEFAULT    (1024*1024*1024)
+#define T2_DMA_WIN_SIZE_DEFAULT    (1024*1024*1024)
 
-#define T2_DMA_WIN_BASE	(1024UL*1024UL*1024UL)
+extern unsigned int T2_DMA_WIN_BASE;
+extern unsigned int T2_DMA_WIN_SIZE;
+
+#else /* SRM_SETUP */
+#define T2_DMA_WIN_BASE	(1024*1024*1024)
 #define T2_DMA_WIN_SIZE	(1024*1024*1024)
+#endif /* SRM_SETUP */
+
+/* GAMMA-SABLE is a SABLE with EV5-based CPUs */
+#ifdef CONFIG_ALPHA_GAMMA
+#  define GAMMA_BIAS		0x8000000000UL
+#else /* GAMMA */
+#  define GAMMA_BIAS		0x0000000000UL
+#endif /* GAMMA */
 
 /*
  * Memory spaces:
  */
-#define T2_CONF			        (IDENT_ADDR + 0x390000000UL)
-#define T2_IO				(IDENT_ADDR + 0x3a0000000UL)
-#define T2_SPARSE_MEM			(IDENT_ADDR + 0x200000000UL)
-#define T2_DENSE_MEM		        (IDENT_ADDR + 0x3c0000000UL)
-
-#define T2_IOCSR			(IDENT_ADDR + 0x38e000000UL)
-#define T2_CERR1			(IDENT_ADDR + 0x38e000020UL)
-#define T2_CERR2			(IDENT_ADDR + 0x38e000040UL)
-#define T2_CERR3			(IDENT_ADDR + 0x38e000060UL)
-#define T2_PERR1			(IDENT_ADDR + 0x38e000080UL)
-#define T2_PERR2			(IDENT_ADDR + 0x38e0000a0UL)
-#define T2_PSCR				(IDENT_ADDR + 0x38e0000c0UL)
-#define T2_HAE_1			(IDENT_ADDR + 0x38e0000e0UL)
-#define T2_HAE_2			(IDENT_ADDR + 0x38e000100UL)
-#define T2_HBASE			(IDENT_ADDR + 0x38e000120UL)
-#define T2_WBASE1			(IDENT_ADDR + 0x38e000140UL)
-#define T2_WMASK1			(IDENT_ADDR + 0x38e000160UL)
-#define T2_TBASE1			(IDENT_ADDR + 0x38e000180UL)
-#define T2_WBASE2			(IDENT_ADDR + 0x38e0001a0UL)
-#define T2_WMASK2			(IDENT_ADDR + 0x38e0001c0UL)
-#define T2_TBASE2			(IDENT_ADDR + 0x38e0001e0UL)
-#define T2_TLBBR			(IDENT_ADDR + 0x38e000200UL)
+#define T2_CONF		        (IDENT_ADDR + GAMMA_BIAS + 0x390000000UL)
+#define T2_IO			(IDENT_ADDR + GAMMA_BIAS + 0x3a0000000UL)
+#define T2_SPARSE_MEM		(IDENT_ADDR + GAMMA_BIAS + 0x200000000UL)
+#define T2_DENSE_MEM	        (IDENT_ADDR + GAMMA_BIAS + 0x3c0000000UL)
+
+#define T2_IOCSR		(IDENT_ADDR + GAMMA_BIAS + 0x38e000000UL)
+#define T2_CERR1		(IDENT_ADDR + GAMMA_BIAS + 0x38e000020UL)
+#define T2_CERR2		(IDENT_ADDR + GAMMA_BIAS + 0x38e000040UL)
+#define T2_CERR3		(IDENT_ADDR + GAMMA_BIAS + 0x38e000060UL)
+#define T2_PERR1		(IDENT_ADDR + GAMMA_BIAS + 0x38e000080UL)
+#define T2_PERR2		(IDENT_ADDR + GAMMA_BIAS + 0x38e0000a0UL)
+#define T2_PSCR			(IDENT_ADDR + GAMMA_BIAS + 0x38e0000c0UL)
+#define T2_HAE_1		(IDENT_ADDR + GAMMA_BIAS + 0x38e0000e0UL)
+#define T2_HAE_2		(IDENT_ADDR + GAMMA_BIAS + 0x38e000100UL)
+#define T2_HBASE		(IDENT_ADDR + GAMMA_BIAS + 0x38e000120UL)
+#define T2_WBASE1		(IDENT_ADDR + GAMMA_BIAS + 0x38e000140UL)
+#define T2_WMASK1		(IDENT_ADDR + GAMMA_BIAS + 0x38e000160UL)
+#define T2_TBASE1		(IDENT_ADDR + GAMMA_BIAS + 0x38e000180UL)
+#define T2_WBASE2		(IDENT_ADDR + GAMMA_BIAS + 0x38e0001a0UL)
+#define T2_WMASK2		(IDENT_ADDR + GAMMA_BIAS + 0x38e0001c0UL)
+#define T2_TBASE2		(IDENT_ADDR + GAMMA_BIAS + 0x38e0001e0UL)
+#define T2_TLBBR		(IDENT_ADDR + GAMMA_BIAS + 0x38e000200UL)
 
-#define T2_HAE_3			(IDENT_ADDR + 0x38e000240UL)
-#define T2_HAE_4			(IDENT_ADDR + 0x38e000260UL)
+#define T2_HAE_3		(IDENT_ADDR + GAMMA_BIAS + 0x38e000240UL)
+#define T2_HAE_4		(IDENT_ADDR + GAMMA_BIAS + 0x38e000260UL)
 
 #define HAE_ADDRESS	                T2_HAE_1
 
@@ -88,14 +105,14 @@
  *                                              
  *
  */
-#define CPU0_BASE               (IDENT_ADDR + 0x380000000L)
-#define CPU1_BASE               (IDENT_ADDR + 0x381000000L)
-#define CPU2_BASE               (IDENT_ADDR + 0x382000000L)
-#define CPU3_BASE               (IDENT_ADDR + 0x383000000L)
-#define MEM0_BASE               (IDENT_ADDR + 0x388000000L)
-#define MEM1_BASE               (IDENT_ADDR + 0x389000000L)
-#define MEM2_BASE               (IDENT_ADDR + 0x38a000000L)
-#define MEM3_BASE               (IDENT_ADDR + 0x38b000000L)
+#define CPU0_BASE               (IDENT_ADDR + GAMMA_BIAS + 0x380000000L)
+#define CPU1_BASE               (IDENT_ADDR + GAMMA_BIAS + 0x381000000L)
+#define CPU2_BASE               (IDENT_ADDR + GAMMA_BIAS + 0x382000000L)
+#define CPU3_BASE               (IDENT_ADDR + GAMMA_BIAS + 0x383000000L)
+#define MEM0_BASE               (IDENT_ADDR + GAMMA_BIAS + 0x388000000L)
+#define MEM1_BASE               (IDENT_ADDR + GAMMA_BIAS + 0x389000000L)
+#define MEM2_BASE               (IDENT_ADDR + GAMMA_BIAS + 0x38a000000L)
+#define MEM3_BASE               (IDENT_ADDR + GAMMA_BIAS + 0x38b000000L)
 
 #ifdef __KERNEL__
 
@@ -198,6 +215,133 @@
  * HHH = 31:29 HAE_MEM CSR
  * 
  */
+#ifdef CONFIG_ALPHA_SRM_SETUP
+
+extern unsigned long t2_sm_base;
+
+extern inline unsigned long __readb(unsigned long addr)
+{
+	unsigned long result, shift, work;
+
+	if ((addr >= t2_sm_base) && (addr <= (t2_sm_base + MEM_R1_MASK)))
+	  work = (((addr & MEM_R1_MASK) << 5) + T2_SPARSE_MEM + 0x00);
+	else
+	if ((addr >= 512*1024) && (addr < 1024*1024)) /* check HOLE */
+	  work = (((addr & MEM_R1_MASK) << 5) + T2_SPARSE_MEM + 0x00);
+	else
+	{
+#if 0
+	  printk("__readb: address 0x%lx not covered by HAE\n", addr);
+#endif
+	  return 0x0ffUL;
+	}
+	shift = (addr & 0x3) << 3;
+	result = *(vuip) work;
+	result >>= shift;
+	return 0x0ffUL & result;
+}
+
+extern inline unsigned long __readw(unsigned long addr)
+{
+	unsigned long result, shift, work;
+
+	if ((addr >= t2_sm_base) && (addr <= (t2_sm_base + MEM_R1_MASK)))
+	  work = (((addr & MEM_R1_MASK) << 5) + T2_SPARSE_MEM + 0x08);
+	else
+	if ((addr >= 512*1024) && (addr < 1024*1024)) /* check HOLE */
+	  work = (((addr & MEM_R1_MASK) << 5) + T2_SPARSE_MEM + 0x08);
+	else
+	{
+#if 0
+	  printk("__readw: address 0x%lx not covered by HAE\n", addr);
+#endif
+	  return 0x0ffffUL;
+	}
+	shift = (addr & 0x3) << 3;
+	result = *(vuip) work;
+	result >>= shift;
+	return 0x0ffffUL & result;
+}
+
+/* on SABLE with T2, we must use SPARSE memory even for 32-bit access */
+extern inline unsigned long __readl(unsigned long addr)
+{
+	unsigned long result, work;
+
+	if ((addr >= t2_sm_base) && (addr <= (t2_sm_base + MEM_R1_MASK)))
+	  work = (((addr & MEM_R1_MASK) << 5) + T2_SPARSE_MEM + 0x18);
+	else
+	if ((addr >= 512*1024) && (addr < 1024*1024)) /* check HOLE */
+	  work = (((addr & MEM_R1_MASK) << 5) + T2_SPARSE_MEM + 0x18);
+	else
+	{
+#if 0
+	  printk("__readl: address 0x%lx not covered by HAE\n", addr);
+#endif
+	  return 0x0ffffffffUL;
+	}
+	result = *(vuip) work;
+	return 0xffffffffUL & result;
+}
+
+extern inline void __writeb(unsigned char b, unsigned long addr)
+{
+	unsigned long work;
+
+	if ((addr >= t2_sm_base) && (addr <= (t2_sm_base + MEM_R1_MASK)))
+	  work = (((addr & MEM_R1_MASK) << 5) + T2_SPARSE_MEM + 0x00);
+	else
+	if ((addr >= 512*1024) && (addr < 1024*1024)) /* check HOLE */
+	  work = (((addr & MEM_R1_MASK) << 5) + T2_SPARSE_MEM + 0x00);
+	else
+	{
+#if 0
+	  printk("__writeb: address 0x%lx not covered by HAE\n", addr);
+#endif
+	  return;
+	}
+	*(vuip) work = b * 0x01010101;
+}
+
+extern inline void __writew(unsigned short b, unsigned long addr)
+{
+	unsigned long work;
+
+	if ((addr >= t2_sm_base) && (addr <= (t2_sm_base + MEM_R1_MASK)))
+	  work = (((addr & MEM_R1_MASK) << 5) + T2_SPARSE_MEM + 0x08);
+	else
+	if ((addr >= 512*1024) && (addr < 1024*1024)) /* check HOLE */
+	  work = (((addr & MEM_R1_MASK) << 5) + T2_SPARSE_MEM + 0x08);
+	else
+	{
+#if 0
+	  printk("__writew: address 0x%lx not covered by HAE\n", addr);
+#endif
+	  return;
+	}
+	*(vuip) work = b * 0x00010001;
+}
+
+/* on SABLE with T2, we must use SPARSE memory even for 32-bit access */
+extern inline void __writel(unsigned int b, unsigned long addr)
+{
+	unsigned long work;
+
+	if ((addr >= t2_sm_base) && (addr <= (t2_sm_base + MEM_R1_MASK)))
+	  work = (((addr & MEM_R1_MASK) << 5) + T2_SPARSE_MEM + 0x18);
+	else
+	if ((addr >= 512*1024) && (addr < 1024*1024)) /* check HOLE */
+	  work = (((addr & MEM_R1_MASK) << 5) + T2_SPARSE_MEM + 0x18);
+	{
+#if 0
+	  printk("__writel: address 0x%lx not covered by HAE\n", addr);
+#endif
+	  return;
+	}
+	*(vuip) work = b;
+}
+
+#else /* SRM_SETUP */
 
 extern inline unsigned long __readb(unsigned long addr)
 {
@@ -205,7 +349,7 @@
 
 	shift = (addr & 0x3) * 8 ;
 	msb = addr & 0xE0000000 ;
-	addr &= MEM_SP1_MASK ;
+	addr &= MEM_R1_MASK ;
 	if (msb != hae.cache) {
 	  set_hae(msb);
 	}
@@ -220,7 +364,7 @@
 
 	shift = (addr & 0x3) * 8;
 	msb = addr & 0xE0000000 ;
-	addr &= MEM_SP1_MASK ;
+	addr &= MEM_R1_MASK ;
 	if (msb != hae.cache) {
 	  set_hae(msb);
 	}
@@ -235,7 +379,7 @@
 	unsigned long result, msb;
 
 	msb = addr & 0xE0000000 ;
-	addr &= MEM_SP1_MASK ;
+	addr &= MEM_R1_MASK ;
 	if (msb != hae.cache) {
 	  set_hae(msb);
 	}
@@ -248,7 +392,7 @@
         unsigned long msb ; 
 
 	msb = addr & 0xE0000000 ;
-	addr &= MEM_SP1_MASK ;
+	addr &= MEM_R1_MASK ;
 	if (msb != hae.cache) {
 	  set_hae(msb);
 	}
@@ -260,7 +404,7 @@
         unsigned long msb ; 
 
 	msb = addr & 0xE0000000 ;
-	addr &= MEM_SP1_MASK ;
+	addr &= MEM_R1_MASK ;
 	if (msb != hae.cache) {
 	  set_hae(msb);
 	}
@@ -273,12 +417,14 @@
         unsigned long msb ; 
 
 	msb = addr & 0xE0000000 ;
-	addr &= MEM_SP1_MASK ;
+	addr &= MEM_R1_MASK ;
 	if (msb != hae.cache) {
 	  set_hae(msb);
 	}
 	*(vuip) ((addr << 5) + T2_SPARSE_MEM + 0x18) = b;
 }
+
+#endif /* SRM_SETUP */
 
 #define inb(port) \
 (__builtin_constant_p((port))?__inb(port):_inb(port))
diff -u --recursive --new-file v2.1.91/linux/include/asm-alpha/tsunami.h linux/include/asm-alpha/tsunami.h
--- v2.1.91/linux/include/asm-alpha/tsunami.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-alpha/tsunami.h	Mon Mar 30 00:21:41 1998
@@ -0,0 +1,483 @@
+#ifndef __ALPHA_TSUNAMI__H__
+#define __ALPHA_TSUNAMI__H__
+
+#include <linux/config.h>
+#include <linux/types.h>
+
+/*
+ * TSUNAMI/TYPHOON are the internal names for the core logic chipset which
+ * provides memory controller and PCI access for the 21264 based systems.
+ *
+ * This file is based on:
+ *
+ * Tsunami System Programmers Manual
+ * Preliminary, Chapters 2-5
+ *
+ */
+
+#define BYTE_ENABLE_SHIFT 5
+#define TRANSFER_LENGTH_SHIFT 3
+
+#ifdef CONFIG_ALPHA_SRM_SETUP
+/* if we are using the SRM PCI setup, we'll need to use variables instead */
+#define TSUNAMI_DMA_WIN_BASE_DEFAULT    (1024*1024*1024)
+#define TSUNAMI_DMA_WIN_SIZE_DEFAULT    (1024*1024*1024)
+
+extern unsigned int TSUNAMI_DMA_WIN_BASE;
+extern unsigned int TSUNAMI_DMA_WIN_SIZE;
+
+#else /* SRM_SETUP */
+#define TSUNAMI_DMA_WIN_BASE	(1024*1024*1024)
+#define TSUNAMI_DMA_WIN_SIZE	(1024*1024*1024)
+#endif /* SRM_SETUP */
+
+#ifdef USE_48_BIT_KSEG
+#define TS_BIAS 0x80000000000UL
+#else
+#define TS_BIAS 0x10000000000UL
+#endif
+
+/*
+ * CChip and DChip registers
+ */
+#define	TSUNAMI_CSR_CSC		(IDENT_ADDR + TS_BIAS + 0x1A0000000UL)
+#define	TSUNAMI_CSR_MTR		(IDENT_ADDR + TS_BIAS + 0x1A0000040UL)
+#define	TSUNAMI_CSR_MISC	(IDENT_ADDR + TS_BIAS + 0x1A0000080UL)
+#define	TSUNAMI_CSR_MPD		(IDENT_ADDR + TS_BIAS + 0x1A00000C0UL)
+#define	TSUNAMI_CSR_AAR0	(IDENT_ADDR + TS_BIAS + 0x1A0000100UL)
+#define	TSUNAMI_CSR_AAR1	(IDENT_ADDR + TS_BIAS + 0x1A0000140UL)
+#define	TSUNAMI_CSR_AAR2	(IDENT_ADDR + TS_BIAS + 0x1A0000180UL)
+#define	TSUNAMI_CSR_AAR3	(IDENT_ADDR + TS_BIAS + 0x1A00001C0UL)
+#define	TSUNAMI_CSR_DIM0	(IDENT_ADDR + TS_BIAS + 0x1A0000200UL)
+#define	TSUNAMI_CSR_DIM1	(IDENT_ADDR + TS_BIAS + 0x1A0000240UL)
+#define	TSUNAMI_CSR_DIR0	(IDENT_ADDR + TS_BIAS + 0x1A0000280UL)
+#define	TSUNAMI_CSR_DIR1	(IDENT_ADDR + TS_BIAS + 0x1A00002C0UL)
+
+#define	TSUNAMI_CSR_DRIR	(IDENT_ADDR + TS_BIAS + 0x1A0000300UL)
+#define	TSUNAMI_CSR_PRBEN	(IDENT_ADDR + TS_BIAS + 0x1A0000340UL)
+#define	TSUNAMI_CSR_IIC	       	(IDENT_ADDR + TS_BIAS + 0x1A0000380UL)
+#define	TSUNAMI_CSR_WDR	       	(IDENT_ADDR + TS_BIAS + 0x1A00003C0UL)
+#define	TSUNAMI_CSR_MPR0	(IDENT_ADDR + TS_BIAS + 0x1A0000400UL)
+#define	TSUNAMI_CSR_MPR1	(IDENT_ADDR + TS_BIAS + 0x1A0000440UL)
+#define	TSUNAMI_CSR_MPR2	(IDENT_ADDR + TS_BIAS + 0x1A0000480UL)
+#define	TSUNAMI_CSR_MPR3	(IDENT_ADDR + TS_BIAS + 0x1A00004C0UL)
+#define	TSUNAMI_CSR_TTR		(IDENT_ADDR + TS_BIAS + 0x1A0000580UL)
+#define	TSUNAMI_CSR_TDR		(IDENT_ADDR + TS_BIAS + 0x1A00005C0UL)
+#define	TSUNAMI_CSR_DSC	       	(IDENT_ADDR + TS_BIAS + 0x1B0000800UL)
+#define	TSUNAMI_CSR_STR		(IDENT_ADDR + TS_BIAS + 0x1B0000840UL)
+#define	TSUNAMI_CSR_DREV	(IDENT_ADDR + TS_BIAS + 0x1B0000880UL)
+
+/*
+ * PChip registers
+ */
+#define	TSUNAMI_PCHIP0_WSBA0  	(IDENT_ADDR + TS_BIAS + 0x180000000UL)
+#define	TSUNAMI_PCHIP0_WSBA1  	(IDENT_ADDR + TS_BIAS + 0x180000040UL)
+#define	TSUNAMI_PCHIP0_WSBA2  	(IDENT_ADDR + TS_BIAS + 0x180000080UL)
+#define	TSUNAMI_PCHIP0_WSBA3  	(IDENT_ADDR + TS_BIAS + 0x1800000C0UL)
+
+#define	TSUNAMI_PCHIP0_WSM0  	(IDENT_ADDR + TS_BIAS + 0x180000100UL)
+#define	TSUNAMI_PCHIP0_WSM1  	(IDENT_ADDR + TS_BIAS + 0x180000140UL)
+#define	TSUNAMI_PCHIP0_WSM2  	(IDENT_ADDR + TS_BIAS + 0x180000180UL)
+#define	TSUNAMI_PCHIP0_WSM3  	(IDENT_ADDR + TS_BIAS + 0x1800001C0UL)
+#define	TSUNAMI_PCHIP0_TBA0  	(IDENT_ADDR + TS_BIAS + 0x180000200UL)
+#define	TSUNAMI_PCHIP0_TBA1  	(IDENT_ADDR + TS_BIAS + 0x180000240UL)
+#define	TSUNAMI_PCHIP0_TBA2  	(IDENT_ADDR + TS_BIAS + 0x180000280UL)
+#define	TSUNAMI_PCHIP0_TBA3  	(IDENT_ADDR + TS_BIAS + 0x1800002C0UL)
+
+#define	TSUNAMI_PCHIP0_PCTL  	(IDENT_ADDR + TS_BIAS + 0x180000300UL)
+#define	TSUNAMI_PCHIP0_PLAT  	(IDENT_ADDR + TS_BIAS + 0x180000340UL)
+#define	TSUNAMI_PCHIP0_RESERVED	(IDENT_ADDR + TS_BIAS + 0x180000380UL)
+#define	TSUNAMI_PCHIP0_PERROR	(IDENT_ADDR + TS_BIAS + 0x1800003c0UL)
+#define	TSUNAMI_PCHIP0_PERRMASK	(IDENT_ADDR + TS_BIAS + 0x180000400UL)
+#define	TSUNAMI_PCHIP0_PERRSET 	(IDENT_ADDR + TS_BIAS + 0x180000440UL)
+#define	TSUNAMI_PCHIP0_TLBIV  	(IDENT_ADDR + TS_BIAS + 0x180000480UL)
+#define	TSUNAMI_PCHIP0_TLBIA 	(IDENT_ADDR + TS_BIAS + 0x1800004C0UL)
+#define	TSUNAMI_PCHIP0_PMONCTL	(IDENT_ADDR + TS_BIAS + 0x180000500UL)
+#define	TSUNAMI_PCHIP0_PMONCNT	(IDENT_ADDR + TS_BIAS + 0x180000540UL)
+
+#define	TSUNAMI_PCHIP1_WSBA0  	(IDENT_ADDR + TS_BIAS + 0x380000000UL)
+#define	TSUNAMI_PCHIP1_WSBA1  	(IDENT_ADDR + TS_BIAS + 0x380000040UL)
+#define	TSUNAMI_PCHIP1_WSBA2  	(IDENT_ADDR + TS_BIAS + 0x380000080UL)
+#define	TSUNAMI_PCHIP1_WSBA3  	(IDENT_ADDR + TS_BIAS + 0x3800000C0UL)
+#define	TSUNAMI_PCHIP1_WSM0  	(IDENT_ADDR + TS_BIAS + 0x380000100UL)
+#define	TSUNAMI_PCHIP1_WSM1  	(IDENT_ADDR + TS_BIAS + 0x380000140UL)
+#define	TSUNAMI_PCHIP1_WSM2  	(IDENT_ADDR + TS_BIAS + 0x380000180UL)
+#define	TSUNAMI_PCHIP1_WSM3  	(IDENT_ADDR + TS_BIAS + 0x3800001C0UL)
+
+#define	TSUNAMI_PCHIP1_TBA0  	(IDENT_ADDR + TS_BIAS + 0x380000200UL)
+#define	TSUNAMI_PCHIP1_TBA1  	(IDENT_ADDR + TS_BIAS + 0x380000240UL)
+#define	TSUNAMI_PCHIP1_TBA2  	(IDENT_ADDR + TS_BIAS + 0x380000280UL)
+#define	TSUNAMI_PCHIP1_TBA3  	(IDENT_ADDR + TS_BIAS + 0x3800002C0UL)
+
+#define	TSUNAMI_PCHIP1_PCTL  	(IDENT_ADDR + TS_BIAS + 0x380000300UL)
+#define	TSUNAMI_PCHIP1_PLAT  	(IDENT_ADDR + TS_BIAS + 0x380000340UL)
+#define	TSUNAMI_PCHIP1_RESERVED	(IDENT_ADDR + TS_BIAS + 0x380000380UL)
+#define	TSUNAMI_PCHIP1_PERROR	(IDENT_ADDR + TS_BIAS + 0x3800003c0UL)
+#define	TSUNAMI_PCHIP1_PERRMASK	(IDENT_ADDR + TS_BIAS + 0x380000400UL)
+#define	TSUNAMI_PCHIP1_PERRSET	(IDENT_ADDR + TS_BIAS + 0x380000440UL)
+#define	TSUNAMI_PCHIP1_TLBIV  	(IDENT_ADDR + TS_BIAS + 0x380000480UL)
+#define	TSUNAMI_PCHIP1_TLBIA	(IDENT_ADDR + TS_BIAS + 0x3800004C0UL)
+#define	TSUNAMI_PCHIP1_PMONCTL	(IDENT_ADDR + TS_BIAS + 0x380000500UL)
+#define	TSUNAMI_PCHIP1_PMONCNT	(IDENT_ADDR + TS_BIAS + 0x380000540UL)
+
+/*                                                                          */
+/* TSUNAMI Pchip Error register.                                            */
+/*                                                                          */
+#define perror_m_lost 0x1
+#define perror_m_serr 0x2
+#define perror_m_perr 0x4
+#define perror_m_dcrto 0x8
+#define perror_m_sge 0x10
+#define perror_m_ape 0x20
+#define perror_m_ta 0x40
+#define perror_m_rdpe 0x80
+#define perror_m_nds 0x100
+#define perror_m_rto 0x200
+#define perror_m_uecc 0x400
+#define perror_m_cre 0x800
+#define perror_m_addrl 0xFFFFFFFF0000UL
+#define perror_m_addrh 0x7000000000000UL
+#define perror_m_cmd 0xF0000000000000UL
+#define perror_m_syn 0xFF00000000000000UL
+union TPchipPERROR {   
+    struct  {
+        unsigned int perror_v_lost : 1;
+        unsigned perror_v_serr : 1;
+        unsigned perror_v_perr : 1;
+        unsigned perror_v_dcrto : 1;
+        unsigned perror_v_sge : 1;
+        unsigned perror_v_ape : 1;
+        unsigned perror_v_ta : 1;
+        unsigned perror_v_rdpe : 1;
+        unsigned perror_v_nds : 1;
+        unsigned perror_v_rto : 1;
+        unsigned perror_v_uecc : 1;
+        unsigned perror_v_cre : 1;                 
+        unsigned perror_v_rsvd1 : 4;
+        unsigned perror_v_addrl : 32;
+        unsigned perror_v_addrh : 3;
+        unsigned perror_v_rsvd2 : 1;
+        unsigned perror_v_cmd : 4;
+        unsigned perror_v_syn : 8;
+        } perror_r_bits;
+    int perror_q_whole [2];
+    } ;                       
+/*                                                                          */
+/* TSUNAMI Pchip Window Space Base Address register.                        */
+/*                                                                          */
+#define wsba_m_ena 0x1                
+#define wsba_m_sg 0x2
+#define wsba_m_ptp 0x4
+#define wsba_m_addr 0xFFF00000  
+#define wmask_k_sz1gb 0x3FF00000                   
+union TPchipWSBA {
+    struct  {
+        unsigned wsba_v_ena : 1;
+        unsigned wsba_v_sg : 1;
+        unsigned wsba_v_ptp : 1;
+        unsigned wsba_v_rsvd1 : 17;
+        unsigned wsba_v_addr : 12;
+        unsigned wsba_v_rsvd2 : 32;
+        } wsba_r_bits;
+    int wsba_q_whole [2];
+    } ;
+/*									    */
+/* TSUNAMI Pchip Control Register					    */
+/*									    */
+#define pctl_m_fdsc 0x1
+#define pctl_m_fbtb 0x2
+#define pctl_m_thdis 0x4
+#define pctl_m_chaindis 0x8
+#define pctl_m_tgtlat 0x10
+#define pctl_m_hole 0x20
+#define pctl_m_mwin 0x40
+#define pctl_m_arbena 0x80
+#define pctl_m_prigrp 0x7F00
+#define pctl_m_ppri 0x8000
+#define pctl_m_rsvd1 0x30000
+#define pctl_m_eccen 0x40000
+#define pctl_m_padm 0x80000
+#define pctl_m_cdqmax 0xF00000
+#define pctl_m_rev 0xFF000000
+#define pctl_m_crqmax 0xF00000000UL
+#define pctl_m_ptpmax 0xF000000000UL
+#define pctl_m_pclkx 0x30000000000UL
+#define pctl_m_fdsdis 0x40000000000UL
+#define pctl_m_fdwdis 0x80000000000UL
+#define pctl_m_ptevrfy 0x100000000000UL
+#define pctl_m_rpp 0x200000000000UL
+#define pctl_m_pid 0xC00000000000UL
+#define pctl_m_rsvd2 0xFFFF000000000000UL
+
+union TPchipPCTL {
+    struct {
+	unsigned pctl_v_fdsc : 1;
+	unsigned pctl_v_fbtb : 1;
+	unsigned pctl_v_thdis : 1;
+	unsigned pctl_v_chaindis : 1;
+	unsigned pctl_v_tgtlat : 1;
+	unsigned pctl_v_hole : 1;
+	unsigned pctl_v_mwin : 1;
+	unsigned pctl_v_arbena : 1;
+	unsigned pctl_v_prigrp : 7;
+	unsigned pctl_v_ppri : 1;
+	unsigned pctl_v_rsvd1 : 2;
+	unsigned pctl_v_eccen : 1;
+	unsigned pctl_v_padm : 1;
+	unsigned pctl_v_cdqmax : 4;
+	unsigned pctl_v_rev : 8;
+	unsigned pctl_v_crqmax : 4;
+	unsigned pctl_v_ptpmax : 4;
+	unsigned pctl_v_pclkx : 2;
+	unsigned pctl_v_fdsdis : 1;
+	unsigned pctl_v_fdwdis : 1;
+	unsigned pctl_v_ptevrfy : 1;
+	unsigned pctl_v_rpp : 1;
+	unsigned pctl_v_pid : 2;
+	unsigned pctl_v_rsvd2 : 16;
+	} pctl_r_bits;
+    int pctl_q_whole [2];
+} ;
+/*                                                                          */
+/* TSUNAMI Pchip Error Mask Register.                                       */
+/*                                                                          */
+#define perrmask_m_lost 0x1
+#define perrmask_m_serr 0x2
+#define perrmask_m_perr 0x4
+#define perrmask_m_dcrto 0x8
+#define perrmask_m_sge 0x10
+#define perrmask_m_ape 0x20
+#define perrmask_m_ta 0x40
+#define perrmask_m_rdpe 0x80
+#define perrmask_m_nds 0x100
+#define perrmask_m_rto 0x200
+#define perrmask_m_uecc 0x400
+#define perrmask_m_cre 0x800
+#define perrmask_m_rsvd 0xFFFFFFFFFFFFF000UL
+union TPchipPERRMASK {   
+    struct  {
+        unsigned int perrmask_v_lost : 1;
+        unsigned perrmask_v_serr : 1;
+        unsigned perrmask_v_perr : 1;
+        unsigned perrmask_v_dcrto : 1;
+        unsigned perrmask_v_sge : 1;
+        unsigned perrmask_v_ape : 1;
+        unsigned perrmask_v_ta : 1;
+        unsigned perrmask_v_rdpe : 1;
+        unsigned perrmask_v_nds : 1;
+        unsigned perrmask_v_rto : 1;
+        unsigned perrmask_v_uecc : 1;
+        unsigned perrmask_v_cre : 1;                 
+        unsigned perrmask_v_rsvd1 : 20;
+	unsigned perrmask_v_rsvd2 : 32;
+        } perrmask_r_bits;
+    int perrmask_q_whole [2];
+    } ;                       
+
+/*
+ * Memory spaces:
+ */
+#define TSUNAMI_PCI0_MEM		(IDENT_ADDR + TS_BIAS + 0x000000000UL)
+#define TSUNAMI_PCI0_IACK_SC		(IDENT_ADDR + TS_BIAS + 0x1F8000000UL)
+#define TSUNAMI_PCI0_IO			(IDENT_ADDR + TS_BIAS + 0x1FC000000UL)
+#define TSUNAMI_PCI0_CONF		(IDENT_ADDR + TS_BIAS + 0x1FE000000UL)
+
+#define TSUNAMI_PCI1_MEM		(IDENT_ADDR + TS_BIAS + 0x200000000UL)
+#define TSUNAMI_PCI1_IACK_SC		(IDENT_ADDR + TS_BIAS + 0x3F8000000UL)
+#define TSUNAMI_PCI1_IO			(IDENT_ADDR + TS_BIAS + 0x3FC000000UL)
+#define TSUNAMI_PCI1_CONF		(IDENT_ADDR + TS_BIAS + 0x3FE000000UL)
+
+#define HAE_ADDRESS 0
+
+#ifdef __KERNEL__
+
+/*
+ * Translate physical memory address as seen on (PCI) bus into
+ * a kernel virtual address and vv.
+ */
+extern inline unsigned long virt_to_bus(void * address)
+{
+	return virt_to_phys(address) + TSUNAMI_DMA_WIN_BASE;
+}
+
+extern inline void * bus_to_virt(unsigned long address)
+{
+	return phys_to_virt(address - TSUNAMI_DMA_WIN_BASE);
+}
+
+/*
+ * I/O functions:
+ *
+ * TSUNAMI, the 21??? PCI/memory support chipset for the EV6 (21264)
+ * can only use linear accesses to get at PCI memory and I/O spaces.
+ */
+
+/* HACK ALERT! HACK ALERT! */
+/* HACK ALERT! HACK ALERT! */
+
+/* only using PCI bus 0 for now in all routines */
+
+/* HACK ALERT! HACK ALERT! */
+/* HACK ALERT! HACK ALERT! */
+
+
+#define vuip	volatile unsigned int *
+
+extern inline unsigned int __inb(unsigned long addr)
+{
+	register unsigned long result;
+
+	__asm__ __volatile__ (
+		 "ldbu %0,%1"
+		 : "=r" (result)
+		 : "m"  (*(unsigned char *)(addr+TSUNAMI_PCI0_IO)));
+
+	return result;
+}
+
+extern inline void __outb(unsigned char b, unsigned long addr)
+{
+	__asm__ __volatile__ (
+		 "stb %1,%0\n\t"
+		 "mb"
+		 : : "m" (*(unsigned char *)(addr+TSUNAMI_PCI0_IO)), "r" (b));
+}
+
+extern inline unsigned int __inw(unsigned long addr)
+{
+	register unsigned long result;
+
+	__asm__ __volatile__ (
+		 "ldwu %0,%1"
+		 : "=r" (result)
+		 : "m"  (*(unsigned short *)(addr+TSUNAMI_PCI0_IO)));
+
+	return result;
+}
+
+extern inline void __outw(unsigned short b, unsigned long addr)
+{
+	__asm__ __volatile__ (
+		 "stw %1,%0\n\t"
+		 "mb"
+		 : : "m" (*(unsigned short *)(addr+TSUNAMI_PCI0_IO)), "r" (b));
+}
+
+extern inline unsigned int __inl(unsigned long addr)
+{
+	register unsigned long result;
+
+	__asm__ __volatile__ (
+		 "ldl %0,%1"
+		 : "=r" (result)
+		 : "m"  (*(unsigned int *)(addr+TSUNAMI_PCI0_IO)));
+
+	return result;
+}
+
+extern inline void __outl(unsigned int b, unsigned long addr)
+{
+	__asm__ __volatile__ (
+		 "stl %1,%0\n\t"
+		 "mb"
+		 : : "m" (*(unsigned int *)(addr+TSUNAMI_PCI0_IO)), "r" (b));
+}
+
+/*
+ * Memory functions.  all accesses are done through linear space.
+ */
+
+extern inline unsigned long __readb(unsigned long addr)
+{
+	register unsigned long result;
+
+	__asm__ __volatile__ (
+		 "ldbu %0,%1"
+		 : "=r" (result)
+		 : "m"  (*(unsigned char *)(addr+TSUNAMI_PCI0_MEM)));
+
+	return result;
+}
+
+extern inline unsigned long __readw(unsigned long addr)
+{
+	register unsigned long result;
+
+	__asm__ __volatile__ (
+		 "ldwu %0,%1"
+		 : "=r" (result)
+		 : "m"  (*(unsigned short *)(addr+TSUNAMI_PCI0_MEM)));
+
+	return result;
+}
+
+extern inline unsigned long __readl(unsigned long addr)
+{
+	register unsigned long result;
+
+	__asm__ __volatile__ (
+		 "ldl %0,%1"
+		 : "=r" (result)
+		 : "m"  (*(unsigned int *)(addr+TSUNAMI_PCI0_MEM)));
+
+	return result;
+}
+
+extern inline void __writeb(unsigned char b, unsigned long addr)
+{
+	__asm__ __volatile__ (
+		 "stb %1,%0\n\t"
+		 "mb"
+		 : : "m" (*(unsigned char *)(addr+TSUNAMI_PCI0_MEM)), "r" (b));
+}
+
+extern inline void __writew(unsigned short b, unsigned long addr)
+{
+	__asm__ __volatile__ (
+		 "stw %1,%0\n\t"
+		 "mb"
+		 : : "m" (*(unsigned short *)(addr+TSUNAMI_PCI0_MEM)), "r" (b));
+}
+
+extern inline void __writel(unsigned int b, unsigned long addr)
+{
+	__asm__ __volatile__ (
+		 "stl %1,%0\n\t"
+		 "mb"
+		 : : "m" (*(unsigned int *)(addr+TSUNAMI_PCI0_MEM)), "r" (b));
+}
+
+#define inb(port) __inb((port))
+#define inw(port) __inw((port))
+#define inl(port) __inl((port))
+
+#define outb(v, port) __outb((v),(port))
+#define outw(v, port) __outw((v),(port))
+#define outl(v, port) __outl((v),(port))
+
+#define readb(a)	__readb((unsigned long)(a))
+#define readw(a)	__readw((unsigned long)(a))
+#define readl(a)	__readl((unsigned long)(a))
+
+#define writeb(v,a)	__writeb((v),(unsigned long)(a))
+#define writew(v,a)	__writew((v),(unsigned long)(a))
+#define writel(v,a)	__writel((v),(unsigned long)(a))
+
+#undef vuip
+
+extern unsigned long tsunami_init (unsigned long mem_start,
+				 unsigned long mem_end);
+
+#endif /* __KERNEL__ */
+
+/*
+ * Data structure for handling TSUNAMI machine checks:
+ */
+struct el_TSUNAMI_sysdata_mcheck {
+};
+
+#define RTC_PORT(x)	(0x70 + (x))
+#define RTC_ADDR(x)	(0x80 | (x))
+#define RTC_ALWAYS_BCD	0
+
+#endif /* __ALPHA_TSUNAMI__H__ */
diff -u --recursive --new-file v2.1.91/linux/include/asm-i386/bugs.h linux/include/asm-i386/bugs.h
--- v2.1.91/linux/include/asm-i386/bugs.h	Fri Feb  6 15:32:54 1998
+++ linux/include/asm-i386/bugs.h	Wed Apr  1 17:30:31 1998
@@ -148,7 +148,7 @@
 }
 
 /*
- *	B step AMD K6 before B 9729AIJW have hardware bugs that can cause
+ *	B step AMD K6 before B 9730xxxx have hardware bugs that can cause
  *	misexecution of code under Linux. Owners of such processors should
  *	contact AMD for precise details and a CPU swap.
  *
@@ -195,10 +195,10 @@
 		printk(KERN_INFO "AMD K6 stepping B detected - ");
 		/* -- cut here -- */
 		if (d > 20*K6_BUG_LOOP) 
-			printk(KERN_INFO "system stability may be impaired when more than 32 MB are used.\n");
+			printk("system stability may be impaired when more than 32 MB are used.\n");
 		else 
-			printk(KERN_INFO "probably OK (after B9730xxxx).\n");
-		printk(KERN_INFO "Please see http://www.chorus.com/bpc/k6bug.html\n");
+			printk("probably OK (after B9730xxxx).\n");
+		printk(KERN_INFO "Please see http://www.chorus.com/poulot/k6bug.html\n");
 	}
 }
 
diff -u --recursive --new-file v2.1.91/linux/include/asm-i386/ide.h linux/include/asm-i386/ide.h
--- v2.1.91/linux/include/asm-i386/ide.h	Thu Mar 27 14:40:06 1997
+++ linux/include/asm-i386/ide.h	Sun Mar 29 12:24:05 1998
@@ -16,7 +16,7 @@
 typedef unsigned short ide_ioreg_t;
 
 #ifndef MAX_HWIFS
-#define MAX_HWIFS	4
+#define MAX_HWIFS	6
 #endif
 
 #define ide_sti()	sti()
@@ -28,6 +28,8 @@
 		case 0x170: return 15;
 		case 0x1e8: return 11;
 		case 0x168: return 10;
+		case 0x1e0: return 8;
+		case 0x160: return 12;
 		default:
 			return 0;
 	}
@@ -40,6 +42,8 @@
 		case 1:	return 0x170;
 		case 2: return 0x1e8;
 		case 3: return 0x168;
+		case 4: return 0x1e0;
+		case 5: return 0x160;
 		default:
 			return 0;
 	}
diff -u --recursive --new-file v2.1.91/linux/include/asm-i386/processor.h linux/include/asm-i386/processor.h
--- v2.1.91/linux/include/asm-i386/processor.h	Tue Mar 17 22:18:15 1998
+++ linux/include/asm-i386/processor.h	Wed Apr  1 17:30:30 1998
@@ -179,7 +179,7 @@
 
 #define start_thread(regs, new_eip, new_esp) do {\
 	unsigned long seg = __USER_DS; \
-	__asm__("mov %w0,%%fs ; mov %w0,%%gs":"=r" (seg) :"0" (seg)); \
+	__asm__("movl %w0,%%fs ; movl %w0,%%gs":"=r" (seg) :"0" (seg)); \
 	set_fs(USER_DS); \
 	regs->xds = seg; \
 	regs->xes = seg; \
diff -u --recursive --new-file v2.1.91/linux/include/asm-i386/unistd.h linux/include/asm-i386/unistd.h
--- v2.1.91/linux/include/asm-i386/unistd.h	Thu Mar 26 15:57:05 1998
+++ linux/include/asm-i386/unistd.h	Tue Mar 31 10:38:09 1998
@@ -188,6 +188,7 @@
 #define __NR_pread		180
 #define __NR_pwrite		181
 #define __NR_chown		182
+#define __NR_getcwd		183
 
 /* user-visible error numbers are in the range -1 - -122: see <asm-i386/errno.h> */
 
diff -u --recursive --new-file v2.1.91/linux/include/linux/b1lli.h linux/include/linux/b1lli.h
--- v2.1.91/linux/include/linux/b1lli.h	Thu May 29 21:53:10 1997
+++ linux/include/linux/b1lli.h	Wed Apr  1 16:20:56 1998
@@ -1,11 +1,25 @@
 /*
- * $Id: b1lli.h,v 1.1 1997/03/04 21:27:32 calle Exp $
+ * $Id: b1lli.h,v 1.3 1998/01/31 10:54:37 calle Exp $
  *
  * ISDN lowlevel-module for AVM B1-card.
  *
  * Copyright 1996 by Carsten Paeth (calle@calle.in-berlin.de)
  *
  * $Log: b1lli.h,v $
+ * Revision 1.3  1998/01/31 10:54:37  calle
+ * include changes for PCMCIA cards from 2.0 version
+ *
+ * Revision 1.2  1997/12/10 19:38:42  calle
+ * get changes from 2.0 tree
+ *
+ * Revision 1.1.2.2  1997/11/26 16:57:26  calle
+ * more changes for B1/M1/T1.
+ *
+ * Revision 1.1.2.1  1997/11/26 10:47:01  calle
+ * prepared for M1 (Mobile) and T1 (PMX) cards.
+ * prepared to set configuration after load to support other D-channel
+ * protocols, point-to-point and leased lines.
+ *
  * Revision 1.1  1997/03/04 21:27:32  calle
  * First version in isdn4linux
  *
@@ -32,10 +46,22 @@
 	avmb1_t4file t4file;
 } avmb1_loaddef;
 
+typedef struct avmb1_loadandconfigdef {
+	int contr;
+	avmb1_t4file t4file;
+        avmb1_t4file t4config; 
+} avmb1_loadandconfigdef;
+
 typedef struct avmb1_resetdef {
 	int contr;
 } avmb1_resetdef;
 
+typedef struct avmb1_getdef {
+	int contr;
+	int cardtype;
+	int cardstate;
+} avmb1_getdef;
+
 /*
  * struct for adding new cards 
  */
@@ -44,25 +70,39 @@
 	int irq;
 } avmb1_carddef;
 
-#define	AVMB1_LOAD	0	/* load image to card */
-#define AVMB1_ADDCARD	1	/* add a new card */
-#define AVMB1_RESETCARD	2	/* reset a card */
+#define AVM_CARDTYPE_B1	0
+#define AVM_CARDTYPE_T1	1
+#define AVM_CARDTYPE_M1	2
+#define AVM_CARDTYPE_M2	3
 
+typedef struct avmb1_extcarddef {
+	int port;
+	int irq;
+        int cardtype;
+} avmb1_extcarddef;
+
+#define	AVMB1_LOAD		0	/* load image to card */
+#define AVMB1_ADDCARD		1	/* add a new card */
+#define AVMB1_RESETCARD		2	/* reset a card */
+#define	AVMB1_LOAD_AND_CONFIG	3	/* load image and config to card */
+#define	AVMB1_ADDCARD_WITH_TYPE	4	/* add a new card, with cardtype */
+#define AVMB1_GET_CARDINFO	5	/* get cardtype */
 
 
-#ifdef __KERNEL__
 
 /*
  * card states for startup
  */
 
-#define CARD_NONE	0
+#define CARD_FREE	0
 #define CARD_DETECTED	1
 #define CARD_LOADING	2
 #define CARD_INITSTATE	4
 #define CARD_RUNNING	5
 #define CARD_ACTIVE	6
 
+#ifdef __KERNEL__
+
 #define	AVMB1_PORTLEN	0x1f
 
 #define AVM_MAXVERSION	8
@@ -81,6 +121,7 @@
 	int cnr;
 	unsigned short port;
 	unsigned irq;
+	int cardtype;
 	volatile unsigned short cardstate;
 	int interrupt;
 	int blocked;
@@ -108,14 +149,15 @@
 
 
 /* b1lli.c */
-int B1_detect(unsigned short base);
+int B1_detect(unsigned short base, int cardtype);
 void B1_reset(unsigned short base);
 int B1_load_t4file(unsigned short base, avmb1_t4file * t4file);
+int B1_load_config(unsigned short base, avmb1_t4file * config);
 int B1_loaded(unsigned short base);
-unsigned char B1_assign_irq(unsigned short base, unsigned irq);
+unsigned char B1_assign_irq(unsigned short base, unsigned irq, int cardtype);
 unsigned char B1_enable_irq(unsigned short base);
 unsigned char B1_disable_irq(unsigned short base);
-int B1_valid_irq(unsigned irq);
+int B1_valid_irq(unsigned irq, int cardtype);
 void B1_handle_interrupt(avmb1_card * card);
 void B1_send_init(unsigned short port,
 	    unsigned int napps, unsigned int nncci, unsigned int cardnr);
@@ -133,8 +175,17 @@
 void avmb1_handle_capimsg(avmb1_card * card, __u16 appl, struct sk_buff *skb);
 void avmb1_card_ready(avmb1_card * card);
 
-int avmb1_addcard(int port, int irq);
-int avmb1_probecard(int port, int irq);
+/* standard calls, with check and allocation of resources */
+int avmb1_addcard(int port, int irq, int cardtype);
+int avmb1_probecard(int port, int irq, int cardtype);
+
+
+int avmb1_resetcard(int cardnr);
+
+/* calls for pcmcia driver */
+int avmb1_detectcard(int port, int irq, int cardtype);
+int avmb1_registercard(int port, int irq, int cardtype, int allocio);
+int avmb1_unregistercard(int cnr, int freeio);
 
 #endif				/* __KERNEL__ */
 
diff -u --recursive --new-file v2.1.91/linux/include/linux/binfmts.h linux/include/linux/binfmts.h
--- v2.1.91/linux/include/linux/binfmts.h	Wed Dec 10 11:38:21 1997
+++ linux/include/linux/binfmts.h	Wed Apr  1 17:30:31 1998
@@ -2,6 +2,7 @@
 #define _LINUX_BINFMTS_H
 
 #include <linux/ptrace.h>
+#include <linux/capability.h>
 
 /*
  * MAX_ARG_PAGES defines the number of pages allocated for arguments
@@ -21,6 +22,7 @@
 	int java;		/* Java binary, prevent recursive invocation */
 	struct dentry * dentry;
 	int e_uid, e_gid;
+	kernel_cap_t cap_inheritable, cap_permitted, cap_effective;
 	int argc, envc;
 	char * filename;	/* Name of binary */
 	unsigned long loader, exec;
@@ -62,6 +64,8 @@
 extern unsigned long setup_arg_pages(unsigned long p, struct linux_binprm * bprm);
 extern unsigned long copy_strings(int argc,char ** argv,unsigned long *page,
 		unsigned long p, int from_kmem);
+
+extern void compute_creds(struct linux_binprm *binprm);
 
 /* this eventually goes away */
 #define change_ldt(a,b) setup_arg_pages(a,b)
diff -u --recursive --new-file v2.1.91/linux/include/linux/blk.h linux/include/linux/blk.h
--- v2.1.91/linux/include/linux/blk.h	Mon Feb 23 18:12:11 1998
+++ linux/include/linux/blk.h	Wed Apr  1 17:30:46 1998
@@ -45,75 +45,33 @@
 #endif /* IDE_DRIVER */
 
 #define SUBSECTOR(block) (CURRENT->current_nr_sectors > 0)
-#ifdef CONFIG_CDROM
-extern int cdrom_init(void);
-#endif CONFIG_CDROM 
-#ifdef CONFIG_ISP16_CDI
+
+/*
+ * Initialization functions.
+ */
 extern int isp16_init(void);
-#endif CONFIG_ISP16_CDI
-#ifdef CONFIG_CDU31A
 extern int cdu31a_init(void);
-#endif CONFIG_CDU31A
-#ifdef CONFIG_ATARI_ACSI
 extern int acsi_init(void);
-#endif CONFIG_ATARI_ACSI
-#ifdef CONFIG_MCD
 extern int mcd_init(void);
-#endif CONFIG_MCD
-#ifdef CONFIG_MCDX
 extern int mcdx_init(void);
-#endif CONFIG_MCDX
-#ifdef CONFIG_SBPCD
 extern int sbpcd_init(void);
-#endif CONFIG_SBPCD
-#ifdef CONFIG_AZTCD
 extern int aztcd_init(void);
-#endif CONFIG_AZTCD
-#ifdef CONFIG_CDU535
 extern int sony535_init(void);
-#endif CONFIG_CDU535
-#ifdef CONFIG_GSCD
 extern int gscd_init(void);
-#endif CONFIG_GSCD
-#ifdef CONFIG_CM206
 extern int cm206_init(void);
-#endif CONFIG_CM206
-#ifdef CONFIG_OPTCD
 extern int optcd_init(void);
-#endif CONFIG_OPTCD
-#ifdef CONFIG_SJCD
 extern int sjcd_init(void);
-#endif CONFIG_SJCD
-#ifdef CONFIG_CDI_INIT
 extern int cdi_init(void);
-#endif CONFIG_CDI_INIT
-#ifdef CONFIG_BLK_DEV_HD
 extern int hd_init(void);
-#endif
-#ifdef CONFIG_BLK_DEV_IDE
 extern int ide_init(void);
-#endif
-#ifdef CONFIG_BLK_DEV_XD
 extern int xd_init(void);
-#endif
-#ifdef CONFIG_BLK_DEV_LOOP
 extern int loop_init(void);
-#endif
-#ifdef CONFIG_BLK_DEV_MD
 extern int md_init(void);
-#endif CONFIG_BLK_DEV_MD
-#ifdef CONFIG_APBLOCK
 extern int ap_init(void);
-#endif
-#ifdef CONFIG_DDV
 extern int ddv_init(void);
-#endif
-#ifdef CONFIG_AMIGA_Z2RAM
 extern int z2_init(void);
-#endif
-#ifdef CONFIG_MAC_FLOPPY
 extern int swim3_init(void);
-#endif
+extern int ps2esdi_init(void);
 
 extern void set_device_ro(kdev_t dev,int flag);
 void add_blkdev_randomness(int major);
@@ -134,9 +92,6 @@
 extern int initrd_below_start_ok; /* 1 if it is not an error if initrd_start < memory_start */
 void initrd_init(void);
 
-#endif
-#ifdef CONFIG_BLK_DEV_PS2
-extern int ps2esdi_init(void);
 #endif
 
 #define RO_IOCTLS(dev,where) \
diff -u --recursive --new-file v2.1.91/linux/include/linux/capability.h linux/include/linux/capability.h
--- v2.1.91/linux/include/linux/capability.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/capability.h	Wed Apr  1 17:30:31 1998
@@ -0,0 +1,206 @@
+/*
+ * This is <linux/capability.h>
+ *
+ * Andrew G. Morgan <morgan@parc.power.net>
+ * Alexander Kjeldaas <astor@guardian.no>
+ * with help from Aleph1, Roland Buresund and Andrew Main.
+ */ 
+
+#ifndef _LINUX_CAPABILITY_H
+#define _LINUX_CAPABILITY_H
+
+#include <linux/types.h>
+#include <linux/fs.h>
+
+/* User-level do most of the mapping between kernel and user
+   capabilities based on the version tag given by the kernel. The
+   kernel might be somewhat backwards compatible, but don't bet on
+   it. */
+
+#define _LINUX_CAPABILITY_VERSION  0x19980330
+
+typedef struct _user_cap_struct {
+	__u32 version;
+	__u32 size;
+	__u8  cap[1];
+} *cap_t;
+
+#ifdef __KERNEL__
+
+typedef struct kernel_cap_struct {
+	int cap;
+} kernel_cap_t;
+
+#endif
+
+
+/**
+ ** POSIX-draft defined capabilities. 
+ **/
+
+/* In a system with the [_POSIX_CHOWN_RESTRICTED] option defined, this
+   overrides the restriction of changing file ownership and group
+   ownership. */
+
+#define CAP_CHOWN            0
+
+/* Override all DAC access, including ACL execute access if
+   [_POSIX_ACL] is defined. Excluding DAC access covered by
+   CAP_LINUX_IMMUTABLE */
+
+#define CAP_DAC_OVERRIDE     1
+
+/* Overrides all DAC restrictions regarding read and search on files
+   and directories, including ACL restrictions if [_POSIX_ACL] is
+   defined. Excluding DAC access covered by CAP_LINUX_IMMUTABLE */
+
+#define CAP_DAC_READ_SEARCH  2
+    
+/* Overrides all restrictions about allowed operations on files, where
+   file owner ID must be equal to the user ID, except where CAP_FSETID
+   is applicable. It doesn't override MAC and DAC restrictions. */
+
+#define CAP_FOWNER           3
+
+/* Overrides the following restrictions that the effective user ID
+   shall match the file owner ID when setting the S_ISUID and S_ISGID
+   bits on that file; that the effective group ID (or one of the
+   supplementary group IDs shall match the file owner ID when setting
+   the S_ISGID bit on that file; that the S_ISUID and S_ISGID bits are
+   cleared on successful return from chown(2). */
+
+#define CAP_FSETID           4
+
+/* Used to decide between falling back on the old suser() or fsuser(). */
+
+#define CAP_FS_MASK          0x1f
+
+/* Overrides the restriction that the real or effective user ID of a
+   process sending a signal must match the real or effective user ID
+   of the process receiving the signal. */
+
+#define CAP_KILL             5
+
+/* Allows setgid(2) manipulation */
+
+#define CAP_SETGID           6
+
+/* Allows setuid(2) manipulation */
+
+#define CAP_SETUID           7
+
+
+/**
+ ** Linux-specific capabilities
+ **/
+
+/* Transfer any capability in your permitted set to any pid,
+   remove any capability in your permitted set from any pid */
+
+#define CAP_SETPCAP          8
+
+/* Allow modification of S_IMMUTABLE and S_APPEND file attributes */
+
+#define CAP_LINUX_IMMUTABLE  9
+
+/* Allows binding to TCP/UDP sockets below 1024 */
+
+#define CAP_NET_BIND_SERVICE 10
+
+/* Allow broadcasting, listen to multicast */
+
+#define CAP_NET_BROADCAST    11
+
+/* Allow interface configuration */
+/* Allow configuring of firewall stuff */
+/* Allow setting debug option on sockets */
+/* Allow modification of routing tables */
+
+#define CAP_NET_ADMIN        12
+
+/* Allow use of RAW sockets */
+/* Allow use of PACKET sockets */
+
+#define CAP_NET_RAW          13
+
+/* Allow locking of segments in memory */
+
+#define CAP_IPC_LOCK         14
+
+/* Override IPC ownership checks */
+
+#define CAP_IPC_OWNER        15
+
+/* Insert and remove kernel modules */
+
+#define CAP_SYS_MODULE       16
+
+/* Allow ioperm/iopl access */
+
+#define CAP_SYS_RAWIO        17
+
+/* Allow use of chroot() */
+
+#define CAP_SYS_CHROOT       18
+
+/* Allow ptrace() of any process */
+
+#define CAP_SYS_PTRACE       19
+
+/* Allow configuration of process accounting */
+
+#define CAP_SYS_PACCT        20
+
+/* Allow configuration of the secure attention key */
+/* Allow administration of the random device */
+/* Allow device administration */
+/* Allow examination and configuration of disk quotas */
+/* System Admin functions: mount et al */
+
+#define CAP_SYS_ADMIN        21
+
+/* Allow use of reboot() */
+
+#define CAP_SYS_BOOT         22
+
+/* Allow use of renice() on others, and raising of priority */
+
+#define CAP_SYS_NICE         23
+
+/* Override resource limits */
+
+#define CAP_SYS_RESOURCE     24
+
+/* Allow manipulation of system clock */
+
+#define CAP_SYS_TIME         25
+
+/* Allow configuration of tty devices */
+
+#define CAP_SYS_TTY_CONFIG   26
+
+#ifdef __KERNEL__
+
+/*
+ * Internal kernel functions only
+ */
+
+#define CAP_EMPTY_SET       {  0 }
+#define CAP_FULL_SET        { ~0 }
+
+#define CAP_TO_MASK(x) (1 << (x))
+#define cap_raise(c, flag)   (c.cap |=  CAP_TO_MASK(flag))
+#define cap_lower(c, flag)   (c.cap &= ~CAP_TO_MASK(flag))
+#define cap_raised(c, flag)  (c.cap &   CAP_TO_MASK(flag))
+
+#define cap_isclear(c) (!c.cap)
+
+#define cap_copy(dest,src) do { (dest).cap = (src).cap; } while(0)
+#define cap_clear(c)       do {  c.cap =  0; } while(0)
+#define cap_set_full(c)    do {  c.cap = ~0; } while(0)
+
+#define cap_is_fs_cap(c)     ((c) & CAP_FS_MASK)
+
+#endif /* __KERNEL__ */
+
+#endif /* !_LINUX_CAPABILITY_H */
diff -u --recursive --new-file v2.1.91/linux/include/linux/concap.h linux/include/linux/concap.h
--- v2.1.91/linux/include/linux/concap.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/concap.h	Wed Apr  1 16:20:56 1998
@@ -0,0 +1,113 @@
+/* $Id: concap.h,v 1.1 1998/02/01 00:15:11 keil Exp $
+*/
+#ifndef _LINUX_CONCAP_H
+#define _LINUX_CONCAP_H
+#ifdef __KERNEL__
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+
+/* Stuff to support encapsulation protocols genericly. The encapsulation
+   protocol is processed at the uppermost layer of the network interface.
+
+   (c) 1997 by Henner Eisen <eis@baty.hanse.de>
+   This software is subject to the GNU General Public License.
+
+   Based on a ideas developed in a 'synchronous device' thread in the
+   linux-x25 mailing list contributed by Alan Cox, Thomasz Motylewski
+   and Jonathan Naylor.
+
+   For more documetation on this refer to Documentation/isdn/README.concap
+   */
+
+struct concap_proto_ops;
+struct concap_device_ops;
+
+/* this manages all data needed by the encapsulation protocol
+ */
+struct concap_proto{
+	struct device *net_dev;		/* net device using our service  */
+	struct concap_device_ops *dops; /* callbacks provided by device */
+ 	struct concap_proto_ops  *pops; /* callbacks provided by us */
+	int flags;
+	void *proto_data;               /* protocol specific private data, to
+					   be accessed via *pops methods only*/
+	/*
+	  :
+	  whatever 
+	  :
+	  */
+};
+
+/* Operations to be supported by the net device. Called by the encapsulation
+ * protocol entity. No receive method is offered because the encapsulation
+ * protocol directly calls netif_rx().
+ */
+struct concap_device_ops{
+
+	/* to request data is submitted by device*/ 
+	int (*data_req)(struct concap_proto *, struct sk_buff *);
+
+	/* Control methods must be set to NULL by devices which do not
+	   support connection control.*/
+	/* to request a connection is set up */ 
+	int (*connect_req)(struct concap_proto *);
+
+	/* to request a connection is released */
+	int (*disconn_req)(struct concap_proto *);	
+};
+
+/* Operations to be supported by the encapsulation protocol. Called by
+ * device driver.
+ */
+struct concap_proto_ops{
+
+	/* create a new encapsulation protocol instance of same type */
+	struct concap_proto *  (*proto_new) (void);
+
+	/* delete encapsulation protocol instance and free all its resources.
+	   cprot may no loger be referenced after calling this */
+	void (*proto_del)(struct concap_proto *cprot);
+
+	/* initialize the protocol's data. To be called at interface startup
+	   or when the device driver resets the interface. All services of the
+	   encapsulation protocol may be used after this*/
+	int (*restart)(struct concap_proto *cprot, 
+		       struct device *ndev,
+		       struct concap_device_ops *dops);
+
+	/* inactivate an encapsulation protocol instance. The encapsulation
+	   protocol may not call any *dops methods after this. */
+	int (*close)(struct concap_proto *cprot);
+
+	/* process a frame handed down to us by upper layer */
+	int (*encap_and_xmit)(struct concap_proto *cprot, struct sk_buff *skb);
+
+	/* to be called for each data entity received from lower layer*/ 
+	int (*data_ind)(struct concap_proto *cprot, struct sk_buff *skb);
+
+	/* to be called when a connection was set up/down.
+	   Protocols that don't process these primitives might fill in
+	   dummy methods here */
+	int (*connect_ind)(struct concap_proto *cprot);
+	int (*disconn_ind)(struct concap_proto *cprot);
+  /*
+    Some network device support functions, like net_header(), rebuild_header(),
+    and others, that depend solely on the encapsulation protocol, might
+    be provided here, too. The net device would just fill them in its
+    corresponding fields when it is opened.
+    */
+};
+
+/* dummy restart/close/connect/reset/disconn methods
+ */
+extern int concap_nop(struct concap_proto *cprot); 
+
+/* dummy submit method
+ */
+extern int concap_drop_skb(struct concap_proto *cprot, struct sk_buff *skb);
+#endif
+#endif
+
+
+
+
diff -u --recursive --new-file v2.1.91/linux/include/linux/console.h linux/include/linux/console.h
--- v2.1.91/linux/include/linux/console.h	Tue Jan 20 16:44:58 1998
+++ linux/include/linux/console.h	Wed Apr  1 14:58:55 1998
@@ -80,7 +80,7 @@
  */
 
 #define CON_PRINTBUFFER	(1)
-#define CON_FIRST	(2)
+#define CON_CONSDEV	(2) /* Last on the command line */
 #define CON_ENABLED	(4)
 
 struct console
diff -u --recursive --new-file v2.1.91/linux/include/linux/if_ppp.h linux/include/linux/if_ppp.h
--- v2.1.91/linux/include/linux/if_ppp.h	Mon Jan 12 15:28:18 1998
+++ linux/include/linux/if_ppp.h	Wed Apr  1 16:19:57 1998
@@ -73,12 +73,9 @@
 #define SC_LOG_OUTPKT	0x00040000	/* log contents of pkts sent */
 #define SC_LOG_RAWIN	0x00080000	/* log all chars received */
 #define SC_LOG_FLUSH	0x00100000	/* log all chars flushed */
-#define	SC_MASK		0x0fE0ffff	/* bits that user can change */
+#define	SC_MASK		0x0f0000ff	/* bits that user can change */
 
 /* state bits */
-#define	SC_ESCAPED	0x80000000	/* saw a PPP_ESCAPE */
-#define	SC_FLUSH	0x40000000	/* flush input until next PPP_FLAG */
-#define SC_VJ_RESET	0x20000000	/* Need to reset the VJ decompressor */
 #define SC_XMIT_BUSY	0x10000000	/* ppp_write_wakeup is active */
 #define SC_RCV_ODDP	0x08000000	/* have rcvd char with odd parity */
 #define SC_RCV_EVNP	0x04000000	/* have rcvd char with even parity */
diff -u --recursive --new-file v2.1.91/linux/include/linux/isdn.h linux/include/linux/isdn.h
--- v2.1.91/linux/include/linux/isdn.h	Fri Jan 30 11:28:10 1998
+++ linux/include/linux/isdn.h	Wed Apr  1 16:20:56 1998
@@ -1,4 +1,10 @@
-/* $Id: isdn.h,v 1.29 1997/05/27 15:18:02 fritz Exp $
+/* Changes for X.25 support:
+   Added ISDN_NET_ENCAP_X25IFACE macro.
+   Additional field in isdn_net_dev_s and isdn_net_local to support
+   generic encapsulation protocols. 
+*/
+
+/* $Id: isdn.h,v 1.37 1998/02/22 19:45:24 fritz Exp $
  *
  * Main header for the Linux ISDN subsystem (linklevel).
  *
@@ -21,6 +27,37 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: isdn.h,v $
+ * Revision 1.37  1998/02/22 19:45:24  fritz
+ * Some changes regarding V.110
+ *
+ * Revision 1.36  1998/02/20 17:35:55  fritz
+ * Added V.110 stuff.
+ *
+ * Revision 1.35  1998/01/31 22:14:14  keil
+ * changes for 2.1.82
+ *
+ * Revision 1.34  1997/10/09 21:28:11  fritz
+ * New HL<->LL interface:
+ *   New BSENT callback with nr. of bytes included.
+ *   Sending without ACK.
+ *   New L1 error status (not yet in use).
+ *   Cleaned up obsolete structures.
+ * Implemented Cisco-SLARP.
+ * Changed local net-interface data to be dynamically allocated.
+ * Removed old 2.0 compatibility stuff.
+ *
+ * Revision 1.33  1997/08/21 14:44:22  fritz
+ * Moved triggercps to end of struct for backwards-compatibility.
+ *
+ * Revision 1.32  1997/08/21 09:49:46  fritz
+ * Increased NET_DV
+ *
+ * Revision 1.31  1997/06/22 11:57:07  fritz
+ * Added ability to adjust slave triggerlevel.
+ *
+ * Revision 1.30  1997/06/17 13:07:23  hipp
+ * compression changes , MP changes
+ *
  * Revision 1.29  1997/05/27 15:18:02  fritz
  * Added changes for recent 2.1.x kernels:
  *   changed return type of isdn_close
@@ -181,13 +218,15 @@
 #define IIOCDRVCTL  _IO('I',128)
 
 /* Packet encapsulations for net-interfaces */
-#define ISDN_NET_ENCAP_ETHER     0
-#define ISDN_NET_ENCAP_RAWIP     1
-#define ISDN_NET_ENCAP_IPTYP     2
-#define ISDN_NET_ENCAP_CISCOHDLC 3
-#define ISDN_NET_ENCAP_SYNCPPP   4
-#define ISDN_NET_ENCAP_UIHDLC    5
-
+#define ISDN_NET_ENCAP_ETHER      0
+#define ISDN_NET_ENCAP_RAWIP      1
+#define ISDN_NET_ENCAP_IPTYP      2
+#define ISDN_NET_ENCAP_CISCOHDLC  3 /* Without SLARP and keepalive */
+#define ISDN_NET_ENCAP_SYNCPPP    4
+#define ISDN_NET_ENCAP_UIHDLC     5
+#define ISDN_NET_ENCAP_CISCOHDLCK 6 /* With SLARP and keepalive    */
+#define ISDN_NET_ENCAP_X25IFACE   7 /* Documentation/networking/x25-iface.txt*/
+#define ISDN_NET_ENCAP_MAX_ENCAP  ISDN_NET_ENCAP_X25IFACE
 /* Facility which currently uses an ISDN-channel */
 #define ISDN_USAGE_NONE       0
 #define ISDN_USAGE_RAW        1
@@ -219,7 +258,7 @@
   int  outgoing;
 } isdn_net_ioctl_phone;
 
-#define NET_DV 0x02 /* Data version for net_cfg     */
+#define NET_DV 0x04 /* Data version for net_cfg     */
 #define TTY_DV 0x04 /* Data version for iprofd etc. */
 
 typedef struct {
@@ -244,6 +283,7 @@
   int  cbhup;        /* Flag: Reject Call before Callback     */
   int  pppbind;      /* ippp device for bindings              */
   int  chargeint;    /* Use fixed charge interval length      */
+  int  triggercps;   /* BogoCPS needed for triggering slave   */
 } isdn_net_ioctl_cfg;
 
 #ifdef __KERNEL__
@@ -287,6 +327,10 @@
 #include <linux/isdn_ppp.h>
 #endif
 
+#ifdef CONFIG_ISDN_X25
+#  include <linux/concap.h>
+#endif
+
 #include <linux/isdnif.h>
 
 #define ISDN_DRVIOCTL_MASK       0x7f  /* Mask for Device-ioctl */
@@ -317,21 +361,23 @@
                              ((x & ISDN_USAGE_MASK)==ISDN_USAGE_VOICE)     )
 
 /* Timer-delays and scheduling-flags */
-#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
-#define ISDN_TIMER_MODEMXMIT 8
-#define ISDN_TIMER_NETDIAL   16
-#define ISDN_TIMER_NETHANGUP 32
-#define ISDN_TIMER_IPPP      64
+#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_KEEPINT    10 /* Cisco-Keepalive = ISDN_TIMER_1SEC * this factor */
+#define ISDN_TIMER_MODEMREAD   1
+#define ISDN_TIMER_MODEMPLUS   2
+#define ISDN_TIMER_MODEMRING   4
+#define ISDN_TIMER_MODEMXMIT   8
+#define ISDN_TIMER_NETDIAL    16 
+#define ISDN_TIMER_NETHANGUP  32
+#define ISDN_TIMER_IPPP       64 
+#define ISDN_TIMER_KEEPALIVE 128 /* Cisco-Keepalive */
 #define ISDN_TIMER_FAST      (ISDN_TIMER_MODEMREAD | ISDN_TIMER_MODEMPLUS | \
                               ISDN_TIMER_MODEMXMIT)
 #define ISDN_TIMER_SLOW      (ISDN_TIMER_MODEMRING | ISDN_TIMER_NETHANGUP | \
-                              ISDN_TIMER_NETDIAL)
+                              ISDN_TIMER_NETDIAL | ISDN_TIMER_KEEPALIVE)
 
 /* Timeout-Values for isdn_net_dial() */
 #define ISDN_TIMER_DTIMEOUT10 (10*HZ/(ISDN_TIMER_02SEC*(ISDN_TIMER_RES+1)))
@@ -362,6 +408,15 @@
   char num[ISDN_MSNLEN];
 } isdn_net_phone;
 
+/*
+   Principles when extending structures for generic encapsulation protocol
+   ("concap") support:
+   - Stuff which is hardware specific (here i4l-specific) goes in 
+     the netdev -> local structure (here: isdn_net_local)
+   - Stuff which is encapsulation protocol specific goes in the structure
+     which holds the linux device structure (here: isdn_net_device)
+*/
+
 /* Local interface-data */
 typedef struct isdn_net_local_s {
   ulong                  magic;
@@ -413,6 +468,7 @@
   int                    sqfull;       /* Flag: netdev-queue overloaded    */
   ulong                  sqfull_stamp; /* Start-Time of overload           */
   ulong                  slavedelay;   /* Dynamic bundling delaytime       */
+  int                    triggercps;   /* BogoCPS needed for trigger slave */
   struct device          *srobin;      /* Ptr to Master device for slaves  */
   isdn_net_phone         *phone[2];    /* List of remote-phonenumbers      */
 				       /* phone[0] = Incoming Numbers      */
@@ -425,16 +481,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*/
-
                                        /* Ptr to orig. hard_header_cache   */
-  int                    (*org_hhc)(struct neighbour *neigh,
+  int                    (*org_hhc)(
+				    struct neighbour *neigh,
 				    struct hh_cache *hh);
-
                                        /* Ptr to orig. header_cache_update */
   void                   (*org_hcu)(struct hh_cache *,
 				    struct device *,
                                     unsigned char *);
   int  pppbind;                        /* ippp device for bindings         */
+#ifdef CONFIG_ISDN_X25
+  struct concap_device_ops *dops;      /* callbacks used by encapsulator   */
+#endif
+  int  cisco_loop;                     /* Loop counter for Cisco-SLARP     */
+  ulong cisco_myseq;                   /* Local keepalive seq. for Cisco   */
+  ulong cisco_yourseq;                 /* Remote keepalive seq. for Cisco  */
 } isdn_net_local;
 
 #ifdef CONFIG_ISDN_PPP
@@ -451,14 +512,18 @@
 
 /* the interface itself */
 typedef struct isdn_net_dev_s {
-  isdn_net_local  local;
+  isdn_net_local *local;
   isdn_net_local *queue;
   void           *next;                /* Pointer to next isdn-interface   */
-  struct device   dev;	               /* interface to upper levels        */
+  struct device   dev;	       /* interface to upper levels        */
 #ifdef CONFIG_ISDN_PPP
   struct mpqueue *mp_last; 
   struct ippp_bundle ib;
 #endif
+#ifdef CONFIG_ISDN_X25
+  struct concap_proto  *cprot; /* connection oriented encapsulation protocol */
+#endif
+
 } isdn_net_dev;
 
 /*===================== End of ip-over-ISDN stuff ===========================*/
@@ -477,7 +542,8 @@
 #define ISDN_ASYNC_PGRP_LOCKOUT       0x0200 /* Lock cua opens on pgrp       */
 #define ISDN_ASYNC_CALLOUT_NOHUP      0x0400 /* No hangup for cui            */
 #define ISDN_ASYNC_SPLIT_TERMIOS      0x0008 /* Sep. termios for dialin/out  */
-#define ISDN_SERIAL_XMIT_SIZE           4000 /* Maximum bufsize for write    */
+#define ISDN_SERIAL_XMIT_SIZE           1024 /* Default bufsize for write    */
+#define ISDN_SERIAL_XMIT_MAX            4000 /* Maximum bufsize for write    */
 #define ISDN_SERIAL_TYPE_NORMAL            1
 #define ISDN_SERIAL_TYPE_CALLOUT           2
 
@@ -499,18 +565,19 @@
 
 /* Private data of AT-command-interpreter */
 typedef struct atemu {
-  u_char              profile[ISDN_MODEM_ANZREG]; /* Modem-Regs. Profile 0 */
-  u_char              mdmreg[ISDN_MODEM_ANZREG];  /* Modem-Registers       */
-  char                pmsn[ISDN_MSNLEN]; /* EAZ/MSNs Profile 0             */
-  char                msn[ISDN_MSNLEN];/* EAZ/MSN                          */
+	u_char       profile[ISDN_MODEM_ANZREG]; /* Modem-Regs. Profile 0              */
+	u_char       mdmreg[ISDN_MODEM_ANZREG];  /* Modem-Registers                    */
+	char         pmsn[ISDN_MSNLEN];          /* EAZ/MSNs Profile 0                 */
+	char         msn[ISDN_MSNLEN];           /* EAZ/MSN                            */
 #ifdef CONFIG_ISDN_AUDIO
-  u_char              vpar[10];        /* Voice-parameters                 */
-  int                 lastDLE;         /* Flag for voice-coding: DLE seen  */
+	u_char       vpar[10];                   /* Voice-parameters                   */
+	int          lastDLE;                    /* Flag for voice-coding: DLE seen    */
 #endif
-  int                 mdmcmdl;         /* Length of Modem-Commandbuffer    */
-  int                 pluscount;       /* Counter for +++ sequence         */
-  int                 lastplus;        /* Timestamp of last +              */
-  char                mdmcmd[255];     /* Modem-Commandbuffer              */
+	int          mdmcmdl;                    /* Length of Modem-Commandbuffer      */
+	int          pluscount;                  /* Counter for +++ sequence           */
+	int          lastplus;                   /* Timestamp of last +                */
+	char         mdmcmd[255];                /* Modem-Commandbuffer                */
+	unsigned int charge;                     /* Charge units of current connection */
 } atemu;
 
 /* Private data (similar to async_struct in <linux/serial.h>) */
@@ -590,8 +657,8 @@
 
 struct sqqueue {
   struct sqqueue *next;
-  int sqno_start;
-  int sqno_end;
+  long sqno_start;
+  long sqno_end;
   struct sk_buff *skb;
   long timer;
 };
@@ -599,7 +666,7 @@
 struct mpqueue {
   struct mpqueue *next;
   struct mpqueue *last;
-  int    sqno;
+  long sqno;
   struct sk_buff *skb;
   int BEbyte;
   unsigned long time;
@@ -638,18 +705,44 @@
   struct slcompress *slcomp;
 #endif
   unsigned long debug;
-  struct isdn_ppp_compressor *compressor;
+  struct isdn_ppp_compressor *compressor,*link_compressor;
+  void *decomp_stat,*comp_stat,*link_decomp_stat,*link_comp_stat;
 };
 
 #endif
 
 /*======================== End of sync-ppp stuff ===========================*/
 
+/*======================== Start of V.110 stuff ============================*/
+#define V110_BUFSIZE 1024
+
+typedef struct {
+	int nbytes;                    /* 1 Matrixbyte -> nbytes in stream     */
+	int nbits;                     /* Number of used bits in streambyte    */
+	unsigned char key;             /* Bitmask in stream eg. 11 (nbits=2)   */
+	int decodelen;                 /* Amount of data in decodebuf          */
+	int SyncInit;                  /* Number of sync frames to send        */
+	unsigned char *OnlineFrame;    /* Precalculated V110 idle frame        */
+	unsigned char *OfflineFrame;   /* Precalculated V110 sync Frame        */
+	int framelen;                  /* Length of frames                     */
+	int skbuser;                   /* Number of unacked userdata skbs      */
+	int skbidle;                   /* Number of unacked idle/sync skbs     */
+	int introducer;                /* Local vars for decoder               */
+	int dbit;
+	unsigned char b;
+	int skbres;                    /* space to reserve in outgoing skb     */
+	int maxsize;                   /* maxbufsize of lowlevel driver        */
+	unsigned char *encodebuf;      /* temporary buffer for encoding        */
+	unsigned char decodebuf[V110_BUFSIZE]; /* incomplete V110 matrices     */
+} isdn_v110_stream;
+
+/*========================= End of V.110 stuff =============================*/
+
 /*======================= Start of general stuff ===========================*/
 
 typedef struct {
-  char *next;
-  char *private;
+	char *next;
+	char *private;
 } infostruct;
 
 /* Description of hardware-level-driver */
@@ -704,6 +797,9 @@
   isdn_net_dev      *st_netdev[ISDN_MAX_CHANNELS]; /* stat netdev-pointers   */
   ulong             ibytes[ISDN_MAX_CHANNELS]; /* Statistics incoming bytes  */
   ulong             obytes[ISDN_MAX_CHANNELS]; /* Statistics outgoing bytes  */
+  int               v110emu[ISDN_MAX_CHANNELS];/* V.110 emulator-mode 0=none */
+  atomic_t          v110use[ISDN_MAX_CHANNELS];/* Usage-Semaphore for stream */
+  isdn_v110_stream  *v110[ISDN_MAX_CHANNELS];  /* V.110 private data         */
 } isdn_dev;
 
 extern isdn_dev *dev;
diff -u --recursive --new-file v2.1.91/linux/include/linux/isdn_ppp.h linux/include/linux/isdn_ppp.h
--- v2.1.91/linux/include/linux/isdn_ppp.h	Thu May 29 21:53:11 1997
+++ linux/include/linux/isdn_ppp.h	Wed Apr  1 16:20:56 1998
@@ -1,23 +1,8 @@
 #ifndef _LINUX_ISDN_PPP_H
 #define _LINUX_ISDN_PPP_H
 
-struct isdn_ppp_compressor
-{
-    struct isdn_ppp_compressor *next,*prev;
-    int num;    /* proto num */
-    void *priv; /* private data for compressor */
-	int (*open)(struct isdn_ppp_compressor *);
-	int (*close)(struct isdn_ppp_compressor *);
-	int (*reset)(struct isdn_ppp_compressor *,int type);
-	int (*config)(struct isdn_ppp_compressor *,void *data,int data_len);
-	struct sk_buff *(*compress)(struct isdn_ppp_compressor *,struct sk_buff *);
-	struct sk_buff *(*uncompress)(struct isdn_ppp_compressor *,struct sk_buff *);
-};
-
 extern int isdn_ppp_dial_slave(char *);
 extern int isdn_ppp_hangup_slave(char *);
-extern int isdn_ppp_register_compressor(struct isdn_ppp_compressor *);
-extern int isdn_ppp_unregister_compressor(struct isdn_ppp_compressor *);
 
 #define CALLTYPE_INCOMING 0x1
 #define CALLTYPE_OUTGOING 0x2
@@ -31,18 +16,17 @@
 	int charge_units;
 };
 
- 
 #define PPPIOCGCALLINFO _IOWR('t',128,struct pppcallinfo)
 #define PPPIOCBUNDLE   _IOW('t',129,int)
 #define PPPIOCGMPFLAGS _IOR('t',130,int)
 #define PPPIOCSMPFLAGS _IOW('t',131,int)
 #define PPPIOCSMPMTU   _IOW('t',132,int)
 #define PPPIOCSMPMRU   _IOW('t',133,int)
+#define PPPIOCGCOMPRESSORS _IOR('t',134,unsigned long)
+#define PPPIOCSCOMPRESSOR _IOW('t',135,int)
 
-#define PPPIOCGCOMPRESSORS  _IOR('t',134,unsigned long)
-#define PPPIOCSCOMPRESSOR   _IOW('t',135,int)
-
-#define PPP_MP         0x003d
+#define PPP_MP          0x003d
+#define PPP_LINK_COMP   0x00fb
 
 #define SC_MP_PROT       0x00000200
 #define SC_REJ_MP_PROT   0x00000400
@@ -52,4 +36,36 @@
 #define MP_END_FRAG    0x40
 #define MP_BEGIN_FRAG  0x80
 
-#endif
+#ifdef __KERNEL__
+/*
+ * this is an 'old friend' from ppp-comp.h under a new name 
+ * check the original include for more information
+ */
+struct isdn_ppp_compressor {
+	struct isdn_ppp_compressor *next,*prev;
+	int num; /* CCP compression protocol number */
+	void *(*comp_alloc) (unsigned char *options, int opt_len);
+	void (*comp_free) (void *state);
+	int  (*comp_init) (void *state, unsigned char *options, int opt_len,
+		 int unit, int opthdr, int debug);
+	void (*comp_reset) (void *state);
+	int  (*compress) (void *state,struct sk_buff *in, struct sk_buff *skb_out,
+		 int proto);
+	void (*comp_stat) (void *state, struct compstat *stats);
+	void *(*decomp_alloc) (unsigned char *options, int opt_len);
+	void (*decomp_free) (void *state);
+	int  (*decomp_init) (void *state, unsigned char *options,
+			int opt_len, int unit, int opthdr, int mru, int debug);
+	void (*decomp_reset) (void *state);
+	int  (*decompress) (void *state, unsigned char *ibuf, int isize, unsigned char *obuf, int osize);
+	void (*incomp) (void *state, unsigned char *ibuf, int icnt);
+	void (*decomp_stat) (void *state, struct compstat *stats);
+};
+
+extern int isdn_ppp_register_compressor(struct isdn_ppp_compressor *);
+extern int isdn_ppp_unregister_compressor(struct isdn_ppp_compressor *);
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_ISDN_PPP_H */
+
diff -u --recursive --new-file v2.1.91/linux/include/linux/isdnif.h linux/include/linux/isdnif.h
--- v2.1.91/linux/include/linux/isdnif.h	Mon Nov  3 10:08:48 1997
+++ linux/include/linux/isdnif.h	Wed Apr  1 16:20:56 1998
@@ -1,4 +1,8 @@
-/* $Id: isdnif.h,v 1.20 1997/05/27 15:18:06 fritz Exp $
+/* X25 changes:
+   Added constants ISDN_PROTO_L2_X25DTE/DCE and corresponding ISDN_FEATURE_..
+   */
+
+/* $Id: isdnif.h,v 1.23 1998/02/20 17:36:52 fritz Exp $
  *
  * Linux ISDN subsystem
  *
@@ -22,6 +26,22 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: isdnif.h,v $
+ * Revision 1.23  1998/02/20 17:36:52  fritz
+ * Added L2-protocols for V.110, changed FEATURE-Flag-constants.
+ *
+ * Revision 1.22  1998/01/31 22:14:12  keil
+ * changes for 2.1.82
+ *
+ * Revision 1.21  1997/10/09 21:28:13  fritz
+ * New HL<->LL interface:
+ *   New BSENT callback with nr. of bytes included.
+ *   Sending without ACK.
+ *   New L1 error status (not yet in use).
+ *   Cleaned up obsolete structures.
+ * Implemented Cisco-SLARP.
+ * Changed local net-interface data to be dynamically allocated.
+ * Removed old 2.0 compatibility stuff.
+ *
  * Revision 1.20  1997/05/27 15:18:06  fritz
  * Added changes for recent 2.1.x kernels:
  *   changed return type of isdn_close
@@ -104,20 +124,28 @@
 #define ISDN_PTYPE_EURO      2   /* EDSS1-protocol       */
 #define ISDN_PTYPE_LEASED    3   /* for leased lines     */
 #define ISDN_PTYPE_NI1       4   /* US NI-1 protocol     */
+#define ISDN_PTYPE_MAX       7   /* Max. 8 Protocols     */
 
 /*
  * Values for Layer-2-protocol-selection
  */
-#define ISDN_PROTO_L2_X75I   0   /* X75/LAPB with I-Frames      */
-#define ISDN_PROTO_L2_X75UI  1   /* X75/LAPB with UI-Frames     */
-#define ISDN_PROTO_L2_X75BUI 2   /* X75/LAPB with UI-Frames     */
-#define ISDN_PROTO_L2_HDLC   3   /* HDLC                        */
-#define ISDN_PROTO_L2_TRANS  4   /* Transparent (Voice)         */
+#define ISDN_PROTO_L2_X75I   0   /* X75/LAPB with I-Frames            */
+#define ISDN_PROTO_L2_X75UI  1   /* X75/LAPB with UI-Frames           */
+#define ISDN_PROTO_L2_X75BUI 2   /* X75/LAPB with UI-Frames           */
+#define ISDN_PROTO_L2_HDLC   3   /* HDLC                              */
+#define ISDN_PROTO_L2_TRANS  4   /* Transparent (Voice)               */
+#define ISDN_PROTO_L2_X25DTE 5   /* X25/LAPB DTE mode                 */
+#define ISDN_PROTO_L2_X25DCE 6   /* X25/LAPB DCE mode                 */
+#define ISDN_PROTO_L2_V11096 7   /* V.110 bitrate adaption 9600 Baud  */
+#define ISDN_PROTO_L2_V11019 8   /* V.110 bitrate adaption 19200 Baud */
+#define ISDN_PROTO_L2_V11038 9   /* V.110 bitrate adaption 38400 Baud */
+#define ISDN_PROTO_L2_MAX    15  /* Max. 16 Protocols                 */
 
 /*
  * Values for Layer-3-protocol-selection
  */
 #define ISDN_PROTO_L3_TRANS  0   /* Transparent                 */
+#define ISDN_PROTO_L3_MAX    7   /* Max. 8 Protocols            */
 
 #ifdef __KERNEL__
 
@@ -127,7 +155,7 @@
  * Commands from linklevel to lowlevel
  *
  */
-#define ISDN_CMD_IOCTL   0       /* Perform ioctl                         */
+#define ISDN_CMD_IOCTL    0       /* Perform ioctl                         */
 #define ISDN_CMD_DIAL     1       /* Dial out                              */
 #define ISDN_CMD_ACCEPTD  2       /* Accept an incoming call on D-Chan.    */
 #define ISDN_CMD_ACCEPTB  3       /* Request B-Channel connect.            */
@@ -166,6 +194,13 @@
 #define ISDN_STAT_NODCH   268    /* Signal no D-Channel                   */
 #define ISDN_STAT_ADDCH   269    /* Add more Channels                     */
 #define ISDN_STAT_CAUSE   270    /* Cause-Message                         */
+#define ISDN_STAT_L1ERR   271    /* Signal Layer-1 Error                  */
+
+/*
+ * Values for errcode field
+ */
+#define ISDN_STAT_L1ERR_SEND 1
+#define ISDN_STAT_L1ERR_RECV 2
 
 /*
  * Values for feature-field of interface-struct.
@@ -176,15 +211,29 @@
 #define ISDN_FEATURE_L2_X75BUI  (0x0001 << ISDN_PROTO_L2_X75BUI)
 #define ISDN_FEATURE_L2_HDLC    (0x0001 << ISDN_PROTO_L2_HDLC)
 #define ISDN_FEATURE_L2_TRANS   (0x0001 << ISDN_PROTO_L2_TRANS)
+#define ISDN_FEATURE_L2_X25DTE  (0x0001 << ISDN_PROTO_L2_X25DTE)
+#define ISDN_FEATURE_L2_X25DCE  (0x0001 << ISDN_PROTO_L2_X25DCE)
+#define ISDN_FEATURE_L2_V11096  (0x0001 << ISDN_PROTO_L2_V11096)
+#define ISDN_FEATURE_L2_V11019  (0x0001 << ISDN_PROTO_L2_V11019)
+#define ISDN_FEATURE_L2_V11038  (0x0001 << ISDN_PROTO_L2_V11038)
+
+#define ISDN_FEATURE_L2_MASK    (0x0FFFF) /* Max. 16 protocols */
+#define ISDN_FEATURE_L2_SHIFT   (0)
 
 /* Layer 3 */
-#define ISDN_FEATURE_L3_TRANS   (0x0100 << ISDN_PROTO_L3_TRANS)
+#define ISDN_FEATURE_L3_TRANS   (0x10000 << ISDN_PROTO_L3_TRANS)
+
+#define ISDN_FEATURE_L3_MASK    (0x0FF0000) /* Max. 8 Protocols */
+#define ISDN_FEATURE_L3_SHIFT   (16)
 
 /* Signaling */
-#define ISDN_FEATURE_P_UNKNOWN  (0x1000 << ISDN_PTYPE_UNKNOWN)
-#define ISDN_FEATURE_P_1TR6     (0x1000 << ISDN_PTYPE_1TR6)
-#define ISDN_FEATURE_P_EURO     (0x1000 << ISDN_PTYPE_EURO)
-#define ISDN_FEATURE_P_NI1      (0x1000 << ISDN_PTYPE_NI1)
+#define ISDN_FEATURE_P_UNKNOWN  (0x1000000 << ISDN_PTYPE_UNKNOWN)
+#define ISDN_FEATURE_P_1TR6     (0x1000000 << ISDN_PTYPE_1TR6)
+#define ISDN_FEATURE_P_EURO     (0x1000000 << ISDN_PTYPE_EURO)
+#define ISDN_FEATURE_P_NI1      (0x1000000 << ISDN_PTYPE_NI1)
+
+#define ISDN_FEATURE_P_MASK     (0x0FF000000) /* Max. 8 Protocols */
+#define ISDN_FEATURE_P_SHIFT    (24)
 
 typedef struct setup_parm {
     char phone[32];         /* Remote Phone-Number */
@@ -204,6 +253,8 @@
   int   command;                 /* Command or Status (see above)         */
   ulong arg;                     /* Additional Data                       */
   union {
+	ulong errcode;               /* Type of error with STAT_L1ERR         */
+	int   length;                /* Amount of bytes sent with STAT_BSENT  */
 	char  num[50];               /* Additional Data                       */
 	setup_parm setup;
   } parm;
@@ -238,19 +289,6 @@
    */
   unsigned short hl_hdrlen;
 
-  /* Receive-Callback
-   * Parameters:
-   *             int    Driver-ID
-   *             int    local channel-number (0 ...)
-   *             u_char pointer to received data (in Kernel-Space, volatile)
-   *             int    length of data
-   *
-   * NOTE: This callback is obsolete, and will be removed when all
-   *       current LL-drivers support rcvcall_skb. Do NOT use for new
-   *       drivers.
-   */
-  void (*rcvcallb)(int, int, u_char*, int);
-
   /*
    * Receive-Callback using sk_buff's
    * Parameters:
@@ -269,6 +307,7 @@
    *                   num     = depending on status-type.
    */
   int (*statcallb)(isdn_ctrl*);
+
   /* Send command
    * Parameters:
    *             isdn_ctrl*
@@ -278,31 +317,16 @@
    *                   num     = depending on command.
    */
   int (*command)(isdn_ctrl*);
-  /* Send Data
-   * Parameters:
-   *             int    driverId
-   *             int    local channel-number (0 ...)
-   *             u_char pointer to data
-   *             int    length of data
-   *             int    Flag: 0 = Call form Kernel-Space (use memcpy,
-   *                              no schedule allowed) 
-   *                          1 = Data is in User-Space (use memcpy_fromfs,
-   *                              may schedule)
-   *
-   * NOTE: This call is obsolete, and will be removed when all
-   *       current LL-drivers support writebuf_skb. Do NOT use for new
-   *       drivers.
-   */
-  int (*writebuf)(int, int, const u_char*, int, int);
 
   /*
    * Send data using sk_buff's
    * Parameters:
    *             int                    driverId
    *             int                    local channel-number (0...)
+   *             int                    Flag: Need ACK for this packet.
    *             struct sk_buff *skb    Data to send
    */
-  int (*writebuf_skb) (int, int, struct sk_buff *);
+  int (*writebuf_skb) (int, int, int, struct sk_buff *);
 
   /* Send raw D-Channel-Commands
    * Parameters:
@@ -316,6 +340,7 @@
    *             int    local channel-number (0 ...)
    */
   int (*writecmd)(const u_char*, int, int, int, int);
+
   /* Read raw Status replies
    *             u_char pointer data (volatile)
    *             int    length of buffer
@@ -327,6 +352,7 @@
    *             int    local channel-number (0 ...)
    */
   int (*readstat)(u_char*, int, int, int, int);
+
   char id[20];
 } isdn_if;
 
@@ -339,8 +365,7 @@
  *              supporting sk_buff's should set this to 0.
  * command      Address of Command-Handler.
  * features     Bitwise coded Features of this driver. (use ISDN_FEATURE_...)
- * writebuf     Address of Send-Command-Handler. OBSOLETE do NOT use anymore.
- * writebuf_skb Address of Skbuff-Send-Handler. (NULL if not supported)
+ * writebuf_skb Address of Skbuff-Send-Handler.
  * writecmd        "    "  D-Channel  " which accepts raw D-Ch-Commands.
  * readstat        "    "  D-Channel  " which delivers raw Status-Data.
  *
@@ -348,72 +373,16 @@
  *
  * channels      Driver-ID assigned to this driver. (Must be used on all
  *               subsequent callbacks.
- * rcvcallb      Address of handler for received data. OBSOLETE, do NOT use anymore.
- * rcvcallb_skb  Address of handler for received Skbuff's. (NULL if not supp.)
+ * rcvcallb_skb  Address of handler for received Skbuff's.
  * statcallb        "    "     "    for status-changes.
  *
  */
 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>
-
-static inline unsigned long copy_from_user(void *to, const void *from, unsigned long n)
-{
-	int i;
-	if ((i = verify_area(VERIFY_READ, from, n)) != 0)
-		return i;
-	memcpy_fromfs(to, from, n);
-	return 0;
-}
-
-static inline unsigned long copy_to_user(void *to, const void *from, unsigned long n)
-{
-	int i;
-	if ((i = verify_area(VERIFY_WRITE, to, n)) != 0)
-		return i;
-	memcpy_tofs(to, from, n);
-	return 0;
-}
-
-#define GET_USER(x, addr) ( x = get_user(addr) )
-#define RWTYPE int
-#define LSTYPE int
-#define RWARG int
-#define LSARG off_t
-#else
 #include <asm/uaccess.h>
-#define GET_USER get_user
-#define PUT_USER put_user
-#define RWTYPE ssize_t
-#define LSTYPE long long
-#define RWARG size_t
-#define LSARG long long
-#endif
-
-#if (LINUX_VERSION_CODE < 0x02010F)
-#define SET_SKB_FREE(x) ( x->free = 1 )
-#else
-#define SET_SKB_FREE(x)
-#endif
-
-#if (LINUX_VERSION_CODE < 0x02011F)
-#define CLOSETYPE void
-#define CLOSEVAL
-#else
-#define CLOSETYPE int
-#define CLOSEVAL (0)
-#endif
-
-#if (LINUX_VERSION_CODE < 0x020125)
-#define test_and_clear_bit clear_bit
-#define test_and_set_bit set_bit
-#endif
 
 #endif /* __KERNEL__ */
 #endif /* isdnif_h */
diff -u --recursive --new-file v2.1.91/linux/include/linux/lists.h linux/include/linux/lists.h
--- v2.1.91/linux/include/linux/lists.h	Tue Jan  9 23:27:39 1996
+++ linux/include/linux/lists.h	Mon Mar 30 00:21:41 1998
@@ -13,7 +13,7 @@
 
 #define DLIST_INIT(listnam)	                                \
 	(listnam).dl_prev = &(listnam);				\
-	(listnam).dl_last = &(listnam);
+	(listnam).dl_next = &(listnam);
 
 #define DLIST_NEXT(listnam)	listnam.dl_next
 #define DLIST_PREV(listnam)	listnam.dl_prev
@@ -38,3 +38,25 @@
 	node->listnam.dl_next->listnam.dl_prev =		\
 		node->listnam.dl_prev;				\
 	} while (0)
+
+/*
+ * queue-style operations, which have a head and tail
+ */
+
+#define QUEUE_INIT(head, listnam, ptype)				\
+	(head)->listnam.dl_prev = (head)->listnam.dl_next = (ptype)(head);
+
+#define QUEUE_FIRST(head, listnam) (head)->DLIST_NEXT(listnam)
+#define QUEUE_LAST(head, listnam) (head)->DLIST_PREV(listnam)
+#define QUEUE_EMPTY(head, listnam) \
+	((QUEUE_FIRST(head, listnam) == QUEUE_LAST(head, listnam)) && \
+	 ((u_long)QUEUE_FIRST(head, listnam) == (u_long)head)) 
+
+#define QUEUE_ENTER(head, new, listnam, ptype) do {		\
+	(new)->listnam.dl_prev = (ptype)(head);			\
+	(new)->listnam.dl_next = (head)->listnam.dl_next;	\
+	(head)->listnam.dl_next->listnam.dl_prev = (new);	\
+	(head)->listnam.dl_next = (new);			\
+	} while (0)
+
+#define QUEUE_REMOVE(head, node, listnam) DLIST_DELETE(node, listnam)
diff -u --recursive --new-file v2.1.91/linux/include/linux/major.h linux/include/linux/major.h
--- v2.1.91/linux/include/linux/major.h	Tue Dec  2 09:19:03 1997
+++ linux/include/linux/major.h	Sun Mar 29 12:24:05 1998
@@ -73,6 +73,9 @@
 #define MKISS_MAJOR	55
 #define DSP56K_MAJOR    55   /* DSP56001 processor device */
 
+#define IDE4_MAJOR	56
+#define IDE5_MAJOR	57
+
 #define SPECIALIX_NORMAL_MAJOR 75
 #define SPECIALIX_CALLOUT_MAJOR 76
 
diff -u --recursive --new-file v2.1.91/linux/include/linux/pci.h linux/include/linux/pci.h
--- v2.1.91/linux/include/linux/pci.h	Tue Mar 10 10:03:35 1998
+++ linux/include/linux/pci.h	Mon Mar 30 00:21:41 1998
@@ -93,8 +93,8 @@
 #define  PCI_BASE_ADDRESS_MEM_TYPE_1M	0x02	/* Below 1M */
 #define  PCI_BASE_ADDRESS_MEM_TYPE_64	0x04	/* 64 bit address */
 #define  PCI_BASE_ADDRESS_MEM_PREFETCH	0x08	/* prefetchable? */
-#define  PCI_BASE_ADDRESS_MEM_MASK	(~0x0f)
-#define  PCI_BASE_ADDRESS_IO_MASK	(~0x03)
+#define  PCI_BASE_ADDRESS_MEM_MASK	(~0x0fUL)
+#define  PCI_BASE_ADDRESS_IO_MASK	(~0x03UL)
 /* bit 1 is reserved if address_space = 1 */
 
 /* Header type 0 (normal devices) */
@@ -503,7 +503,7 @@
 
 #define PCI_VENDOR_ID_CONTAQ		0x1080
 #define PCI_DEVICE_ID_CONTAQ_82C599	0x0600
-/* ??? Alpha SX164 has reference to device nr 0xc693 as a CYPRESS bridge.  */
+#define PCI_DEVICE_ID_CONTAQ_82C693	0xC693
 
 #define PCI_VENDOR_ID_FOREX		0x1083
 
@@ -933,6 +933,9 @@
 #define PCI_SLOT(devfn)		(((devfn) >> 3) & 0x1f)
 #define PCI_FUNC(devfn)		((devfn) & 0x07)
 
+/* create an index into the pci_dev base_address[] array from an offset */
+#define PCI_BASE_INDEX(o) (((o)-PCI_BASE_ADDRESS_0)>>2)
+
 #ifdef __KERNEL__
 /*
  * There is one pci_dev structure for each slot-number/function-number
@@ -987,6 +990,7 @@
 
 extern struct pci_bus	pci_root;	/* root bus */
 extern struct pci_dev	*pci_devices;	/* list of all devices */
+extern struct pci_dev   *pci_find_dev (unsigned char bus, unsigned char devfn);
 
 extern unsigned long pci_init (unsigned long mem_start, unsigned long mem_end);
 
diff -u --recursive --new-file v2.1.91/linux/include/linux/ppp-comp.h linux/include/linux/ppp-comp.h
--- v2.1.91/linux/include/linux/ppp-comp.h	Tue Dec 23 10:57:31 1997
+++ linux/include/linux/ppp-comp.h	Fri Mar 27 17:45:16 1998
@@ -24,11 +24,11 @@
  * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
  * OR MODIFICATIONS.
  *
- * $Id: ppp-comp.h,v 1.7 1995/05/01 01:43:37 paulus Exp $
+ * $Id: ppp-comp.h,v 1.6 1997/11/27 06:04:44 paulus Exp $
  */
 
 /*
- *  ==FILEVERSION 971024==
+ *  ==FILEVERSION 980319==
  *
  *  NOTE TO MAINTAINERS:
  *     If you modify this file at all, please set the above date.
@@ -173,7 +173,8 @@
  * Definitions for Deflate.
  */
 
-#define CI_DEFLATE		24	/* config option for Deflate */
+#define CI_DEFLATE		26	/* config option for Deflate */
+#define CI_DEFLATE_DRAFT	24	/* value used in original draft RFC */
 #define CILEN_DEFLATE		4	/* length of its config option */
 
 #define DEFLATE_MIN_SIZE	8
diff -u --recursive --new-file v2.1.91/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- v2.1.91/linux/include/linux/proc_fs.h	Fri Feb  6 15:32:55 1998
+++ linux/include/linux/proc_fs.h	Wed Apr  1 17:30:38 1998
@@ -327,6 +327,7 @@
 extern int proc_statfs(struct super_block *, struct statfs *, int);
 extern void proc_read_inode(struct inode *);
 extern void proc_write_inode(struct inode *);
+extern int proc_permission(struct inode *, int);
 
 extern int proc_match(int, const char *,struct proc_dir_entry *);
 
diff -u --recursive --new-file v2.1.91/linux/include/linux/sched.h linux/include/linux/sched.h
--- v2.1.91/linux/include/linux/sched.h	Tue Mar 17 22:18:15 1998
+++ linux/include/linux/sched.h	Wed Apr  1 17:30:31 1998
@@ -20,6 +20,8 @@
 #include <linux/tty.h>
 #include <linux/sem.h>
 #include <linux/signal.h>
+#include <linux/capability.h>
+#include <linux/securebits.h>
 
 /*
  * cloning flags:
@@ -220,8 +222,6 @@
 	pid_t session;
 	/* boolean value for session group leader */
 	int leader;
-	int ngroups;
-	gid_t groups[NGROUPS];
 	/* 
 	 * pointers to (original) parent process, youngest child, younger sibling,
 	 * older sibling, respectively.  (p->father can be replaced with 
@@ -237,8 +237,6 @@
 	struct task_struct **tarray_ptr;
 
 	struct wait_queue *wait_chldexit;	/* for wait4() */
-	uid_t uid,euid,suid,fsuid;
-	gid_t gid,egid,sgid,fsgid;
 	unsigned long timeout, policy, rt_priority;
 	unsigned long it_real_value, it_prof_value, it_virt_value;
 	unsigned long it_real_incr, it_prof_incr, it_virt_incr;
@@ -253,6 +251,12 @@
 	unsigned long old_maj_flt;	/* old value of maj_flt */
 	unsigned long dec_flt;		/* page fault count of the last time */
 	unsigned long swap_cnt;		/* number of pages to swap on next pass */
+/* process credentials */
+	uid_t uid,euid,suid,fsuid;
+	gid_t gid,egid,sgid,fsgid;
+	int ngroups;
+	gid_t	groups[NGROUPS];
+        kernel_cap_t   cap_effective, cap_inheritable, cap_permitted;
 /* limits */
 	struct rlimit rlim[RLIM_NLIMITS];
 	unsigned short used_math;
@@ -334,18 +338,20 @@
 /* schedlink */	&init_task,&init_task, &init_task, &init_task, \
 /* ec,brk... */	0,0,0,0,0,0, \
 /* pid etc.. */	0,0,0,0,0, \
-/* suppl grps*/ 0, {0,}, \
 /* proc links*/ &init_task,&init_task,NULL,NULL,NULL, \
 /* pidhash */	NULL, NULL, \
 /* tarray */	&task[0], \
 /* chld wait */	NULL, \
-/* uid etc */	0,0,0,0,0,0,0,0, \
 /* timeout */	0,SCHED_OTHER,0,0,0,0,0,0,0, \
 /* timer */	{ NULL, NULL, 0, 0, it_real_fn }, \
 /* utime */	{0,0,0,0},0, \
 /* per cpu times */ {0, }, {0, }, \
 /* flt */	0,0,0,0,0,0, \
 /* swp */	0,0,0,0,0, \
+/* process credentials */					\
+/* uid etc */	0,0,0,0,0,0,0,0,				\
+/* suppl grps*/ 0, {0,},					\
+/* caps */      CAP_FULL_SET, CAP_FULL_SET, CAP_FULL_SET,    \
 /* rlimits */   INIT_RLIMITS, \
 /* math */	0, \
 /* comm */	"swapper", \
@@ -444,8 +450,6 @@
 extern unsigned long prof_len;
 extern unsigned long prof_shift;
 
-extern int securelevel;	/* system security level */
-
 #define CURRENT_TIME (xtime.tv_sec)
 
 extern void FASTCALL(__wake_up(struct wait_queue ** p, unsigned int mode));
@@ -530,10 +534,13 @@
  * For correctness, the above considerations need to be extended to
  * fsuser(). This is done, along with moving fsuser() checks to be
  * last.
+ *
+ * These will be removed, but in the mean time, when the SECURE_NOROOT 
+ * flag is set, uids don't grant privilege.
  */
 extern inline int suser(void)
 {
-	if (current->euid == 0) {
+	if (!issecure(SECURE_NOROOT) && current->euid == 0) { 
 		current->flags |= PF_SUPERPRIV;
 		return 1;
 	}
@@ -542,7 +549,27 @@
 
 extern inline int fsuser(void)
 {
-	if (current->fsuid == 0) {
+	if (!issecure(SECURE_NOROOT) && current->fsuid == 0) {
+		current->flags |= PF_SUPERPRIV;
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ * capable() checks for a particular capability.  
+ * New privilege checks should use this interface, rather than suser() or
+ * fsuser(). See include/linux/capability.h for defined capabilities.
+ */
+
+extern inline int capable(int cap)
+{
+#if 0 /* not yet */
+	if (cap_raised(current->cap_effective, cap))
+#else
+	if (cap_is_fs_cap(cap) ? current->fsuid == 0 : current->euid == 0)
+#endif
+        {
 		current->flags |= PF_SUPERPRIV;
 		return 1;
 	}
diff -u --recursive --new-file v2.1.91/linux/include/linux/securebits.h linux/include/linux/securebits.h
--- v2.1.91/linux/include/linux/securebits.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/securebits.h	Wed Apr  1 16:26:34 1998
@@ -0,0 +1,30 @@
+#ifndef _LINUX_SECUREBITS_H
+#define _LINUX_SECUREBITS_H 1
+
+#define SECUREBITS_DEFAULT 0x00000000
+
+extern unsigned securebits;
+
+/* When set UID 0 has no special privileges. When unset, we support
+   inheritance of root-permissions and suid-root executablew under
+   compatibility mode. We raise the effective and inheritable bitmasks
+   *of the executable file* if the effective uid of the new process is
+   0. If the real uid is 0, we raise the inheritable bitmask of the
+   executable file. */
+#define SECURE_NOROOT            0
+
+/* When set, setuid to/from uid 0 does not trigger capability-"fixes"
+   to be compatible with old programs relying on set*uid to loose
+   privileges. When unset, setuid doesn't change privileges. */
+#define SECURE_NO_SETUID_FIXUP   2
+
+/* Each securesetting is implemented using two bits. One bit specify
+   whether the setting is on or off. The other bit specify whether the
+   setting is fixed or not. A setting which is fixed cannot be changed
+   from user-level. */
+
+#define issecure(X) ( (1 << (X+1)) & SECUREBITS_DEFAULT ? 	\
+		      (1 << (X)) & SECUREBITS_DEFAULT :		\
+		      (1 << (X)) & securebits )
+
+#endif /* !_LINUX_SECUREBITS_H */
diff -u --recursive --new-file v2.1.91/linux/include/linux/skbuff.h linux/include/linux/skbuff.h
--- v2.1.91/linux/include/linux/skbuff.h	Thu Mar 26 15:57:06 1998
+++ linux/include/linux/skbuff.h	Wed Apr  1 17:30:39 1998
@@ -29,21 +29,18 @@
 #define CHECKSUM_HW 1
 #define CHECKSUM_UNNECESSARY 2
 
-struct sk_buff_head 
-{
+struct sk_buff_head {
 	struct sk_buff	* next;
 	struct sk_buff	* prev;
 	__u32		qlen;		/* Must be same length as a pointer
 					   for using debugging */
 };
 
-struct sk_buff 
-{
+struct sk_buff {
 	struct sk_buff	* next;			/* Next buffer in list 				*/
 	struct sk_buff	* prev;			/* Previous buffer in list 			*/
 	struct sk_buff_head * list;		/* List we are on				*/
 	struct sock	*sk;			/* Socket we are owned by 			*/
-	unsigned long	when;			/* used to compute rtt's			*/
 	struct timeval	stamp;			/* Time we arrived				*/
 	struct device	*dev;			/* Device we arrived on/are leaving by		*/
 
@@ -78,16 +75,14 @@
 
 	struct  dst_entry *dst;
 
-#if (defined(__alpha__) || defined(__sparc64__)) && (defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE))
+#if (defined(__alpha__) || defined(__sparc_v9__)) && (defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE))
 	char		cb[48];	   /* sorry. 64bit pointers have a price */
+#elif (defined(__alpha__) || defined(__sparc_v9__))
+	char		cb[40];
 #else
-	char    	cb[32];
+	char    	cb[36];
 #endif
 
-	__u32		seq;			/* TCP sequence number				*/
-	__u32		end_seq;		/* seq [+ fin] [+ syn] + datalen		*/
-	__u32		ack_seq;		/* TCP ack sequence number			*/
-  
 	unsigned int 	len;			/* Length of actual data			*/
 	unsigned int	csum;			/* Checksum 					*/
 	volatile char 	used;			/* Data moved to user and not MSG_PEEK		*/
@@ -102,10 +97,6 @@
 	unsigned short	security;		/* Security level of packet			*/
 	unsigned int	truesize;		/* Buffer size 					*/
 
-#ifndef SLAB_SKB
-	atomic_t	count;			/* reference count				*/
-	struct sk_buff	*data_skb;		/* Link to the actual data skb			*/
-#endif
 	unsigned char	*head;			/* Head of buffer 				*/
 	unsigned char	*data;			/* Data head pointer				*/
 	unsigned char	*tail;			/* Tail pointer					*/
@@ -381,12 +372,17 @@
  *	Place a packet after a given packet in a list.
  */
 
+extern __inline__ void __skb_append(struct sk_buff *old, struct sk_buff *newsk)
+{
+	__skb_insert(newsk, old, old->next, old->list);
+}
+
 extern __inline__ void skb_append(struct sk_buff *old, struct sk_buff *newsk)
 {
 	unsigned long flags;
 
 	spin_lock_irqsave(&skb_queue_lock, flags);
-	__skb_insert(newsk, old, old->next, old->list);
+	__skb_append(old, newsk);
 	spin_unlock_irqrestore(&skb_queue_lock, flags);
 }
 
@@ -461,12 +457,7 @@
 	if(skb->tail>skb->end)
 	{
 		__label__ here;
-#if 1
-		printk(KERN_DEBUG "skbput: over: %p:tail=%p:end=%p:len=%u\n",
-						&&here, skb->tail, skb->end, len);
-#else
 		panic(skb_put_errstr,&&here,len);
-#endif
 here:          ;
 	}
 	return tmp;
diff -u --recursive --new-file v2.1.91/linux/include/linux/sysctl.h linux/include/linux/sysctl.h
--- v2.1.91/linux/include/linux/sysctl.h	Thu Mar 26 15:57:06 1998
+++ linux/include/linux/sysctl.h	Wed Apr  1 16:19:57 1998
@@ -153,7 +153,6 @@
 	NET_IPV4_TCP_TIMESTAMPS,
 	NET_IPV4_TCP_WINDOW_SCALING,
 	NET_IPV4_TCP_SACK,
-	NET_IPV4_TCP_VEGAS_CONG_AVOID,
 	NET_IPV4_DEFAULT_TTL,
 	NET_IPV4_AUTOCONFIG,
 	NET_IPV4_NO_PMTU_DISC,
diff -u --recursive --new-file v2.1.91/linux/include/linux/types.h linux/include/linux/types.h
--- v2.1.91/linux/include/linux/types.h	Fri Feb  6 15:32:54 1998
+++ linux/include/linux/types.h	Wed Apr  1 16:19:57 1998
@@ -69,6 +69,28 @@
 typedef unsigned int		uint;
 typedef unsigned long		ulong;
 
+#ifndef __BIT_TYPES_DEFINED__
+#define __BIT_TYPES_DEFINED__
+
+typedef		__u8		u_int8_t;
+typedef		__s8		int8_t;
+typedef		__u16		u_int16_t;
+typedef		__s16		int16_t;
+typedef		__u32		u_int32_t;
+typedef		__s32		int32_t;
+
+#endif /* !(__BIT_TYPES_DEFINED__) */
+
+typedef		__u8		uint8_t;
+typedef		__u16		uint16_t;
+typedef		__u32		uint32_t;
+
+#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
+typedef		__u64		uint64_t;
+typedef		__u64		u_int64_t;
+typedef		__s64		int64_t;
+#endif
+
 #endif /* __KERNEL_STRICT_NAMES */
 
 /*
diff -u --recursive --new-file v2.1.91/linux/include/linux/ufs_fs.h linux/include/linux/ufs_fs.h
--- v2.1.91/linux/include/linux/ufs_fs.h	Tue Mar 10 10:03:35 1998
+++ linux/include/linux/ufs_fs.h	Wed Apr  1 17:30:30 1998
@@ -260,7 +260,7 @@
 			__u32	fs_qfmask[2];	/* ~usb_fmask */
 			__s32	fs_state;	/* file system state time stamp */
 		} fs_44;
-	} fs_u;
+	} fs_u  __attribute__ ((packed));
 	__s32	fs_postblformat;	/* format of positional layout tables */
 	__s32	fs_nrpos;		/* number of rotational positions */
 	__s32	fs_postbloff;		/* (__s16) rotation block list head */
diff -u --recursive --new-file v2.1.91/linux/include/linux/vmalloc.h linux/include/linux/vmalloc.h
--- v2.1.91/linux/include/linux/vmalloc.h	Fri Feb  6 15:32:54 1998
+++ linux/include/linux/vmalloc.h	Wed Apr  1 17:30:32 1998
@@ -16,7 +16,7 @@
 struct vm_struct * get_vm_area(unsigned long size);
 void vfree(void * addr);
 void * vmalloc(unsigned long size);
-int vread(char *buf, char *addr, int count);
+long vread(char *buf, char *addr, unsigned long count);
 void vmfree_area_pages(unsigned long address, unsigned long size);
 int vmalloc_area_pages(unsigned long address, unsigned long size);
 
diff -u --recursive --new-file v2.1.91/linux/include/net/sock.h linux/include/net/sock.h
--- v2.1.91/linux/include/net/sock.h	Thu Mar 26 15:57:06 1998
+++ linux/include/net/sock.h	Wed Apr  1 17:31:27 1998
@@ -83,12 +83,8 @@
 
 #include <asm/atomic.h>
 
-/*
- *	The AF_UNIX specific socket options
- */
- 
-struct unix_opt
-{
+/* The AF_UNIX specific socket options */
+struct unix_opt {
 	int 			family;
 	char *			name;
 	int  			locks;
@@ -105,8 +101,7 @@
 #ifdef CONFIG_NETLINK
 struct netlink_callback;
 
-struct netlink_opt
-{
+struct netlink_opt {
 	pid_t			pid;
 	unsigned		groups;
 	pid_t			dst_pid;
@@ -117,13 +112,9 @@
 };
 #endif
 
-/*
- *	Once the IPX ncpd patches are in these are going into protinfo
- */
-
+/* Once the IPX ncpd patches are in these are going into protinfo. */
 #if defined(CONFIG_IPX) || defined(CONFIG_IPX_MODULE)
-struct ipx_opt
-{
+struct ipx_opt {
 	ipx_address		dest_addr;
 	ipx_interface		*intrfc;
 	unsigned short		port;
@@ -141,8 +132,7 @@
 #endif
 
 #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
-struct ipv6_pinfo
-{
+struct ipv6_pinfo {
 	struct in6_addr 	saddr;
 	struct in6_addr 	rcv_saddr;
 	struct in6_addr		daddr;
@@ -191,8 +181,7 @@
 	__u32	end_seq;
 };
 
-struct tcp_opt
-{
+struct tcp_opt {
 	int	tcp_header_len;	/* Bytes of tcp header to send		*/
 
 /*
@@ -214,7 +203,7 @@
 	__u32	lrcvtime;	/* timestamp of last received data packet*/
 	__u32	srtt;		/* smothed round trip time << 3		*/
 
-	__u32	ato;		/* delayed ack timeout */
+	__u32	ato;		/* delayed ack timeout			*/
 	__u32	snd_wl1;	/* Sequence for window update		*/
 
 	__u32	snd_wl2;	/* Ack sequence for update		*/
@@ -229,13 +218,14 @@
  	__u32	snd_cwnd;	/* Sending congestion window		*/
 	__u32	rto;		/* retransmit timeout			*/
 
-	__u32	packets_out;	/* Packets which are "in flight" */
-	__u32	high_seq;	/* highest sequence number sent by onset of congestion */
+	__u32	packets_out;	/* Packets which are "in flight"	*/
+	__u32	fackets_out;	/* Non-retrans SACK'd packets		*/
+	__u32	retrans_out;	/* Fast-retransmitted packets out	*/
+	__u32	high_seq;	/* snd_nxt at onset of congestion	*/
 /*
  *	Slow start and congestion control (see also Nagle, and Karn & Partridge)
  */
  	__u32	snd_ssthresh;	/* Slow start size threshold		*/
-	__u16	snd_cwnd_cnt;
 	__u8	dup_acks;	/* Consequetive duplicate acks seen from other end */
 	__u8	delayed_acks;
 
@@ -275,7 +265,6 @@
 	struct tcp_sack_block selective_acks[4]; /* The SACKS themselves*/
 
  	struct timer_list	probe_timer;		/* Probes	*/
-	__u32	basertt;	/* Vegas baseRTT			*/
 	__u32	window_clamp;	/* XXX Document this... -DaveM		*/
 	__u32	probes_out;	/* unanswered 0 window probes		*/
 	__u32	syn_seq;
@@ -286,7 +275,6 @@
 	struct open_request	*syn_wait_queue;
 	struct open_request	**syn_wait_last;
 	int syn_backlog;
-
 };
 
  	
@@ -335,12 +323,7 @@
 #define SOCK_DEBUG(sk, msg...) do { } while (0)
 #endif
 
-/*
- *  TCP will start to use the new protinfo while *still using the old* fields 
- */
-
-struct sock 
-{
+struct sock {
 	/* This must be first. */
 	struct sock		*sklist_next;
 	struct sock		*sklist_prev;
@@ -349,28 +332,29 @@
 	struct sock		*bind_next;
 	struct sock		**bind_pprev;
 
-	/* Main hash linkage for various protocol lookup tables. */
-	struct sock		*next;
-	struct sock		**pprev;
-
 	/* Socket demultiplex comparisons on incoming packets. */
 	__u32			daddr;		/* Foreign IPv4 addr			*/
 	__u32			rcv_saddr;	/* Bound local IPv4 addr		*/
-	int			bound_dev_if;	/* Bound device index if != 0		*/
+	__u16			dport;		/* Destination port			*/
 	unsigned short		num;		/* Local port				*/
+	int			bound_dev_if;	/* Bound device index if != 0		*/
+
+	/* Main hash linkage for various protocol lookup tables. */
+	struct sock		*next;
+	struct sock		**pprev;
+
 	volatile unsigned char	state,		/* Connection state			*/
 				zapped;		/* In ax25 & ipx means not linked	*/
 	__u16			sport;		/* Source port				*/
-	__u16			dport;		/* Destination port			*/
 
-	unsigned short		family;
-	unsigned char		reuse,
-				nonagle;
+	unsigned short		family;		/* Address family			*/
+	unsigned char		reuse,		/* SO_REUSEADDR setting			*/
+				nonagle;	/* Disable Nagle algorithm?		*/
 
-	int			sock_readers;	/* user count				*/
-	int			rcvbuf;
+	int			sock_readers;	/* User count				*/
+	int			rcvbuf;		/* Size of receive buffer in bytes	*/
 
-	struct wait_queue	**sleep;
+	struct wait_queue	**sleep;	/* Sock wait queue			*/
 	struct dst_entry	*dst_cache;	/* Destination cache			*/
 	atomic_t		rmem_alloc;	/* Receive queue bytes committed	*/
 	struct sk_buff_head	receive_queue;	/* Incoming packets			*/
@@ -379,13 +363,12 @@
 	atomic_t		omem_alloc;	/* "o" is "option" or "other" */
 	__u32			saddr;		/* Sending source			*/
 	unsigned int		allocation;	/* Allocation mode			*/
-	int			sndbuf;
+	int			sndbuf;		/* Size of send buffer in bytes		*/
 	struct sock		*prev;
 
-  /*
-   *	Not all are volatile, but some are, so we
-   * 	might as well say they all are.
-   */
+	/* Not all are volatile, but some are, so we might as well say they all are.
+	 * XXX Make this a flag word -DaveM
+	 */
 	volatile char		dead,
 				done,
 				urginline,
@@ -408,9 +391,9 @@
 
 	struct proto		*prot;
 
-/*
- *	mss is min(mtu, max_window) 
- */
+	/* mss is min(mtu, max_window)
+	 * XXX Fix this, mtu only used in one TCP place and that is it -DaveM
+	 */
 	unsigned short		mtu;       /* mss negotiated in the syn's */
 	unsigned short		mss;       /* current eff. mss - can change */
 	unsigned short		user_mss;  /* mss requested by user in ioctl */
@@ -450,13 +433,10 @@
 	struct sock_filter      *filter_data;
 #endif /* CONFIG_FILTER */
 
-/*
- *	This is where all the private (optional) areas that don't
- *	overlap will eventually live. 
- */
-
-	union
-	{
+	/* This is where all the private (optional) areas that don't
+	 * overlap will eventually live. 
+	 */
+	union {
 		void *destruct_hook;
 	  	struct unix_opt	af_unix;
 #if defined(CONFIG_ATALK) || defined(CONFIG_ATALK_MODULE)
@@ -488,9 +468,7 @@
 #endif
 	} protinfo;  		
 
-/* 
- *	IP 'private area' or will be eventually 
- */
+	/* IP 'private area' or will be eventually. */
 	int			ip_ttl;			/* TTL setting */
 	int			ip_tos;			/* TOS */
 	unsigned	   	ip_cmsg_flags;
@@ -504,31 +482,18 @@
 	__u32			ip_mc_addr;
 	struct ip_mc_socklist	*ip_mc_list;		/* Group array */
 
-/*
- *	This part is used for the timeout functions (timer.c). 
- */
- 
+	/* This part is used for the timeout functions (timer.c). */
 	int			timeout;	/* What are we waiting for? */
-	struct timer_list	timer;		/* This is the TIME_WAIT/receive timer
-						 * when we are doing IP
-						 */
+	struct timer_list	timer;		/* This is the sock cleanup timer. */
 	struct timeval		stamp;
 
- /*
-  *	Identd 
-  */
-  
+	/* Identd */
 	struct socket		*socket;
 
-  /*
-   *	RPC layer private data
-   */
+	/* RPC layer private data */
 	void			*user_data;
   
-  /*
-   *	Callbacks 
-   */
-   
+	/* Callbacks */
 	void			(*state_change)(struct sock *sk);
 	void			(*data_ready)(struct sock *sk,int bytes);
 	void			(*write_space)(struct sock *sk);
@@ -539,14 +504,11 @@
 	void                    (*destruct)(struct sock *sk);
 };
 
-/*
- *	IP protocol blocks we attach to sockets.
- *	socket layer -> transport layer interface
- *	transport -> network interface is defined by struct inet_proto
+/* IP protocol blocks we attach to sockets.
+ * socket layer -> transport layer interface
+ * transport -> network interface is defined by struct inet_proto
  */
- 
-struct proto 
-{
+struct proto {
 	/* These must be first. */
 	struct sock		*sklist_next;
 	struct sock		*sklist_prev;
@@ -608,16 +570,10 @@
 #define TIME_DONE	7	/* Used to absorb those last few packets */
 #define TIME_PROBE0	8
 
-/*
- *	About 10 seconds 
- */
-
+/* About 10 seconds */
 #define SOCK_DESTROY_TIME (10*HZ)
 
-/*
- *	Sockets 0-1023 can't be bound to unless you are superuser 
- */
- 
+/* Sockets 0-1023 can't be bound to unless you are superuser */
 #define PROT_SOCK	1024
 
 #define SHUTDOWN_MASK	3
diff -u --recursive --new-file v2.1.91/linux/include/net/tcp.h linux/include/net/tcp.h
--- v2.1.91/linux/include/net/tcp.h	Thu Mar 26 15:57:06 1998
+++ linux/include/net/tcp.h	Wed Apr  1 17:34:16 1998
@@ -154,16 +154,16 @@
 	struct sock		*sklist_prev;
 	struct sock		*bind_next;
 	struct sock		**bind_pprev;
-	struct sock		*next;
-	struct sock		**pprev;
 	__u32			daddr;
 	__u32			rcv_saddr;
-	int			bound_dev_if;
+	__u16			dport;
 	unsigned short		num;
+	int			bound_dev_if;
+	struct sock		*next;
+	struct sock		**pprev;
 	unsigned char		state,
 				zapped;
 	__u16			sport;
-	__u16			dport;
 	unsigned short		family;
 	unsigned char		reuse,
 				nonagle;
@@ -181,6 +181,43 @@
 
 extern kmem_cache_t *tcp_timewait_cachep;
 
+/* Socket demux engine toys. */
+#ifdef __BIG_ENDIAN
+#define TCP_COMBINED_PORTS(__sport, __dport) \
+	(((__u32)(__sport)<<16) | (__u32)(__dport))
+#else /* __LITTLE_ENDIAN */
+#define TCP_COMBINED_PORTS(__sport, __dport) \
+	(((__u32)(__dport)<<16) | (__u32)(__sport))
+#endif
+
+#if defined(__alpha__) || defined(__sparc_v9__)
+#ifdef __BIG_ENDIAN
+#define TCP_V4_ADDR_COOKIE(__name, __saddr, __daddr) \
+	__u64 __name = (((__u64)(__saddr))<<32)|((__u64)(__daddr));
+#else /* __LITTLE_ENDIAN */
+#define TCP_V4_ADDR_COOKIE(__name, __saddr, __daddr) \
+	__u64 __name = (((__u64)(__daddr))<<32)|((__u64)(__saddr));
+#endif /* __BIG_ENDIAN */
+#define TCP_IPV4_MATCH(__sk, __cookie, __saddr, __daddr, __ports, __dif)\
+	(((*((__u64 *)&((__sk)->daddr)))== (__cookie))	&&		\
+	 ((*((__u32 *)&((__sk)->dport)))== (__ports))   &&		\
+	 (!((__sk)->bound_dev_if) || ((__sk)->bound_dev_if == (__dif))))
+#else /* 32-bit arch */
+#define TCP_V4_ADDR_COOKIE(__name, __saddr, __daddr)
+#define TCP_IPV4_MATCH(__sk, __cookie, __saddr, __daddr, __ports, __dif)\
+	(((__sk)->daddr			== (__saddr))	&&		\
+	 ((__sk)->rcv_saddr		== (__daddr))	&&		\
+	 ((*((__u32 *)&((__sk)->dport)))== (__ports))   &&		\
+	 (!((__sk)->bound_dev_if) || ((__sk)->bound_dev_if == (__dif))))
+#endif /* 64-bit arch */
+
+#define TCP_IPV6_MATCH(__sk, __saddr, __daddr, __ports, __dif)			   \
+	(((*((__u32 *)&((__sk)->dport)))== (__ports))   			&& \
+	 ((__sk)->family		== AF_INET6)				&& \
+	 !ipv6_addr_cmp(&(__sk)->net_pinfo.af_inet6.daddr, (__saddr))		&& \
+	 !ipv6_addr_cmp(&(__sk)->net_pinfo.af_inet6.rcv_saddr, (__daddr))	&& \
+	 (!((__sk)->bound_dev_if) || ((__sk)->bound_dev_if == (__dif))))
+
 /* tcp_ipv4.c: These sysctl variables need to be shared between v4 and v6
  * because the v6 tcp code to intialize a connection needs to interoperate
  * with the v4 code using the same variables.
@@ -266,8 +303,6 @@
 #define TCP_KEEPALIVE_TIME (180*60*HZ)		/* two hours */
 #define TCP_KEEPALIVE_PROBES	9		/* Max of 9 keepalive probes	*/
 #define TCP_KEEPALIVE_PERIOD ((75*HZ)>>2)	/* period of keepalive check	*/
-#define TCP_NO_CHECK	0	/* turn to one if you want the default
-				 * to be no checksum			*/
 
 #define TCP_SYNACK_PERIOD	(HZ/2)
 #define TCP_QUICK_TRIES		8  /* How often we try to retransmit, until
@@ -305,14 +340,6 @@
 #define TCPOLEN_SACK_BASE_ALIGNED	4
 #define TCPOLEN_SACK_PERBLOCK		8
 
-/*
- *	TCP Vegas constants
- */
-
-#define TCP_VEGAS_ALPHA		2	/*  v_cong_detect_top_nseg */
-#define TCP_VEGAS_BETA		4	/*  v_cong_detect_bot_nseg */
-#define TCP_VEGAS_GAMMA		1	/*  v_exp_inc_nseg	   */
-
 struct open_request;
 
 struct or_calltable {
@@ -552,6 +579,7 @@
 extern void tcp_write_xmit(struct sock *);
 extern void tcp_time_wait(struct sock *);
 extern int tcp_retransmit_skb(struct sock *, struct sk_buff *);
+extern void tcp_fack_retransmit(struct sock *);
 extern void tcp_xmit_retransmit_queue(struct sock *);
 extern void tcp_simple_retransmit(struct sock *);
 
@@ -651,10 +679,17 @@
 
 /* This is what the send packet queueing engine uses to pass
  * TCP per-packet control information to the transmission
- * code.
+ * code.  We also store the host-order sequence numbers in
+ * here too.  This is 36 bytes on 32-bit architectures,
+ * 40 bytes on 64-bit machines, if this grows please adjust
+ * skbuff.h:skbuff->cb[xxx] size appropriately.
  */
 struct tcp_skb_cb {
-	__u8	flags;			/* TCP header flags.		*/
+	struct inet_skb_parm	header;	/* For incoming frames		*/
+	__u32		seq;		/* Starting sequence number	*/
+	__u32		end_seq;	/* SEQ + FIN + SYN + datalen	*/
+	unsigned long	when;		/* used to compute rtt's	*/
+	__u8		flags;		/* TCP header flags.		*/
 
 	/* NOTE: These must match up to the flags byte in a
 	 *       real TCP header.
@@ -666,15 +701,41 @@
 #define TCPCB_FLAG_ACK		0x10
 #define TCPCB_FLAG_URG		0x20
 
-	__u8	sacked;			/* State flags for SACK/FACK.	*/
+	__u8		sacked;		/* State flags for SACK/FACK.	*/
 #define TCPCB_SACKED_ACKED	0x01	/* SKB ACK'd by a SACK block	*/
 #define TCPCB_SACKED_RETRANS	0x02	/* SKB retransmitted		*/
 
-	__u16	urg_ptr;		/* Valid w/URG flags is set.	*/
+	__u16		urg_ptr;	/* Valid w/URG flags is set.	*/
+	__u32		ack_seq;	/* Sequence number ACK'd	*/
 };
 
 #define TCP_SKB_CB(__skb)	((struct tcp_skb_cb *)&((__skb)->cb[0]))
 
+/* We store the congestion window as a packet count, shifted by
+ * a factor so that implementing the 1/2 MSS ssthresh rules
+ * is easy.
+ */
+#define TCP_CWND_SHIFT	1
+
+/* This determines how many packets are "in the network" to the best
+ * or our knowledge.  In many cases it is conservative, but where
+ * detailed information is available from the receiver (via SACK
+ * blocks etc.) we can make more agressive calculations.
+ *
+ * Use this for decisions involving congestion control, use just
+ * tp->packets_out to determine if the send queue is empty or not.
+ *
+ * Read this equation as:
+ *
+ *	"Packets sent once on transmission queue" MINUS
+ *	"Packets acknowledged by FACK information" PLUS
+ *	"Packets fast retransmitted"
+ */
+static __inline__ int tcp_packets_in_flight(struct tcp_opt *tp)
+{
+	return tp->packets_out - tp->fackets_out + tp->retrans_out;
+}
+
 /* This checks if the data bearing packet SKB (usually tp->send_head)
  * should be put on the wire right now.
  */
@@ -682,7 +743,6 @@
 {
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 	int nagle_check = 1;
-	int len;
 
 	/*	RFC 1122 - section 4.2.3.4
 	 *
@@ -697,13 +757,13 @@
 	 *
 	 * 	Don't use the nagle rule for urgent data.
 	 */
-	len = skb->end_seq - skb->seq;
-	if (!sk->nonagle && len < (sk->mss >> 1) && tp->packets_out && 
+	if (!sk->nonagle && skb->len < (sk->mss >> 1) && tp->packets_out &&
 	    !(TCP_SKB_CB(skb)->flags & TCPCB_FLAG_URG))
 		nagle_check = 0;
 
-	return (nagle_check && tp->packets_out < tp->snd_cwnd &&
-		!after(skb->end_seq, tp->snd_una + tp->snd_wnd) &&
+	return (nagle_check &&
+		(tcp_packets_in_flight(tp) < (tp->snd_cwnd>>TCP_CWND_SHIFT)) &&
+		!after(TCP_SKB_CB(skb)->end_seq, tp->snd_una + tp->snd_wnd) &&
 		tp->retransmits == 0);
 }
 
diff -u --recursive --new-file v2.1.91/linux/init/main.c linux/init/main.c
--- v2.1.91/linux/init/main.c	Tue Mar 17 22:18:15 1998
+++ linux/init/main.c	Wed Apr  1 14:35:32 1998
@@ -1144,6 +1144,13 @@
 	smp_begin();
 #endif	
 
+#ifdef CONFIG_KMOD
+	{
+		extern int kmod_init(void);
+		kmod_init();
+	}
+#endif
+
 #ifdef CONFIG_UMSDOS_FS
 	{
 		/*
@@ -1176,13 +1183,6 @@
 				printk(KERN_ERR "Change root to /initrd: "
 				    "error %d\n",error);
 		}
-	}
-#endif
-
-#ifdef CONFIG_KMOD
-	{
-		extern int kmod_init(void);
-		kmod_init();
 	}
 #endif
 
diff -u --recursive --new-file v2.1.91/linux/ipc/shm.c linux/ipc/shm.c
--- v2.1.91/linux/ipc/shm.c	Tue Mar 10 10:03:35 1998
+++ linux/ipc/shm.c	Mon Mar 30 00:21:41 1998
@@ -632,12 +632,14 @@
 {
 	struct vm_area_struct *shmd, *shmdnext;
 
+	lock_kernel();
 	for (shmd = current->mm->mmap; shmd; shmd = shmdnext) {
 		shmdnext = shmd->vm_next;
 		if (shmd->vm_ops == &shm_vm_ops
 		    && shmd->vm_start - shmd->vm_offset == (ulong) shmaddr)
 			do_munmap(shmd->vm_start, shmd->vm_end - shmd->vm_start);
 	}
+	unlock_kernel();
 	return 0;
 }
 
diff -u --recursive --new-file v2.1.91/linux/kernel/exit.c linux/kernel/exit.c
--- v2.1.91/linux/kernel/exit.c	Tue Mar 17 22:18:15 1998
+++ linux/kernel/exit.c	Mon Mar 30 12:52:17 1998
@@ -299,7 +299,10 @@
 	 *	as a result of our exiting, and if they have any stopped
 	 *	jobs, send them a SIGHUP and then a SIGCONT.  (POSIX 3.2.2.2)
 	 */
-	while ((p = current->p_cptr) != NULL) {
+
+	write_lock_irq(&tasklist_lock);
+	while (current->p_cptr != NULL) {
+		p = current->p_cptr;
 		current->p_cptr = p->p_osptr;
 		p->p_ysptr = NULL;
 		p->flags &= ~(PF_PTRACED|PF_TRACESYS);
@@ -318,13 +321,19 @@
 		 * outside, so the child pgrp is now orphaned.
 		 */
 		if ((p->pgrp != current->pgrp) &&
-		    (p->session == current->session) &&
-		    is_orphaned_pgrp(p->pgrp) &&
-		    has_stopped_jobs(p->pgrp)) {
-			kill_pg(p->pgrp,SIGHUP,1);
-			kill_pg(p->pgrp,SIGCONT,1);
+		    (p->session == current->session)) {
+			int pgrp = p->pgrp;
+
+			write_unlock_irq(&tasklist_lock);
+			if (is_orphaned_pgrp(pgrp) && has_stopped_jobs(pgrp)) {
+				kill_pg(pgrp,SIGHUP,1);
+				kill_pg(pgrp,SIGCONT,1);
+			}
+			write_lock_irq(&tasklist_lock);
 		}
 	}
+	write_unlock_irq(&tasklist_lock);
+
 	if (current->leader)
 		disassociate_ctty(1);
 }
diff -u --recursive --new-file v2.1.91/linux/kernel/fork.c linux/kernel/fork.c
--- v2.1.91/linux/kernel/fork.c	Tue Mar 17 22:18:15 1998
+++ linux/kernel/fork.c	Mon Mar 30 12:52:17 1998
@@ -208,6 +208,7 @@
 	int retval;
 
 	flush_cache_mm(current->mm);
+	down(&current->mm->mmap_sem);
 	pprev = &mm->mmap;
 	for (mpnt = current->mm->mmap ; mpnt ; mpnt = mpnt->vm_next) {
 		struct file *file;
@@ -257,6 +258,7 @@
 
 fail_nomem:
 	flush_tlb_mm(current->mm);
+	up(&current->mm->mmap_sem);
 	return retval;
 }
 
@@ -514,6 +516,7 @@
 		/* ?? should we just memset this ?? */
 		for(i = 0; i < smp_num_cpus; i++)
 			p->per_cpu_utime[i] = p->per_cpu_stime[i] = 0;
+		spin_lock_init(&p->sigmask_lock);
 	}
 #endif
 	p->lock_depth = 0;
diff -u --recursive --new-file v2.1.91/linux/kernel/ksyms.c linux/kernel/ksyms.c
--- v2.1.91/linux/kernel/ksyms.c	Tue Mar 17 22:18:15 1998
+++ linux/kernel/ksyms.c	Wed Apr  1 16:26:35 1998
@@ -295,6 +295,7 @@
 EXPORT_SYMBOL(bh_base);
 EXPORT_SYMBOL(add_timer);
 EXPORT_SYMBOL(del_timer);
+EXPORT_SYMBOL(mod_timer);
 EXPORT_SYMBOL(tq_timer);
 EXPORT_SYMBOL(tq_immediate);
 EXPORT_SYMBOL(tq_scheduler);
@@ -388,7 +389,6 @@
 EXPORT_SYMBOL(event);
 EXPORT_SYMBOL(__down);
 EXPORT_SYMBOL(__up);
-EXPORT_SYMBOL(securelevel);
 
 /* all busmice */
 EXPORT_SYMBOL(add_mouse_randomness);
diff -u --recursive --new-file v2.1.91/linux/kernel/printk.c linux/kernel/printk.c
--- v2.1.91/linux/kernel/printk.c	Thu Feb 12 20:56:13 1998
+++ linux/kernel/printk.c	Wed Apr  1 14:58:55 1998
@@ -54,7 +54,7 @@
 static unsigned long log_start = 0;
 static unsigned long logged_chars = 0;
 struct console_cmdline console_cmdline[MAX_CMDLINECONSOLES];
-static int selected_console = 0;
+static int preferred_console = -1;
 
 /*
  *	Setup a list of consoles. Called from init/main.c
@@ -95,11 +95,13 @@
 	 */
 	for(i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0]; i++)
 		if (strcmp(console_cmdline[i].name, name) == 0 &&
-			  console_cmdline[i].index == idx)
+			  console_cmdline[i].index == idx) {
+				preferred_console = i;
 				return;
+		}
 	if (i == MAX_CMDLINECONSOLES)
 		return;
-	selected_console = 1;
+	preferred_console = i;
 	c = &console_cmdline[i];
 	memcpy(c->name, name, sizeof(c->name));
 	c->options = options;
@@ -336,13 +338,13 @@
 	 *	didn't select a console we take the first one
 	 *	that registers here.
 	 */
-	if (selected_console == 0) {
+	if (preferred_console < 0) {
 		if (console->index < 0)
 			console->index = 0;
 		if (console->setup == NULL ||
 		    console->setup(console, NULL) == 0) {
-			console->flags |= CON_ENABLED | CON_FIRST;
-			selected_console = 1;
+			console->flags |= CON_ENABLED | CON_CONSDEV;
+			preferred_console = 0;
 		}
 	}
 
@@ -362,8 +364,8 @@
 			break;
 		console->flags |= CON_ENABLED;
 		console->index = console_cmdline[i].index;
-		if (i == 0)
-			console->flags |= CON_FIRST;
+		if (i == preferred_console)
+			console->flags |= CON_CONSDEV;
 		break;
 	}
 
@@ -374,7 +376,7 @@
 	 *	Put this console in the list - keep the
 	 *	preferred driver at the head of the list.
 	 */
-	if ((console->flags & CON_FIRST) || console_drivers == NULL) {
+	if ((console->flags & CON_CONSDEV) || console_drivers == NULL) {
 		console->next = console_drivers;
 		console_drivers = console;
 	} else {
diff -u --recursive --new-file v2.1.91/linux/kernel/sched.c linux/kernel/sched.c
--- v2.1.91/linux/kernel/sched.c	Thu Mar 26 15:57:06 1998
+++ linux/kernel/sched.c	Wed Apr  1 16:26:35 1998
@@ -47,7 +47,7 @@
  * kernel variables
  */
 
-int securelevel = 0;			/* system security level */
+unsigned securebits = SECUREBITS_DEFAULT; /* systemwide security settings */
 
 long tick = (1000000 + HZ/2) / HZ;	/* timer interrupt period */
 
diff -u --recursive --new-file v2.1.91/linux/kernel/sysctl.c linux/kernel/sysctl.c
--- v2.1.91/linux/kernel/sysctl.c	Thu Mar 26 15:57:06 1998
+++ linux/kernel/sysctl.c	Wed Apr  1 16:26:35 1998
@@ -33,7 +33,7 @@
 #include <linux/nfs_fs.h>
 #endif
 
-#ifdef CONFIG_SYSCTL
+#if defined(CONFIG_SYSCTL) && defined(CONFIG_PROC_FS)
 
 /* External variables not in a header file. */
 extern int panic_timeout;
@@ -45,6 +45,9 @@
 extern char modprobe_path[];
 extern int kmod_unload_delay;
 #endif
+#ifdef CONFIG_CHR_DEV_SG
+extern int sg_big_buff;
+#endif
 
 #ifdef __sparc__
 extern char reboot_command [];
@@ -52,8 +55,6 @@
 
 static int parse_table(int *, int, void *, size_t *, void *, size_t,
 		       ctl_table *, void **);
-static int do_securelevel_strategy (ctl_table *, int *, int, void *, size_t *,
-				    void *, size_t, void **);
 
 
 static ctl_table root_table[];
@@ -156,8 +157,6 @@
 	 0644, NULL, &proc_dointvec},
 	{KERN_DENTRY, "dentry-state", &dentry_stat, 6*sizeof(int),
 	 0444, NULL, &proc_dointvec},
-	{KERN_SECURELVL, "securelevel", &securelevel, sizeof(int),
-	 0444, NULL, &proc_dointvec, (ctl_handler *)&do_securelevel_strategy},
 	{KERN_PANIC, "panic", &panic_timeout, sizeof(int),
 	 0644, NULL, &proc_dointvec},
 #ifdef CONFIG_BLK_DEV_INITRD
@@ -184,6 +183,10 @@
 	{KERN_KMOD_UNLOAD_DELAY, "kmod_unload_delay", &kmod_unload_delay,
 	sizeof(int), 0644, NULL, &proc_dointvec},
 #endif
+#ifdef CONFIG_CHR_DEV_SG
+	{KERN_NRFILE, "sg-big-buff", &sg_big_buff, sizeof (int),
+	 0444, NULL, &proc_dointvec},
+#endif
 	{0}
 };
 
@@ -408,29 +411,6 @@
 	return 0;
 }
 
-/*
- * This function only checks permission for changing the security level
- * If the tests are successful, the actual change is done by
- * do_sysctl_strategy
- */
-static int do_securelevel_strategy (ctl_table *table, 
-				    int *name, int nlen,
-				    void *oldval, size_t *oldlenp,
-				    void *newval, size_t newlen, void **context)
-{
-	int level;
-
-	if (newval && newlen) {
-		if (newlen != sizeof (int))
-			return -EINVAL;
-		if(copy_from_user (&level, newval, newlen))
-			return -EFAULT;
-		if (level < securelevel && current->pid != 1)
-			return -EPERM;
-	}
-	return 0;
-}
-
 struct ctl_table_header *register_sysctl_table(ctl_table * table, 
 					       int insert_at_head)
 {
@@ -1031,7 +1011,7 @@
 }
 
 
-#else /* CONFIG_SYSCTL */
+#else /* CONFIG_PROC_FS && CONFIG_SYSCTL */
 
 
 extern asmlinkage int sys_sysctl(struct __sysctl_args *args)
@@ -1087,7 +1067,7 @@
 {
 }
 
-#endif /* CONFIG_SYSCTL */
+#endif /* CONFIG_PROC_FS && CONFIG_SYSCTL */
 
 
 
diff -u --recursive --new-file v2.1.91/linux/mm/page_alloc.c linux/mm/page_alloc.c
--- v2.1.91/linux/mm/page_alloc.c	Thu Mar 26 15:57:06 1998
+++ linux/mm/page_alloc.c	Mon Mar 30 17:24:35 1998
@@ -126,7 +126,17 @@
 {
 	int retval = 0;
 	unsigned long flags;
-	struct free_area_struct * list = NULL;
+	struct free_area_struct * list;
+
+	/*
+	 * If we have more than about 6% of all memory free,
+	 * consider it to be good enough for anything.
+	 * It may not be, due to fragmentation, but we
+	 * don't want to keep on forever trying to find
+	 * free unfragmented memory.
+	 */
+	if (nr_free_pages > num_physpages >> 4)
+		return nr+1;
 
 	list = free_area + NR_MEM_LISTS;
 	spin_lock_irqsave(&page_alloc_lock, flags);
diff -u --recursive --new-file v2.1.91/linux/mm/vmalloc.c linux/mm/vmalloc.c
--- v2.1.91/linux/mm/vmalloc.c	Mon Jul  7 08:18:56 1997
+++ linux/mm/vmalloc.c	Wed Apr  1 14:51:22 1998
@@ -208,11 +208,15 @@
 	return addr;
 }
 
-int vread(char *buf, char *addr, int count)
+long vread(char *buf, char *addr, unsigned long count)
 {
 	struct vm_struct **p, *tmp;
 	char *vaddr, *buf_start = buf;
 	int n;
+
+	/* Don't allow overflow */
+	if ((unsigned long) addr + count < count)
+		count = -(unsigned long) addr;
 
 	for (p = &vmlist; (tmp = *p) ; p = &tmp->next) {
 		vaddr = (char *) tmp->addr;
diff -u --recursive --new-file v2.1.91/linux/net/bridge/br.c linux/net/bridge/br.c
--- v2.1.91/linux/net/bridge/br.c	Tue Feb 17 13:12:50 1998
+++ linux/net/bridge/br.c	Wed Apr  1 16:19:57 1998
@@ -13,8 +13,31 @@
  *	as published by the Free Software Foundation; either version
  *	2 of the License, or (at your option) any later version.
  *
- *	Fixes:
- *		Yury Shevchuk	:	Bridge with non bridging ports
+ * Fixes:
+ *	Yury Shevchuk	:	Bridge with non bridging ports
+ *	Jean-Rene Peulve: jr.peulve@aix.pacwan.net 		Jan/Feb 98
+ *			support Linux 2.0
+ *			Handle Receive config bpdu
+ *			kick mark_bh to send Spanning Tree pdus
+ *			bridgeId comparison using htonl()
+ *			make STP interoperable with other vendors
+ *			wrong test in root_selection()
+ *			add more STP debug info 
+ *			some performance improvments
+ *			do not clear bridgeId.mac  while setting priority
+ *			do not reset port priority when starting bridge
+ *			make port priority from user value and port number
+ *			maintains user port state out of device state
+ *			broacast/multicast storm limitation
+ *			forwarding statistics
+ *			stop br_tick when bridge is turn off
+ *			add local MACs in avl_tree to forward up stack
+ *			fake receive on right port for IP/ARP 
+ *			ages tree even if packet does not cross bridge
+ *			add BRCMD_DISPLAY_FDB (ioctl for now)
+ *
+ *	Alan Cox:	Merged Jean-Rene's stuff, reformatted stuff a bit
+ *			so blame me first if its broken ;)
  *
  *	Todo:
  *		Don't bring up devices automatically. Start ports disabled
@@ -42,11 +65,17 @@
 #include <linux/string.h>
 #include <linux/skbuff.h>
 #include <linux/if_arp.h>
+#include <linux/ip.h>
+#include <linux/version.h>
 #include <linux/init.h>
 #include <asm/uaccess.h>
 #include <asm/system.h>
 #include <net/br.h>
 
+#ifndef min
+#define min(a, b) (((a) <= (b)) ? (a) : (b))
+#endif
+
 static void transmit_config(int port_no);
 static int root_bridge(void);
 static int supersedes_port_info(int port_no, Config_bpdu *config);
@@ -80,7 +109,7 @@
 static void enable_port(int port_no);
 static void disable_port(int port_no);
 static void set_bridge_priority(bridge_id_t *new_bridge_id);
-static void set_port_priority(int port_no, unsigned short new_port_id);
+static void set_port_priority(int port_no);
 static void set_path_cost(int port_no, unsigned short path_cost);
 static void start_hello_timer(void);
 static void stop_hello_timer(void);
@@ -104,11 +133,12 @@
 static void br_tick(unsigned long arg);
 static int br_forward(struct sk_buff *skb, int port);	/* 3.7 */
 static int br_port_cost(struct device *dev);	/* 4.10.2 */
-static void br_bpdu(struct sk_buff *skb); /* consumes skb */
+static void br_bpdu(struct sk_buff *skb, int port); /* consumes skb */
 static int br_cmp(unsigned int *a, unsigned int *b);
 static int send_tcn_bpdu(int port_no, Tcn_bpdu *bpdu);
 static int send_config_bpdu(int port_no, Config_bpdu *config_bpdu);
 static int find_port(struct device *dev);
+static void br_add_local_mac(unsigned char *mac);
 static int br_flood(struct sk_buff *skb, int port);
 static int br_drop(struct sk_buff *skb);
 static int br_learn(struct sk_buff *skb, int port);	/* 3.8 */
@@ -116,8 +146,21 @@
 static unsigned char bridge_ula[ETH_ALEN] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
 static Bridge_data     bridge_info;			  /* (4.5.3)	 */
 Port_data       port_info[All_ports];		  /* (4.5.5)	 */
-Config_bpdu     config_bpdu[All_ports];
-Tcn_bpdu        tcn_bpdu[All_ports];
+
+/* JRP: fdb cache 1/port save kmalloc/kfree on every frame */
+struct fdb	*newfdb[All_ports];
+int allocated_fdb_cnt = 0;
+
+/* broacast/multicast storm limitation */
+int max_mcast_per_period = MAX_MCAST_PER_PERIOD;
+int mcast_hold_time	 = MCAST_HOLD_TIME;
+
+/* JRP: next two bpdu are copied to skbuff so we need only 1 of each */
+static Config_bpdu	config_bpdu;
+static Tcn_bpdu		tcn_bpdu;
+static unsigned char	port_priority[All_ports];
+static unsigned char	user_port_state[All_ports];
+
 static Timer    hello_timer;			  /* (4.5.4.1)	 */
 static Timer    tcn_timer;			  /* (4.5.4.2)	 */
 static Timer    topology_change_timer;		  /* (4.5.4.3)	 */
@@ -129,6 +172,7 @@
 unsigned int fdb_aging_time = FDB_TIMEOUT; 
 
 struct br_stat br_stats;
+#define br_stats_cnt br_stats.packet_cnts
 
 static struct timer_list tl; /* for 1 second timer... */
 
@@ -154,23 +198,28 @@
 #define BR_PROTOCOL_HASH(x) (x % BR_MAX_PROTOCOLS)
 
 /* Checks if that protocol type is to be bridged */
+
 int br_protocol_ok(unsigned short protocol)
 {
 	unsigned x;
 	
 	/* See if protocol statistics are to be kept */
 	if (br_stats.flags & BR_PROT_STATS)
-	{ for(x=0;x<BR_MAX_PROT_STATS &&
-	     br_stats.prot_id[x]!=protocol && br_stats.prot_id[x];x++) ;
-	  if (x<BR_MAX_PROT_STATS)
-	  { br_stats.prot_id[x]=protocol;br_stats.prot_counter[x]++;
-	  }
+	{
+		for(x=0;x<BR_MAX_PROT_STATS && br_stats.prot_id[x]!=protocol && br_stats.prot_id[x];x++);
+		if (x<BR_MAX_PROT_STATS)
+		{
+			br_stats.prot_id[x]=protocol;br_stats.prot_counter[x]++;
+		}
 	}
 
-	for (x=BR_PROTOCOL_HASH(protocol); br_stats.protocols[x]!=0;) {
-		if (br_stats.protocols[x]==protocol) return !br_stats.policy;
+	for (x=BR_PROTOCOL_HASH(protocol); br_stats.protocols[x]!=0;) 
+	{
+		if (br_stats.protocols[x]==protocol)
+			return !br_stats.policy;
 		x++;
-		if (x==BR_MAX_PROTOCOLS) x=0;
+		if (x==BR_MAX_PROTOCOLS)
+			x=0;
 	}
 	return br_stats.policy;
 }
@@ -209,7 +258,7 @@
 /*
  * this section of code was graciously borrowed from the IEEE 802.1d
  * specification section 4.9.1 starting on pg 69.  It has been
- * modified somewhat to fit within out framework and structure.  It
+ * modified somewhat to fit within our framework and structure.  It
  * implements the spanning tree algorithm that is the heart of the
  * 802.1d bridging protocol.
  */
@@ -219,42 +268,44 @@
 	if (hold_timer[port_no].active) {	  /* (4.6.1.3.1)	 */
 		port_info[port_no].config_pending = TRUE;	/* (4.6.1.3.1)	 */
 	} else {				  /* (4.6.1.3.2)	 */
-		config_bpdu[port_no].type = BPDU_TYPE_CONFIG;
-		config_bpdu[port_no].root_id = bridge_info.designated_root;
+		config_bpdu.type = BPDU_TYPE_CONFIG;
+		config_bpdu.root_id = bridge_info.designated_root;
 		/* (4.6.1.3.2(1)) */
-		config_bpdu[port_no].root_path_cost = bridge_info.root_path_cost;
+		config_bpdu.root_path_cost = bridge_info.root_path_cost;
 		/* (4.6.1.3.2(2)) */
-		config_bpdu[port_no].bridge_id = bridge_info.bridge_id;
+		config_bpdu.bridge_id = bridge_info.bridge_id;
 		/* (4.6.1.3.2(3)) */
-		config_bpdu[port_no].port_id = port_info[port_no].port_id;
+		config_bpdu.port_id = port_info[port_no].port_id;
 		/*
 		 * (4.6.1.3.2(4))
 		 */
 		if (root_bridge()) {
-			config_bpdu[port_no].message_age = Zero;	/* (4.6.1.3.2(5)) */
+			config_bpdu.message_age = Zero;	/* (4.6.1.3.2(5)) */
 		} else {
-			config_bpdu[port_no].message_age
+			config_bpdu.message_age
 				= message_age_timer[bridge_info.root_port].value
 				+ Message_age_increment;	/* (4.6.1.3.2(6)) */
 		}
 
-		config_bpdu[port_no].max_age = bridge_info.max_age;	/* (4.6.1.3.2(7)) */
-		config_bpdu[port_no].hello_time = bridge_info.hello_time;
-		config_bpdu[port_no].forward_delay = bridge_info.forward_delay;
-		config_bpdu[port_no].flags = 0;
-		config_bpdu[port_no].flags |=
-			port_info[port_no].top_change_ack ? TOPOLOGY_CHANGE_ACK : 0;
-		/* (4.6.1.3.2(8)) */
+		config_bpdu.max_age = bridge_info.max_age;/* (4.6.1.3.2(7)) */
+		config_bpdu.hello_time = bridge_info.hello_time;
+		config_bpdu.forward_delay = bridge_info.forward_delay;
+		config_bpdu.top_change_ack = 
+			port_info[port_no].top_change_ack;
+							/* (4.6.1.3.2(8)) */
 		port_info[port_no].top_change_ack = 0;
-		/* (4.6.1.3.2(8)) */
-		config_bpdu[port_no].flags |=
-			bridge_info.top_change ? TOPOLOGY_CHANGE : 0;
-		/* (4.6.1.3.2(9)) */
 
-		send_config_bpdu(port_no, &config_bpdu[port_no]);
+		config_bpdu.top_change = 
+			bridge_info.top_change; 	/* (4.6.1.3.2(9)) */
+
+		send_config_bpdu(port_no, &config_bpdu);
 		port_info[port_no].config_pending = FALSE;	/* (4.6.1.3.2(10)) */
 		start_hold_timer(port_no);	  /* (4.6.1.3.2(11)) */
 	}
+/* JRP: we want the frame to be xmitted even if no other traffic.
+ *	net_bh() will do a dev_transmit() that kicks all devices
+ */
+	mark_bh(NET_BH);
 }
 
 static int root_bridge(void)
@@ -314,8 +365,7 @@
 	bridge_info.max_age = config->max_age;	  /* (4.6.3.3)	 */
 	bridge_info.hello_time = config->hello_time;
 	bridge_info.forward_delay = config->forward_delay;
-	if (config->flags & TOPOLOGY_CHANGE)
-		bridge_info.top_change = 1;
+	bridge_info.top_change = config->top_change;
 }
 
 static void config_bpdu_generation(void)
@@ -353,8 +403,8 @@
 	int             port_no;
 
 	port_no = bridge_info.root_port;
-	tcn_bpdu[port_no].type = BPDU_TYPE_TOPO_CHANGE;
-	send_tcn_bpdu(port_no, &tcn_bpdu[bridge_info.root_port]);	/* (4.6.6.3)     */
+	tcn_bpdu.type = BPDU_TYPE_TOPO_CHANGE;
+	send_tcn_bpdu(port_no, &tcn_bpdu);	/* (4.6.6.3)     */
 }
 
 static void configuration_update(void)	/* (4.6.7) */
@@ -420,7 +470,7 @@
 					)	  /* (4.6.8.3.1(4)) */
 				       ||
 				       ((port_info[port_no].designated_port
-				      = port_info[root_port].designated_port
+/* JRP: was missing an "=" ! */	      == port_info[root_port].designated_port
 					 )
 					&&
 					(port_info[port_no].port_id
@@ -433,6 +483,10 @@
 	bridge_info.root_port = root_port;	  /* (4.6.8.3.1)	 */
 
 	if (root_port == No_port) {		  /* (4.6.8.3.2)	 */
+#ifdef DEBUG_STP
+		if (br_stats.flags & BR_DEBUG)
+			printk(KERN_DEBUG "root_selection: becomes root\n");
+#endif
 		bridge_info.designated_root = bridge_info.bridge_id;
 		/* (4.6.8.3.2(1)) */
 		bridge_info.root_path_cost = Zero;/* (4.6.8.3.2(2)) */
@@ -450,6 +504,8 @@
 	int             port_no;
 
 	for (port_no = One; port_no <= No_of_ports; port_no++) {	/* (4.6.9.3)	 */
+		if(port_info[port_no].state == Disabled)
+			continue;
 		if (designated_port(port_no)	  /* (4.6.9.3.1)	 */
 				||
 				(
@@ -498,19 +554,32 @@
 static void port_state_selection(void)
 {						  /* (4.6.11) */
 	int             port_no;
+	char		*state_str;
 	for (port_no = One; port_no <= No_of_ports; port_no++) {
+
+		if(port_info[port_no].state == Disabled)
+			continue;
 		if (port_no == bridge_info.root_port) {	/* (4.6.11.3.1) */
-			port_info[port_no].config_pending = FALSE;	/* (4.6.11.3~1(1)) */
+			state_str = "root";
+			port_info[port_no].config_pending = FALSE;	/* (4.6.11.3.1(1)) */
 			port_info[port_no].top_change_ack = 0;
 			make_forwarding(port_no); /* (4.6.11.3.1(2)) */
 		} else if (designated_port(port_no)) {	/* (4.6.11.3.2) */
+			state_str = "designated";
 			stop_message_age_timer(port_no);	/* (4.6.11.3.2(1)) */
 			make_forwarding(port_no); /* (4.6.11.3.2(2)) */
 		} else {			  /* (4.6.11.3.3) */
+			state_str = "blocking";
 			port_info[port_no].config_pending = FALSE;	/* (4.6.11.3.3(1)) */
 			port_info[port_no].top_change_ack = 0;
 			make_blocking(port_no);	  /* (4.6.11.3.3(2)) */
 		}
+#ifdef DEBUG_STP
+		if (br_stats.flags & BR_DEBUG)
+			printk(KERN_DEBUG "port_state_selection: becomes %s port %d\n",
+				state_str, port_no);
+#endif
+		
 	}
 
 }
@@ -525,6 +594,11 @@
 
 static void topology_change_detection(void)
 {						  /* (4.6.14)       */
+#ifdef DEBUG_STP
+	if ((br_stats.flags & BR_DEBUG)
+	    && (bridge_info.top_change_detected == 0))
+		printk(KERN_DEBUG "topology_change_detected\n");
+#endif
 	if (root_bridge()) {			  /* (4.6.14.3.1)   */
 		bridge_info.top_change = 1;
 		start_topology_change_timer();	  /* (4.6.14.3.1(2)) */
@@ -532,12 +606,16 @@
 		transmit_tcn();			  /* (4.6.14.3.2(1)) */
 		start_tcn_timer();		  /* (4.6.14.3.2(2)) */
 	}
-	bridge_info.top_change = 1;
+	bridge_info.top_change_detected = 1;	/* (4.6.14.3.3) */
 }
 
 static void topology_change_acknowledged(void)
 {						  /* (4.6.15) */
-	bridge_info.top_change_detected = 0;
+#ifdef DEBUG_STP
+	if (br_stats.flags & BR_DEBUG)
+		printk(KERN_DEBUG "topology_change_acked\n");
+#endif
+	bridge_info.top_change_detected = 0;	/* (4.6.15.3.1) */
 	stop_tcn_timer();			  /* (4.6.15.3.2) */
 }
 
@@ -574,10 +652,16 @@
 
 static void received_config_bpdu(int port_no, Config_bpdu *config)		  /* (4.7.1)	 */
 {
-	int         root;
+	int root;
 
 	root = root_bridge();
 	if (port_info[port_no].state != Disabled) {
+
+#ifdef DEBUG_STP
+		if (br_stats.flags & BR_DEBUG)
+			printk(KERN_DEBUG "received_config_bpdu: port %d\n",
+				port_no);
+#endif
 		if (supersedes_port_info(port_no, config)) {	/* (4.7.1.1)	 *//* (4.
 								 * 6.2.2)	 */
 			record_config_information(port_no, config);	/* (4.7.1.1.1)	 */
@@ -588,7 +672,7 @@
 			/* (4.6.11.2.1)	 */
 			if ((!root_bridge()) && root) {	/* (4.7.1.1.4)	 */
 				stop_hello_timer();
-				if (bridge_info.top_change_detected) {	/* (4.7.1.1.5~ */
+				if (bridge_info.top_change_detected) {	/* (4.7.1.1.5 */
 					stop_topology_change_timer();
 					transmit_tcn();	/* (4.6.6.1)	 */
 					start_tcn_timer();
@@ -598,7 +682,7 @@
 				record_config_timeout_values(config);	/* (4.7.1.1.6)	 */
 				/* (4.6.3.2)	 */
 				config_bpdu_generation();	/* (4.6.4.2.1)	 */
-				if (config->flags & TOPOLOGY_CHANGE_ACK) {	/* (4.7.1.1.7)    */
+				if (config->top_change_ack) {	/* (4.7.1.1.7)    */
 					topology_change_acknowledged();	/* (4.6.15.2)	 */
 				}
 			}
@@ -612,6 +696,11 @@
 static void received_tcn_bpdu(int port_no, Tcn_bpdu *tcn)			  /* (4.7.2)	 */
 {
 	if (port_info[port_no].state != Disabled) {
+#ifdef DEBUG_STP
+		if (br_stats.flags & BR_DEBUG)
+			printk(KERN_DEBUG "received_tcn_bpdu: port %d\n",
+				port_no);
+#endif
 		if (designated_port(port_no)) {
 			topology_change_detection();	/* (4.7.2.1)	 */
 			/* (4.6.14.2.1)	 */
@@ -628,9 +717,14 @@
 
 static void message_age_timer_expiry(int port_no) /* (4.7.4)	 */
 {
-	int         root;
+	int root;
 	root = root_bridge();
 
+#ifdef DEBUG_STP
+	if (br_stats.flags & BR_DEBUG)
+		printk(KERN_DEBUG "message_age_timer_expiry: port %d\n",
+			port_no);
+#endif
 	become_designated_port(port_no);	  /* (4.7.4.1)	 */
 	/* (4.6.10.2.1)	 */
 	configuration_update();			  /* (4.7.4.2)	 */
@@ -653,12 +747,17 @@
 
 static void forward_delay_timer_expiry(int port_no)	/* (4.7.5)	 */
 {
-	if (port_info[port_no].state == Listening) {	/* (4.7.5.1)	 */
+	if (port_info[port_no].state == Listening) 
+	{						/* (4.7.5.1)	 */
 		set_port_state(port_no, Learning);	/* (4.7.5.1.1)	 */
 		start_forward_delay_timer(port_no);	/* (4.7.5.1.2)	 */
-	} else if (port_info[port_no].state == Learning) {	/* (4.7.5.2) */
+	}
+	else if (port_info[port_no].state == Learning) 
+	{
+							/* (4.7.5.2) */
 		set_port_state(port_no, Forwarding);	/* (4.7.5.2.1) */
-		if (designated_for_some_port()) { /* (4.7.5.2.2) */
+		if (designated_for_some_port()) 
+		{ 					/* (4.7.5.2.2) */
 			topology_change_detection();	/* (4.6.14.2.2) */
 
 		}
@@ -667,13 +766,15 @@
 
 static int designated_for_some_port(void)
 {
-	int             port_no;
+	int port_no;
 
-
-	for (port_no = One; port_no <= No_of_ports; port_no++) {
+	for (port_no = One; port_no <= No_of_ports; port_no++) 
+	{
+		if(port_info[port_no].state == Disabled)
+			continue;
 		if ((br_cmp(port_info[port_no].designated_bridge.BRIDGE_ID,
-				bridge_info.bridge_id.BRIDGE_ID) == 0)
-			) {
+				bridge_info.bridge_id.BRIDGE_ID) == 0)) 
+		{
 			return (TRUE);
 		}
 	}
@@ -688,26 +789,38 @@
 
 static void topology_change_timer_expiry(void)
 {						  /* (4.7.7)	 */
-	bridge_info.top_change_detected = 0;
+	bridge_info.top_change_detected = 0;	/* (4.7.7.1) */
 	bridge_info.top_change = 0;
 	  /* (4.7.7.2)	 */
 }
 
 static void hold_timer_expiry(int port_no)	  /* (4.7.8)	 */
 {
-	if (port_info[port_no].config_pending) {
+	if (port_info[port_no].config_pending) 
+	{
 		transmit_config(port_no);	  /* (4.7.8.1)	 */
 	}					  /* (4.6.1.2.3)	 */
 }
 
 __initfunc(void br_init(void))
 {						  /* (4.8.1)	 */
-	int             port_no;
+	int port_no;
+
+	printk(KERN_INFO "Ethernet Bridge 005 for NET3.037 (Linux 2.1)\n");
+
+	/*
+	 * Form initial topology change time.
+	 * The topology change timer is only used if this is the root bridge.
+	 */
+	
+	bridge_info.topology_change_time = BRIDGE_MAX_AGE + BRIDGE_FORWARD_DELAY;       /* (4.5.3.13) */
 
-	printk(KERN_INFO "Ethernet Bridge 003 for NET3.037 (Linux 2.1)\n");
 	bridge_info.designated_root = bridge_info.bridge_id;	/* (4.8.1.1)	 */
 	bridge_info.root_path_cost = Zero;
 	bridge_info.root_port = No_port;
+#ifdef DEBUG_STP
+	printk(KERN_INFO "br_init: becomes root\n");
+#endif
 
 	bridge_info.bridge_max_age = BRIDGE_MAX_AGE;
 	bridge_info.bridge_hello_time = BRIDGE_HELLO_TIME;
@@ -722,17 +835,22 @@
 	bridge_info.top_change = 0;
 	stop_tcn_timer();
 	stop_topology_change_timer();
+	memset(newfdb, 0, sizeof(newfdb));
 	for (port_no = One; port_no <= No_of_ports; port_no++) {	/* (4.8.1.4) */
+		/* initial state = Enable */
+		user_port_state[port_no] = ~Disabled;
+		port_priority[port_no] = 128;
 		br_init_port(port_no);
 		disable_port(port_no);
 	}
+#if 0 /* JRP: We are not UP ! Wait for the start command */
 	port_state_selection();			  /* (4.8.1.5)	 */
 	config_bpdu_generation();		  /* (4.8.1.6)	 */
-	
 	/* initialize system timer */
 	tl.expires = jiffies+HZ;	/* 1 second */
 	tl.function = br_tick;
 	add_timer(&tl);
+#endif	
 
 	register_netdevice_notifier(&br_dev_notifier);
 	br_stats.flags = 0; /*BR_UP | BR_DEBUG*/;	/* enable bridge */
@@ -741,8 +859,14 @@
 	/*start_hello_timer();*/
 }
 
+static inline unsigned short make_port_id(int port_no)
+{
+	 return (port_priority[port_no] << 8) | port_no;
+}
+
 static void br_init_port(int port_no)
 {
+	port_info[port_no].port_id = make_port_id(port_no);
 	become_designated_port(port_no);	  /* (4.8.1.4.1) */
 	set_port_state(port_no, Blocking);	  /* (4.8.1.4.2)    */
 	port_info[port_no].top_change_ack = 0;
@@ -787,10 +911,12 @@
                                  		  /* (4.8.4)	 */
 {
 
-	int         root;
-	int             port_no;
+	int root;
+	int port_no;
 	root = root_bridge();
 	for (port_no = One; port_no <= No_of_ports; port_no++) {	/* (4.8.4.2) */
+		if(port_info[port_no].state == Disabled)
+			continue;
 		if (designated_port(port_no)) {
 			port_info[port_no].designated_bridge = *new_bridge_id;
 		}
@@ -810,9 +936,10 @@
 	}
 }
 
-static void set_port_priority(int port_no, unsigned short new_port_id)
+static void set_port_priority(int port_no)
                                   		  /* (4.8.5)	 */
-{
+{int new_port_id = make_port_id(port_no);
+
 	if (designated_port(port_no)) {		  /* (4.8.5.2)	 */
 		port_info[port_no].designated_port = new_port_id;
 	}
@@ -825,7 +952,8 @@
 			 < port_info[port_no].designated_port
 
 			 )
-		) {
+		) 
+	{
 		become_designated_port(port_no);  /* (4.8.5.4.1) */
 		port_state_selection();		  /* (4.8.5.4.2) */
 	}
@@ -841,27 +969,33 @@
 
 static void br_tick(unsigned long arg)
 {
-	int             port_no;
+	int port_no;
 
-	if (hello_timer_expired()) {
+	if(!(br_stats.flags & BR_UP))
+		return;			 /* JRP: we have been shot down */
+
+	if (hello_timer_expired())
 		hello_timer_expiry();
-	}
-	if (tcn_timer_expired()) {
+
+	if (tcn_timer_expired())
 		tcn_timer_expiry();
-	}
-	if (topology_change_timer_expired()) {
+
+	if (topology_change_timer_expired())
 		topology_change_timer_expiry();
-	}
-	for (port_no = One; port_no <= No_of_ports; port_no++) {
-		if (forward_delay_timer_expired(port_no)) {
+
+	for (port_no = One; port_no <= No_of_ports; port_no++) 
+	{
+		if(port_info[port_no].state == Disabled)
+			continue;
+
+		if (forward_delay_timer_expired(port_no)) 
 			forward_delay_timer_expiry(port_no);
-		}
-		if (message_age_timer_expired(port_no)) {
+
+		if (message_age_timer_expired(port_no))
 			message_age_timer_expiry(port_no);
-		}
-		if (hold_timer_expired(port_no)) {
+
+		if (hold_timer_expired(port_no))
 			hold_timer_expiry(port_no);
-		}
 	}
 	/* call me again sometime... */
 	tl.expires = jiffies+HZ;	/* 1 second */
@@ -882,7 +1016,8 @@
 
 static int hello_timer_expired(void)
 {
-	if (hello_timer.active && (++hello_timer.value >= bridge_info.hello_time)) {
+	if (hello_timer.active && (++hello_timer.value >= bridge_info.hello_time)) 
+	{
 		hello_timer.active = FALSE;
 		return (TRUE);
 	}
@@ -902,8 +1037,8 @@
 
 static int tcn_timer_expired(void)
 {
-	if (tcn_timer.active && (++tcn_timer.value >=
-				 bridge_info.bridge_hello_time)) {
+	if (tcn_timer.active && (++tcn_timer.value >= bridge_info.bridge_hello_time)) 
+	{
 		tcn_timer.active = FALSE;
 		return (TRUE);
 	}
@@ -925,9 +1060,8 @@
 static int topology_change_timer_expired(void)
 {
 	if (topology_change_timer.active
-			&& (++topology_change_timer.value
-			    >= bridge_info.topology_change_time
-			    )) {
+		&& (++topology_change_timer.value >= bridge_info.topology_change_time )) 
+	{
 		topology_change_timer.active = FALSE;
 		return (TRUE);
 	}
@@ -947,8 +1081,8 @@
 
 static int message_age_timer_expired(int port_no)
 {
-	if (message_age_timer[port_no].active &&
-	      (++message_age_timer[port_no].value >= bridge_info.max_age)) {
+	if (message_age_timer[port_no].active && (++message_age_timer[port_no].value >= bridge_info.max_age)) 
+	{
 		message_age_timer[port_no].active = FALSE;
 		return (TRUE);
 	}
@@ -968,12 +1102,12 @@
 
 static int forward_delay_timer_expired(int port_no)
 {
-		if (forward_delay_timer[port_no].active &&
-				(++forward_delay_timer[port_no].value >= bridge_info.forward_delay)) {
-			forward_delay_timer[port_no].active = FALSE;
-			return (TRUE);
-		}
-		return (FALSE);
+	if (forward_delay_timer[port_no].active && (++forward_delay_timer[port_no].value >= bridge_info.forward_delay)) 
+	{
+		forward_delay_timer[port_no].active = FALSE;
+		return (TRUE);
+	}
+	return (FALSE);
 }
 
 static void start_hold_timer(int port_no)
@@ -990,7 +1124,8 @@
 static int hold_timer_expired(int port_no)
 {
 	if (hold_timer[port_no].active &&
-		   (++hold_timer[port_no].value >= bridge_info.hold_time)) {
+		   (++hold_timer[port_no].value >= bridge_info.hold_time)) 
+	{
 		hold_timer[port_no].active = FALSE;
 		return (TRUE);
 	}
@@ -998,113 +1133,112 @@
 
 }
 
-static int send_config_bpdu(int port_no, Config_bpdu *config_bpdu)
+static struct sk_buff *alloc_bridge_skb(int port_no, int pdu_size, char *pdu_name)
 {
 	struct sk_buff *skb;
 	struct device *dev = port_info[port_no].dev;
-	int size;
 	struct ethhdr *eth;
-	
-	if (port_info[port_no].state == Disabled) {
-		printk(KERN_DEBUG "send_config_bpdu: port %i not valid\n",port_no);
-		return(-1);
+	int size = dev->hard_header_len + BRIDGE_LLC1_HS + pdu_size;
+	unsigned char *llc_buffer;
+	int pad_size = 60 - size; 
+ 
+	size = 60;	/* minimum Ethernet frame - CRC */
+
+	if (port_info[port_no].state == Disabled) 
+	{
+		printk(KERN_DEBUG "send_%s_bpdu: port %i not valid\n", pdu_name, port_no);
+		return NULL;
 	}
+
+  	skb = alloc_skb(size, GFP_ATOMIC);
+  	if (skb == NULL) 
+ 	{
+ 		printk(KERN_DEBUG "send_%s_bpdu: no skb available\n", pdu_name);
+ 		return NULL;
+ 	}
+  	skb->dev = dev;
+ 	skb->mac.raw = skb->h.raw = skb_put(skb,size);
+ 	memset(skb->h.raw + 60 - pad_size, 0xa5, pad_size);
+  	eth = skb->mac.ethernet;
+  	memcpy(eth->h_dest, bridge_ula, ETH_ALEN);
+  	memcpy(eth->h_source, dev->dev_addr, ETH_ALEN);
+ 
+  	if (br_stats.flags & BR_DEBUG)
+ 		printk("send_%s_bpdu: port %i src %02x:%02x:%02x:%02x:%02x:%02x\n",
+ 			pdu_name,
+  			port_no,
+  			eth->h_source[0],
+  			eth->h_source[1],
+  			eth->h_source[2],
+  			eth->h_source[3],
+  			eth->h_source[4],
+ 			eth->h_source[5]);
+#if 0
+ /* 8038 is used in older DEC spanning tree protocol which uses a
+  * different pdu layout as well
+  */
+ 	eth->h_proto = htons(0x8038);
+#endif
+	eth->h_proto = htons(pdu_size + BRIDGE_LLC1_HS);
+  
+  	skb->h.raw += skb->dev->hard_header_len;
+ 	llc_buffer = skb->h.raw;
+ 	*llc_buffer++ = BRIDGE_LLC1_DSAP;
+ 	*llc_buffer++ = BRIDGE_LLC1_SSAP;
+ 	*llc_buffer++ = BRIDGE_LLC1_CTRL;
+ 	/* set h.raw to where the bpdu starts */
+ 	skb->h.raw += BRIDGE_LLC1_HS;
+  
+ 	/* mark that we've been here... */
+  	skb->pkt_bridged = IS_BRIDGED;
+ 	return skb;
+}
+ 
+static int send_config_bpdu(int port_no, Config_bpdu *config_bpdu)
+{
+	struct sk_buff *skb;
 	
-	if (br_stats.flags & BR_DEBUG)
-		printk("send_config_bpdu: ");
 	/*
-	 * create and send the message
+	 *	Create and send the message
 	 */
-	size = dev->hard_header_len + sizeof(Config_bpdu);
-	skb = alloc_skb(size, GFP_ATOMIC);
-	if (skb == NULL) 
-	{
-		printk(KERN_DEBUG "send_config_bpdu: no skb available\n");
+	 
+ 	skb = alloc_bridge_skb(port_no, BRIDGE_BPDU_8021_CONFIG_SIZE,
+				"config");
+	if (skb == NULL)
 		return(-1);
-	}
-	skb->dev = dev;
-	skb->mac.raw = skb->h.raw = skb_put(skb, size);
-	eth = skb->mac.ethernet;
-	memcpy(eth->h_dest, bridge_ula, ETH_ALEN);
-	memcpy(eth->h_source, dev->dev_addr, ETH_ALEN);
-	if (br_stats.flags & BR_DEBUG)
-		printk("port %i src %02x:%02x:%02x:%02x:%02x:%02x\
-			dest %02x:%02x:%02x:%02x:%02x:%02x\n", 
-			port_no,
-			eth->h_source[0],
-			eth->h_source[1],
-			eth->h_source[2],
-			eth->h_source[3],
-			eth->h_source[4],
-			eth->h_source[5],
-			eth->h_dest[0],
-			eth->h_dest[1],
-			eth->h_dest[2],
-			eth->h_dest[3],
-			eth->h_dest[4],
-			eth->h_dest[5]);
-	eth->h_proto = htons(0x8038);
 
-	skb->h.raw += skb->dev->hard_header_len;
-	memcpy(skb->h.raw, config_bpdu, sizeof(Config_bpdu));
+	/* copy fields before "flags" */
+	memcpy(skb->h.raw, config_bpdu, BRIDGE_BPDU_8021_CONFIG_FLAG_OFFSET);
 
-	/* won't get bridged again... */
-	skb->pkt_bridged = IS_BRIDGED;
-	skb->dev=dev;
-	dev_queue_xmit(skb);
-	return(0);
-}
+	/* build the "flags" field */
+	*(skb->h.raw+BRIDGE_BPDU_8021_CONFIG_FLAG_OFFSET) = 0;
+	if (config_bpdu->top_change_ack)
+		*(skb->h.raw+BRIDGE_BPDU_8021_CONFIG_FLAG_OFFSET) |= 0x80;
+	if (config_bpdu->top_change)
+		*(skb->h.raw+BRIDGE_BPDU_8021_CONFIG_FLAG_OFFSET) |= 0x01;
+
+	config_bpdu_hton(config_bpdu);
+	/* copy the rest */
+	memcpy(skb->h.raw+BRIDGE_BPDU_8021_CONFIG_FLAG_OFFSET+1,
+		 (char*)&(config_bpdu->root_id),
+		 BRIDGE_BPDU_8021_CONFIG_SIZE-1-BRIDGE_BPDU_8021_CONFIG_FLAG_OFFSET);
 
+ 	dev_queue_xmit(skb);
+  	return(0);
+}
+  
 static int send_tcn_bpdu(int port_no, Tcn_bpdu *bpdu)
 {
 	struct sk_buff *skb;
-	struct device *dev = port_info[port_no].dev;
-	int size;
-	struct ethhdr *eth;
-	
-	if (port_info[port_no].state == Disabled) {
-		printk(KERN_DEBUG "send_tcn_bpdu: port %i not valid\n",port_no);
-		return(-1);
-		}
-	if (br_stats.flags & BR_DEBUG)
-		printk("send_tcn_bpdu: ");
-	size = sizeof(Tcn_bpdu) + dev->hard_header_len;
-	skb = alloc_skb(size, GFP_ATOMIC);
-	if (skb == NULL) {
-		printk(KERN_DEBUG "send_tcn_bpdu: no skb available\n");
-		return(-1);
-		}
-	skb->dev = dev;
-	skb->mac.raw = skb->h.raw = skb_put(skb,size);
-	eth = skb->mac.ethernet;
-	memcpy(eth->h_dest, bridge_ula, ETH_ALEN);
-	memcpy(eth->h_source, dev->dev_addr, ETH_ALEN);
-	if (br_stats.flags & BR_DEBUG)
-		printk("port %i src %02x:%02x:%02x:%02x:%02x:%02x\
-			dest %02x:%02x:%02x:%02x:%02x:%02x\n", 
-			port_no,
-			eth->h_source[0],
-			eth->h_source[1],
-			eth->h_source[2],
-			eth->h_source[3],
-			eth->h_source[4],
-			eth->h_source[5],
-			eth->h_dest[0],
-			eth->h_dest[1],
-			eth->h_dest[2],
-			eth->h_dest[3],
-			eth->h_dest[4],
-			eth->h_dest[5]);
-	eth->h_proto = htons(0x8038);
-
-	skb->h.raw += skb->dev->hard_header_len;
-	memcpy(skb->h.raw, bpdu, sizeof(Tcn_bpdu));
-
-	/* mark that we've been here... */
-	skb->pkt_bridged = IS_BRIDGED;
-	skb->dev=dev;
-	dev_queue_xmit(skb);
-	return(0);
+ 	
+	skb = alloc_bridge_skb(port_no, sizeof(Tcn_bpdu), "tcn");
+	if (skb == NULL)
+  		return(-1);
+  
+  	memcpy(skb->h.raw, bpdu, sizeof(Tcn_bpdu));
+  
+ 	dev_queue_xmit(skb);
+  	return(0);
 }
 
 static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr)
@@ -1116,52 +1250,59 @@
 	if (dev->flags & IFF_LOOPBACK)
 		return(NOTIFY_DONE);
 
-	switch (event) {
-	case NETDEV_DOWN:
-		if (br_stats.flags & BR_DEBUG)
-			printk("br_device_event: NETDEV_DOWN...\n");
-		/* find our device and mark it down */
-		for (i = One; i <= No_of_ports; i++) {
-			if (port_info[i].dev == dev) {
-				disable_port(i);
-				return NOTIFY_DONE;
-				break;
+	switch (event) 
+	{
+		case NETDEV_DOWN:
+			if (br_stats.flags & BR_DEBUG)
+				printk(KERN_DEBUG "br_device_event: NETDEV_DOWN...\n");
+			/* find our device and mark it down */
+			for (i = One; i <= No_of_ports; i++) 
+			{
+				if (port_info[i].dev == dev) 
+				{
+					disable_port(i);
+					return NOTIFY_DONE;
+					break;
+				}
 			}
-		}
-		break;
-	case NETDEV_UP:
-		if (br_stats.flags & BR_DEBUG)
-			printk("br_device_event: NETDEV_UP...\n");
-		/* Only handle ethernet ports */
-		if(dev->type!=ARPHRD_ETHER && dev->type!=ARPHRD_LOOPBACK)
-			return NOTIFY_DONE;
-		/* look up an unused device and enable it */
-		for (i = One; i <= No_of_ports; i++) {
-			if ((port_info[i].dev == (struct device *)0) ||
-				(port_info[i].dev == dev)) {
-				port_info[i].dev = dev;
-				enable_port(i);
-				set_path_cost(i, br_port_cost(dev));
-				set_port_priority(i, 128); 
-				port_info[i].port_id = i;
-				/* set bridge addr from 1st device addr */
-				if ((bridge_info.bridge_id.BRIDGE_ID[0] == 0) &&
-						(bridge_info.bridge_id.BRIDGE_ID[1] == 0)) {
-					memcpy(bridge_info.bridge_id.BRIDGE_ID_ULA, dev->dev_addr, 6);
-					bridge_info.bridge_id.BRIDGE_PRIORITY = port_info[i].port_id;
-					set_bridge_priority(&bridge_info.bridge_id);
-				}	
-				make_forwarding(i);
+			break;
+		case NETDEV_UP:
+			if (br_stats.flags & BR_DEBUG)
+				printk(KERN_DEBUG "br_device_event: NETDEV_UP...\n");
+			/* Only handle ethernet ports */
+			if(dev->type!=ARPHRD_ETHER && dev->type!=ARPHRD_LOOPBACK)
 				return NOTIFY_DONE;
-				break;
+			/* look up an unused device and enable it */
+			for (i = One; i <= No_of_ports; i++) 
+			{
+				if (port_info[i].dev == NULL || port_info[i].dev == dev) 
+				{
+					port_info[i].dev = dev;
+					port_info[i].port_id = i;
+					/* set bridge addr from 1st device addr */
+					if (((htonl(bridge_info.bridge_id.BRIDGE_ID[0])&0xffff) == 0) &&
+						(bridge_info.bridge_id.BRIDGE_ID[1] == 0)) 
+					{
+						memcpy(bridge_info.bridge_id.BRIDGE_ID_ULA, dev->dev_addr, 6);
+						if(bridge_info.bridge_id.BRIDGE_PRIORITY == 0)
+							bridge_info.bridge_id.BRIDGE_PRIORITY = htons(32768);
+						set_bridge_priority(&bridge_info.bridge_id);
+					}
+					br_add_local_mac(dev->dev_addr);
+					if((br_stats.flags & BR_UP) &&
+				   		(user_port_state[i] != Disabled)) 
+				   	{
+				   		/* don't start if user said so */
+							enable_port(i);
+						set_path_cost(i, br_port_cost(dev));
+						set_port_priority(i); 
+						make_forwarding(i);
+					}
+					return NOTIFY_DONE;
+					break;
+				}
 			}
-		}
-		break;
-#if 0
-	default:
-		printk("br_device_event: unknown event [%x]\n",
-			(unsigned int)event);
-#endif			
+			break;
 	}
 	return NOTIFY_DONE;
 }
@@ -1175,10 +1316,9 @@
 int br_receive_frame(struct sk_buff *skb)	/* 3.5 */
 {
 	int port;
+	Port_data  *p;
 	struct ethhdr *eth;
 	
-	if (br_stats.flags & BR_DEBUG)
-		printk("br_receive_frame: ");
 	/* sanity */
 	if (!skb) {
 		printk(KERN_CRIT "br_receive_frame: no skb!\n");
@@ -1189,88 +1329,79 @@
 
 	/* check for loopback */
 	if (skb->dev->flags & IFF_LOOPBACK)
-		return(0);
+		return 0 ;
 
 	port = find_port(skb->dev);
 	
+	if(!port)
+		return 0;
+	
 	skb->h.raw = skb->mac.raw;
 	eth = skb->mac.ethernet;
-	if (br_stats.flags & BR_DEBUG)
-		printk("port %i src %02x:%02x:%02x:%02x:%02x:%02x\
-			dest %02x:%02x:%02x:%02x:%02x:%02x\n", 
-			port,
-			eth->h_source[0],
-			eth->h_source[1],
-			eth->h_source[2],
-			eth->h_source[3],
-			eth->h_source[4],
-			eth->h_source[5],
-			eth->h_dest[0],
-			eth->h_dest[1],
-			eth->h_dest[2],
-			eth->h_dest[3],
-			eth->h_dest[4],
-			eth->h_dest[5]);
+	p = &port_info[port];
+ 
+ 	if(p->state == Disabled) 
+ 	{
+ 		/* We are here if BR_UP even if this port is Disabled.
+ 		 * Send everything up
+ 		 */
+ 		skb->pkt_type = PACKET_HOST;
+ 		++br_stats_cnt.port_disable_up_stack;
+ 		return(0);	/* pass frame up our stack (this will */
+ 				/* happen in net_bh() in dev.c) */
+ 	}
+ 
+ 	/* Here only if not disable.
+ 	 * Remark: only frames going up will show up in NIT (tcpdump)
+ 	 */
 
-	if (!port) {
-		if(br_stats.flags&BR_DEBUG)
-			printk("\nbr_receive_frame: no port!\n");
-		return(0);
+	/* JRP: even if port is Blocking we need to process the Spanning Tree
+	 * frames to keep the port in that state
+	 */
+	if (memcmp(eth->h_dest, bridge_ula, ETH_ALEN) == 0) 
+	{
+		++br_stats_cnt.rcv_bpdu;
+		br_bpdu(skb, port); /* br_bpdu consumes skb */
+		return(1);
 	}
-
-	switch (port_info[port].state) 
+	switch (p->state) 
 	{
 		case Learning:
-			(void) br_learn(skb, port);	/* 3.8 */
+			if(br_learn(skb, port)) 
+			{	/* 3.8 */
+				++br_stats_cnt.drop_multicast;
+				return br_drop(skb);
+			}
 			/* fall through */
 		case Listening:
-			/* process BPDUs */
-			if (memcmp(eth->h_dest, bridge_ula, 6) == 0) {
-				br_bpdu(skb);
-				return(1); /* br_bpdu consumes skb */
-			}
 			/* fall through */
 		case Blocking:
-			/* fall through */
-		case Disabled:
-			/* should drop frames, but for now, we let
-			 * them get passed up to the next higher layer
+			++br_stats_cnt.notForwarding;
 			return(br_drop(skb));	
-			 */
-			return(0);	/* pass frame up stack */
+		/*
+		case Disabled: is now handled before this switch !
+		Keep the break to allow GCC to use a jmp table.
+		 */
 			break;
 		case Forwarding:
-			(void) br_learn(skb, port);	/* 3.8 */
-			/* process BPDUs */
-			if (memcmp(eth->h_dest, bridge_ula, 
-					ETH_ALEN) == 0) 
-			{
-				/*printk("frame bpdu processor for me!!!\n");*/
-				br_bpdu(skb);
-				return(1); /* br_bpdu consumes skb */
-			}
-			/* is frame for me? */	
-			if (memcmp(eth->h_dest, 
-					port_info[port].dev->dev_addr, 
-					ETH_ALEN) == 0) 
-			{
-				/* Packet is for us */
-				skb->pkt_type = PACKET_HOST;
-				return(0);	/* pass frame up our stack (this will */
-						/* happen in net_bh() in dev.c) */
+			if(br_learn(skb, port)) {	/* 3.8 */
+				++br_stats_cnt.drop_multicast;
+				return br_drop(skb);
 			}
 			/* Now this frame came from one of bridged
-			   ports, and it appears to be not for me;
-			   this means we should attempt to forward it.
-			   But actually this frame can still be for me
-			   [as well] if it is destined to one of our
-			   multicast groups.  br_forward() will not
-			   consume the frame if this is the case */
+			   ports this means we should attempt to forward it.
+			   JRP: local addresses are now in the AVL tree,
+			   br_forward will pass frames up if it matches
+			   one of our local MACs or if it is a multicast
+			   group address.
+			   br_forward() will not consume the frame if this
+			   is the case */
 			return(br_forward(skb, port));
 		default:
 			printk(KERN_DEBUG "br_receive_frame: port [%i] unknown state [%i]\n",
-				port, port_info[port].state);
-			return(0);	/* pass frame up stack? */
+				port, p->state);
+			++br_stats_cnt.unknown_state;
+			return(br_drop(skb));	/* discard frame */
 	}
 }
 
@@ -1304,15 +1435,17 @@
 
 	/* if bridging is not enabled on the port we are going to send
            to, we have nothing to do with this frame, hands off */
-	if (! find_port(skb->dev))
+	if (((port=find_port(skb->dev))==0)||(port_info[port].state==Disabled)) {
+		++br_stats_cnt.port_disable;
 		return(0);
-
+	}
+	++br_stats_cnt.port_not_disable;
 	skb->mac.raw = skb->h.raw = skb->data;
 	eth = skb->mac.ethernet;
-	port = 0;	/* an impossible port */	
+	port = 0;	/* an impossible port (locally generated) */	
 	if (br_stats.flags & BR_DEBUG)
-		printk("br_tx_fr : port %i src %02x:%02x:%02x:%02x:%02x:%02x\
-	  		dest %02x:%02x:%02x:%02x:%02x:%02x\n", 
+		printk("br_tx_fr : port %i src %02x:%02x:%02x:%02x:%02x:%02x"
+	  		" dest %02x:%02x:%02x:%02x:%02x:%02x\n", 
 			port,
 			eth->h_source[0],
 			eth->h_source[1],
@@ -1329,55 +1462,135 @@
 	return(br_forward(skb, port));
 }
 
+static void br_add_local_mac(unsigned char *mac)
+{
+	struct fdb *f;
+	f = (struct fdb *)kmalloc(sizeof(struct fdb), GFP_ATOMIC);
+	if (!f) 
+	{
+		printk(KERN_CRIT "br_add_local_mac: unable to malloc fdb\n");
+		return;
+	}
+	f->port = 0;	/* dest port == 0 =>local */
+	memcpy(f->ula, mac, 6);
+	f->timer = 0;	/* will not aged anyway */
+	f->flags = 0;	/* not valid => br_forward special route */
+	/*
+	 * add entity to AVL tree.  If entity already
+	 * exists in the tree, update the fields with
+	 * what we have here.
+  	 */
+	if (br_avl_insert(f) != NULL) 
+	{
+		/* Already in */
+		kfree(f);
+	}
+}
+
+/* Avoid broadcast loop by limiting the number of broacast frames per
+ * period. The idea is to limit this per source
+ * returns: 0 if limit is not reached
+ *          1 if frame should be dropped
+ */
+
+static inline int mcast_quench(struct fdb *f)
+{
+	if(f->mcast_count++ == 0) /* first time */
+		f->mcast_timer = jiffies;
+	else {
+		if(f->mcast_count > max_mcast_per_period) {
+			if(jiffies > (f->mcast_timer + mcast_hold_time))
+				f->mcast_count = 0;
+			else	return 1;
+		}
+	}
+	return 0;
+}
+
 /*
  * this routine returns 0 when it learns (or updates) from the
- * frame, and -1 if the frame is simply discarded due to port
- * state or lack of resources...
+ * frame, and 1 if we must dropped the frame.
  */
 
 static int br_learn(struct sk_buff *skb, int port)	/* 3.8 */
 {
-	struct fdb *f;
+	struct fdb *f, *oldfdb;
+	Port_data  *p = &port_info[port];
+	struct ethhdr *eth = skb->mac.ethernet;
+
+	/* JRP: no reason to check port state again. We are called by
+	 * br_receive_frame() only when in Learning or Forwarding
+	 * Remark: code not realigned yet to keep diffs smaller
+	 */
 
-	switch (port_info[port].state) {
-		case Listening:
-		case Blocking:
-		case Disabled:
-		default:
-			return(-1);
-			/* break; */
-		case Learning:
-		case Forwarding:
-			/* don't keep group addresses in the tree */
-			if (skb->mac.ethernet->h_source[0] & 0x01)
-				return(-1);
-			
-			f = (struct fdb *)kmalloc(sizeof(struct fdb), 
-				GFP_ATOMIC);
+	/* don't keep group addresses in the tree */
+	if (eth->h_source[0] & 0x01)
+		return 0;
 
-			if (!f) {
-				printk(KERN_DEBUG "br_learn: unable to malloc fdb\n");
-				return(-1);
-			}
-			f->port = port;	/* source port */
-			memcpy(f->ula, skb->mac.ethernet->h_source, 6);
-			f->timer = CURRENT_TIME;
-			f->flags = FDB_ENT_VALID;
-			/*
-			 * add entity to AVL tree.  If entity already
-			 * exists in the tree, update the fields with
-			 * what we have here.
-		  	 */
-			if (br_avl_insert(f) == 0) { /* update */
-				kfree(f);
-				return(0);
-			}
-			/* add to head of port chain */
-			f->fdb_next = port_info[port].fdb;
-			port_info[port].fdb = f;
-			return(0);
-			/* break */
+	if((f= newfdb[port]) == NULL) 
+	{
+		newfdb[port] = f = (struct fdb *)kmalloc(sizeof(struct fdb), GFP_ATOMIC);
+		if (!f) 
+		{
+			printk(KERN_DEBUG "br_learn: unable to malloc fdb\n");
+			return(-1); /* this drop the frame */
+		}
 	}
+	f->port = port;	/* source port */
+	memcpy(f->ula, eth->h_source, 6);
+	f->timer = CURRENT_TIME;
+	f->flags = FDB_ENT_VALID;
+	/*
+	 * add entity to AVL tree.  If entity already
+	 * exists in the tree, update the fields with
+	 * what we have here.
+	 */
+	if ((oldfdb = br_avl_insert(f))) 
+	{
+		/* update if !NULL */
+		if((eth->h_dest[0] & 0x01) &&  /* multicast */ mcast_quench(oldfdb))
+			return 1;
+		return 0;
+	}
+	newfdb[port] = NULL;	/* force kmalloc next time */
+	f->mcast_count = 0;
+	/* add to head of port chain */
+	f->fdb_next = p->fdb;
+	p->fdb = f;
+	allocated_fdb_cnt++;
+	return 0;
+}
+
+/* JRP: always called under br_receive_frame(). No need for Q protection. */
+
+void requeue_fdb(struct fdb *node, int new_port)
+{
+	Port_data *p = &port_info[node->port];
+
+	/* dequeue */
+	if(p->fdb == node)
+		p->fdb = node->fdb_next;
+	else 
+	{
+		struct fdb *prev;
+
+		for(prev = p->fdb; prev; prev = prev->fdb_next)
+			if (prev->fdb_next == node)
+				break;
+
+		if(prev != NULL)
+			prev->fdb_next = node->fdb_next;
+		else 
+		{
+			/*	Forget about this update. */
+			printk(KERN_ERR "br:requeue_fdb\n");
+			return;
+		}
+	}
+	/* enqueue */
+	node->port = new_port;
+	node->fdb_next = port_info[new_port].fdb;
+	port_info[new_port].fdb = node;
 }
 
 /*
@@ -1429,26 +1642,43 @@
 		 *	This probably should be dropped since the flood will
 		 *	have sent it anyway.
 		 */
-		if (port == 0) 			/* locally generated */
+		if (port == 0) 
+		{
+			/* Locally generated */
+			++br_stats_cnt.local_multicast;
 			return(br_dev_drop(skb));
+		}
+		++br_stats_cnt.forwarded_multicast;
 		return(0);
-	} else {
-		/* locate port to forward to */
+	}
+	else 
+	{
+		/* unicast frame, locate port to forward to */
 		f = br_avl_find_addr(skb->mac.ethernet->h_dest);
 		/*
 		 *	Send flood and drop.
 		 */
-		if (!f || !(f->flags & FDB_ENT_VALID)) {
-		 	/* not found; flood all ports */
+		if (!f || !(f->flags & FDB_ENT_VALID)) 
+		{
+	 		if(f && (f->port == 0)) 
+	 		{
+				skb->pkt_type = PACKET_HOST;
+				++br_stats_cnt.forwarded_unicast_up_stack;
+				return(0);
+			}
+			/* not found or too old; flood all ports */
+			++br_stats_cnt.flood_unicast;
 			br_flood(skb, port);
 			return(br_dev_drop(skb));
 		}
 		/*
 		 *	Sending
 		 */
-		if (f->port!=port && port_info[f->port].state == Forwarding) {
-			/* has entry expired? */
-			if (f->timer + fdb_aging_time < CURRENT_TIME) {
+		if (f->port!=port && port_info[f->port].state == Forwarding) 
+		{
+			/* Has entry expired? */
+			if (f->timer + fdb_aging_time < CURRENT_TIME) 
+			{
 				/* timer expired, invalidate entry */
 				f->flags &= ~FDB_ENT_VALID;
 				if (br_stats.flags & BR_DEBUG)
@@ -1456,9 +1686,11 @@
 				/*
 				 *	Send flood and drop original
 				 */
+				++br_stats_cnt.aged_flood_unicast;
 				br_flood(skb, port);
 				return(br_dev_drop(skb));
 			}
+			++br_stats_cnt.forwarded_unicast;
 			/* mark that's we've been here... */
 			skb->pkt_bridged = IS_BRIDGED;
 			
@@ -1477,7 +1709,25 @@
 			skb->priority = 1;
 			dev_queue_xmit(skb);
 			return(1);	/* skb has been consumed */
-		} else {
+		}
+		else 
+		{
+			/* JRP: Needs to aged entry as well, if topology changes
+			 * the entry would not age. Got this while swapping
+			 * two cables !
+			 *
+			 *	Has entry expired?
+			 */
+			 
+			if (f->timer + fdb_aging_time < CURRENT_TIME) 
+			{
+				/* timer expired, invalidate entry */
+				f->flags &= ~FDB_ENT_VALID;
+				if (br_stats.flags & BR_DEBUG)
+					printk("fdb entry expired...\n");
+				++br_stats_cnt.drop_same_port_aged;
+			}
+			else ++br_stats_cnt.drop_same_port;
 			/*
 			 *	Arrived on the right port, we discard
 			 */
@@ -1499,7 +1749,7 @@
 
 	for (i = One; i <= No_of_ports; i++) 
 	{
-		if (i == port)
+		if (i == port)	/* don't send back where we got it */
 			continue;
 		if (port_info[i].state == Forwarding) 
 		{
@@ -1515,8 +1765,12 @@
 			
 /*			printk("Flood to port %d\n",i);*/
 			nskb->h.raw = nskb->data + ETH_HLEN;
+#if LINUX_VERSION_CODE >= 0x20100
 			nskb->priority = 1;
 			dev_queue_xmit(nskb);
+#else
+			dev_queue_xmit(nskb,nskb->dev,1);
+#endif
 		}
 	}
 	return(0);
@@ -1527,12 +1781,16 @@
 	int i;
 
 	for (i = One; i <= No_of_ports; i++)
-		if ((port_info[i].dev == dev) && 
-			(port_info[i].state != Disabled))
+		if (port_info[i].dev == dev)
 			return(i);
 	return(0);
 }
 
+/*
+ *	FIXME: This needs to come from the device structs, eg for
+ *	10,100,1Gbit ethernet.
+ */
+ 
 static int br_port_cost(struct device *dev)	/* 4.10.2 */
 {
 	if (strncmp(dev->name, "eth", 3) == 0)	/* ethernet */
@@ -1546,43 +1804,103 @@
  * this routine always consumes the skb 
  */
 
-static void br_bpdu(struct sk_buff *skb) /* consumes skb */
+static void br_bpdu(struct sk_buff *skb, int port) /* consumes skb */
 {
-	Tcn_bpdu *bpdu;
-	int port;
-
-	port = find_port(skb->dev);
-	if (port == 0) {	/* unknown port */
-		br_drop(skb);
-		return;
-	}
-		
-	bpdu = (Tcn_bpdu *) (skb->data + ETH_HLEN);
-	switch (bpdu->type) {
-		case BPDU_TYPE_CONFIG:
-			received_config_bpdu(port, (Config_bpdu *)bpdu);
-			break;
-		case BPDU_TYPE_TOPO_CHANGE:
-			received_tcn_bpdu(port, bpdu);
-			break;
-		default:
-			printk(KERN_DEBUG "br_bpdu: received unknown bpdu, type = %i\n",
-				bpdu->type);
+	char *bufp = skb->data + ETH_HLEN;
+	Tcn_bpdu *bpdu = (Tcn_bpdu *) (bufp + BRIDGE_LLC1_HS);
+	Config_bpdu rcv_bpdu;
+
+	if((*bufp++ == BRIDGE_LLC1_DSAP) && (*bufp++ == BRIDGE_LLC1_SSAP) &&
+		(*bufp++ == BRIDGE_LLC1_CTRL) &&
+		(bpdu->protocol_id == BRIDGE_BPDU_8021_PROTOCOL_ID) &&
+		(bpdu->protocol_version_id == BRIDGE_BPDU_8021_PROTOCOL_VERSION_ID)) 
+	{
+  
+  		switch (bpdu->type) 
+  		{
+	  		case BPDU_TYPE_CONFIG:
+ 				/* realign for portability to RISC */
+ 				memcpy((char*)&rcv_bpdu, bufp,
+ 					BRIDGE_BPDU_8021_CONFIG_FLAG_OFFSET);
+ 				bufp+= BRIDGE_BPDU_8021_CONFIG_FLAG_OFFSET;
+ 				rcv_bpdu.top_change_ack =
+ 					(*bufp & TOPOLOGY_CHANGE_ACK) != 0;
+ 				rcv_bpdu.top_change =
+ 					(*bufp & TOPOLOGY_CHANGE) != 0;
+ 				bufp++;
+ 				memcpy((char*)&rcv_bpdu.root_id, bufp,
+ 					BRIDGE_BPDU_8021_CONFIG_SIZE-1
+        	                         -BRIDGE_BPDU_8021_CONFIG_FLAG_OFFSET);
+ 				config_bpdu_ntoh(&rcv_bpdu);
+ 				received_config_bpdu(port, &rcv_bpdu);
+ 				break;
+ 			
+			case BPDU_TYPE_TOPO_CHANGE:
+				received_tcn_bpdu(port, bpdu);
+				break;
+			default:
+				printk(KERN_DEBUG "br_bpdu: received unknown bpdu, type = %i\n", bpdu->type);
 			/* break; */
+		}
 	}
 	br_drop(skb);
 }
 
+struct fdb_info *get_fdb_info(int user_buf_size, int *copied,int *notcopied)
+{
+	int fdb_size, i, built = 0;
+	struct fdb_info *fdbi, *fdbis;
+
+	*copied = user_buf_size - sizeof(struct fdb_info_hdr);
+	*copied /= sizeof(struct fdb_info);
+	*copied = min(*copied, allocated_fdb_cnt);
+	*notcopied = allocated_fdb_cnt - *copied;
+	if(*copied == 0)
+		return NULL;
+	fdb_size = *copied * sizeof(struct fdb_info);
+	fdbis = kmalloc(fdb_size, GFP_KERNEL);
+	if(fdbis == NULL)
+		return NULL;
+	fdbi = fdbis;
+
+	for(i=One; i<=No_of_ports;i++)
+	{
+		struct fdb *fdb;
+
+		cli();
+		fdb = port_info[i].fdb;
+		while(fdb) 
+		{
+			memcpy(fdbi->ula, fdb->ula, ETH_ALEN);
+			fdbi->port = fdb->port;
+			fdbi->flags = fdb->flags;
+			fdbi->timer = fdb->timer;
+			fdbi++;
+			if(++built == *copied) 
+			{
+				sti();
+				return fdbis;
+			}
+			fdb = fdb->fdb_next;
+		}
+		sti();
+	}
+	printk(KERN_DEBUG "get_fdb_info: built=%d\n", built);
+	return fdbis;
+}
+
 int br_ioctl(unsigned int cmd, void *arg)
 {
-	int err;
+	int err, i;
 	struct br_cf bcf;
+	bridge_id_t new_id;
 
 	switch(cmd)
 	{
 		case SIOCGIFBR:	/* get bridging control blocks */
 			memcpy(&br_stats.bridge_data, &bridge_info, sizeof(Bridge_data));
 			memcpy(&br_stats.port_data, &port_info, sizeof(Port_data)*No_of_ports);
+
 			err = copy_to_user(arg, &br_stats, sizeof(struct br_stat));
 			if (err)
 			{
@@ -1590,17 +1908,33 @@
 			}
 			return err;
 		case SIOCSIFBR:
-			if (!suser())
-				return -EPERM;
 			err = copy_from_user(&bcf, arg, sizeof(struct br_cf));
 			if (err)
 				return -EFAULT; 
-			switch (bcf.cmd) {
+			if (bcf.cmd != BRCMD_DISPLAY_FDB && !suser())
+				return -EPERM;
+			switch (bcf.cmd) 
+			{
 				case BRCMD_BRIDGE_ENABLE:
 					if (br_stats.flags & BR_UP)
 						return(-EALREADY);	
 					printk(KERN_DEBUG "br: enabling bridging function\n");
 					br_stats.flags |= BR_UP;	/* enable bridge */
+					for(i=One;i<=No_of_ports; i++)
+					{
+						/* don't start if user said so */
+						if((user_port_state[i] != Disabled)
+							&& port_info[i].dev) 
+						{
+							enable_port(i);
+						}
+					}
+					port_state_selection();	  /* (4.8.1.5)	 */
+					config_bpdu_generation();  /* (4.8.1.6)	 */
+					/* initialize system timer */
+					tl.expires = jiffies+HZ;	/* 1 second */
+					tl.function = br_tick;
+					add_timer(&tl);
 					start_hello_timer();
 					break;
 				case BRCMD_BRIDGE_DISABLE:
@@ -1609,35 +1943,41 @@
 					printk(KERN_DEBUG "br: disabling bridging function\n");
 					br_stats.flags &= ~BR_UP;	/* disable bridge */
 					stop_hello_timer();
-#if 0					
 					for (i = One; i <= No_of_ports; i++)
 						if (port_info[i].state != Disabled)
 							disable_port(i);
-#endif							
 					break;
 				case BRCMD_PORT_ENABLE:
 					if (port_info[bcf.arg1].dev == 0)
 						return(-EINVAL);
-					if (port_info[bcf.arg1].state != Disabled)
+					if (user_port_state[bcf.arg1] != Disabled)
 						return(-EALREADY);
 					printk(KERN_DEBUG "br: enabling port %i\n",bcf.arg1);
-					enable_port(bcf.arg1);
+					user_port_state[bcf.arg1] = ~Disabled;
+					if(br_stats.flags & BR_UP)
+						enable_port(bcf.arg1);
 					break;
 				case BRCMD_PORT_DISABLE:
 					if (port_info[bcf.arg1].dev == 0)
 						return(-EINVAL);
-					if (port_info[bcf.arg1].state == Disabled)
+					if (user_port_state[bcf.arg1] == Disabled)
 						return(-EALREADY);
 					printk(KERN_DEBUG "br: disabling port %i\n",bcf.arg1);
-					disable_port(bcf.arg1);
+					user_port_state[bcf.arg1] = Disabled;
+					if(br_stats.flags & BR_UP)
+						disable_port(bcf.arg1);
 					break;
 				case BRCMD_SET_BRIDGE_PRIORITY:
-					set_bridge_priority((bridge_id_t *)&bcf.arg1);
+					new_id = bridge_info.bridge_id;
+					new_id.BRIDGE_PRIORITY = htons(bcf.arg1);
+					set_bridge_priority(&new_id);
 					break;
 				case BRCMD_SET_PORT_PRIORITY:
-					if (port_info[bcf.arg1].dev == 0)
+					if((port_info[bcf.arg1].dev == 0)
+					    || (bcf.arg2 & ~0xff))
 						return(-EINVAL);
-					set_port_priority(bcf.arg1, bcf.arg2);
+					port_priority[bcf.arg1] = bcf.arg2;
+					set_port_priority(bcf.arg1);
 					break;
 				case BRCMD_SET_PATH_COST:
 					if (port_info[bcf.arg1].dev == 0)
@@ -1664,6 +2004,36 @@
 					memset(&br_stats.prot_id,0,sizeof(br_stats.prot_id));
 					memset(&br_stats.prot_counter,0,sizeof(br_stats.prot_counter));
 					break;
+				case BRCMD_DISPLAY_FDB:
+				{
+					struct fdb_info_hdr *user_buf = (void*) bcf.arg1;
+					struct fdb_info *u_fdbs, *fdbis;
+					int copied, notcopied;
+					u32 j = CURRENT_TIME;
+
+					if(bcf.arg2<sizeof(struct fdb_info_hdr))
+						return -EINVAL;
+					put_user(j, &user_buf->cmd_time);
+					if(allocated_fdb_cnt == 0) 
+					{
+						put_user(0, &user_buf->copied);
+						put_user(0, &user_buf->not_copied);
+						return 0;
+					}
+					fdbis = get_fdb_info(bcf.arg2, &copied, &notcopied);
+					put_user(copied, &user_buf->copied);
+					put_user(notcopied, &user_buf->not_copied);
+					if(!fdbis)
+						return -ENOMEM;
+					u_fdbs = (struct fdb_info *) (user_buf+1);
+					err = copy_to_user(u_fdbs, fdbis, copied*sizeof(struct fdb_info));
+					kfree(fdbis);
+					if (err)
+					{
+						err = -EFAULT;
+					}
+					return err;
+				}
 				default:
 					return -EINVAL;
 			}
@@ -1680,12 +2050,13 @@
 	int i;	
 	for (i=0; i<2; i++) 
 	{
-		if (a[i] == b[i])
-			continue;
-		if (a[i] < b[i])
-			return(1);
-		if (a[i] > b[i])
+		/* JRP: compares prty then MAC address in memory byte order
+		 * OK optimizer does htonl() only once per long !
+		 */
+		if (htonl(a[i]) < htonl(b[i]))
 			return(-1);
+		if (htonl(a[i]) > htonl(b[i]))
+			return(1);
 	}
 	return(0);
 }
diff -u --recursive --new-file v2.1.91/linux/net/bridge/br_tree.c linux/net/bridge/br_tree.c
--- v2.1.91/linux/net/bridge/br_tree.c	Thu Mar 27 14:40:14 1997
+++ linux/net/bridge/br_tree.c	Wed Apr  1 16:19:57 1998
@@ -1,6 +1,7 @@
 /*
- * this code is derived from the avl functions in mmap.c
+ *	This code is derived from the avl functions in mmap.c
  */
+
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/string.h>
@@ -16,6 +17,10 @@
  * Written by Bruno Haible <haible@ma2s2.mathematik.uni-karlsruhe.de>.
  * Taken from mmap.c, extensively modified by John Hayes 
  * <hayes@netplumbing.com>
+ * 98-02 Modified by Jean-Rene Peulve jr.peulve@aix.pacwan.net
+ *		update port number when topology change
+ *		return oldfdb when updating, for broadcast storm checking
+ *		call addr_cmp once per node
  */
 
 static struct fdb fdb_head;
@@ -50,8 +55,7 @@
  *    foreach node in tree->fdb_avl_right: node->fdb_avl_key >= tree->fdb_avl_key.
  */
 
-static int
-fdb_init(void)
+static int fdb_init(void)
 {
 	fdb_head.fdb_avl_height = 0;
 	fdb_head.fdb_avl_left = (struct fdb *)0;
@@ -109,7 +113,6 @@
 }
 
 
-#if (0)
 /*
  * Rebalance a tree.
  * After inserting or deleting a node of a tree we have a sequence of subtrees
@@ -196,10 +199,14 @@
 	printk_avl(&fdb_head);
 #endif /* DEBUG_AVL */
 }
-#endif /* (0) */
 
-/* Insert a node into a tree. */
-int br_avl_insert (struct fdb * new_node)
+/* Insert a node into a tree.
+ * Performance improvement:
+ *	 call addr_cmp() only once per node and use result in a switch.
+ * Return old node address if we knew that MAC address already
+ * Return NULL if we insert the new node
+ */
+struct fdb *br_avl_insert (struct fdb * new_node)
 {
 	struct fdb ** nodeplace = fhpp;
 	struct fdb ** stack[avl_maxheight];
@@ -214,15 +221,38 @@
 		if (node == avl_br_empty)
 			break;
 		*stack_ptr++ = nodeplace; stack_count++;
-		if (addr_cmp(new_node->ula, node->ula) == 0) { /* update */
+		switch(addr_cmp(new_node->ula, node->ula)) {
+		case 0: /* update */
+                   if (node->port == new_node->port) {
 			node->flags = new_node->flags;
 			node->timer = new_node->timer;	
-			return(0);
-		}
-		if (addr_cmp(new_node->ula, node->ula) < 0) {
-			nodeplace = &node->fdb_avl_left;
-		} else {
-			nodeplace = &node->fdb_avl_right;
+		   } else if (!(node->flags & FDB_ENT_VALID) &&
+				node->port) {
+			/* update fdb but never for local interfaces */
+#if (DEBUG_AVL)
+			printk("node 0x%x:port changed old=%d new=%d\n",
+				(unsigned int)node, node->port,new_node->port);
+#endif
+			/* JRP: update port as well if the topology change !
+			 * Don't do this while entry is still valid otherwise
+			 * a broadcast that we flooded and is reentered by another
+			 * port would mess up the good port number.
+			 * The fdb list per port needs to be updated as well.
+			 */
+			requeue_fdb(node, new_node->port);
+			node->flags = new_node->flags;
+			node->timer = new_node->timer;	
+#if (DEBUG_AVL)
+			printk_avl(&fdb_head);
+#endif /* DEBUG_AVL */
+		   }
+		   return node;		/* pass old fdb to caller */
+
+		case 1: /* new_node->ula > node->ula */
+		   nodeplace = &node->fdb_avl_right;
+		   break;
+		default: /* -1 => new_node->ula < node->ula */
+		   nodeplace = &node->fdb_avl_left;
 		}
 	}
 #if (DEBUG_AVL)
@@ -239,17 +269,14 @@
 	new_node->fdb_avl_right = avl_br_empty;
 	new_node->fdb_avl_height = 1;
 	*nodeplace = new_node;
-#if (0)	
 	br_avl_rebalance(stack_ptr,stack_count);
-#endif /* (0) */
 #ifdef DEBUG_AVL
 	printk_avl(&fdb_head);
 #endif /* DEBUG_AVL */
-	return(1);
+	return NULL;		/* this is a new node */
 }
 
 
-#if (0)
 /* Removes a node out of a tree. */
 static int br_avl_remove (struct fdb * node_to_delete)
 {
@@ -302,7 +329,6 @@
 	br_avl_rebalance(stack_ptr,stack_count);
 	return(0);
 }
-#endif /* (0) */
 
 #ifdef DEBUG_AVL
 
@@ -311,13 +337,14 @@
 {
 	if (tree != avl_br_empty) {
 		printk("(");
-		printk("%02x:%02x:%02x:%02x:%02x:%02x",
+		printk("%02x:%02x:%02x:%02x:%02x:%02x(%d)",
 			tree->ula[0],
 			tree->ula[1],
 			tree->ula[2],
 			tree->ula[3],
 			tree->ula[4],
-			tree->ula[5]);
+			tree->ula[5],
+			tree->port);
 		if (tree->fdb_avl_left != avl_br_empty) {
 			printk_avl(tree->fdb_avl_left);
 			printk("<");
@@ -330,7 +357,6 @@
 	}
 }
 
-#if (0)
 static char *avl_check_point = "somewhere";
 
 /* check a tree's consistency and balancing */
@@ -387,7 +413,6 @@
 	avl_checkright(tree->fdb_avl_right,tree->fdb_avl_key);
 }
 
-#endif /* (0) */
 #endif /* DEBUG_AVL */
 
 static int addr_cmp(unsigned char a1[], unsigned char a2[])
diff -u --recursive --new-file v2.1.91/linux/net/core/skbuff.c linux/net/core/skbuff.c
--- v2.1.91/linux/net/core/skbuff.c	Thu Mar 26 15:57:10 1998
+++ linux/net/core/skbuff.c	Fri Mar 27 18:12:32 1998
@@ -273,7 +273,6 @@
 	n->csum = skb->csum;
 	n->list=NULL;
 	n->sk=NULL;
-	n->when=skb->when;
 	n->dev=skb->dev;
 	n->priority=skb->priority;
 	n->protocol=skb->protocol;
@@ -281,9 +280,6 @@
 	n->h.raw=skb->h.raw+offset;
 	n->nh.raw=skb->nh.raw+offset;
 	n->mac.raw=skb->mac.raw+offset;
-	n->seq=skb->seq;
-	n->end_seq=skb->end_seq;
-	n->ack_seq=skb->ack_seq;
 	memcpy(n->cb, skb->cb, sizeof(skb->cb));
 	n->used=skb->used;
 	n->is_clone=0;
@@ -323,7 +319,6 @@
 	memcpy(n->data,skb->data,skb->len);
 	n->list=NULL;
 	n->sk=NULL;
-	n->when=skb->when;
 	n->priority=skb->priority;
 	n->protocol=skb->protocol;
 	n->dev=skb->dev;
@@ -332,9 +327,6 @@
 	n->nh.raw=skb->nh.raw+offset;
 	n->mac.raw=skb->mac.raw+offset;
 	memcpy(n->cb, skb->cb, sizeof(skb->cb));
-	n->seq=skb->seq;
- 	n->end_seq=skb->end_seq;
-	n->ack_seq=skb->ack_seq;
 	n->used=skb->used;
 	n->is_clone=0;
 	atomic_set(&n->users, 1);
diff -u --recursive --new-file v2.1.91/linux/net/ipv4/af_inet.c linux/net/ipv4/af_inet.c
--- v2.1.91/linux/net/ipv4/af_inet.c	Thu Mar 26 15:57:13 1998
+++ linux/net/ipv4/af_inet.c	Fri Mar 27 18:12:32 1998
@@ -5,7 +5,7 @@
  *
  *		AF_INET protocol family socket handler.
  *
- * Version:	$Id: af_inet.c,v 1.66 1998/03/21 07:27:58 davem Exp $
+ * Version:	$Id: af_inet.c,v 1.68 1998/03/27 07:02:42 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -352,7 +352,6 @@
 		if (protocol && protocol != IPPROTO_TCP)
 			goto free_and_noproto;
 		protocol = IPPROTO_TCP;
-		sk->no_check = TCP_NO_CHECK;
 		if (ipv4_config.no_pmtu_disc)
 			sk->ip_pmtudisc = IP_PMTUDISC_DONT;
 		else
diff -u --recursive --new-file v2.1.91/linux/net/ipv4/devinet.c linux/net/ipv4/devinet.c
--- v2.1.91/linux/net/ipv4/devinet.c	Tue Mar 17 22:18:16 1998
+++ linux/net/ipv4/devinet.c	Fri Mar 27 18:12:32 1998
@@ -1,7 +1,7 @@
 /*
  *	NET3	IP device support routines.
  *
- *	Version: $Id: devinet.c,v 1.19 1998/03/08 20:52:35 davem Exp $
+ *	Version: $Id: devinet.c,v 1.20 1998/03/27 07:02:44 davem Exp $
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -19,7 +19,7 @@
  *
  *	Changes:
  *	        Alexey Kuznetsov:	pa_* fields are replaced with ifaddr lists.
- 		Cyrus Durgin:		updated for kmod
+ *		Cyrus Durgin:		updated for kmod
  */
 
 #include <linux/config.h>
diff -u --recursive --new-file v2.1.91/linux/net/ipv4/ip_masq_app.c linux/net/ipv4/ip_masq_app.c
--- v2.1.91/linux/net/ipv4/ip_masq_app.c	Thu Feb 12 20:56:14 1998
+++ linux/net/ipv4/ip_masq_app.c	Wed Apr  1 16:21:32 1998
@@ -506,7 +506,7 @@
         struct sk_buff *n_skb;
 	int offset;
 
-	maxsize = skb->truesize - sizeof(struct sk_buff);
+	maxsize = skb->truesize;
 
         diff = n_len - o_len;
         o_offset = o_buf - (char*) skb->data;
@@ -547,7 +547,6 @@
                 offset = n_skb->data - skb->data;
                 n_skb->nh.raw = skb->nh.raw + offset;
                 n_skb->h.raw = skb->h.raw + offset;
-                n_skb->when = skb->when;
                 n_skb->dev = skb->dev;
                 n_skb->mac.raw = skb->mac.raw + offset;
                 n_skb->pkt_type = skb->pkt_type;
diff -u --recursive --new-file v2.1.91/linux/net/ipv4/ip_masq_mod.c linux/net/ipv4/ip_masq_mod.c
--- v2.1.91/linux/net/ipv4/ip_masq_mod.c	Thu Mar 26 15:57:13 1998
+++ linux/net/ipv4/ip_masq_mod.c	Fri Mar 27 18:12:32 1998
@@ -2,10 +2,9 @@
  *		IP_MASQ_MOD masq modules support
  *
  *
- * Version:	@(#)ip_masq_mod.c  0.02      97/10/30
- *
  * Author:	Juan Jose Ciarlante, <jjciarla@raiz.uncu.edu.ar>
  *
+ * 	$Id: ip_masq_mod.c,v 1.4 1998/03/27 07:02:45 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *	modify it under the terms of the GNU General Public License
@@ -287,7 +286,6 @@
 
 /*
  *	Module control entry
- *	no need to lock (already locked in ip_masq.c)
  */
 int ip_masq_mod_ctl(int optname, struct ip_fw_masqctl *mctl, int optlen)
 {
diff -u --recursive --new-file v2.1.91/linux/net/ipv4/ip_output.c linux/net/ipv4/ip_output.c
--- v2.1.91/linux/net/ipv4/ip_output.c	Thu Mar 26 15:57:13 1998
+++ linux/net/ipv4/ip_output.c	Fri Mar 27 18:12:32 1998
@@ -5,7 +5,7 @@
  *
  *		The Internet Protocol (IP) output module.
  *
- * Version:	$Id: ip_output.c,v 1.50 1998/03/20 09:12:08 davem Exp $
+ * Version:	$Id: ip_output.c,v 1.51 1998/03/28 00:55:34 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -428,7 +428,6 @@
 			return error;
 		}
 
-		skb->when=jiffies;
 		skb->priority = sk->priority;
 		skb->dst = dst_clone(&rt->u.dst);
 		skb_reserve(skb, hh_len);
@@ -573,7 +572,6 @@
 		 *	Fill in the control structures
 		 */
 		 
-		skb->when = jiffies;
 		skb->priority = sk->priority;
 		skb->dst = dst_clone(&rt->u.dst);
 		skb_reserve(skb, hh_len);
@@ -765,7 +763,6 @@
 		 *	Set up data on packet
 		 */
 
-		skb2->when = skb->when;
 		skb2->pkt_type = skb->pkt_type;
 		skb2->priority = skb->priority;
 		skb_reserve(skb2, (dev->hard_header_len+15)&~15);
diff -u --recursive --new-file v2.1.91/linux/net/ipv4/sysctl_net_ipv4.c linux/net/ipv4/sysctl_net_ipv4.c
--- v2.1.91/linux/net/ipv4/sysctl_net_ipv4.c	Thu Mar 26 15:57:13 1998
+++ linux/net/ipv4/sysctl_net_ipv4.c	Wed Apr  1 16:19:57 1998
@@ -1,7 +1,7 @@
 /*
  * sysctl_net_ipv4.c: sysctl interface to net IPV4 subsystem.
  *
- * $Id: sysctl_net_ipv4.c,v 1.30 1998/03/23 23:56:29 davem Exp $
+ * $Id: sysctl_net_ipv4.c,v 1.31 1998/03/30 08:41:41 davem Exp $
  *
  * Begun April 1, 1996, Mike Shaver.
  * Added /proc/sys/net/ipv4 directory entry (empty =) ). [MS]
@@ -70,9 +70,6 @@
 
 int tcp_retr1_max = 255; 
 
-extern int tcp_sysctl_congavoid(ctl_table *ctl, int write, struct file * filp,
-				void *buffer, size_t *lenp);
-
 struct ipv4_config ipv4_config;
 
 extern ctl_table ipv4_route_table[];
@@ -108,9 +105,6 @@
         {NET_IPV4_TCP_SACK, "tcp_sack",
          &sysctl_tcp_sack, sizeof(int), 0644, NULL,
          &proc_dointvec},
-	{NET_IPV4_TCP_VEGAS_CONG_AVOID, "tcp_vegas_cong_avoid",
-	 &sysctl_tcp_cong_avoidance, sizeof(int), 0644,
-	 NULL, &tcp_sysctl_congavoid },
         {NET_IPV4_FORWARD, "ip_forward",
          &ipv4_devconf.forwarding, sizeof(int), 0644, NULL,
          &ipv4_sysctl_forward},
diff -u --recursive --new-file v2.1.91/linux/net/ipv4/tcp.c linux/net/ipv4/tcp.c
--- v2.1.91/linux/net/ipv4/tcp.c	Thu Mar 26 15:57:13 1998
+++ linux/net/ipv4/tcp.c	Wed Apr  1 16:19:57 1998
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp.c,v 1.104 1998/03/22 22:10:30 davem Exp $
+ * Version:	$Id: tcp.c,v 1.108 1998/03/29 08:43:51 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -506,13 +506,13 @@
 	/* Do until a push or until we are out of data. */
 	do {
 		/* Found a hole so stops here. */
-		if (before(counted, skb->seq))	/* should not happen */
+		if (before(counted, TCP_SKB_CB(skb)->seq))	/* should not happen */
 			break;
 
 		/* Length - header but start from where we are up to
 		 * avoid overlaps.
 		 */
-		sum = skb->len - (counted - skb->seq);
+		sum = skb->len - (counted - TCP_SKB_CB(skb)->seq);
 		if (skb->h.th->syn)
 			sum++;
 		if (sum > 0) {
@@ -788,7 +788,7 @@
 				 */
 				if (skb_tailroom(skb) > 0 &&
 				    (mss_now - copy) > 0 &&
-				    tp->snd_nxt < skb->end_seq) {
+				    tp->snd_nxt < TCP_SKB_CB(skb)->end_seq) {
 					int last_byte_was_odd = (copy % 4);
 
 					copy = mss_now - copy;
@@ -809,7 +809,7 @@
 							copy, skb->csum, &err);
 					}
 					tp->write_seq += copy;
-					skb->end_seq += copy;
+					TCP_SKB_CB(skb)->end_seq += copy;
 					from += copy;
 					copied += copy;
 					seglen -= copy;
@@ -839,7 +839,7 @@
 			if(copy > seglen)
 				copy = seglen;
 
-			tmp = MAX_HEADER + sk->prot->max_header + 15;
+			tmp = MAX_HEADER + sk->prot->max_header;
 			queue_it = 0;
 			if (copy < min(mss_now, tp->max_window >> 1) &&
 			    !(flags & MSG_OOB)) {
@@ -897,8 +897,8 @@
 			from += copy;
 			copied += copy;
 
-			skb->seq = tp->write_seq;
-			skb->end_seq = skb->seq + copy;
+			TCP_SKB_CB(skb)->seq = tp->write_seq;
+			TCP_SKB_CB(skb)->end_seq = TCP_SKB_CB(skb)->seq + copy;
 
 			/* This advances tp->write_seq for us. */
 			tcp_send_skb(sk, skb, queue_it);
@@ -1135,12 +1135,12 @@
 			/* Now that we have two receive queues this 
 			 * shouldn't happen.
 			 */
-			if (before(*seq, skb->seq)) {
+			if (before(*seq, TCP_SKB_CB(skb)->seq)) {
 				printk(KERN_INFO "recvmsg bug: copied %X seq %X\n",
-				       *seq, skb->seq);
+				       *seq, TCP_SKB_CB(skb)->seq);
 				break;
 			}
-			offset = *seq - skb->seq;
+			offset = *seq - TCP_SKB_CB(skb)->seq;
 			if (skb->h.th->syn)
 				offset--;
 			if (offset < skb->len)
@@ -1225,10 +1225,8 @@
 		 *	a crash when cleanup_rbuf() gets called.
 		 */
 		err = memcpy_toiovec(msg->msg_iov, ((unsigned char *)skb->h.th) + skb->h.th->doff*4 + offset, used);
-		
 		if (err) {
 			/* Exception. Bailout! */
-			*seq -= err;
 			atomic_dec(&skb->users);
 			copied = -EFAULT;
 			break;
@@ -1409,7 +1407,7 @@
 	 *  descriptor close, not protocol-sourced closes, because the
 	 *  reader process may not have drained the data yet!
 	 */
-	while((skb=skb_dequeue(&sk->receive_queue))!=NULL) {
+	while((skb=__skb_dequeue(&sk->receive_queue))!=NULL) {
 		data_was_unread++;
 		kfree_skb(skb);
 	}
@@ -1625,8 +1623,16 @@
 		tcp_dec_slow_timer(TCP_SLT_KEEPALIVE);
 }
 
+extern void __skb_cb_too_small_for_tcp(int, int);
+
 __initfunc(void tcp_init(void))
 {
+	struct sk_buff *skb = NULL;
+
+	if(sizeof(struct tcp_skb_cb) > sizeof(skb->cb))
+		__skb_cb_too_small_for_tcp(sizeof(struct tcp_skb_cb),
+					   sizeof(skb->cb));
+
 	tcp_openreq_cachep = kmem_cache_create("tcp_open_request",
 						   sizeof(struct open_request),
 					       0, SLAB_HWCACHE_ALIGN,
diff -u --recursive --new-file v2.1.91/linux/net/ipv4/tcp_input.c linux/net/ipv4/tcp_input.c
--- v2.1.91/linux/net/ipv4/tcp_input.c	Thu Mar 26 15:57:13 1998
+++ linux/net/ipv4/tcp_input.c	Wed Apr  1 16:19:57 1998
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_input.c,v 1.98 1998/03/23 22:54:48 davem Exp $
+ * Version:	$Id: tcp_input.c,v 1.103 1998/03/30 08:41:12 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -50,15 +50,6 @@
 #include <net/tcp.h>
 #include <linux/ipsec.h>
 
-typedef void			(*tcp_sys_cong_ctl_t)(struct sock *sk,
-						      u32 seq, u32 ack,
-						      u32 seq_rtt);
-
-static void tcp_cong_avoid_vanj(struct sock *sk, u32 seq, u32 ack,
-				u32 seq_rtt);
-static void tcp_cong_avoid_vegas(struct sock *sk, u32 seq, u32 ack,
-				 u32 seq_rtt);
-
 #ifdef CONFIG_SYSCTL
 #define SYNC_INIT 0 /* let the user enable it */
 #else
@@ -80,8 +71,6 @@
 int sysctl_tcp_stdurg;
 int sysctl_tcp_rfc1337;
 
-static tcp_sys_cong_ctl_t tcp_sys_cong_ctl_f = &tcp_cong_avoid_vanj;
-
 /* There is something which you must keep in mind when you analyze the
  * behavior of the tp->ato delayed ack timeout interval.  When a
  * connection starts up, we want to ack as quickly as possible.  The
@@ -164,7 +153,7 @@
 static __inline__ void tcp_set_rto(struct tcp_opt *tp)
 {
 	tp->rto = (tp->srtt >> 3) + tp->mdev;
-	tp->rto += (tp->rto >> 2) + (tp->rto >> (tp->snd_cwnd-1));
+	tp->rto += (tp->rto >> 2) + (tp->rto >> ((tp->snd_cwnd>>TCP_CWND_SHIFT)-1));
 }
  
 
@@ -176,7 +165,7 @@
  * way to avoid the problem. Is it possible to drop the lower
  * bound and still avoid trouble with BSD stacks? Perhaps
  * some modification to the RTO calculation that takes delayed
- * ack bais into account? This needs serious thought. -- erics
+ * ack bias into account? This needs serious thought. -- erics
  */
 static __inline__ void tcp_bound_rto(struct tcp_opt *tp)
 {
@@ -193,19 +182,27 @@
 	 * test is last_ack_sent <= end_seq.
 	 * (RFC1323 stated last_ack_sent < end_seq.)
 	 */
-	if (!before(end_seq,tp->last_ack_sent)) {
-		tp->ts_recent = tp->rcv_tsval;
-		tp->ts_recent_stamp = jiffies;
+	if (!before(end_seq, tp->last_ack_sent)) {
+		/* PAWS bug workaround wrt. ACK frames, the PAWS discard
+		 * extra check below makes sure this can only happen
+		 * for pure ACK frames.  -DaveM
+		 */
+		if((s32)(tp->rcv_tsval - tp->ts_recent) >= 0) {
+			tp->ts_recent = tp->rcv_tsval;
+			tp->ts_recent_stamp = jiffies;
+		}
 	}
 }
 
 #define PAWS_24DAYS	(HZ * 60 * 60 * 24 * 24)
 
-extern __inline__ int tcp_paws_discard(struct tcp_opt *tp)
+extern __inline__ int tcp_paws_discard(struct tcp_opt *tp, struct tcphdr *th, __u16 len)
 {
 	/* ts_recent must be younger than 24 days */
 	return (((jiffies - tp->ts_recent_stamp) >= PAWS_24DAYS) ||
-		((s32)(tp->rcv_tsval-tp->ts_recent) < 0));
+		(((s32)(tp->rcv_tsval-tp->ts_recent) < 0) &&
+		 /* Sorry, PAWS as specified is broken wrt. pure-ACKs -DaveM */
+		 (len != (th->doff * 4))));
 }
 
 
@@ -266,15 +263,34 @@
 		struct sk_buff *skb = skb_peek(&sk->write_queue);
 		__u32 start_seq = ntohl(sp->start_seq);
 		__u32 end_seq = ntohl(sp->end_seq);
+		int fack_count = 0;
 
 		while((skb != NULL) &&
 		      (skb != tp->send_head) &&
 		      (skb != (struct sk_buff *)&sk->write_queue)) {
+			/* The retransmission queue is always in order, so
+			 * we can short-circuit the walk early.
+			 */
+			if(!before(start_seq, TCP_SKB_CB(skb)->end_seq))
+				break;
+
 			/* We play conservative, we don't allow SACKS to partially
 			 * tag a sequence space.
 			 */
-			if(!after(start_seq, skb->seq) && !before(end_seq, skb->end_seq))
+			fack_count++;
+			if(!after(start_seq, TCP_SKB_CB(skb)->seq) &&
+			   !before(end_seq, TCP_SKB_CB(skb)->end_seq)) {
+				/* If this was a retransmitted frame, account for it. */
+				if(TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_RETRANS)
+					tp->retrans_out--;
 				TCP_SKB_CB(skb)->sacked |= TCPCB_SACKED_ACKED;
+
+				/* RULE: All new SACKs will either decrease retrans_out
+				 *       or advance fackets_out.
+				 */
+				if(fack_count > tp->fackets_out)
+					tp->fackets_out = fack_count;
+			}
 			skb = skb->next;
 		}
 		sp++; /* Move on to the next SACK block. */
@@ -388,19 +404,43 @@
 	return 1;
 }
 
+#if 0 /* Not working yet... -DaveM */
+static void tcp_compute_tsack(struct sock *sk, struct tcp_opt *tp)
+{
+	struct sk_buff *skb = skb_peek(&sk->write_queue);
+	__u32 tstamp = tp->rcv_tsecr;
+	int fack_count = 0;
+
+	while((skb != NULL) &&
+	      (skb != tp->send_head) &&
+	      (skb != (struct sk_buff *)&sk->write_queue)) {
+		if(TCP_SKB_CB(skb)->when == tstamp) {
+			__u8 sacked = TCP_SKB_CB(skb)->sacked;
+
+			sacked |= TCPCB_SACKED_ACKED;
+			if(sacked & TCPCB_SACKED_RETRANS)
+				tp->retrans_out--;
+			TCP_SKB_CB(skb)->sacked = sacked;
+		}
+		if(!before(TCP_SKB_CB(skb)->when, tstamp))
+			fack_count++;
+		skb = skb->next;
+	}
+	if(fack_count > tp->fackets_out)
+		tp->fackets_out = fack_count;
+}
+#endif
+
 #define FLAG_DATA		0x01 /* Incoming frame contained data.		*/
 #define FLAG_WIN_UPDATE		0x02 /* Incoming ACK was a window update.	*/
 #define FLAG_DATA_ACKED		0x04 /* This ACK acknowledged new data.		*/
 #define FLAG_RETRANS_DATA_ACKED	0x08 /* "" "" some of which was retransmitted.	*/
 
-static __inline__ void clear_fast_retransmit(struct sock *sk)
+static __inline__ void clear_fast_retransmit(struct tcp_opt *tp)
 {
-	struct tcp_opt *tp=&(sk->tp_pinfo.af_tcp);
+	if (tp->dup_acks > 3)
+		tp->snd_cwnd = (tp->snd_ssthresh << TCP_CWND_SHIFT);
 
-	if (tp->dup_acks > 3) {
-		tp->retrans_head = NULL;
-		tp->snd_cwnd = max(tp->snd_ssthresh, 1);
-	}
 	tp->dup_acks = 0;
 }
 
@@ -409,10 +449,9 @@
  */
 static void tcp_fast_retrans(struct sock *sk, u32 ack, int not_dup)
 {
-	struct tcp_opt *tp=&(sk->tp_pinfo.af_tcp);
+	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 
-	/*
-	 * Note: If not_dup is set this implies we got a
+	/* Note: If not_dup is set this implies we got a
 	 * data carrying packet or a window update.
 	 * This carries no new information about possible
 	 * lost packets, so we have to ignore it for the purposes
@@ -422,22 +461,31 @@
 	 * the code below much more complex. For now if I see such
 	 * a packet I clear the fast retransmit phase.
 	 */
-
 	if (ack == tp->snd_una && tp->packets_out && (not_dup == 0)) {
 		/* This is the standard reno style fast retransmit branch. */
 
+#if 0	/* Not working yet... -DaveM */
+		/* If not doing SACK, but doing timestamps, compute timestamp
+		 * based pseudo-SACKs when we see duplicate ACKs.
+		 */
+		if(!tp->sack_ok && tp->saw_tstamp)
+			tcp_compute_tsack(sk, tp);
+#endif
                 /* 1. When the third duplicate ack is received, set ssthresh 
                  * to one half the current congestion window, but no less 
                  * than two segments. Retransmit the missing segment.
                  */
 		if (tp->high_seq == 0 || after(ack, tp->high_seq)) {
 			tp->dup_acks++;
-			if (tp->dup_acks == 3) {
+			if ((tp->fackets_out > 3) || (tp->dup_acks == 3)) {
 				tp->dup_acks++;
-                                tp->snd_ssthresh = max(tp->snd_cwnd >> 1, 2);
-                                tp->snd_cwnd = tp->snd_ssthresh + 3;
+                                tp->snd_ssthresh = max(tp->snd_cwnd >> (TCP_CWND_SHIFT + 1), 2);
+                                tp->snd_cwnd = (tp->snd_ssthresh + 3) << TCP_CWND_SHIFT;
 				tp->high_seq = tp->snd_nxt;
-                                tcp_retransmit_skb(sk, skb_peek(&sk->write_queue));
+				if(!tp->fackets_out)
+					tcp_retransmit_skb(sk, skb_peek(&sk->write_queue));
+				else
+					tcp_fack_retransmit(sk);
                                 tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
 			}
 		}
@@ -446,10 +494,22 @@
                  * cwnd by the segment size. [...] Transmit a packet...
                  *
                  * Packet transmission will be done on normal flow processing
-                 * since we're not in "retransmit mode"
+                 * since we're not in "retransmit mode".  We do not use duplicate
+		 * ACKs to artificially inflate the congestion window when
+		 * doing FACK.
                  */
-                if (tp->dup_acks > 3)
-                        tp->snd_cwnd++;
+                if (tp->dup_acks > 3) {
+			if(!tp->fackets_out) {
+				tp->snd_cwnd += (1 << TCP_CWND_SHIFT);
+			} else {
+				/* Fill any further holes which may have appeared.
+				 * We may want to change this to run every further
+				 * multiple-of-3 dup ack increments, to be more robust
+				 * against out-of-order packet delivery.  -DaveM
+				 */
+				tcp_fack_retransmit(sk);
+			}
+		}
 	} else if (tp->high_seq != 0) {
 		/* In this branch we deal with clearing the Floyd style
 		 * block on duplicate fast retransmits, and if requested
@@ -463,15 +523,17 @@
 			 * Note that we do want to accept a window
 			 * update since this is expected with Hoe's algorithm.
 			 */
-			clear_fast_retransmit(sk);
+			clear_fast_retransmit(tp);
 
 			/* After we have cleared up to high_seq we can
 			 * clear the Floyd style block.
 			 */
-			if (after(ack, tp->high_seq))
+			if (!before(ack, tp->high_seq)) {
 				tp->high_seq = 0;
+				tp->fackets_out = 0;
+			}
 		} else if (tp->dup_acks >= 3) {
-			if (sysctl_tcp_hoe_retransmits) {
+			if (!tp->fackets_out) {
 				/* Hoe Style. We didn't ack the whole
 				 * window. Take this as a cue that
 				 * another packet was lost and retransmit it.
@@ -490,131 +552,34 @@
                                 	tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
 				}
 			} else {
-				/* Reno style. We didn't ack the whole
-				 * window, now we have to drop out of
-				 * fast retransmit and wait for a timeout.
+				/* FACK style, fill any remaining holes in
+				 * receiver's queue.
 				 */
-				clear_fast_retransmit(sk);
+				tcp_fack_retransmit(sk);
 			}
 		}
 	}
 }
 
-/*
- *      TCP slow start and congestion avoidance in two flavors:
- *      RFC 1122 and TCP Vegas.
+/* This is Jacobson's slow start and congestion avoidance. 
+ * SIGCOMM '88, p. 328.
  *
- *      This is a /proc/sys configurable option. 
+ * FIXME: What happens when the congestion window gets larger
+ * than the maximum receiver window by some large factor
+ * Suppose the pipeline never looses packets for a long
+ * period of time, then traffic increases causing packet loss.
+ * The congestion window should be reduced, but what it should
+ * be reduced to is not clear, since 1/2 the old window may
+ * still be larger than the maximum sending rate we ever achieved.
  */
-
-#define SHIFT_FACTOR 16
-
-static void tcp_cong_avoid_vegas(struct sock *sk, u32 seq, u32 ack,
-				 u32 seq_rtt)
+static void tcp_cong_avoid(struct tcp_opt *tp, u32 seq, u32 ack, u32 seq_rtt)
 {
-	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
-	unsigned int actual, expected;
-	unsigned int inv_rtt, inv_basertt, inv_basebd;
-	u32 snt_bytes;
-
-	/*	From:
-	 *      TCP Vegas: New Techniques for Congestion 
-	 *	Detection and Avoidance.
-	 *
-	 *	Warning: This code is a scratch implementation taken
-	 *	from the paper only. The code they distribute seams
-	 *	to have improved several things over the initial spec.
-	 */
-
-	if (!seq_rtt)
-		seq_rtt = 1;
-
-	if (tp->basertt)
-		tp->basertt = min(seq_rtt, tp->basertt);
-	else
-		tp->basertt = seq_rtt;
-
-	/*	actual	 = throughput for this segment.
-	 *	expected = number_of_bytes in transit / BaseRTT
-	 */
-
-	snt_bytes = ack - seq;
-
-	inv_rtt = (1 << SHIFT_FACTOR) / seq_rtt;
-	inv_basertt = (1 << SHIFT_FACTOR) / tp->basertt;
-
-	actual =  snt_bytes * inv_rtt;
-
-	expected = (tp->snd_nxt - tp->snd_una) * inv_basertt;
-
-	inv_basebd = sk->mss * inv_basertt;
-
-	/* Slow Start */
-	if (tp->snd_cwnd < tp->snd_ssthresh &&
-	    (seq == tp->snd_nxt ||
-	     (expected - actual <= TCP_VEGAS_GAMMA * inv_basebd))) {
-		/* "Vegas allows exponential growth only every other RTT" */
-		if (tp->snd_cwnd_cnt++) {
-			tp->snd_cwnd++;
-			tp->snd_cwnd_cnt = 0;
-		}
-	} else {
-		/* Congestion Avoidance */
-		if (expected - actual <= TCP_VEGAS_ALPHA * inv_basebd) {
-			/* Increase Linearly */
-			if (tp->snd_cwnd_cnt++ >= tp->snd_cwnd) {
-				tp->snd_cwnd++;
-				tp->snd_cwnd_cnt = 0;
-			}
-		}
-
-		if (expected - actual >= TCP_VEGAS_BETA * inv_basebd) {
-			/* Decrease Linearly */
-			if (tp->snd_cwnd_cnt++ >= tp->snd_cwnd) {
-				tp->snd_cwnd--;
-				tp->snd_cwnd_cnt = 0;
-			}
-
-			/* Never less than 2 segments. */
-			if (tp->snd_cwnd < 2)
-				tp->snd_cwnd = 2;
-		}
-	}
-}
-
-static void tcp_cong_avoid_vanj(struct sock *sk, u32 seq, u32 ack, u32 seq_rtt)
-{
-	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
-	
-        /* This is Jacobson's slow start and congestion avoidance. 
-         * SIGCOMM '88, p. 328.  Because we keep cong_window in 
-         * integral mss's, we can't do cwnd += 1 / cwnd.  
-         * Instead, maintain a counter and increment it once every 
-         * cwnd times.  
-	 * FIXME: Check to be sure the mathematics works out right
-	 * on this trick when we have to reduce the congestion window.
-	 * The snd_cwnd_cnt has to be reset properly when reduction events
-	 * happen.
-	 * FIXME: What happens when the congestion window gets larger
-	 * than the maximum receiver window by some large factor
-	 * Suppose the pipeline never looses packets for a long
-	 * period of time, then traffic increases causing packet loss.
-	 * The congestion window should be reduced, but what it should
-	 * be reduced to is not clear, since 1/2 the old window may
-	 * still be larger than the maximum sending rate we ever achieved.
-         */
-        if (tp->snd_cwnd <= tp->snd_ssthresh) {
+        if ((tp->snd_cwnd>>TCP_CWND_SHIFT) <= tp->snd_ssthresh) {
                 /* In "safe" area, increase. */
-                tp->snd_cwnd++;
+                tp->snd_cwnd += (1 << TCP_CWND_SHIFT);
 	} else {
-                /* In dangerous area, increase slowly.  In theory this is
-                 * tp->snd_cwnd += 1 / tp->snd_cwnd
-                 */
-                if (tp->snd_cwnd_cnt >= tp->snd_cwnd) {
-                        tp->snd_cwnd++;
-                        tp->snd_cwnd_cnt = 0;
-                } else 
-                        tp->snd_cwnd_cnt++;
+                /* In dangerous area, increase slowly. */
+		tp->snd_cwnd += 1;
         }       
 }
 
@@ -632,7 +597,7 @@
 		 * discard it as it's confirmed to have arrived at
 		 * the other end.
 		 */
-		if (after(skb->end_seq, ack))
+		if (after(TCP_SKB_CB(skb)->end_seq, ack))
 			break;
 
 		/* Initial outgoing SYN's get put onto the write_queue
@@ -643,16 +608,30 @@
 		 * quickly.  This is severely frowned upon behavior.
 		 */
 		if(!(TCP_SKB_CB(skb)->flags & TCPCB_FLAG_SYN)) {
+			__u8 sacked = TCP_SKB_CB(skb)->sacked;
+
 			acked |= FLAG_DATA_ACKED;
-			if(TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_RETRANS)
+			if(sacked & TCPCB_SACKED_RETRANS) {
 				acked |= FLAG_RETRANS_DATA_ACKED;
+
+				/* XXX The race is, fast retrans frame -->
+				 * XXX retrans timeout sends older frame -->
+				 * XXX ACK arrives for fast retrans frame -->
+				 * XXX retrans_out goes negative --> splat.
+				 * XXX Please help me find a better way -DaveM
+				 */
+				if(tp->retrans_out)
+					tp->retrans_out--;
+			}
+			if(tp->fackets_out)
+				tp->fackets_out--;
 		} else {
 			tp->retrans_head = NULL;
 		}		
 		tp->packets_out--;
-		*seq = skb->seq;
-		*seq_rtt = now - skb->when;
-		skb_unlink(skb);
+		*seq = TCP_SKB_CB(skb)->seq;
+		*seq_rtt = now - TCP_SKB_CB(skb)->when;
+		__skb_unlink(skb, skb->list);
 		kfree_skb(skb);
 	}
 
@@ -672,7 +651,7 @@
 	
 	/* should always be non-null */
 	if (tp->send_head != NULL &&
-	    !before (ack + tp->snd_wnd, tp->send_head->end_seq)) {
+	    !before (ack + tp->snd_wnd, TCP_SKB_CB(tp->send_head)->end_seq)) {
 		tp->backoff = 0;
 		tp->pending = 0;
 		tcp_clear_xmit_timer(sk, TIME_PROBE0);
@@ -693,6 +672,8 @@
 	if (tp->retransmits) {
 		if (tp->packets_out == 0) {
 			tp->retransmits = 0;
+			tp->fackets_out = 0;
+			tp->retrans_out = 0;
 			tp->backoff = 0;
 			tcp_set_rto(tp);
 		} else {
@@ -703,7 +684,7 @@
 	} else {
 		tcp_set_rto(tp);
 		if (flag & FLAG_DATA_ACKED)
-			(*tcp_sys_cong_ctl_f)(sk, seq, ack, seq_rtt);
+			tcp_cong_avoid(tp, seq, ack, seq_rtt);
 	}
 	/* NOTE: safe here so long as cong_ctl doesn't use rto */
 	tcp_bound_rto(tp);
@@ -712,7 +693,7 @@
 static void tcp_ack_packets_out(struct sock *sk, struct tcp_opt *tp)
 {
 	struct sk_buff *skb = skb_peek(&sk->write_queue);
-	long when = tp->rto - (jiffies - skb->when);
+	long when = tp->rto - (jiffies - TCP_SKB_CB(skb)->when);
 
 	/* Some data was ACK'd, if still retransmitting (due to a
 	 * timeout), resend more of the retransmit queue.  The
@@ -801,8 +782,11 @@
 	} else {
 		/* If we were retransmiting don't count rtt estimate. */
 		if (tp->retransmits) {
-			if (tp->packets_out == 0)
+			if (tp->packets_out == 0) {
 				tp->retransmits = 0;
+				tp->fackets_out = 0;
+				tp->retrans_out = 0;
+			}
 		} else {
 			/* We don't have a timestamp. Can only use
 			 * packets that are not retransmitted to determine
@@ -812,13 +796,14 @@
 			 * where the network delay has increased suddenly.
 			 * I.e. Karn's algorithm. (SIGCOMM '87, p5.)
 			 */
-			if ((flag & FLAG_DATA_ACKED) &&
-			    !(flag & FLAG_RETRANS_DATA_ACKED)) {
-				tp->backoff = 0;
-				tcp_rtt_estimator(tp, seq_rtt);
-				tcp_set_rto(tp);
-				tcp_bound_rto(tp);
-				(*tcp_sys_cong_ctl_f)(sk, seq, ack, seq_rtt);
+			if (flag & FLAG_DATA_ACKED) {
+				if(!(flag & FLAG_RETRANS_DATA_ACKED)) {
+					tp->backoff = 0;
+					tcp_rtt_estimator(tp, seq_rtt);
+					tcp_set_rto(tp);
+					tcp_bound_rto(tp);
+				}
+				tcp_cong_avoid(tp, seq, ack, seq_rtt);
 			}
 		}
 	}
@@ -898,7 +883,7 @@
 	 *	(2)  returns to TIME-WAIT state if the SYN turns out 
 	 *	to be an old duplicate".
 	 */
-	if(th->syn && !th->rst && after(skb->seq, tw->rcv_nxt)) {
+	if(th->syn && !th->rst && after(TCP_SKB_CB(skb)->seq, tw->rcv_nxt)) {
 		struct sock *sk;
 		struct tcp_func *af_specific = tw->af_specific;
 		__u32 isn;
@@ -1051,7 +1036,7 @@
  
 static void tcp_fin(struct sk_buff *skb, struct sock *sk, struct tcphdr *th)
 {
-	sk->tp_pinfo.af_tcp.fin_seq = skb->end_seq;
+	sk->tp_pinfo.af_tcp.fin_seq = TCP_SKB_CB(skb)->end_seq;
 
 	tcp_send_ack(sk);
 
@@ -1174,14 +1159,14 @@
 	 * "in order". ;-)  This also satisfies the requirements
 	 * of RFC2018 about ordering of SACKs.
 	 */
-	if(sp->end_seq == skb->seq) {
-		sp->end_seq = skb->end_seq;
+	if(sp->end_seq == TCP_SKB_CB(skb)->seq) {
+		sp->end_seq = TCP_SKB_CB(skb)->end_seq;
 		tcp_sack_maybe_coalesce(tp, sp);
-	} else if(sp->start_seq == skb->end_seq) {
+	} else if(sp->start_seq == TCP_SKB_CB(skb)->end_seq) {
 		/* Re-ordered arrival, in this case, can be optimized
 		 * as well.
 		 */
-		sp->start_seq = skb->seq;
+		sp->start_seq = TCP_SKB_CB(skb)->seq;
 		tcp_sack_maybe_coalesce(tp, sp);
 	} else {
 		int cur_sacks = tp->num_sacks;
@@ -1195,12 +1180,12 @@
 			int this_sack;
 
 			for(this_sack = 1; this_sack < cur_sacks; this_sack++, swap++) {
-				if((swap->end_seq == skb->seq) ||
-				   (swap->start_seq == skb->end_seq)) {
-					if(swap->end_seq == skb->seq)
-						swap->end_seq = skb->end_seq;
+				if((swap->end_seq == TCP_SKB_CB(skb)->seq) ||
+				   (swap->start_seq == TCP_SKB_CB(skb)->end_seq)) {
+					if(swap->end_seq == TCP_SKB_CB(skb)->seq)
+						swap->end_seq = TCP_SKB_CB(skb)->end_seq;
 					else
-						swap->start_seq = skb->seq;
+						swap->start_seq = TCP_SKB_CB(skb)->seq;
 					tcp_sack_swap(sp, swap);
 					tcp_sack_maybe_coalesce(tp, sp);
 					return;
@@ -1221,8 +1206,8 @@
 		}
 
 		/* Build head SACK, and we're done. */
-		sp->start_seq = skb->seq;
-		sp->end_seq = skb->end_seq;
+		sp->start_seq = TCP_SKB_CB(skb)->seq;
+		sp->end_seq = TCP_SKB_CB(skb)->end_seq;
 		if(tp->num_sacks < max_sacks)
 			tp->num_sacks++;
 	}
@@ -1234,9 +1219,13 @@
 	int num_sacks = tp->num_sacks;
 	int this_sack;
 
-	/* We know this removed SKB will eat from the front of a SACK. */
+	/* This is an in order data segment _or_ an out-of-order SKB being
+	 * moved to the receive queue, so we know this removed SKB will eat
+	 * from the front of a SACK.
+	 */
 	for(this_sack = 0; this_sack < num_sacks; this_sack++, sp++) {
-		if(sp->start_seq == skb->seq)
+		if(!after(sp->start_seq, TCP_SKB_CB(skb)->seq) &&
+		   before(sp->start_seq, TCP_SKB_CB(skb)->end_seq))
 			break;
 	}
 
@@ -1247,7 +1236,7 @@
 	if(this_sack >= num_sacks)
 		return;
 
-	sp->start_seq = skb->end_seq;
+	sp->start_seq = TCP_SKB_CB(skb)->end_seq;
 	if(!before(sp->start_seq, sp->end_seq)) {
 		/* Zap this SACK, by moving forward any other SACKS. */
 		for(this_sack += 1; this_sack < num_sacks; this_sack++, sp++) {
@@ -1266,12 +1255,12 @@
 	int this_sack;
 
 	for(this_sack = 0; this_sack < num_sacks; this_sack++, tp++) {
-		if(sp->end_seq == old_skb->end_seq)
+		if(sp->end_seq == TCP_SKB_CB(old_skb)->end_seq)
 			break;
 	}
 	if(this_sack >= num_sacks)
 		return;
-	sp->end_seq = new_skb->end_seq;
+	sp->end_seq = TCP_SKB_CB(new_skb)->end_seq;
 }
 
 /* This one checks to see if we can put data from the
@@ -1283,23 +1272,24 @@
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 
 	while ((skb = skb_peek(&tp->out_of_order_queue))) {
-		if (after(skb->seq, tp->rcv_nxt))
+		if (after(TCP_SKB_CB(skb)->seq, tp->rcv_nxt))
 			break;
 
-		if (!after(skb->end_seq, tp->rcv_nxt)) {
+		if (!after(TCP_SKB_CB(skb)->end_seq, tp->rcv_nxt)) {
 			SOCK_DEBUG(sk, "ofo packet was already received \n");
-			skb_unlink(skb);
+			__skb_unlink(skb, skb->list);
 			kfree_skb(skb);
 			continue;
 		}
 		SOCK_DEBUG(sk, "ofo requeuing : rcv_next %X seq %X - %X\n",
-			   tp->rcv_nxt, skb->seq, skb->end_seq);
+			   tp->rcv_nxt, TCP_SKB_CB(skb)->seq,
+			   TCP_SKB_CB(skb)->end_seq);
 
 		if(tp->sack_ok)
 			tcp_sack_remove_skb(tp, skb);
-		skb_unlink(skb);
-		skb_queue_tail(&sk->receive_queue, skb);
-		tp->rcv_nxt = skb->end_seq;
+		__skb_unlink(skb, skb->list);
+		__skb_queue_tail(&sk->receive_queue, skb);
+		tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq;
 		if(skb->h.th->fin)
 			tcp_fin(skb, sk, skb->h.th);
 	}
@@ -1314,12 +1304,12 @@
 	 *  Packets in sequence go to the receive queue.
 	 *  Out of sequence packets to out_of_order_queue.
 	 */
-	if (skb->seq == tp->rcv_nxt) {
+	if (TCP_SKB_CB(skb)->seq == tp->rcv_nxt) {
 		/* Ok. In sequence. */
 	queue_and_out:
 		dst_confirm(sk->dst_cache);
-		skb_queue_tail(&sk->receive_queue, skb);
-		tp->rcv_nxt = skb->end_seq;
+		__skb_queue_tail(&sk->receive_queue, skb);
+		tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq;
 		if(skb->h.th->fin) {
 			tcp_fin(skb, sk, skb->h.th);
 		} else {
@@ -1341,18 +1331,19 @@
 	}
 	
 	/* An old packet, either a retransmit or some packet got lost. */
-	if (!after(skb->end_seq, tp->rcv_nxt)) {
+	if (!after(TCP_SKB_CB(skb)->end_seq, tp->rcv_nxt)) {
 		/* A retransmit, 2nd most common case.  Force an imediate ack. */
-		SOCK_DEBUG(sk, "retransmit received: seq %X\n", skb->seq);
+		SOCK_DEBUG(sk, "retransmit received: seq %X\n", TCP_SKB_CB(skb)->seq);
 		tcp_enter_quickack_mode(tp);
 		kfree_skb(skb);
 		return;
 	}
 
-	if (before(skb->seq, tp->rcv_nxt)) {
+	if (before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt)) {
 		/* Partial packet, seq < rcv_next < end_seq */
 		SOCK_DEBUG(sk, "partial packet: rcv_next %X seq %X - %X\n",
-			   tp->rcv_nxt, skb->seq, skb->end_seq);
+			   tp->rcv_nxt, TCP_SKB_CB(skb)->seq,
+			   TCP_SKB_CB(skb)->end_seq);
 
 		goto queue_and_out;
 	}
@@ -1365,25 +1356,25 @@
 	tp->pred_flags = 0;
 
 	SOCK_DEBUG(sk, "out of order segment: rcv_next %X seq %X - %X\n",
-		   tp->rcv_nxt, skb->seq, skb->end_seq);
+		   tp->rcv_nxt, TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq);
 
 	if (skb_peek(&tp->out_of_order_queue) == NULL) {
 		/* Initial out of order segment, build 1 SACK. */
 		if(tp->sack_ok) {
 			tp->num_sacks = 1;
-			tp->selective_acks[0].start_seq = skb->seq;
-			tp->selective_acks[0].end_seq = skb->end_seq;
+			tp->selective_acks[0].start_seq = TCP_SKB_CB(skb)->seq;
+			tp->selective_acks[0].end_seq = TCP_SKB_CB(skb)->end_seq;
 		}
-		skb_queue_head(&tp->out_of_order_queue,skb);
+		__skb_queue_head(&tp->out_of_order_queue,skb);
 	} else {
 		for(skb1=tp->out_of_order_queue.prev; ; skb1 = skb1->prev) {
 			/* Already there. */
-			if (skb->seq == skb1->seq) {
+			if (TCP_SKB_CB(skb)->seq == TCP_SKB_CB(skb1)->seq) {
 				if (skb->len >= skb1->len) {
 					if(tp->sack_ok)
 						tcp_sack_extend(tp, skb1, skb);
-					skb_append(skb1, skb);
-					skb_unlink(skb1);
+					__skb_append(skb1, skb);
+					__skb_unlink(skb1, skb1->list);
 					kfree_skb(skb1);
 				} else {
 					/* A duplicate, smaller than what is in the
@@ -1394,8 +1385,8 @@
 				break;
 			}
 			
-			if (after(skb->seq, skb1->seq)) {
-				skb_append(skb1,skb);
+			if (after(TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb1)->seq)) {
+				__skb_append(skb1, skb);
 				if(tp->sack_ok)
 					tcp_sack_new_ofo_skb(sk, skb);
 				break;
@@ -1403,7 +1394,7 @@
 
                         /* See if we've hit the start. If so insert. */
 			if (skb1 == skb_peek(&tp->out_of_order_queue)) {
-				skb_queue_head(&tp->out_of_order_queue,skb);
+				__skb_queue_head(&tp->out_of_order_queue,skb);
 				if(tp->sack_ok)
 					tcp_sack_new_ofo_skb(sk, skb);
 				break;
@@ -1455,8 +1446,8 @@
 	struct sk_buff *skb;
 
 	if ((skb = tp->send_head)) {
-		if (!after(skb->end_seq, tp->snd_una + tp->snd_wnd) &&
-		    tp->packets_out < tp->snd_cwnd ) {
+		if (!after(TCP_SKB_CB(skb)->end_seq, tp->snd_una + tp->snd_wnd) &&
+		    tcp_packets_in_flight(tp) < (tp->snd_cwnd >> TCP_CWND_SHIFT)) {
 			/* Put more data onto the wire. */
 			tcp_write_xmit(sk);
 		} else if (tp->packets_out == 0 && !tp->pending) {
@@ -1601,7 +1592,7 @@
 	/* Start with the end because there are probably the least
 	 * useful packets (crossing fingers).
 	 */
-	while ((skb = skb_dequeue_tail(&tp->out_of_order_queue))) { 
+	while ((skb = __skb_dequeue_tail(&tp->out_of_order_queue))) { 
 		kfree_skb(skb);
 		if (atomic_read(&sk->rmem_alloc) <= sk->rcvbuf)
 			return;
@@ -1616,15 +1607,16 @@
 			break;
 
 		/* Never remove packets that have been already acked */
-		if (before(skb->end_seq, tp->last_ack_sent+1)) {
+		if (before(TCP_SKB_CB(skb)->end_seq, tp->last_ack_sent+1)) {
 			printk(KERN_DEBUG "prune_queue: hit acked data c=%x,%x,%x\n",
-				tp->copied_seq, skb->end_seq, tp->last_ack_sent);
+				tp->copied_seq, TCP_SKB_CB(skb)->end_seq, tp->last_ack_sent);
 			break; 
 		}
-		skb_unlink(skb);
-		tp->rcv_nxt = skb->seq;
+		__skb_unlink(skb, skb->list);
+		tp->rcv_nxt = TCP_SKB_CB(skb)->seq;
 		SOCK_DEBUG(sk, "prune_queue: removing %x-%x (c=%x)\n",
-			   skb->seq, skb->end_seq, tp->copied_seq); 
+			   TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq,
+			   tp->copied_seq); 
 		kfree_skb(skb);
 		if (atomic_read(&sk->rmem_alloc) <= sk->rcvbuf) 
 			break;
@@ -1658,13 +1650,13 @@
 	 */
 	if (tcp_fast_parse_options(sk, th, tp)) {
 		if (tp->saw_tstamp) {
-			if (tcp_paws_discard(tp)) {
+			if (tcp_paws_discard(tp, th, len)) {
 				if (!th->rst) {
 					tcp_send_ack(sk);
 					goto discard;
 				}
 			}
-			tcp_replace_ts_recent(tp,skb->end_seq);
+			tcp_replace_ts_recent(tp, TCP_SKB_CB(skb)->end_seq);
 		}
 	}
 
@@ -1678,11 +1670,12 @@
 	 *	 space for instance)
 	 */
 
-	if (flg == tp->pred_flags && skb->seq == tp->rcv_nxt) {
+	if (flg == tp->pred_flags && TCP_SKB_CB(skb)->seq == tp->rcv_nxt) {
 		if (len <= th->doff*4) {
 			/* Bulk data transfer: sender */
 			if (len == th->doff*4) {
-				tcp_ack(sk, th, skb->seq, skb->ack_seq, len); 
+				tcp_ack(sk, th, TCP_SKB_CB(skb)->seq,
+					TCP_SKB_CB(skb)->ack_seq, len); 
 				kfree_skb(skb); 
 				tcp_data_snd_check(sk);
 				return 0;
@@ -1690,7 +1683,7 @@
 				tcp_statistics.TcpInErrs++;
 				goto discard;
 			}
-		} else if (skb->ack_seq == tp->snd_una) {
+		} else if (TCP_SKB_CB(skb)->ack_seq == tp->snd_una) {
 			/* Bulk data transfer: receiver */
 			if (atomic_read(&sk->rmem_alloc) > sk->rcvbuf) 
 				goto discard;
@@ -1700,8 +1693,8 @@
 			/* DO NOT notify forward progress here.
 			 * It saves dozen of CPU instructions in fast path. --ANK
 			 */
-			skb_queue_tail(&sk->receive_queue, skb);
-			tp->rcv_nxt = skb->end_seq;
+			__skb_queue_tail(&sk->receive_queue, skb);
+			tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq;
 
 			/* FIN bit check is not done since if FIN is set in
 			 * this frame, the pred_flags won't match up. -DaveM
@@ -1719,11 +1712,11 @@
 		}
 	}
 
-	if (!tcp_sequence(tp, skb->seq, skb->end_seq)) {
+	if (!tcp_sequence(tp, TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq)) {
 		if (!th->rst) {
-			if (after(skb->seq, tp->rcv_nxt)) {
+			if (after(TCP_SKB_CB(skb)->seq, tp->rcv_nxt)) {
 				SOCK_DEBUG(sk, "seq:%d end:%d wup:%d wnd:%d\n",
-					   skb->seq, skb->end_seq,
+					   TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq,
 					   tp->rcv_wup, tp->rcv_wnd);
 			}
 			tcp_send_ack(sk);
@@ -1731,7 +1724,7 @@
 		}
 	}
 
-	if(th->syn && skb->seq != tp->syn_seq) {
+	if(th->syn && TCP_SKB_CB(skb)->seq != tp->syn_seq) {
 		SOCK_DEBUG(sk, "syn in established state\n");
 		tcp_statistics.TcpInErrs++;
 		tcp_reset(sk, skb);
@@ -1744,7 +1737,7 @@
 	}
 
 	if(th->ack)
-		tcp_ack(sk, th, skb->seq, skb->ack_seq, len);
+		tcp_ack(sk, th, TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->ack_seq, len);
 	
 	/* Process urgent data. */
 	tcp_urg(sk, th, len);
@@ -1793,7 +1786,7 @@
 		flg &= __constant_htonl(0x00170000);
 		/* Only SYN set? */
 		if (flg == __constant_htonl(0x00020000)) {
-			if (!after(skb->seq, req->rcv_isn)) {
+			if (!after(TCP_SKB_CB(skb)->seq, req->rcv_isn)) {
 				/*	retransmited syn.
 				 */
 				req->class->rtx_syn_ack(sk, req); 
@@ -1811,8 +1804,8 @@
 		 * but we do it here to prevent syn flood attackers
 		 * from creating big SYN_RECV sockets.
 		 */ 
-		if (!between(skb->ack_seq, req->snt_isn, req->snt_isn+1) ||
-		    !between(skb->seq, req->rcv_isn, 
+		if (!between(TCP_SKB_CB(skb)->ack_seq, req->snt_isn, req->snt_isn+1) ||
+		    !between(TCP_SKB_CB(skb)->seq, req->rcv_isn, 
 			     req->rcv_isn+1+req->rcv_wnd)) {
 			req->class->send_reset(skb);
 			return NULL;
@@ -1885,10 +1878,11 @@
 		 * not be in line code. [AC]
 		 */
 		if(th->ack) {
-			tp->snd_wl1 = skb->seq;
+			tp->snd_wl1 = TCP_SKB_CB(skb)->seq;
 
 			/* We got an ack, but it's not a good ack. */
-			if(!tcp_ack(sk,th, skb->seq, skb->ack_seq, len)) {
+			if(!tcp_ack(sk,th, TCP_SKB_CB(skb)->seq,
+				    TCP_SKB_CB(skb)->ack_seq, len)) {
 				tcp_statistics.TcpAttemptFails++;
 				return 1;
 			}
@@ -1909,13 +1903,13 @@
 			/* Ok.. it's good. Set up sequence numbers and
 			 * move to established.
 			 */
-			tp->rcv_nxt = skb->seq+1;
-			tp->rcv_wup = skb->seq+1;
+			tp->rcv_nxt = TCP_SKB_CB(skb)->seq+1;
+			tp->rcv_wup = TCP_SKB_CB(skb)->seq+1;
 
 			tp->snd_wnd = htons(th->window) << tp->snd_wscale;
-			tp->snd_wl1 = skb->seq;
-			tp->snd_wl2 = skb->ack_seq;
-			tp->fin_seq = skb->seq;
+			tp->snd_wl1 = TCP_SKB_CB(skb)->seq;
+			tp->snd_wl2 = TCP_SKB_CB(skb)->ack_seq;
+			tp->fin_seq = TCP_SKB_CB(skb)->seq;
 
 			tcp_set_state(sk, TCP_ESTABLISHED);
 			tcp_parse_options(sk, th, tp, 0);
@@ -1983,11 +1977,11 @@
 					tp->ts_recent_stamp = jiffies;
 				}
 				
-				tp->rcv_nxt = skb->seq + 1;
-				tp->rcv_wup = skb->seq + 1;
+				tp->rcv_nxt = TCP_SKB_CB(skb)->seq + 1;
+				tp->rcv_wup = TCP_SKB_CB(skb)->seq + 1;
 
 				tp->snd_wnd = htons(th->window);
-				tp->snd_wl1 = skb->seq;
+				tp->snd_wl1 = TCP_SKB_CB(skb)->seq;
 				
 				tcp_send_synack(sk);
 				goto discard;
@@ -2008,18 +2002,18 @@
 		 * guarantee this.
 		 */
 		if (tp->saw_tstamp) {
-			if (tcp_paws_discard(tp)) {
+			if (tcp_paws_discard(tp, th, len)) {
 				if (!th->rst) {
 					tcp_send_ack(sk);
 					goto discard;
 				}
 			}
-			tcp_replace_ts_recent(tp,skb->end_seq);
+			tcp_replace_ts_recent(tp, TCP_SKB_CB(skb)->end_seq);
 		}
 	}
 
 	/* step 1: check sequence number */
-	if (!tcp_sequence(tp, skb->seq, skb->end_seq)) {
+	if (!tcp_sequence(tp, TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq)) {
 		if (!th->rst) {
 			tcp_send_ack(sk);
 			goto discard;
@@ -2050,14 +2044,15 @@
 	 *	original syn. 
 	 */
 
-	if (th->syn && skb->seq!=tp->syn_seq) {
+	if (th->syn && TCP_SKB_CB(skb)->seq != tp->syn_seq) {
 		tcp_reset(sk, skb);
 		return 1;
 	}
 
 	/* step 5: check the ACK field */
 	if (th->ack) {
-		int acceptable = tcp_ack(sk,th,skb->seq, skb->ack_seq,len);
+		int acceptable = tcp_ack(sk, th, TCP_SKB_CB(skb)->seq,
+					 TCP_SKB_CB(skb)->ack_seq, len);
 		
 		switch(sk->state) {
 		case TCP_SYN_RECV:
@@ -2069,10 +2064,10 @@
 				if(!sk->dead)
 					sk->state_change(sk);		
 
-				tp->snd_una = skb->ack_seq;
+				tp->snd_una = TCP_SKB_CB(skb)->ack_seq;
 				tp->snd_wnd = htons(th->window) << tp->snd_wscale;
-				tp->snd_wl1 = skb->seq;
-				tp->snd_wl2 = skb->ack_seq;
+				tp->snd_wl1 = TCP_SKB_CB(skb)->seq;
+				tp->snd_wl2 = TCP_SKB_CB(skb)->ack_seq;
 
 			} else {
 				SOCK_DEBUG(sk, "bad ack\n");
@@ -2117,7 +2112,7 @@
 	switch (sk->state) {
 	case TCP_CLOSE_WAIT:
 	case TCP_CLOSING:
-		if (!before(skb->seq, tp->fin_seq))
+		if (!before(TCP_SKB_CB(skb)->seq, tp->fin_seq))
 			break;
 	
 	case TCP_FIN_WAIT1:
@@ -2127,7 +2122,7 @@
 		 * BSD 4.4 also does reset.
 		 */
 		if ((sk->shutdown & RCV_SHUTDOWN) && sk->dead) {
-			if (after(skb->end_seq - th->fin, tp->rcv_nxt)) {
+			if (after(TCP_SKB_CB(skb)->end_seq - th->fin, tp->rcv_nxt)) {
 				tcp_reset(sk, skb);
 				return 1;
 			}
@@ -2150,27 +2145,4 @@
 		kfree_skb(skb);
 	}
 	return 0;
-}
-
-int tcp_sysctl_congavoid(ctl_table *ctl, int write, struct file * filp,
-			 void *buffer, size_t *lenp)
-{
-	int val = sysctl_tcp_cong_avoidance;
-	int retv;
-	static tcp_sys_cong_ctl_t tab[] = { 
-		tcp_cong_avoid_vanj, 
-		tcp_cong_avoid_vegas
-	};
-
-	retv = proc_dointvec(ctl, write, filp, buffer, lenp);
-
-	if (write) {
-		if ((unsigned)sysctl_tcp_cong_avoidance > 1) {
-			retv = -EINVAL;
-			sysctl_tcp_cong_avoidance = val;
-		} else {
-			tcp_sys_cong_ctl_f = tab[sysctl_tcp_cong_avoidance];
-		}
-	}
-	return retv;
 }
diff -u --recursive --new-file v2.1.91/linux/net/ipv4/tcp_ipv4.c linux/net/ipv4/tcp_ipv4.c
--- v2.1.91/linux/net/ipv4/tcp_ipv4.c	Thu Mar 26 15:57:13 1998
+++ linux/net/ipv4/tcp_ipv4.c	Wed Apr  1 16:19:57 1998
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_ipv4.c,v 1.119 1998/03/22 19:14:47 davem Exp $
+ * Version:	$Id: tcp_ipv4.c,v 1.127 1998/03/30 08:41:25 davem Exp $
  *
  *		IPv4 specific functions
  *
@@ -347,7 +347,9 @@
 					   u32 saddr, u16 sport,
 					   u32 daddr, u16 dport, int dif)
 {
-	unsigned short hnum = ntohs(dport);
+	TCP_V4_ADDR_COOKIE(acookie, saddr, daddr)
+	__u16 hnum = ntohs(dport);
+	__u32 ports = TCP_COMBINED_PORTS(sport, hnum);
 	struct sock *sk;
 	int hash;
 
@@ -359,12 +361,7 @@
 
 	/* Check TCP register quick cache first. */
 	sk = TCP_RHASH(sport);
-	if(sk						&&
-	   sk->daddr		== saddr		&& /* remote address */
-	   sk->dport		== sport		&& /* remote port    */
-	   sk->num		== hnum			&& /* local port     */
-	   sk->rcv_saddr	== daddr		&& /* local address  */
-	   (!sk->bound_dev_if || sk->bound_dev_if == dif))
+	if(sk && TCP_IPV4_MATCH(sk, acookie, saddr, daddr, ports, dif))
 		goto hit;
 
 	/* Optimize here for direct hit, only listening connections can
@@ -372,25 +369,16 @@
 	 */
 	hash = tcp_hashfn(daddr, hnum, saddr, sport);
 	for(sk = tcp_established_hash[hash]; sk; sk = sk->next) {
-		if(sk->daddr		== saddr		&& /* remote address */
-		   sk->dport		== sport		&& /* remote port    */
-		   sk->num		== hnum			&& /* local port     */
-		   sk->rcv_saddr	== daddr		&& /* local address  */
-		   (!sk->bound_dev_if || sk->bound_dev_if == dif)) {
+		if(TCP_IPV4_MATCH(sk, acookie, saddr, daddr, ports, dif)) {
 			if (sk->state == TCP_ESTABLISHED)
 				TCP_RHASH(sport) = sk;
 			goto hit; /* You sunk my battleship! */
 		}
 	}
 	/* Must check for a TIME_WAIT'er before going to listener hash. */
-	for(sk = tcp_established_hash[hash+(TCP_HTABLE_SIZE/2)]; sk; sk = sk->next) {
-		if(sk->daddr		== saddr		&& /* remote address */
-		   sk->dport		== sport		&& /* remote port    */
-		   sk->num		== hnum			&& /* local port     */
-		   sk->rcv_saddr	== daddr		&& /* local address  */
-		   (!sk->bound_dev_if || sk->bound_dev_if == dif))
+	for(sk = tcp_established_hash[hash+(TCP_HTABLE_SIZE/2)]; sk; sk = sk->next)
+		if(TCP_IPV4_MATCH(sk, acookie, saddr, daddr, ports, dif))
 			goto hit;
-	}
 #ifdef USE_QUICKSYNS
 listener_shortcut:
 #endif
@@ -601,8 +589,6 @@
 		sk->mtu = 64;	/* Sanity limit */
 
 	mss = sk->mtu - sizeof(struct iphdr);
-	if (sk->opt)
-		mss -= sk->opt->optlen;
 
 	tp->write_seq = secure_tcp_sequence_number(sk->saddr, sk->daddr,
 						   sk->sport, usin->sin_port);
@@ -773,8 +759,8 @@
 	switch (type) {
 	case ICMP_SOURCE_QUENCH:
 #ifndef OLD_SOURCE_QUENCH /* This is deprecated */
-		tp->snd_ssthresh = max(tp->snd_cwnd >> 1, 2);
-		tp->snd_cwnd = tp->snd_ssthresh;
+		tp->snd_ssthresh = max(tp->snd_cwnd >> (1 + TCP_CWND_SHIFT), 2);
+		tp->snd_cwnd = (tp->snd_ssthresh << TCP_CWND_SHIFT);
 		tp->high_seq = tp->snd_nxt;
 #endif
 		return;
@@ -971,8 +957,6 @@
 	}
 
 	mss = (rt->u.dst.pmtu - sizeof(struct iphdr) - sizeof(struct tcphdr));
-	if (opt)
-		mss -= opt->optlen;
 
 	skb = tcp_make_synack(sk, &rt->u.dst, req, mss);
 	if (skb) {
@@ -1098,7 +1082,7 @@
 
 	req->rcv_wnd = 0;		/* So that tcp_send_synack() knows! */
 
-	req->rcv_isn = skb->seq;
+	req->rcv_isn = TCP_SKB_CB(skb)->seq;
  	tp.tstamp_ok = tp.sack_ok = tp.wscale_ok = tp.snd_wscale = 0;
 	tp.in_mss = 536;
 	tcp_parse_options(NULL, th, &tp, want_cookie);
@@ -1207,12 +1191,13 @@
 		newtp->last_ack_sent = req->rcv_isn + 1;
 		newtp->backoff = 0;
 		newtp->mdev = TCP_TIMEOUT_INIT;
-		newtp->snd_cwnd = 1;
+		newtp->snd_cwnd = (1 << TCP_CWND_SHIFT);
 		newtp->rto = TCP_TIMEOUT_INIT;
 		newtp->packets_out = 0;
+		newtp->fackets_out = 0;
+		newtp->retrans_out = 0;
 		newtp->high_seq = 0;
 		newtp->snd_ssthresh = 0x7fffffff;
-		newtp->snd_cwnd_cnt = 0;
 		newtp->dup_acks = 0;
 		newtp->delayed_acks = 0;
 		init_timer(&newtp->retransmit_timer);
@@ -1317,8 +1302,6 @@
 	if (mtu < 68)
 		mtu = 68;
 	snd_mss = mtu - sizeof(struct iphdr);
-	if (opt)
-		snd_mss -= opt->optlen;
 
 	newsk = tcp_create_openreq_child(sk, req, skb, snd_mss);
 	if (!newsk) 
@@ -1337,8 +1320,7 @@
 	newsk->opt = req->af.v4_req.opt;
 	newsk->mtu = mtu;
 
-	/* Must use the af_specific ops here for the case of IPv6 mapped. */
-	newsk->prot->hash(newsk);
+	tcp_v4_hash(newsk);
 	add_to_prot_sklist(newsk);
 
 	return newsk;
@@ -1357,7 +1339,8 @@
 	if (!req)
 		return;
 	/* Sequence number check required by RFC793 */
-	if (before(skb->seq, req->snt_isn) || after(skb->seq, req->snt_isn+1))
+	if (before(TCP_SKB_CB(skb)->seq, req->snt_isn) ||
+	    after(TCP_SKB_CB(skb)->seq, req->snt_isn+1))
 		return;
 	tcp_synq_unlink(tp, req, prev);
 	req->class->destructor(req);
@@ -1509,9 +1492,10 @@
 	if(!ipsec_sk_policy(sk,skb))
 		goto discard_it;
 
-	skb->seq = ntohl(th->seq);
-	skb->end_seq = skb->seq + th->syn + th->fin + len - th->doff*4;
-	skb->ack_seq = ntohl(th->ack_seq);
+	TCP_SKB_CB(skb)->seq = ntohl(th->seq);
+	TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin +
+				    len - th->doff*4);
+	TCP_SKB_CB(skb)->ack_seq = ntohl(th->ack_seq);
 
 	skb->used = 0;
 
@@ -1662,7 +1646,7 @@
 	/* See draft-stevens-tcpca-spec-01 for discussion of the
 	 * initialization of these values.
 	 */
-	tp->snd_cwnd = 1;
+	tp->snd_cwnd = (1 << TCP_CWND_SHIFT);
 	tp->snd_ssthresh = 0x7fffffff;	/* Infinity */
 
 	sk->priority = 1;
@@ -1690,11 +1674,11 @@
 		tcp_dec_slow_timer(TCP_SLT_KEEPALIVE);
 
 	/* Cleanup up the write buffer. */
-  	while((skb = skb_dequeue(&sk->write_queue)) != NULL)
+  	while((skb = __skb_dequeue(&sk->write_queue)) != NULL)
 		kfree_skb(skb);
 
 	/* Cleans up our, hopefuly empty, out_of_order_queue. */
-  	while((skb = skb_dequeue(&tp->out_of_order_queue)) != NULL)
+  	while((skb = __skb_dequeue(&tp->out_of_order_queue)) != NULL)
 		kfree_skb(skb);
 
 	/* Clean up a locked TCP bind bucket, this only happens if a
diff -u --recursive --new-file v2.1.91/linux/net/ipv4/tcp_output.c linux/net/ipv4/tcp_output.c
--- v2.1.91/linux/net/ipv4/tcp_output.c	Thu Mar 26 15:57:13 1998
+++ linux/net/ipv4/tcp_output.c	Wed Apr  1 16:19:57 1998
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_output.c,v 1.76 1998/03/22 22:10:24 davem Exp $
+ * Version:	$Id: tcp_output.c,v 1.81 1998/03/30 08:41:36 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -99,12 +99,13 @@
 		/* Build TCP header and checksum it. */
 		th->source		= sk->sport;
 		th->dest		= sk->dport;
-		th->seq			= htonl(skb->seq);
+		th->seq			= htonl(TCP_SKB_CB(skb)->seq);
 		th->ack_seq		= htonl(tp->rcv_nxt);
 		th->doff		= (tcp_header_size >> 2);
 		th->res1		= 0;
 		*(((__u8 *)th) + 13)	= tcb->flags;
-		th->window		= htons(tcp_select_window(sk));
+		if(!(tcb->flags & TCPCB_FLAG_SYN))
+			th->window	= htons(tcp_select_window(sk));
 		th->check		= 0;
 		th->urg_ptr		= ntohs(tcb->urg_ptr);
 		if(tcb->flags & TCPCB_FLAG_SYN) {
@@ -114,10 +115,10 @@
 					      sysctl_tcp_sack,
 					      sysctl_tcp_window_scaling,
 					      tp->rcv_wscale,
-					      skb->when);
+					      TCP_SKB_CB(skb)->when);
 		} else {
 			tcp_build_and_update_options((__u32 *)(th + 1),
-						     tp, skb->when);
+						     tp, TCP_SKB_CB(skb)->when);
 		}
 		tp->af_specific->send_check(sk, th, skb->len, skb);
 
@@ -136,13 +137,13 @@
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 
 	/* Advance write_seq and place onto the write_queue. */
-	tp->write_seq += (skb->end_seq - skb->seq);
-	skb_queue_tail(&sk->write_queue, skb);
+	tp->write_seq += (TCP_SKB_CB(skb)->end_seq - TCP_SKB_CB(skb)->seq);
+	__skb_queue_tail(&sk->write_queue, skb);
 
 	if (!force_queue && tp->send_head == NULL && tcp_snd_test(sk, skb)) {
 		/* Send it out now. */
-		skb->when = jiffies;
-		tp->snd_nxt = skb->end_seq;
+		TCP_SKB_CB(skb)->when = jiffies;
+		tp->snd_nxt = TCP_SKB_CB(skb)->end_seq;
 		tp->packets_out++;
 		tcp_transmit_skb(sk, skb_clone(skb, GFP_KERNEL));
 		if(!tcp_timer_is_set(sk, TIME_RETRANS))
@@ -171,9 +172,7 @@
 
 	/* Get a new skb... force flag on. */
 	buff = sock_wmalloc(sk,
-			    (nsize +
-			     MAX_HEADER +
-			     sk->prot->max_header + 15),
+			    (nsize + MAX_HEADER + sk->prot->max_header),
 			    1, GFP_ATOMIC);
 	if (buff == NULL)
 		return -1; /* We'll just try again later. */
@@ -182,8 +181,8 @@
 	skb_reserve(buff, MAX_HEADER + sk->prot->max_header);
 		
 	/* Correct the sequence numbers. */
-	buff->seq = skb->seq + len;
-	buff->end_seq = skb->end_seq;
+	TCP_SKB_CB(buff)->seq = TCP_SKB_CB(skb)->seq + len;
+	TCP_SKB_CB(buff)->end_seq = TCP_SKB_CB(skb)->end_seq;
 	
 	/* PSH and FIN should only be set in the second packet. */
 	flags = TCP_SKB_CB(skb)->flags;
@@ -209,14 +208,14 @@
 	buff->csum = csum_partial_copy(skb->data + len, skb_put(buff, nsize),
 				       nsize, 0);
 
-	skb->end_seq -= nsize;
+	TCP_SKB_CB(skb)->end_seq -= nsize;
 	skb_trim(skb, skb->len - nsize);
 
 	/* Rechecksum original buffer. */
 	skb->csum = csum_partial(skb->data, skb->len, 0);
 
 	/* Link BUFF into the send queue. */
-	skb_append(skb, buff);
+	__skb_append(skb, buff);
 
 	return 0;
 }
@@ -264,8 +263,8 @@
 
 			/* Advance the send_head.  This one is going out. */
 			update_send_head(sk);
-			skb->when = jiffies;
-			tp->snd_nxt = skb->end_seq;
+			TCP_SKB_CB(skb)->when = jiffies;
+			tp->snd_nxt = TCP_SKB_CB(skb)->end_seq;
 			tp->packets_out++;
 			tcp_transmit_skb(sk, skb_clone(skb, GFP_ATOMIC));
 			sent_pkts = 1;
@@ -397,7 +396,7 @@
 			return;
 
 		/* Ok.  We will be able to collapse the packet. */
-		skb_unlink(next_skb);
+		__skb_unlink(next_skb, next_skb->list);
 
 		if(skb->len % 4) {
 			/* Must copy and rechecksum all data. */
@@ -413,7 +412,8 @@
 		}
 	
 		/* Update sequence range on original skb. */
-		skb->end_seq += next_skb->end_seq - next_skb->seq;
+		TCP_SKB_CB(skb)->end_seq +=
+			TCP_SKB_CB(next_skb)->end_seq - TCP_SKB_CB(next_skb)->seq;
 
 		/* Merge over control information. */
 		flags |= TCP_SKB_CB(next_skb)->flags; /* This moves PSH/FIN etc. over */
@@ -444,9 +444,12 @@
  	/* Don't muck with the congestion window here. */
  	tp->dup_acks = 0;
  	tp->high_seq = tp->snd_nxt;
-
- 	/* FIXME: make the current rtt sample invalid */
  	tp->retrans_head = NULL; 
+
+ 	/* Input control flow will see that this was retransmitted
+	 * and not use it for RTT calculation in the absence of
+	 * the timestamp option.
+	 */
  	tcp_retransmit_skb(sk, skb_peek(&sk->write_queue)); 
 }
 
@@ -496,11 +499,12 @@
 
 	/* Ok, we're gonna send it out, update state. */
 	TCP_SKB_CB(skb)->sacked |= TCPCB_SACKED_RETRANS;
+	tp->retrans_out++;
 
 	/* Make a copy, if the first transmission SKB clone we made
 	 * is still in somebodies hands, else make a clone.
 	 */
-	skb->when = jiffies;
+	TCP_SKB_CB(skb)->when = jiffies;
 	if(skb_cloned(skb))
 		skb = skb_copy(skb, GFP_ATOMIC);
 	else
@@ -518,12 +522,14 @@
  * retransmitted data is acknowledged.  It tries to continue
  * resending the rest of the retransmit queue, until either
  * we've sent it all or the congestion window limit is reached.
+ * If doing SACK, the first ACK which comes back for a timeout
+ * based retransmit packet might feed us FACK information again.
+ * If so, we use it to avoid unnecessarily retransmissions.
  */
 void tcp_xmit_retransmit_queue(struct sock *sk)
 {
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 	struct sk_buff *skb;
-	int ct = 0;
 
 	if (tp->retrans_head == NULL)
 		tp->retrans_head = skb_peek(&sk->write_queue);
@@ -539,19 +545,48 @@
 			if(tcp_retransmit_skb(sk, skb))
 				break;
 		
-			/* Count retransmissions locally. */
-			ct++;
-
 			/* Stop retransmitting if we've hit the congestion
 			 * window limit.
 			 */
-			if (ct >= tp->snd_cwnd)
+			if (tp->retrans_out >= (tp->snd_cwnd >> TCP_CWND_SHIFT))
 				break;
 		}
 		update_retrans_head(sk);
 	}
 }
 
+/* Using FACK information, retransmit all missing frames at the receiver
+ * up to the forward most SACK'd packet (tp->fackets_out) if the packet
+ * has not been retransmitted already.
+ */
+void tcp_fack_retransmit(struct sock *sk)
+{
+	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+	struct sk_buff *skb = skb_peek(&sk->write_queue);
+	int packet_cnt = 0;
+
+	while((skb != NULL) &&
+	      (skb != tp->send_head) &&
+	      (skb != (struct sk_buff *)&sk->write_queue)) {
+		__u8 sacked = TCP_SKB_CB(skb)->sacked;
+
+		if(sacked & (TCPCB_SACKED_ACKED | TCPCB_SACKED_RETRANS))
+			goto next_packet;
+
+		/* Ok, retransmit it. */
+		if(tcp_retransmit_skb(sk, skb))
+			break;
+
+		if(tcp_packets_in_flight(tp) >= (tp->snd_cwnd >> TCP_CWND_SHIFT))
+			break;
+next_packet:
+		packet_cnt++;
+		if(packet_cnt >= tp->fackets_out)
+			break;
+		skb = skb->next;
+	}
+}
+
 /* Send a fin.  The caller locks the socket for us.  This cannot be
  * allowed to fail queueing a FIN frame under any circumstances.
  */
@@ -573,7 +608,7 @@
 	if((tp->send_head != NULL) && (skb->len < mss_now)) {
 		/* tcp_write_xmit() takes care of the rest. */
 		TCP_SKB_CB(skb)->flags |= TCPCB_FLAG_FIN;
-		skb->end_seq++;
+		TCP_SKB_CB(skb)->end_seq++;
 		tp->write_seq++;
 	} else {
 		/* Socket is locked, keep trying until memory is available. */
@@ -592,8 +627,8 @@
 		TCP_SKB_CB(skb)->urg_ptr = 0;
 
 		/* FIN eats a sequence byte, write_seq advanced by tcp_send_skb(). */
-		skb->seq = tp->write_seq;
-		skb->end_seq = skb->seq + 1;
+		TCP_SKB_CB(skb)->seq = tp->write_seq;
+		TCP_SKB_CB(skb)->end_seq = TCP_SKB_CB(skb)->seq + 1;
 		tcp_send_skb(sk, skb, 0);
 	}
 }
@@ -621,9 +656,9 @@
 	TCP_SKB_CB(skb)->urg_ptr = 0;
 
 	/* Send it off. */
-	skb->seq = tp->write_seq;
-	skb->end_seq = skb->seq;
-	skb->when = jiffies;
+	TCP_SKB_CB(skb)->seq = tp->write_seq;
+	TCP_SKB_CB(skb)->end_seq = TCP_SKB_CB(skb)->seq;
+	TCP_SKB_CB(skb)->when = jiffies;
 	tcp_transmit_skb(sk, skb);
 }
 
@@ -650,10 +685,10 @@
 	TCP_SKB_CB(skb)->urg_ptr = 0;
 
 	/* SYN eats a sequence byte. */
-	skb->seq = tp->snd_una;
-	skb->end_seq = skb->seq + 1;
-	skb_queue_tail(&sk->write_queue, skb);
-	skb->when = jiffies;
+	TCP_SKB_CB(skb)->seq = tp->snd_una;
+	TCP_SKB_CB(skb)->end_seq = TCP_SKB_CB(skb)->seq + 1;
+	__skb_queue_tail(&sk->write_queue, skb);
+	TCP_SKB_CB(skb)->when = jiffies;
 	tp->packets_out++;
 	tcp_transmit_skb(sk, skb_clone(skb, GFP_ATOMIC));
 	return 0;
@@ -705,9 +740,9 @@
 	th->ack = 1;
 	th->source = sk->sport;
 	th->dest = req->rmt_port;
-	skb->seq = req->snt_isn;
-	skb->end_seq = skb->seq + 1;
-	th->seq = htonl(skb->seq);
+	TCP_SKB_CB(skb)->seq = req->snt_isn;
+	TCP_SKB_CB(skb)->end_seq = TCP_SKB_CB(skb)->seq + 1;
+	th->seq = htonl(TCP_SKB_CB(skb)->seq);
 	th->ack_seq = htonl(req->rcv_isn + 1);
 	if (req->rcv_wnd == 0) { /* ignored for retransmitted syns */
 		__u8 rcv_wscale; 
@@ -722,10 +757,10 @@
 	}
 	th->window = htons(req->rcv_wnd);
 
-	skb->when = jiffies;
+	TCP_SKB_CB(skb)->when = jiffies;
 	tcp_syn_build_options((__u32 *)(th + 1), req->mss, req->tstamp_ok,
 			      req->sack_ok, req->wscale_ok, req->rcv_wscale,
-			      skb->when);
+			      TCP_SKB_CB(skb)->when);
 
 	skb->csum = 0;
 	th->doff = (tcp_header_size >> 2);
@@ -774,9 +809,9 @@
 	TCP_SKB_CB(buff)->sacked = 0;
 	TCP_SKB_CB(buff)->urg_ptr = 0;
 	buff->csum = 0;
-	buff->seq = tp->write_seq++;
-	buff->end_seq = tp->write_seq;
-	tp->snd_nxt = buff->end_seq;
+	TCP_SKB_CB(buff)->seq = tp->write_seq++;
+	TCP_SKB_CB(buff)->end_seq = tp->write_seq;
+	tp->snd_nxt = TCP_SKB_CB(buff)->end_seq;
 
 	tp->window_clamp = dst->window;
 	tcp_select_initial_window(sock_rspace(sk)/2,sk->mss,
@@ -784,7 +819,6 @@
 		&tp->window_clamp,
 		sysctl_tcp_window_scaling,
 		&tp->rcv_wscale);
-
 	/* Ok, now lock the socket before we make it visible to
 	 * the incoming packet engine.
 	 */
@@ -800,10 +834,12 @@
 	tp->rto = dst->rtt;
 	tcp_init_xmit_timers(sk);
 	tp->retransmits = 0;
+	tp->fackets_out = 0;
+	tp->retrans_out = 0;
 
 	/* Send it off. */
-	skb_queue_tail(&sk->write_queue, buff);
-	buff->when = jiffies;
+	__skb_queue_tail(&sk->write_queue, buff);
+	TCP_SKB_CB(buff)->when = jiffies;
 	tp->packets_out++;
 	tcp_transmit_skb(sk, skb_clone(buff, GFP_KERNEL));
 	tcp_statistics.TcpActiveOpens++;
@@ -870,8 +906,8 @@
 		TCP_SKB_CB(buff)->urg_ptr = 0;
 
 		/* Send it off, this clears delayed acks for us. */
-		buff->seq = buff->end_seq = tp->snd_nxt;
-		buff->when = jiffies;
+		TCP_SKB_CB(buff)->seq = TCP_SKB_CB(buff)->end_seq = tp->snd_nxt;
+		TCP_SKB_CB(buff)->when = jiffies;
 		tcp_transmit_skb(sk, buff);
 	}
 }
@@ -904,13 +940,13 @@
 			 * must have been a result SWS avoidance ( sender )
 			 */
 			win_size = tp->snd_wnd - (tp->snd_nxt - tp->snd_una);
-			if (win_size < skb->end_seq - skb->seq) {
+			if (win_size < TCP_SKB_CB(skb)->end_seq - TCP_SKB_CB(skb)->seq) {
 				if (tcp_fragment(sk, skb, win_size))
 					return; /* Let a retransmit get it. */
 			}
 			update_send_head(sk);
-			skb->when = jiffies;
-			tp->snd_nxt = skb->end_seq;
+			TCP_SKB_CB(skb)->when = jiffies;
+			tp->snd_nxt = TCP_SKB_CB(skb)->end_seq;
 			tp->packets_out++;
 			tcp_transmit_skb(sk, skb_clone(skb, GFP_ATOMIC));
 			if (!tcp_timer_is_set(sk, TIME_RETRANS))
@@ -933,9 +969,9 @@
 			 * end to send an ack.  Don't queue or clone SKB, just
 			 * send it.
 			 */
-			skb->seq = tp->snd_nxt - 1;
-			skb->end_seq = skb->seq;
-			skb->when = jiffies;
+			TCP_SKB_CB(skb)->seq = tp->snd_nxt - 1;
+			TCP_SKB_CB(skb)->end_seq = TCP_SKB_CB(skb)->seq;
+			TCP_SKB_CB(skb)->when = jiffies;
 			tcp_transmit_skb(sk, skb);
 		}
 	}
diff -u --recursive --new-file v2.1.91/linux/net/ipv4/tcp_timer.c linux/net/ipv4/tcp_timer.c
--- v2.1.91/linux/net/ipv4/tcp_timer.c	Thu Mar 26 15:57:13 1998
+++ linux/net/ipv4/tcp_timer.c	Wed Apr  1 16:19:57 1998
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_timer.c,v 1.43 1998/03/22 22:10:28 davem Exp $
+ * Version:	$Id: tcp_timer.c,v 1.45 1998/03/30 08:41:31 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -362,13 +362,17 @@
 	 */
 	if(tp->sack_ok) {
 		struct sk_buff *skb = skb_peek(&sk->write_queue);
+		__u8 toclear = TCPCB_SACKED_ACKED;
 
+		if(tp->retransmits == 0)
+			toclear |= TCPCB_SACKED_RETRANS;
 		while((skb != NULL) &&
 		      (skb != tp->send_head) &&
 		      (skb != (struct sk_buff *)&sk->write_queue)) {
-			TCP_SKB_CB(skb)->sacked = 0;
+			TCP_SKB_CB(skb)->sacked &= ~(toclear);
 			skb = skb->next;
 		}
+		tp->fackets_out = 0;
 	}
 
 	/* Retransmission. */
@@ -377,9 +381,9 @@
 		/* remember window where we lost
 		 * "one half of the current window but at least 2 segments"
 		 */
-		tp->snd_ssthresh = max(tp->snd_cwnd >> 1, 2);
-		tp->snd_cwnd_cnt = 0;
-		tp->snd_cwnd = 1;
+		tp->retrans_out = 0;
+		tp->snd_ssthresh = max(tp->snd_cwnd >> (1 + TCP_CWND_SHIFT), 2);
+		tp->snd_cwnd = (1 << TCP_CWND_SHIFT);
 	}
 
 	tp->retransmits++;
diff -u --recursive --new-file v2.1.91/linux/net/ipv6/af_inet6.c linux/net/ipv6/af_inet6.c
--- v2.1.91/linux/net/ipv6/af_inet6.c	Thu Mar 26 15:57:13 1998
+++ linux/net/ipv6/af_inet6.c	Fri Mar 27 18:12:32 1998
@@ -7,7 +7,7 @@
  *
  *	Adapted from linux/net/ipv4/af_inet.c
  *
- *	$Id: af_inet6.c,v 1.29 1998/03/18 07:52:11 davem Exp $
+ *	$Id: af_inet6.c,v 1.30 1998/03/25 00:23:05 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -79,7 +79,6 @@
 		if (protocol && protocol != IPPROTO_TCP) 
 			goto free_and_noproto;
 		protocol = IPPROTO_TCP;
-		sk->no_check = TCP_NO_CHECK;
 		prot = &tcpv6_prot;
 		sock->ops = &inet6_stream_ops;
 	} else if(sock->type == SOCK_DGRAM) {
diff -u --recursive --new-file v2.1.91/linux/net/ipv6/ip6_output.c linux/net/ipv6/ip6_output.c
--- v2.1.91/linux/net/ipv6/ip6_output.c	Thu Mar 26 15:57:13 1998
+++ linux/net/ipv6/ip6_output.c	Wed Apr  1 16:19:58 1998
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: ip6_output.c,v 1.10 1998/03/20 09:12:17 davem Exp $
+ *	$Id: ip6_output.c,v 1.11 1998/03/28 08:29:39 davem Exp $
  *
  *	Based on linux/net/ipv4/ip_output.c
  *
@@ -265,7 +265,6 @@
 		return err;
 
 	last_skb->dst = dst_clone(dst);
-	last_skb->when = jiffies;
 
 	skb_reserve(last_skb, (dst->dev->hard_header_len + 15) & ~15);
 	
@@ -460,8 +459,6 @@
 
 		dev = dst->dev;
 		skb->dst = dst_clone(dst);
-
-		skb->when = jiffies;
 
 		skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
 
diff -u --recursive --new-file v2.1.91/linux/net/ipv6/tcp_ipv6.c linux/net/ipv6/tcp_ipv6.c
--- v2.1.91/linux/net/ipv6/tcp_ipv6.c	Thu Mar 26 15:57:13 1998
+++ linux/net/ipv6/tcp_ipv6.c	Wed Apr  1 16:19:58 1998
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: tcp_ipv6.c,v 1.68 1998/03/22 19:14:50 davem Exp $
+ *	$Id: tcp_ipv6.c,v 1.72 1998/03/30 08:41:52 davem Exp $
  *
  *	Based on: 
  *	linux/net/ipv4/tcp.c
@@ -240,8 +240,9 @@
 					   struct in6_addr *daddr, u16 dport,
 					   int dif)
 {
-	unsigned short hnum = ntohs(dport);
 	struct sock *sk;
+	__u16 hnum = ntohs(dport);
+	__u32 ports = TCP_COMBINED_PORTS(sport, hnum);
 	int hash;
 
 #ifdef USE_QUICKSYNS
@@ -252,13 +253,7 @@
 
 	/* Check TCP register quick cache first. */
 	sk = TCP_RHASH(sport);
-	if(sk						&&
-	   sk->num		== hnum			&& /* local port     */
-	   sk->family		== AF_INET6		&& /* address family */
-	   sk->dport		== sport		&& /* remote port    */
-	   !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.daddr, saddr)	&&
-	   !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr, daddr) &&
-	   (!sk->bound_dev_if || sk->bound_dev_if == dif))
+	if(sk && TCP_IPV6_MATCH(sk, saddr, daddr, ports, dif))
 		goto hit;
 
 	/* Optimize here for direct hit, only listening connections can
@@ -267,28 +262,23 @@
 	hash = tcp_v6_hashfn(daddr, hnum, saddr, sport);
 	for(sk = tcp_established_hash[hash]; sk; sk = sk->next) {
 		/* For IPV6 do the cheaper port and family tests first. */
-		if(sk->num		== hnum			&& /* local port     */
-		   sk->family		== AF_INET6		&& /* address family */
-		   sk->dport		== sport		&& /* remote port    */
-		   !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.daddr, saddr)	&&
-		   !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr, daddr) &&
-		   (!sk->bound_dev_if || sk->bound_dev_if == dif)) {
+		if(TCP_IPV6_MATCH(sk, saddr, daddr, ports, dif)) {
 			if (sk->state == TCP_ESTABLISHED)
 				TCP_RHASH(sport) = sk;
 			goto hit; /* You sunk my battleship! */
 		}
 	}
 	/* Must check for a TIME_WAIT'er before going to listener hash. */
-	for(sk = tcp_established_hash[hash+(TCP_HTABLE_SIZE/2)]; sk; sk = sk->next)
-		if(sk->num		== hnum			&& /* local port     */
-		   sk->family		== AF_INET6		&& /* address family */
-		   sk->dport		== sport) {		   /* remote port    */
+	for(sk = tcp_established_hash[hash+(TCP_HTABLE_SIZE/2)]; sk; sk = sk->next) {
+		if(*((__u32 *)&(sk->dport))	== ports	&&
+		   sk->family			== AF_INET6) {
 			struct tcp_tw_bucket *tw = (struct tcp_tw_bucket *)sk;
 			if(!ipv6_addr_cmp(&tw->v6_daddr, saddr)	&&
 			   !ipv6_addr_cmp(&tw->v6_rcv_saddr, daddr) &&
 			   (!sk->bound_dev_if || sk->bound_dev_if == dif))
 				goto hit;
 		}
+	}
 #ifdef USE_QUICKSYNS
 listener_shortcut:
 #endif
@@ -770,7 +760,7 @@
 
 	req->rcv_wnd = 0;		/* So that tcp_send_synack() knows! */
 
-	req->rcv_isn = skb->seq;
+	req->rcv_isn = TCP_SKB_CB(skb)->seq;
 	req->snt_isn = isn;
 	tp.tstamp_ok = tp.sack_ok = tp.wscale_ok = tp.snd_wscale = 0;
 	tp.in_mss = 536;
@@ -1012,7 +1002,8 @@
 	if (!req)
 		return;
 	/* Sequence number check required by RFC793 */
-	if (before(skb->seq, req->snt_isn) || after(skb->seq, req->snt_isn+1))
+	if (before(TCP_SKB_CB(skb)->seq, req->snt_isn) ||
+	    after(TCP_SKB_CB(skb)->seq, req->snt_isn+1))
 		return;
 	tcp_synq_unlink(tp, req, prev);
 	req->class->destructor(req);
@@ -1078,9 +1069,10 @@
 			goto no_tcp_socket;
 		}
 
-		skb->seq = ntohl(th->seq);
-		skb->end_seq = skb->seq + th->syn + th->fin + len - th->doff*4;
-		skb->ack_seq = ntohl(th->ack_seq);
+		TCP_SKB_CB(skb)->seq = ntohl(th->seq);
+		TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin +
+					    len - th->doff*4);
+		TCP_SKB_CB(skb)->ack_seq = ntohl(th->ack_seq);
 		skb->used = 0;
 		if(sk->state == TCP_TIME_WAIT)
 			goto do_time_wait;
@@ -1300,7 +1292,7 @@
 	/* See draft-stevens-tcpca-spec-01 for discussion of the
 	 * initialization of these values.
 	 */
-	tp->snd_cwnd = 1;
+	tp->snd_cwnd = (1 << TCP_CWND_SHIFT);
 	tp->snd_ssthresh = 0x7fffffff;
 
 	sk->priority = 1;
@@ -1331,14 +1323,14 @@
 	 *	Cleanup up the write buffer.
 	 */
 
-  	while((skb = skb_dequeue(&sk->write_queue)) != NULL)
+  	while((skb = __skb_dequeue(&sk->write_queue)) != NULL)
 		kfree_skb(skb);
 
 	/*
 	 *  Cleans up our, hopefuly empty, out_of_order_queue
 	 */
 
-  	while((skb = skb_dequeue(&tp->out_of_order_queue)) != NULL)
+  	while((skb = __skb_dequeue(&tp->out_of_order_queue)) != NULL)
 		kfree_skb(skb);
 
 	/*
diff -u --recursive --new-file v2.1.91/linux/net/netsyms.c linux/net/netsyms.c
--- v2.1.91/linux/net/netsyms.c	Thu Mar 26 15:57:24 1998
+++ linux/net/netsyms.c	Wed Apr  1 16:19:58 1998
@@ -120,6 +120,7 @@
 EXPORT_SYMBOL(net_families);
 EXPORT_SYMBOL(sock_kmalloc);
 EXPORT_SYMBOL(sock_kfree_s);
+EXPORT_SYMBOL(skb_queue_lock);
 
 #ifdef CONFIG_FILTER
 EXPORT_SYMBOL(sk_run_filter);
diff -u --recursive --new-file v2.1.91/linux/net/socket.c linux/net/socket.c
--- v2.1.91/linux/net/socket.c	Thu Mar 26 15:57:25 1998
+++ linux/net/socket.c	Fri Mar 27 18:12:32 1998
@@ -1144,7 +1144,7 @@
 	unsigned char ctl[sizeof(struct cmsghdr) + 20];	/* 20 is size of ipv6_pktinfo */
 	unsigned char *ctl_buf = ctl;
 	struct msghdr msg_sys;
-	int err, total_len;
+	int err, ctl_len, total_len;
 	
 	lock_kernel();
 
@@ -1161,16 +1161,16 @@
 	err = verify_iovec(&msg_sys, iov, address, VERIFY_READ);
 	if (err < 0) 
 		goto out;
-	
 	total_len=err;
 
 	sock = sockfd_lookup(fd, &err);
 	if (!sock) 
 		goto out_freeiov;
 
-	if (msg_sys.msg_controllen) 
+	ctl_len = msg_sys.msg_controllen; 
+	if (ctl_len) 
 	{
-		if (msg_sys.msg_controllen > sizeof(ctl))
+		if (ctl_len > sizeof(ctl))
 		{
 			/* Suggested by the Advanced Sockets API for IPv6 draft:
 			 * Limit the msg_controllen size by the SO_SNDBUF size.
@@ -1179,15 +1179,13 @@
 			 * SMP machines you have a race to fix here.
 			 */
 			err = -ENOBUFS;
-			ctl_buf = sock_kmalloc(sock->sk, msg_sys.msg_controllen, 
-					       GFP_KERNEL);
+			ctl_buf = sock_kmalloc(sock->sk, ctl_len, GFP_KERNEL);
 			if (ctl_buf == NULL) 
-				goto failed2;
+				goto out_put;
 		}
 		err = -EFAULT;
-		if (copy_from_user(ctl_buf, msg_sys.msg_control, 
-					    msg_sys.msg_controllen))
-			goto failed;
+		if (copy_from_user(ctl_buf, msg_sys.msg_control, ctl_len))
+			goto out_freectl;
 		msg_sys.msg_control = ctl_buf;
 	}
 	msg_sys.msg_flags = flags;
@@ -1196,10 +1194,10 @@
 		msg_sys.msg_flags |= MSG_DONTWAIT;
 	err = sock_sendmsg(sock, &msg_sys, total_len);
 
-failed:
+out_freectl:
 	if (ctl_buf != ctl)    
-		sock_kfree_s(sock->sk, ctl_buf, msg_sys.msg_controllen);
-failed2:
+		sock_kfree_s(sock->sk, ctl_buf, ctl_len);
+out_put:
 	sockfd_put(sock);
 out_freeiov:
 	if (msg_sys.msg_iov != iov)
@@ -1464,13 +1462,10 @@
 	 
 	sk_init();
 
-#ifdef SLAB_SKB
 	/*
 	 *	Initialize skbuff SLAB cache 
 	 */
 	skb_init();
-#endif
-
 
 	/*
 	 *	Wan router layer. 
diff -u --recursive --new-file v2.1.91/linux/scripts/Configure linux/scripts/Configure
--- v2.1.91/linux/scripts/Configure	Tue Feb 17 13:12:51 1998
+++ linux/scripts/Configure	Wed Apr  1 15:10:14 1998
@@ -50,6 +50,9 @@
 # 300397 Phil Blundell (pjb27@cam.ac.uk) - added support for min/max 
 # arguments to "int", allow dep_tristate to take a list of dependencies
 # rather than just one.
+#
+# 090398 Axel Boldt (boldt@math.ucsb.edu) - allow for empty lines in help
+# texts.
 
 #
 # Make sure we're really running bash.
@@ -89,16 +92,18 @@
      var=$(echo "$1"|sed 's/[][\/.^$*]/\\&/g')
      #now pick out the right help text:
      text=$(sed -n "/^$var[ 	]*\$/,\${
-			/^$var[ 	]*\$/b
-			/^#.*/b
-			/^[ 	]*\$/q
+			/^$var[ 	]*\$/c\\
+${var}:\\
+
+			/^#/b
+			/^[^ 	]/q
 			p
 		    }" Documentation/Configure.help)
      if [ -z "$text" ]
      then
 	  echo; echo "  Sorry, no help available for this option yet.";echo
      else
-	  (echo; echo "$text"; echo) | ${PAGER:-more}
+	  (echo; echo "$text") | ${PAGER:-more}
      fi
   else
      echo;
diff -u --recursive --new-file v2.1.91/linux/scripts/Menuconfig linux/scripts/Menuconfig
--- v2.1.91/linux/scripts/Menuconfig	Tue Feb 17 13:12:51 1998
+++ linux/scripts/Menuconfig	Wed Apr  1 15:10:14 1998
@@ -44,6 +44,9 @@
 #
 # 160198 Michael Chastain (mec@shout.net) - fix bug with 'c' command
 # (complement existing value) when used on virgin uninitialized variables.
+#
+# 090398 Axel Boldt (boldt@math.ucsb.edu) - allow for empty lines in help
+# texts.
 #----------------------------------------------------------------------------
 
 
@@ -278,9 +281,11 @@
      var=$(echo "$1"|sed 's/[][\/.^$*]/\\&/g')
      #now pick out the right help text:
      text=$(sed -n "/^$var[ 	]*\$/,\${
-                        /^$var[ 	]*\$/d
-                        /^#.*/d
-			/^[ 	]*\$/q
+                        /^$var[ 	]*\$/c\\
+${var}:\\
+
+                        /^#/b
+                        /^[^ 	]/q
                         s/^  //
                         p
                     }" Documentation/Configure.help)
diff -u --recursive --new-file v2.1.91/linux/scripts/header.tk linux/scripts/header.tk
--- v2.1.91/linux/scripts/header.tk	Thu Feb 12 20:56:15 1998
+++ linux/scripts/header.tk	Wed Apr  1 15:10:14 1998
@@ -36,14 +36,14 @@
 # Define some macros we will need to parse the config.in file.
 #
 proc mainmenu_name { text } {
-	message .header.message -width 400 -relief raised -text "$text"
-	pack .header.label .header.message -side left -padx 15
-        wm title . "$text"
+	message .header.message -width 400 -text "$text"
+	pack .header.message -side left -padx 15
+	wm title . "$text"
 }
 
 proc menu_option { w menu_num text } {
 	button .f0.x$menu_num -text "$text" -width 50 -command "$w .$w \"$text\""
-	pack .f0.x$menu_num -pady 1 -expand on
+	pack .f0.x$menu_num -pady 0 -expand on
 }
 
 #
@@ -370,21 +370,19 @@
 
 	if { [file readable Documentation/Configure.help] == 1} then {
 		set filefound 1
+		# First escape sed regexp special characters in var:
+		set var [exec echo "$var" | sed s/\[\]\[\/.^$*\]/\\\\&/g]
+		# Now pick out right help text:
 		set message [exec sed -n "
 			/^$var\[ 	\]*\$/,\${
 				/^$var\[ 	\]*\$/c\\
 ${var}:\\
 
-				/^#.*/d
-				/^\[ 	\]*\$/bL
-                		H
+				/^#/b
+				/^\[^ 	\]/q
+				s/^  //
+				p
 			}
-			d
-			:L x
-			s/\\n  //
-			s/\\n  / /g
-			p
-			q
 			" Documentation/Configure.help]
 		set found [expr [string length "$message"] > 0]
 	}
@@ -402,8 +400,10 @@
 		label $w.f1.bm -bitmap error
 		wm title $w "RTFM"
 	} else {
-		message $w.f1.m -width 400 -aspect 300 -text $message \
-			 -relief flat
+		text $w.f1.m -width 73 -relief flat -wrap word
+		$w.f1.m insert 0.0 $message
+		$w.f1.m conf -state disabled  -height [$w.f1.m index end]
+
 		label $w.f1.bm -bitmap info
 		wm title $w "Configuration help" 
 	}
@@ -416,7 +416,7 @@
 	frame $w.f2
 	button $w.f2.ok -text "OK" \
 		-width 10 -command "destroy $w; focus $oldFocus"
-	pack $w.f2.ok -side bottom -pady 10 -anchor s
+	pack $w.f2.ok -side bottom -pady 6 -anchor n
 	pack $w.f2 -side bottom -padx 10 -anchor s
 
 	# Finish off the window
@@ -454,7 +454,6 @@
 # buttons which we will stick down at the bottom.
 #
 frame .header
-label .header.label 
 
 frame .f0 
 
diff -u --recursive --new-file v2.1.91/linux/scripts/tail.tk linux/scripts/tail.tk
--- v2.1.91/linux/scripts/tail.tk	Thu Apr 11 23:49:51 1996
+++ linux/scripts/tail.tk	Wed Apr  1 15:10:14 1998
@@ -1,7 +1,3 @@
-
-pack .header -side top -padx 10 -pady 10 -expand on
-pack .f0 -side top -padx 15 -pady 10 -fill y -expand on
-
 #
 # Misc buttons to save/restore state and so forth.
 # 
@@ -46,13 +42,19 @@
 	load_configfile .load "Load Configuration from file" read_config_file
 }
 
-pack  .f0_bot.r.save .f0_bot.r.quit -padx 25 -ipadx 10 -ipady 2 -expand on
-pack  .f0_bot.l.load .f0_bot.l.store -padx 25 -ipadx 10 -ipady 2 -expand on
+#
+# Now pack everything, important things first because of small screens.
+#
+pack  .f0_bot.r.save .f0_bot.r.quit -padx 25 -ipadx 10 -expand on
+pack  .f0_bot.l.load .f0_bot.l.store -padx 25 -ipadx 10 -expand on
+
+pack .f0_bot.r -side left -padx 15 -expand on -fill y
+pack .f0_bot.l -side right -padx 15 -expand on -fill y
 
-pack .f0_bot.r -side left -padx 15 -pady 10 -expand on -fill y
-pack .f0_bot.l -side right -padx 15 -pady 10 -expand on -fill y
+pack .f0_bot -side bottom -fill both -expand on -pady 4
+pack .f0 -side bottom -padx 15 -pady 0 -fill y -expand on
+pack .header -padx 10 -pady 7 -expand on
 
-pack .f0_bot -fill both -expand on
 
 #
 # If we cannot write our config files, disable the write button.
diff -u --recursive --new-file v2.1.91/linux/scripts/tkgen.c linux/scripts/tkgen.c
--- v2.1.91/linux/scripts/tkgen.c	Thu Feb 12 20:56:15 1998
+++ linux/scripts/tkgen.c	Wed Apr  1 15:10:14 1998
@@ -61,6 +61,14 @@
  *                    user switches from one configuration method to
  *                    another.
  *
+ * 1998 03 09
+ * Axel Boldt - Smaller layout of main menu - it's still too big for 800x600.
+ *            - Display help in text window to allow for cut and paste.
+ *            - Allow for empty lines in help texts.
+ *            - update_define should not set all variables unconditionally to
+ *              0: they may have been set to 1 elsewhere. CONFIG_NETLINK is
+ *              an example.
+ *
  * TO DO:
  *   - clean up - there are useless ifdef's everywhere.
  *   - better comments throughout - C code generating tcl is really cryptic.
@@ -73,9 +81,6 @@
  *   - make choice and int/hex input types line up vertically with
  *           bool/tristate.
  *   - general speedups - how?  The canvas seems to slow it down a lot.
- *   - choice buttons should default to the first menu option, rather than a
- *           blank.  Also look up the right variable when the help button
- *           is pressed.
  *   - clean up +/- 16 confusion for enabling/disabling variables; causes
  *           (theoretical, at the moment) problems with dependencies.
  *   
@@ -1124,7 +1129,7 @@
   for(cfg = scfg; cfg != NULL; cfg = cfg->next)
     {
       if( cfg->tok != tok_define ) continue;
-      printf("\tglobal %s; set %s 0\n",  cfg->optionname,  cfg->optionname);
+      printf("\tglobal %s\n",  cfg->optionname);
       cfg->flags |= GLOBAL_WRITTEN;
     }
   for(cfg = scfg; cfg != NULL; cfg = cfg->next)
