diff -u --recursive --new-file v1.3.68/linux/CREDITS linux/CREDITS
--- v1.3.68/linux/CREDITS	Tue Feb 20 14:37:22 1996
+++ linux/CREDITS	Sun Feb 25 11:17:59 1996
@@ -380,10 +380,10 @@
 
 N: Bruno Haible
 E: haible@ma2s2.mathematik.uni-karlsruhe.de
-D: Unified SysV FS based on Xenix FS (part of standard kernel since 0.99.15)
-S: Augartenstrasse 40
-S: D - 76137 Karlsruhe
-S: Germany
+D: SysV FS, shm swapping, memory management fixes
+S: 17 rue Danton
+S: F - 94270 Le Kremlin-Bicętre
+S: France
 
 N: Greg Hankins
 E: gregh@cc.gatech.edu
@@ -983,6 +983,13 @@
 S: Post Office Box 500
 S: Batavia, Illinois 60510
 S: USA
+
+N: Leo Spiekman
+E: spiekman@et.tudelft.nl
+D: Optics Storage 8000AT cdrom driver
+W: http://dutettk.et.tudelft.nl/~spiekman
+S: Utrecht
+S: The Netherlands
 
 N: Drew Sullivan
 E: drew@lethe.north.net
diff -u --recursive --new-file v1.3.68/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v1.3.68/linux/Documentation/Configure.help	Fri Feb 23 13:54:33 1996
+++ linux/Documentation/Configure.help	Mon Feb 26 11:58:02 1996
@@ -1759,9 +1759,14 @@
 CONFIG_NI52
   If you have a network (ethernet) card of this type, say Y and read
   the Ethernet-HOWTO, available via ftp (user: anonymous) in
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you plan to use more than
-  one network card under linux, read the Multiple-Ethernet-mini-HOWTO,
-  available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available
+  as a module ( = code which can be inserted in and removed from the
+  running kernel whenever you want). If you want to compile it as a
+  module, say M here and read Documentation/modules.txt as well as
+  Documentation/networking/net-modules.txt. If you plan to use more
+  than one network card under linux, read the
+  Multiple-Ethernet-mini-HOWTO, available from
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
 
 NI6510 support
 CONFIG_NI65
@@ -2447,6 +2452,18 @@
   running kernel whenever you want), say M here and read
   Documentation/modules.txt.
 
+Standard/generic serial support
+CONFIG_SERIAL
+  This selects whether you want to include the driver for the
+  standard (0x3F8, 0x2F8, etc.) serial ports. Most people will
+  say "y" here, so that they can use serial mice, modems and
+  similar devices. People who might say "n" here are those that
+  are setting up dedicated ethernet WWW/ftp servers, or users
+  that have one of the various bus mice instead of a serial mouse.
+  Note that the Cyclades and Stallion drivers do not need this
+  driver built in for them to work. They are completely independent
+  of each other.
+
 Cyclades async mux support
 CONFIG_CYCLADES
   This is a card which gives you many serial ports. You would need
@@ -2742,6 +2759,51 @@
   executed instructions get recorded in /proc/profile. But since you
   enabled "Kernel profiling support", you must be a kernel hacker and
   hence you know what this is about :-)
+
+ISDN subsystem
+CONFIG_ISDN
+  This allows you to use an ISDN-card for networking connections and as
+  dialin/out device. The isdn-tty's have a builtin AT-compatible modem
+  emulator. Network devices support autodial, channel-bundling, callback
+  and caller-authentication without having a daemon running. A reduced T.70
+  protocol is supported with tty's suitable for german BTX. Currently Cards
+  by Teles and compatibles and ICN are supported. On D-Channel, the protocols
+  EDSS1 and 1TR6 are supported. See Documentation/isdn/README for more
+  information.
+
+Support synchronous PPP
+CONFIG_ISDN_PPP
+  This enables synchronous PPP via ISDN. This protocol is used by Cisco
+  or Sun for example. You will need a special version of pppd (called ipppd)
+  for using this feature. See Documentation/isdn/README.syncppp for more
+  information.
+
+Sypport generic MP (RFC 1717)
+CONFIG_ISDN_MPP
+  With synchronous PPP enabled, it is possible to increase throughput by
+  bundling several ISDN-connections, using this protocol. See
+  Documentation/isdn/README.syncppp for more information.
+
+Use VJ-compression with synchronous PPP
+CONFIG_ISDN_PPP_VJ
+  This enables Van Jacobson headercompression for synchronous PPP.
+
+ICN B1 and B2 support
+CONFIG_ISDN_DRV_ICN
+  This enables support for two kinds of ISDN-cards made by a german company
+  called ICN. 1B is the standard version for a single ISDN line with two
+  B-channels, 2B supports two ISDN lines. For running this card, additional
+  firmware is necessary, which has to be downloaded into the card using
+  a utility which is distributed separately.
+  See Documentation/isdn/README and README.icn for more information.
+
+Teles, NICCY1016PC, Creatix support
+CONFIG_ISDN_DRV_TELES
+  This enables support for the Teles ISDN-cards S0-16.0, S0-16.3, S0-8 and
+  many compatibles. By default, the driver is configured to support
+  a 16.0-type using EDSS1-protocol. See Documentation/isdn/README
+  on how to configure it using 16.3, a different D-channel protocol, or
+  non-standard irq/port/shmem settings.
 
 # need an empty line after last entry, for sed script in Configure.
 
diff -u --recursive --new-file v1.3.68/linux/Documentation/cdrom/optcd linux/Documentation/cdrom/optcd
--- v1.3.68/linux/Documentation/cdrom/optcd	Tue Jan 23 21:15:36 1996
+++ linux/Documentation/cdrom/optcd	Fri Feb 23 09:39:34 1996
@@ -1,27 +1,20 @@
 This is the README file for the Optics Storage 8000 AT CDROM device driver.
 
-The driver contains code to enable an ISP16 interface if it finds one.  It
-didn't originally (although this README erroneously said so), because I think
-this kind of code should go into its own module. But having to use a hack all
-the time in order to use a part of the standard kernel started to annoy me, so
-I copied the ISP16 code by Eric van der Maarel (maarel@marin.nl) from Vadim
-Model's Sanyo sjcd driver. I'll remove it again from this driver when we have
-some common way to talk to ISP16 interfaces.
+This is the driver for the so-called 'DOLPHIN' drive, with the 34-pin
+Sony-compatible interface. For the IDE-compatible Optics Storage 8001
+drive, you will want the ATAPI CDROM driver. If you have a drive that
+works with this driver, and that doesn't report itself as DOLPHIN,
+please drop me a mail.
+
+The support for multisession CDs is in ALPHA stage. If you use it,
+please mail me your experiences. Multisession support can be disables
+at compile time.
 
-My original configuration code for the ISP-16 card can get found at
+You can find some older versions of the driver at
       dutette.et.tudelft.nl:/pub/linux/
 and at Eberhard's mirror
       ftp.gwdg.de:/pub/linux/cdrom/drivers/optics/
 
-Much more elaborate information can be found at ftp:rbrf.msk.su,
-where Vadim Model (vadim@rbrf.msk.su) has made available an ISP16
-device driver.
-Vadim's directory is
-      rbrf.msk.su:/linux/mediamagic/
-and Eberhard is holding a mirror at
-      ftp.gwdg.de:/pub/linux/cdrom/drivers/sanyo/
-
-
 Before you can use the driver, you have to create the device file once:
  # mknod /dev/optcd0 b 17 0
 
@@ -36,15 +29,28 @@
  # insmod /usr/src/linux/modules/optcd.o optcd=0x340
 with the matching address value of your interface card.
 
-I have tried the module with several 1.2.x kernel versions, and it seems to
-work, as far as I tested. It also seems to work for several 1.3.x versions.
-If you use it, I'd appreciate success/failure reports. If you find a bug,
-try recompiling the driver with some strategically chosen #undef DEBUG_...'s
-changed into #defines (you'll find them in .../include/linux/optcd.h) and
-include the messages generated in your bug report. Good luck.
-
-I have inserted code to support multisession. It works for me, although
-it is very slow during disk detection. At this time multisession support
-is to be considered experimental. Please mail me your experiences.
+The driver employs a number of buffers to do read-ahead and block size
+conversion. The number of buffers is configurable in optcd.h, and has
+influence on the driver performance. For my machine (a P75), 6 buffers
+seems optimal, as can be seen from this table:
+
+#bufs	kb/s	%cpu
+1	97	0.1
+2	191	0.3
+3	188	0.2
+4	246	0.3
+5	189	19
+6	280	0.4
+7	281	7.0
+8	246	2.8
+16	281	3.4
+
+If you get a throughput significantly below 300 kb/s, try tweaking
+N_BUFS, and don't forget to mail me your results!
+
+I'd appreciate success/failure reports. If you find a bug, try
+recompiling the driver with some strategically chosen debug options
+(these can be found in optcd.h) and include the messages generated in
+your bug report. Good luck.
 
 Leo Spiekman (spiekman@dutette.et.tudelft.nl)
diff -u --recursive --new-file v1.3.68/linux/Documentation/filesystems/sysv-fs.txt linux/Documentation/filesystems/sysv-fs.txt
--- v1.3.68/linux/Documentation/filesystems/sysv-fs.txt	Sun Sep  3 12:26:47 1995
+++ linux/Documentation/filesystems/sysv-fs.txt	Sun Feb 25 11:17:59 1996
@@ -28,7 +28,7 @@
 
 
 Please report any bugs and suggestions to
-  Bruno Haible <haible@ma2s2.mathematik.uni-karlsruhde.de> or
+  Bruno Haible <haible@ma2s2.mathematik.uni-karlsruhe.de> or
   Pascal Haible <haible@izfm.uni-stuttgart.de> .
 
 
diff -u --recursive --new-file v1.3.68/linux/Documentation/isdn/CREDITS linux/Documentation/isdn/CREDITS
--- v1.3.68/linux/Documentation/isdn/CREDITS	Thu Jan  1 02:00:00 1970
+++ linux/Documentation/isdn/CREDITS	Mon Feb 26 11:58:03 1996
@@ -0,0 +1,42 @@
+
+I want to thank all who contributed to this project and especially to:
+(in alphabetical order)
+
+Thomas Bogendörfer (tsbogend@bigbug.franken.de)
+  Tester, lots of bugfixes and hints.
+
+Alan Cox ()
+  For help getting into standard-kernel.
+
+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.
+
+Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
+  For his Sync-PPP-code.
+
+Karsten Keil (isdn4@temic-ech.spacenet.de)
+  For adding 1TR6-support to the Teles-driver.
+
+Michael Knigge (knick@cove.han.de)
+  For contributing the imon-tool
+
+Andreas Kool (akool@Kool.f.EUnet.de)
+  For contribution of the isdnlog/isdnrep-tool
+
+Pedro Roque Marques
+  For lot of new ideas and writing a new driver coming soon.
+
+Jan den Ouden (denouden@groovin.xs4all.nl)
+  For contribution of the teles-driver
+
+Max Riegel (riegel@max.franken.de)
+  For making the ICN hardware-documentation and test-equipment available.
+
+Gerhard 'Fido' Schneider (fido@wuff.franken.de)
+  For heavy-duty-beta-testing with his BBS ;)
+
+Thomas Uhl (uhl@hn-net.de)
+  For distributing the cards.
+  For pushing me to work ;-)
+
diff -u --recursive --new-file v1.3.68/linux/Documentation/isdn/INTERFACE linux/Documentation/isdn/INTERFACE
--- v1.3.68/linux/Documentation/isdn/INTERFACE	Thu Jan  1 02:00:00 1970
+++ linux/Documentation/isdn/INTERFACE	Mon Feb 26 11:58:03 1996
@@ -0,0 +1,609 @@
+
+Description of the Interface between Linklevel an Harwarelevel
+  of isdn4linux:
+
+
+  The Communication between Linklevel (LL) and Hardwarelevel (HL)
+  is based on the struct isdn_if (defined in isdnif.h).
+
+  An HL-driver can register itself at LL by calling the function
+  register_isdn() with a pointer to that struct. Prior to that, it has
+  to preset some of the fields of isdn_if. The LL sets the rest of
+  the fields. All further communication is done via callbacks using
+  the funtion-pointers defined in isdn_if.
+
+  ATTENTION, CHANGES since version 0.6 are marked with   "***CHANGE0.6"!
+  ATTENTION, CHANGES since version 0.7 are marked with   "***CHANGE0.7"!
+  ATTENTION, CHANGES since version 0.71 are marked with  "***CHANGE0.7.1"!
+
+1. Description of the fields of isdn_if:
+
+  int channels;
+
+    This field has to be set by the HL-driver to the number of channels
+    supported prior to calling register_isdn(). Upon return of the call,
+    the LL puts an id there, which has to be used by the HL-driver when
+    invoking the other callbacks.
+
+  int maxbufsize;
+
+    ***CHANGE0.6: New since this version.
+
+    Also to be preset by the HL-driver. With this value the HL-driver
+    tells to the LL the maximum size of a data-packet it will accept. 
+
+  unsigned long features;
+
+    To be preset by the HL-driver. Using this field, the HL-driver
+    announces the features supported. At the moment this is limited to
+    report the supported layer2 and layer3-protocols. For setting this
+    field the constants ISDN_FEATURE..., declared in isdnif.h have to be
+    used.
+
+    ***CHANGE0.7.1: The line type (1TR6, EDSS1) has to be set.
+
+  unsigned short hl_hdrlen;
+
+    ***CHANGED.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
+    it's internal purposes. Drivers not supporting sk_buff's should put
+    initialize this field to 0.
+
+  void (*rcvcallb)(int, int, u_char*, int);
+
+    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.
+
+    This field will be set by LL. The HL-driver delivers received data-
+    packets by calling this function. Upon calling, the HL-driver must
+    already have it's private data pulled off the head of the sk_buff.
+
+    Parameter:
+      int              driver-Id
+      int              Channel-number locally to the driver. (starting with 0)
+      struct sk_buff * Pointer to sk_buff, containing received data.
+
+  int (*statcallb)(isdn_ctrl*);
+
+    This field will be set by LL. This function has to be called by the
+    HL-driver for signaling status-changes or other events to the LL.
+
+    Parameter:
+      isdn_ctrl*
+
+      The struct isdn_ctrl also defined in isdn_if. The exact meaning of it's
+      fields is described together with the descriptions of the possible
+      events. Here is only a short description of the fields:
+
+        driver  = driver Id.
+        command = event-type. (one of the constants ISDN_STAT_...)
+        arg     = depends on event-type.
+        num     = depends on event-type.
+
+    Returnvalue:
+      0 on success, else -1
+
+  int (*command)(isdn_ctrl*);
+
+    This field has to be preset by the HL-driver. It points to a function,
+    to be called by LL to perform functions like dialing, B-channel
+    setup, etc. The exact meaning of the parameters is described with the
+    descriptions of the possible commands.
+
+    Parameter:
+      isdn_ctrl*
+        driver  = driver-Id
+        command = command to perform. (one of the constants ISDN_CMD_...)
+        arg     = depends on command.
+        num     = depends on command.
+    
+    Returnvalue:
+      >=0 on success, else error-code (-ENODEV etc.)
+
+  int (*writebuf)(int, int, u_char*, int, int);
+
+    This field has to be preset by the HL-driver. The given funtion 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.
+
+    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)
+      struct sk_buff * Pointer to sk_buff containing data to be send via
+                       B-channel.
+
+    Returnvalue:
+      Length of data accepted on success, else error-code (-EINVAL on
+      oversized packets etc.)
+
+NOTE on writebuf and writebuf_skb:
+    The HL-driver may initialize one of the field's to NULL, in which case
+    the LL will call the non-NULL function only.
+
+  int (*writecmd)(u_char*, int, int);
+
+    This field has to be preset by the HL-driver. The given function will be
+    called to perform write-requests on /dev/isdnctrl (i.e. sending commands
+    to the card) The data-format is hardware-specific. This function is
+    intended for debugging only. It is not necessary for normal operation
+    and never will be called by the tty-emulation- or network-code. If
+    this functin is not supported, the driver has to set NULL here.
+
+    Parameter:
+      u_char* pointer to data.
+      int     length of data.
+      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 etc.)
+
+  int (*readstat)(u_char*, int, int);
+
+    This field has to be preset by the HL-driver. The given function will be
+    called to perform read-requests on /dev/isdnctrl (i.e. reading repies
+    from the card) The data-format is hardware-specific. This function is
+    intended for debugging only. It is not necessary for normal operation
+    and never will be called by the tty-emulation- or network-code. If
+    this functin is not supported, the driver has to set NULL here.
+
+    Parameter:
+      u_char* pointer to data.
+      int     length of data.
+      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 on success, else error-code (-EINVAL etc.)
+
+  char id[20];
+       ***CHANGE0.7: New since this version.
+
+   This string has to be preset by the HL-driver. It's purpose is for
+   identification of the driver by the user. Eg.: it is shown in the
+   status-info of /dev/isdninfo. Furthermore it is used as Id for binding
+   net-interfaces to a specific channel. If a string of length zero is
+   given, upon return, isdn4linux will replace it by a generic name. (line0,
+   line1 etc.) It is recommended, to make this string configurabele during
+   module-load-time. (copy a global variable to this string.) For doing that,
+   modules 1.2.8 or newer are necessary.
+
+2. Description of the commands, a HL-driver has to support:
+
+   All commands will be performed by calling the funtion command() described
+   above from within the LL. The field command of the struct-parameter will
+   contain the desired command, the field driver always is set to the
+   apropriate driver-Id.
+
+   Until now, the following commands are defined:
+
+
+   ISDN_CMD_IOCTL:
+
+     This command is intended for performing ioctl-calls for configuring
+     hardware or similar purposes (setting port-adresses, loading firmware
+     etc.) For this purpose, in the LL all ioctl-calls with an argument
+     >= ISDN_IOCTL_DRVIOCTL (0x100) will be handed transparently to this
+     function after substracting 0x100 and placing the result in arg.
+     Example:
+       If a userlevel-program calls ioctl(0x101,...) the function gets
+       called with the field command set to 1.
+
+     Parameter:
+       driver  = driver-Id.
+       command = ISDN_CMD_IOCTL
+       arg     = Original ioctl-cmd - ISDN_IOCTL_DRVIOCTL
+       num     = first bytes filled with (unsigned long)arg
+   
+     Returnvalue:
+       Depending on driver.
+
+  
+  ISDN_CMD_DIAL:
+
+    This command is used to tell the HL-driver it should dial a given
+    number.
+
+    Parameter:
+      driver      = driver-Id.
+      command     = ISDN_CMD_DIAL
+      arg         = channel-number locally to the driver. (starting with 0)
+      num         = An ASCII-String containing the number to dial, the own
+                    EAZ or MSN, the Service-Indicator and the Additional
+                    Info. Format:
+                    "%s,%s,%d,%d" RemotePhoneNumber,EazOrMsn,SI,AI
+                    If the Line has been designed as SPV (a special german
+                    feature, meaning semi-leased-line) the number has to
+                    start with an "S".
+      ***CHANGE0.6: In previous versions the EAZ has been given in the
+                    highbyte of arg.
+    ***CHANGE0.7.1: New since this version: ServiceIndicator and AddInfo.
+
+  ISDN_CMD_ACCEPTD:
+
+    With this command, the HL-driver is told to accept a D-Channel-setup.
+    (Response to an incoming call)
+
+    Parameter:
+      driver      = driver-Id.
+      command     = ISDN_CMD_ACCEPTD
+      arg         = channel-number locally to the driver. (starting with 0)
+      num         = unused.
+
+  ISDN_CMD_ACCEPTB:
+
+    With this command, the HL-driver is told to perform a B-Channel-setup.
+    (after establishing D-Channel-Connection)
+
+    Parameter:
+      driver      = driver-Id.
+      command     = ISDN_CMD_ACCEPTB
+      arg         = channel-number locally to the driver. (starting with 0)
+      num         = unused.
+
+  ISDN_CMD_HANGUP:
+
+    With this command, the HL-driver is told to hangup (B-Channel if
+    established first, then D-Channel). This command is also used for
+    actively rejecting an incoming call.
+
+    Parameter:
+      driver      = driver-Id.
+      command     = ISDN_CMD_HANGUP
+      arg         = channel-number locally to the driver. (starting with 0)
+      num         = unused.
+
+  ISDN_CMD_CLREAZ:
+
+    With this command, the HL-driver is told not to signal incoming
+    calls to the LL.
+
+    Parameter:
+      driver      = driver-Id.
+      command     = ISDN_CMD_CLREAZ
+      arg         = channel-number locally to the driver. (starting with 0)
+      num         = unused.
+
+  ISDN_CMD_SETEAZ:
+
+    With this command, the HL-driver is told to signal incoming calls for
+    the given EAZs/MSNs to the LL.
+
+    Parameter:
+      driver      = driver-Id.
+      command     = ISDN_CMD_SETEAZ
+      arg         = channel-number locally to the driver. (starting with 0)
+      num         = ASCII-String, containing the desired EAZ's/MSN's
+                    (comma-separated). If an empty String is given, the
+                    HL-driver should respond to ALL incoming calls,
+                    regardless of the destination-adress.
+      ***CHANGE0.6: New since this version the "empty-string"-feature.
+
+  ISDN_CMD_GETEAZ: (currently unused)
+
+    With this command, the HL-driver is told to report the current setting
+    given with ISDN_CMD_SETEAZ.
+
+    Parameter:
+      driver      = driver-Id.
+      command     = ISDN_CMD_GETEAZ
+      arg         = channel-number locally to the driver. (starting with 0)
+      num         = ASCII-String, containing the current EAZ's/MSN's
+
+  ISDN_CMD_SETSIL: (currently unused)
+
+    With this command, the HL-driver is told to signal only incoming
+    calls with the given Service-Indicators.
+
+    Parameter:
+      driver      = driver-Id.
+      command     = ISDN_CMD_SETSIL
+      arg         = channel-number locally to the driver. (starting with 0)
+      num         = ASCII-String, containing the desired Service-Indicators.
+
+  ISDN_CMD_GETSIL: (currently unused)
+
+    With this command, the HL-driver is told to return the current
+    Service-Indicators it will respond to.
+
+    Parameter:
+      driver      = driver-Id.
+      command     = ISDN_CMD_SETSIL
+      arg         = channel-number locally to the driver. (starting with 0)
+      num         = ASCII-String, containing the current Service-Indicators.
+
+  ISDN_CMD_SETL2:
+
+    With this command, the HL-driver is told to select the given Layer-2-
+    protocol. This command is issued by the LL prior to ISDN_CMD_DIAL or
+    ISDN_CMD_ACCEPTD.
+
+
+    Parameter:
+      driver      = driver-Id.
+      command     = ISDN_CMD_SETL2
+      arg         = channel-number locally to the driver. (starting with 0)
+                    logical or'ed with (protocol-Id << 8)
+                    protocol-Id is one of the constants ISDN_PROTO_L2...
+      num         = unused.
+
+  ISDN_CMD_GETL2: (currently unused)
+
+    With this command, the HL-driver is told to return the current
+    setting of the Layer-2-protocol.
+
+    Parameter:
+      driver      = driver-Id.
+      command     = ISDN_CMD_GETL2
+      arg         = channel-number locally to the driver. (starting with 0)
+      num         = unused.
+    Returnvalue:
+      current protocol-Id (one of the constants ISDN_L2_PROTO)
+
+  ISDN_CMD_SETL3:
+
+    With this command, the HL-driver is told to select the given Layer-3-
+    protocol. This command is issued by the LL prior to ISDN_CMD_DIAL or
+    ISDN_CMD_ACCEPTD.
+
+
+    Parameter:
+      driver      = driver-Id.
+      command     = ISDN_CMD_SETL3
+      arg         = channel-number locally to the driver. (starting with 0)
+                    logical or'ed with (protocol-Id << 8)
+                    protocol-Id is one of the constants ISDN_PROTO_L3...
+      num         = unused.
+
+  ISDN_CMD_GETL2: (currently unused)
+
+    With this command, the HL-driver is told to return the current
+    setting of the Layer-3-protocol.
+
+    Parameter:
+      driver      = driver-Id.
+      command     = ISDN_CMD_GETL3
+      arg         = channel-number locally to the driver. (starting with 0)
+      num         = unused.
+    Returnvalue:
+      current protocol-Id (one of the constants ISDN_L3_PROTO)
+
+  ISDN_CMD_LOCK:
+
+    With this command the HL-driver is told, that it will be used by the
+    LL and therefore may not be unloaded. The HL-driver should use
+    MOD_INC_USE_COUNT to tell that to the kernel.
+
+    Parameter:
+      driver      = driver-Id.
+      command     = ISDN_CMD_LOCK
+      arg         = unused.
+      num         = unused.
+
+  ISDN_CMD_UNLOCK:
+
+    With this command the HL-driver is told, that it is no more used by the
+    LL and therefore may be unloaded. The HL-driver should use
+    DEC_INC_USE_COUNT to tell that to the kernel.
+
+    Parameter:
+      driver      = driver-Id.
+      command     = ISDN_CMD_UNLOCK
+      arg         = unused.
+      num         = unused.
+
+3. Description of the events to be signaled by the HL-driver to th LL.
+
+  All status-changes are signaled via calling the previously described
+  function statcallb(). The field command of the struct isdn_cmd has
+  to be set by the HL-driver with the apropriate Status-Id (event-number).
+  The field arg has to be set to the channel-number (locally to the driver,
+  starting with 0) to which this event applies. (Exception: STAVAIL-event)
+
+  Until now, the following Status-Ids are defined:
+
+  ISDN_STAT_AVAIL:
+
+    With this call, the HL-driver signals the availability of new data
+    for readstat(). Used only for debugging-purposes, see description
+    of readstat().
+
+    Parameter:
+      driver      = driver-Id
+      command     = ISDN_STAT_STAVAIL
+      arg         = length of available data.
+      num         = unused.
+
+  ISDN_STAT_ICALL:
+
+    With this call, the HL-driver signals an incoming call to the LL.
+
+    Parameter:
+      driver      = driver-Id
+      command     = ISDN_STAT_ICALL
+      arg         = channel-number, locally to the driver. (starting with 0)
+      num         = ASCII-String in the following format:
+                    "%s,%d,%d,%s",CallerNumber,ServiceIndicator,AddInfo,
+                    CalledNumber.
+
+  ISDN_STAT_RUN:
+
+    With this call, the HL-driver signals availability of the ISDN-card.
+    (after initializing, loading firmware)
+
+    Parameter:
+      driver      = driver-Id
+      command     = ISDN_STAT_RUN
+      arg         = unused.
+      num         = unused.
+
+  ISDN_STAT_STOP:
+
+    With this call, the HL-driver signals unavailability of the ISDN-card.
+    (before unloading, while resetting/reconfiguring the card)
+
+    Parameter:
+      driver      = driver-Id
+      command     = ISDN_STAT_STOP
+      arg         = unused.
+      num         = unused.
+
+  ISDN_STAT_DCONN:
+
+   With this call, the HL-driver signals the successful establishement of
+   a D-Channel-connection. (Response to ISDN_CMD_ACCEPTD or ISDN_CMD_DIAL)
+
+    Parameter:
+      driver      = driver-Id
+      command     = ISDN_STAT_DCONN
+      arg         = channel-number, locally to the driver. (starting with 0)
+      num         = unused.
+
+  ISDN_STAT_BCONN:
+
+   With this call, the HL-driver signals the successful establishement of
+   a B-Channel-connection. (Response to ISDN_CMD_ACCEPTB or because the
+   remote-station has initiated establishement)
+
+    Parameter:
+      driver      = driver-Id
+      command     = ISDN_STAT_BCONN
+      arg         = channel-number, locally to the driver. (starting with 0)
+      num         = unused.
+
+  ISDN_STAT_DHUP:
+
+   With this call, the HL-driver signals the shutdown of a
+   D-Channel-connection. This could be a response to a prior ISDN_CMD_HANGUP,
+   or caused by a remote-hangup or if the remote-station has actively
+   rejected a call.
+
+    Parameter:
+      driver      = driver-Id
+      command     = ISDN_STAT_DHUP
+      arg         = channel-number, locally to the driver. (starting with 0)
+      num         = unused.
+
+  ISDN_STAT_BHUP:
+
+   With this call, the HL-driver signals the shutdown of a
+   B-Channel-connection. This could be a response to a prior ISDN_CMD_HANGUP,
+   or caused by a remote-hangup.
+
+    Parameter:
+      driver      = driver-Id
+      command     = ISDN_STAT_BHUP
+      arg         = channel-number, locally to the driver. (starting with 0)
+      num         = unused.
+
+  ISDN_STAT_CINF:
+
+   With this call, the HL-driver delivers charge-unit information to the
+   LL.
+
+    Parameter:
+      driver      = driver-Id
+      command     = ISDN_STAT_CINF
+      arg         = channel-number, locally to the driver. (starting with 0)
+      num         = ASCII string containing charge-units (digits only).
+
+  ISDN_STAT_LOAD: (currently unused)
+
+  ISDN_STAT_UNLOAD:
+
+   With this call, the HL-driver signals that it will be unloaded now. This
+   tells the LL to release all corresponding data-structures.
+
+    Parameter:
+      driver      = driver-Id
+      command     = ISDN_STAT_UNLOAD
+      arg         = unused.
+      num         = unused.
+
+  ISDN_STAT_BSENT:
+
+    With this call the HL-driver signals the delivery of a data-packet.
+    This callback is used by the network-interfaces only, tty-Emulation
+    does not need this call.
+
+    Parameter:
+      driver      = driver-Id
+      command     = ISDN_STAT_BSENT
+      arg         = channel-number, locally to the driver. (starting with 0)
+      num         = unused.
+
+  ISDN_STAT_NODCH:
+
+    With this call, the driver has to respond to a prior ISDN_CMD_DIAL, if
+    no D-Channel is available.
+
+    Parameter:
+      driver      = driver-Id
+      command     = ISDN_STAT_NODCH
+      arg         = channel-number, locally to the driver. (starting with 0)
+      num         = unused.
+
+  ISDN_STAT_ADDCH: (currently unused)
+
+    This call is planned for HL-drivers, which are unable to check card-type
+    or numbers of supported channels before they have loaded any firmware
+    using ioctl. Those HL-driver simply set the channel-parameter to zero
+    or a minimum channel-number when registering, and later if they know
+    the real amount, perform this call, allocating additional channels.
+
+    Parameter:
+      driver      = driver-Id
+      command     = ISDN_STAT_ADDCH
+      arg         = to be defined.
+      num         = to be defined.
+
+  ISDN_STAT_CAUSE:
+
+    With this call, the HL-driver delivers CAUSE-messages to the LL.
+    Currently the LL does not use this messages. Their contents is simply
+    logged via kernel-messages. Therefore, currently the format of the
+    messages is currently completely free. However they should be printable.
+
+    Parameter:
+      driver      = driver-Id
+      command     = ISDN_STAT_NODCH
+      arg         = channel-number, locally to the driver. (starting with 0)
+      num         = ASCII string containing CAUSE-message.
+
diff -u --recursive --new-file v1.3.68/linux/Documentation/isdn/README linux/Documentation/isdn/README
--- v1.3.68/linux/Documentation/isdn/README	Thu Jan  1 02:00:00 1970
+++ linux/Documentation/isdn/README	Mon Feb 26 11:58:03 1996
@@ -0,0 +1,590 @@
+README for the ISDN-subsystem
+
+1. Preface
+
+  1.1 Introduction
+
+  This README describes how to set up and how to use the different parts
+  of the ISDN-subsystem.
+
+  For using the ISDN-subsystem, some additional userlevel programs are
+  necessary. Those programs and some contributed utilities are available
+  at
+
+   ftp.franken.de
+
+   /pub/isdn4linux/isdn4k-utils-<VersionNumber>.tar.gz
+
+
+  We also have set up a mailing-list:
+
+   The isdn4linux-project originates in Germany, and therefore by historical
+   reasons, the mailing-list's primary language is german. However mails
+   written in english have been welcome all the time.
+
+   to subscribe: write a email to majordomo@hub-wue.franken.de,
+   Subject irrelevant, in the message body:
+   subscribe isdn4linux <your_email_address>
+
+   To write to the mailing-list, write to isdn4linux@hub-wue.franken.de
+   
+
+  1.1 Technical details
+
+  In the following Text, the terms MSN and EAZ are used.
+
+  MSN is the abbreviation for (M)ultiple(S)ubscriber(N)umber, and applies
+  to Euro(EDSS1)-type lines. Usually it is simply the phone-number.
+
+  EAZ is the abbreviation of (E)ndgeraete(A)uswahl(Z)iffer and
+  applies to German 1TR6-type lines. This is a one-digit string,
+  simply appended to the base phone-number
+
+  The internal handling is nearly identical, so replace the appropriate
+  term to that one, which applies to your local ISDN-environment.
+
+  When the link-level-module isdn.o is loaded, it supports up to 16
+  low-level-modules with up to 16 channels. (The number 16 is arbitrarily
+  chosen and can be configured at compile-time --ISDN_MAX in isdn.h).
+  A low-level-driver can register itself through an interface (which is
+  defined in isdnif.h) and gets assigned a slot.
+  The following char-devices are made available for each channel:
+  
+  A raw-control-device with the following functions:
+     write: raw D-channel-messages (format: depends on driver).
+     read:  raw D-channel-messages (format: depends on driver).
+     ioctl: depends on driver, for the ICN-driver, the base-address of
+            the ports and the shared memory on the card can be set and read
+            also the boot-code an the protocol software can be loaded into 
+            the card.
+
+   O N L Y !!!  for debugging (no locking against other devices):
+   One raw-data-device with the following functions:
+     write: data to B-channel.
+     read:  data from B-channel.
+
+   In addition the following devices are made available:
+
+   32 tty-devices (16 cuix and 16 ttyIx) with integrated modem-emulator:
+   The functionality is almost the same as that of a serial device
+   (the line-discs are handled by the kernel, which lets you run 
+   SLIP, CSLIP and asynchronous PPP through the devices. We have tested 
+   Seyon, minicom, CSLIP (uri-dip) PPP and mgetty (compiled with NO_FAX),
+   XCept.
+
+   The modem-emulation supports the following:
+           1.3.1 Commands:
+
+               ATA	Answer incoming call.
+	       ATD<No.> Dial, the number may contain:
+                        [0-9] and [,#.*WPT-S]
+                        the latter are ignored until 'S'.
+                        The 'S' must precede the number, if 
+                        the line is a SPV (German 1TR6).
+               ATE0	Echo off.
+	       ATE1     Echo on (default).
+               ATH      Hang-up.
+	       ATH1     Off hook (ignored).
+               ATH0     Hang-up.
+	       ATI      Return "ISDN for Linux...".
+               ATI0        "
+               ATI1        "
+               ATO      On line (data mode).
+               ATQ0     Enable result codes (default).
+               ATQ1     Disable result codes (default).
+               ATSx=y	Set register x to y.
+	       ATSx?    Show contents of register x.
+               ATV0     Numeric responses.
+               ATV1     English responses (default).
+	       ATZ      Load registers and EAZ/MSN from Profile.
+	       AT&Bx    Set Send-Packet-size to x (max. 4000)
+                        The real packet-size may be limited by the
+                        low-level-driver used. i.e.: the Teles-Module-
+                        limit is 2000. You will get NO Error-Message,
+                        if you set it to higher Values, because at the
+                        time of giving this command the corresponding
+                        driver may not be selected (see "Automatic
+                        Assignment") however the size of outgoing packets
+                        will be limited correctly.
+	       AT&D2    DTR-low-edge: Hang up and return to 
+                        command mode (default).
+               AT&D3    Same as AT&D2 but also resets all registers.
+	       AT&Ex    Set the EAZ/MSN for this channel to x.
+	       AT&F     Reset all registers and profile to "factory-defaults"
+	       AT&Sx    Set window-size for Teles-driver (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)
+
+	   1.3.2 Escape sequence:
+               During a connection, the emulation reacts just like
+               a normal modem to the escape sequence <DELAY>+++<DELAY>.
+	       (The escape character - default '+' - can be set in the
+               register 2).
+               The DELAY must at least be 1.5 seconds long and delay 
+               between the escape characters must not exceed 0.5 seconds.
+     
+           1.3.3 Registers:
+
+              Nr.  Default  Description
+              0    0        Answer on ring number.
+                            (no auto-answer if S0=0).
+              1    0        Count of rings.
+              2    43       Escape character.
+                            (a value >= 128 disables the escape sequence).
+              3    13       Carriage return character (ASCII).
+              4    10       Line feed character (ASCII).
+              5    8        Backspace character (ASCII).
+              6    3        Delay in seconds before dialing.
+              7    60       Wait for carrier (ignored).
+              8    2        Pause time for comma (ignored)
+              9    6        Carrier detect time (ignored)
+             10    7        Carrier loss to disconnect time (ignored).
+             11    70       Touch tone timing (ignored).
+             12    69       Bit coded register:
+                            Bit 0:    0 = Suppress response messages.
+                                      1 = Show response messages.
+                            Bit 1:    0 = English response messages.
+                                      1 = Numeric response messages.
+                            Bit 2:    0 = Echo off.
+                                      1 = Echo on.
+                            Bit 3     0 = DCD always on.
+                                      1 = DCD follows carrier.
+                            Bit 4     0 = CTS follows RTS
+                                      1 = Ignore RTS, CTS always on.
+                            Bit 5     0 = Low-edge on DTR: Hang up and
+                                          return to command mode.
+                                      1 = Same as 0 but also resets all
+                                          registers.
+                            Bit 6     0 = DSR always on.
+                                      1 = DSR only on if channel is available.
+                            Bit 7     0 = Cisco-PPP-flag-hack off (default).
+                                      1 = Cisco-PPP-flag-hack on.
+             13   0         Bit coded register:
+                            Bit 0:    0 = Use delayed tty-send-algorithm
+                                      1 = Direct tty-send.
+                            Bit 1:    0 = T.70 protocol (Only for BTX!) off
+                                      1 = T.70 protocol (Only for BTX!) on
+	     14   0         Layer-2 protocol: (with the ICN-driver 
+                                                currently always 0)
+				      0 = X75/LAPB with I-frames
+				      1 = X75/LAPB with UI-frames
+                                      2 = X75/LAPB with BUI-frames
+                                      3 = HDLC
+             15   0         Layer-3 protocol: (at the moment always 0)
+                                      0 = transparent
+	     16   250       Send-Packet-size/16
+             17   8         Window-size for Teles-driver (not yet implemented)
+             18   7         Service-Octet-1
+             19   0         Service-Octet-2
+
+  Last but not least a (at the moment fairly primitive) device to request
+  the line-status (/dev/isdninfo) is made available.
+
+  Automatic assignment of devices to lines:
+
+  All inactive physical lines are listening to all EAZs for incoming
+  calls and are NOT assigned to a specific tty or network interface.
+  When an incoming call is detected, the driver looks first for a network 
+  interfaces and then for an opened tty which:
+
+  1. is configured for the same EAZ.
+  2. has the same protocol settings for the B-channel.
+  3. (only for network interfaces if the security flag is set)
+     contains the caller number in its access list.
+  4. Either the channel is not bound exclusively to another Net-interface, or
+     it is bound AND the other checks apply to exact this Interface.
+     (For usage of the bind-features, refer to the isdnctrl-man-page)
+
+  Only when a matching interface or tty is found, the call is accepted
+  and the "connection" between the low-level-layer and the link-level-layer
+  is established and kept until the end of the connection.
+  In all other cases no connection is established. Isdn4linux can be
+  configured to either do NOTHING in this case (which is useful, if
+  other, external devices with the same EAZ/MSN are connected to the bus)
+  or to reject the call actively. (isdnctrl busreject ...)
+
+  For an outgoing call, the inactive physical lines are searched.
+  The call is placed on the first physical line, which supports the
+  requested protocols for the B-channel. If a net-interface, however
+  is pre-bound to a channel, this channel is used directly.
+
+  This makes it possible to configure several network interfaces and ttys
+  for one EAZ, if the network interfaces are set to secure operation.
+  If an incoming call matches one network interface, it gets connected to it.
+  If another incoming call for the same EAZ arrives, which does not match
+  a network interface, the first tty gets a "RING" and so on.
+  As soon as voice gets supported (with the availability of the Diehl-driver),
+  the service-identifier will be evaluated in addition.
+
+2 System prerequisites:
+
+  ATTENTION! The program "insmod" from the Package "modules-1.2.8" (It's
+             on nearly all newer distributions) has a bug, which makes
+             it impossible to set both driver-Id's when loading the
+             icn-module for the Double-ICN-Card. A patch is supplied
+             in the utility-package called "insmod-1.2.8.patch". Change into
+             the source-directory of insmod, and type
+             "patch < insmod-1.2.8.patch". Then recompile it. This will fix
+             the bug.
+             This bug does NOT occur when using insmod with the Teles-driver
+             or a single ICN-card.
+ 
+3. Lowlevel-driver configuration.
+
+   Configuration depends on how the drivers are built.
+
+   3.1 Drivers built into the kernel.
+
+     3.1.1 Teles driver.
+
+       The Teles driver can be configured using the commandline-feature
+       while loading the kernel with LILO or LOADLIN. It accepts the
+       following syntax:
+
+       teles=p0,i0,m0,d0[,p1,i1,m1,d1 ... ,pn,in,mn,dn][,idstring]
+
+       where
+
+         p0 = portbase of 1st card.                           (default: 0xd80)
+         i0 = irq of 1st card.                                (default: 15)
+         m0 = shared memory of 1st card.                      (default: 0xd0000)
+         d0 = D-channel protocol of 1st card. 1=1TR6, 2=EDSS1 (default: 2)
+
+         p1,i1,m1,d1 = Parameters of second card (defaults: none)
+         pn,in,mn,d1 = Parameters of n'th card (up to 16 cards are supported)
+
+         idstring = Driver-Id for accessing with utilities and identification
+                    when using a Line-monitor. (default: none)
+                    idstring must start with a character!
+
+         The type of the card is determined by the port, irq and shared memory:
+
+           port == 0, shared memory != 0          -> Teles S0-8
+           port != 0, shared memory != 0          -> Teles S0-16.0
+           port != 0, shared memory == 0          -> Teles S0-16.3
+
+       ATTENTION:
+
+       Due to limited hardware-capabilities, there is no way to check the
+       existence of a card. Therefore you need to be shure your card's setup
+       is correct. Also there are bugs in the printed manual of some newer
+       16.3 cards. The manual tells the port has to be 0x180. THIS IS WRONG!!
+       0xd80 is the correct value! Have a look to the kernel-syslog. With
+       most of the cards, you should see a line "HSCX version A:5 B:5" there.
+       
+     3.1.2 ICN driver.
+
+       The ICN driver can be configured using the commandline-feature while
+       loading the kernel with LILO or LOADLIN. It accepts the following
+       syntax
+
+       icn=p,m[,idstring1[,idstring2]]
+
+       where
+
+         p = portbase      (default: 0x320)
+         m = shared memory (default: 0xd0000)
+
+       When using the ICN double card, you MUST define TWO idstrings.
+       idstring must start with a character!
+
+       The ICN driver supports only one card at the moment. If you like to
+       use more than one card, build the modularized version, loading it
+       more than one time, each module driving a single card.
+
+       Using the "icnctrl"-utility, portbase and shared memory can also be
+       changed during runtime.
+
+       The D-channel protocol is configured by loading different firmware
+       into the card's memory using the "icnctrl"-utility.
+
+
+   3.2 Drivers built as modules.
+
+     3.2.1 Teles driver.
+
+       The module teles.o can be configured during "insmod'ing" it by
+       appending it's parameters to the insmod-commandline. The following
+       syntax is accepted:
+
+       io=m0,i0,p0,d0[,m1,i1,p1,d1 ... ,mn,in,pn,dn] teles_id=idstring
+
+       where
+
+         m0,i0,p0,d0 ... mn,in,pn,dn have the same meanings like the
+                                     parameters described for the kernel-
+                                     version above. Watchout: different
+                                     sequence!
+
+     3.2.2 ICN driver.
+
+       The module icn.o can be configured during "insmod'ing" it by
+       appending it's parameters to the insmod-commandline. The following
+       syntax is accepted:
+
+       portbase=p membase=m icn_id=idstring icn_id2=idstring2
+
+       where p, m, idstring1 and idstring2 have the same meanings like
+                                           parameters described for the kernel-
+                                           version above.
+      
+       When using the ICN double card, you MUST define TWO idstrings.
+       idstring must start with a character!
+
+       The ICN driver supports only one card at the moment. If you like to
+       use more than one card, build the modularized version, loading it
+       more than one time, each module driving a single card.
+
+       Using the "icnctrl"-utility, portbase and shared memory can also be
+       changed during runtime.
+
+       The D-channel protocol is configured by loading different firmware
+       into the card's memory using the "icnctrl"-utility.
+
+4. Device-inodes
+
+   The major and minor-numbers and it's names are described in
+   Documentation/devices.txt. The major-numbers are:
+
+     43 for the ISDN-tty's.
+     44 for the ISDN-callout-tty's.
+     45 for control/info/debug devices.
+
+
+5. Application
+
+   a) (Only for ICN-cards) Load the firmware into the card:
+
+       cd icn
+     For 1TR6:
+       icnctrl [-d IDstring] load download/loadpg.bin download/pc_1t_ca.bin
+     For Euro-ISDN:
+       icnctrl [-d IDstring] load download/loadpg.bin download/pc_eu_ca.bin
+
+     When using the ICN-2B, the protocol-software for the second half of
+     the card must be appended to the command line.
+
+     -> The two LEDs at the back cover of the card (ICN-2B: 4 LEDs) must be
+        blinking intermittingly now. If a connection is up, the corresponding
+        led lits continuously.
+
+   b) If you only intend to use ttys, you are nearly ready now.
+
+   c) If you want to have really permanent "Modem"-settings on disk, you
+      can start the daemon iprofd. Give it a path to a file at the command-
+      line. It will store the profile-settings in this file every time
+      an AT&W0 is performed on any ISDN-tty. If the file already exists,
+      all profiles are initialized from this file. If you want to unload
+      any of the modules, kill iprofd first.
+
+   d) For networking, continue: Create an interface:
+       isdnctrl addif isdn0
+
+   e) Set the EAZ (or MSN for Euro-ISDN):
+       isdnctrl eaz isdn0 2
+
+     (For 1TR6 a single digit is allowed, for Euro-ISDN the number is your
+      real MSN e.g.: Phone-Number)
+
+   f) Set the number for outgoing calls on the interface:
+       isdnctrl addphone isdn0 out 1234567
+       ... (this can be executed more than once, all assigned numbers are
+            tried in order)
+      and the number(s) for incoming calls:
+       isdnctrl addphone isdn0 in 1234567
+
+   g) Set the timeout for hang-up:
+       isdnctrl huptimeout isdn0 <timeout_in_seconds>
+
+   h) additionally you may activate charge-hang-up (= Hang up before 
+      next charge-info, this only works, if your isdn-provider transmits
+      the charge-info during and after the connection, it does NOT work
+      with the Teles on an EDSS1-Line.):
+       isdnctrl chargehup isdn0 on
+
+   i) Setup the interface with ifconfig as usual, and set a route to it.
+
+   j) (optional) If you run X11 and have Tcl/Tk-wish Version4.0, you can use
+     the script tools/tcltk/isdnmon. You can add actions for line-status
+     changes. See the comments at the beginning of the script for how to
+     do that. There are other tty-based tools in the tools-subdirectory
+     contributed by Michael Knigge (imon), Volker Götz (imontty) and
+     Andreas Kool (isdnmon).
+
+   k) For initial testing, you can set the verbose-level to 2 (default: 0).
+      Then all incoming calls are logged, even if they are not addressed
+      to one of the configured net-interfaces:
+      isdnctrl verbose 2
+
+  Now you are ready! A ping to the set address should now result in a
+  dial-out (look at syslog kernel-messages).
+  The phone-numbers and EAZs can be assigned at any time with isdnctrl.
+  You can add as many interfaces as you like with addif following the
+  directions above. Of course, there may be some limitations. But we have 
+  tested as many as 20 interfaces without any problem. However, if you 
+  don't give an interface name to addif, the  kernel will assign a name 
+  which starts with "eth". The number of "eth"-interfaces is limited by
+  the kernel.
+
+5. Additional options for isdnctrl:
+
+   "isdnctrl secure <InterfaceName> on" 
+   Only incoming calls, for which the caller-id is listed in the access
+   list of the interface are accepted. You can add caller-id's With the
+   command "isdnctrl addphone <InterfaceName> in <caller-id>"
+   Euro-ISDN does not transmit the leading '0' of the caller-id for an
+   incoming call, therefore you should configure it accordingly.
+   If the real number for the dialout e.g. is "09311234567" the the number
+   to configure here is "9311234567". The pattern-match function 
+   works similar to the shell mechanism.
+
+     ?     one arbitrary digit
+     *     zero or arbitrary many digits
+     [123] one of the digits in the list
+     [1-5] one digit between '1' and '5'
+           a '^' as the first character in a list inverts the list
+
+
+   "isdnctrl secure <InterfaceName> off" 
+   Switch of secure operation (default).
+
+   "isdnctrl ihup <InterfaceName> [on|off]" 
+   Switch the hang-up-timer for incoming calls on or off.
+
+   "isdnctrl eaz <InterfaceName>" 
+   Returns the EAZ of an interface.
+
+   "isdnctrl delphone <InterfaceName> in|out <number>" 
+   Deletes a number from one of the the access-lists of the interface.
+
+   "isdnctrl delif <InterfaceName>" 
+   Removes the interface (and possible slaves) from the kernel.
+   (You have to unregister it with "ifconfig <InterfaceName> down" before).
+
+   "isdnctrl callback <InterfaceName> [on|off]" 
+   Switches an interface to callback-mode. In this mode, an incoming call
+   will be rejected and after this the remote-station will be called. If
+   you test this feature by using ping, some routers will re-dial very
+   quickly, so that the callback from isdn4linux may not be recognized.
+   In this case use ping with the option -i <sec> to increase the interval
+   between echo-packets.
+
+   "isdnctrl encap <InterfaceName> <EncapType>"
+   Selects the type of packet-encapsulation. The encapsulation can be changed
+   only while an interface is down.
+
+   At the moment th following Values are supported:
+ 
+   rawip    (Default) Selects raw-IP-encapsulation. This means, MAC-headers
+            are stripped off.  
+   ip       IP with type-field. Same as IP but the type-field of the MAC-header
+            is preserved.
+   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.
+            The Ethernet-address of the interface is faked, from it's
+            IP-address: fc:fc:i1:i2:i3:i4, where i1-4 are the IP-addr.-values.
+   syncppp  Synchronous PPP
+
+   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
+   mentioned above.
+
+   "isdnctrl l2_prot <InterfaceName> <L2-ProtocolName>" 
+   Selects a layer-2-protocol. 
+   (With the ICN-driver and the Teles-driver, "x75i" and "hdlc" is available.
+   With other drivers, "x75ui", "x75bui" may be possible too.)
+
+   isdnctrl l3_prot <InterfaceName> <L3-ProtocolName> 
+   The same for layer-3. (At the moment only "trans" is allowed)
+
+   "isdnctrl list <InterfaceName>" 
+   Shows all parameters of an interface and the charge-info.
+   Try "all" as the interface name.
+
+   "isdnctrl hangup <InterfaceName>"
+   Forces hangup of an interface.
+
+   "isdnctrl bind <InterfaceName> <DriverId>,<ChannelNumber> [exclusive]"
+   If you are using more than one ISDN-Card, it is sometimes necessary to
+   dial out using a specific Card or even preserve a specific Channel for
+   Dialout of a specific net-interface. This can be done with the above
+   command. Replace <DriverId> by whatever you assigned while loading the
+   module. The <ChannelNumber> is counting from zero. the upper Limit
+   depends on the card used. At the Moment no card supports more than
+   2 Channels, so the upper limit is one.
+
+   "isdnctrl unbind <InterfaceName>"
+   unbinds a previously bound interface.
+
+   "isdnctrl busreject <DriverId> on|off"
+   If switched on, isdn4linux replies a REJECT to incoming calls, it
+   cannot match to any configured interface.
+   If switched off, nothing happens in this case.
+   You normally should NOT enable this feature, if the ISDN-adaptor is not
+   the only device, connected to the S0-bus. Otherwise it could happen, that
+   isdn4linux rejects an incoming call, which belongs to another device on
+   the bus. 
+
+   "isdnctrl addslave <InterfaceName> <SlaveName>
+   Creates a slave interface for channel-bundling. Slave interfaces are
+   not seen by the kernel, but their ISDN-part can be configured with
+   isdnctrl as usual. (Phone numbers, EAZ/MSN, timeouts etc.) If more
+   than two channels are to be bundled, feel free to create as many as you
+   want. InterfaceName must be a real interface, NOT a slave. Slave interfaces
+   start dialing, if the master interface resp. the previous slave interface
+   has a load of more than 7000 cps. They hangup if the load goes under 7000
+   cps, according to their "huptimeout"-parameter.
+
+   "isdnctrl sdelay <InterfaceName> secs."
+   This sets the minimum time an Interface has to be fully loaded, until
+   it sends an dial-request to it's slave.
+
+   "isdnctrl dial <InterfaceName>"
+   Forces an interface to start dialing even if no packets are to be
+   transferred.
+
+   "isdnctrl mapping <DriverId> MSN0,MSN1,MSN2,...MSN9"
+   This installs a mapping table for EAZ<->MSN-mapping for a single line.
+   Missing MSN's have to be given as "-" or can be omitted, if at the end
+   of the commandline.
+   With this command, it's now possible to have an interface listening to
+   mixed 1TR6- and Euro-Type lines. In this case, the interface has to be
+   configured to a 1TR6-type EAZ (one digit). The mapping is also valid
+   for tty-emulation. Seen from the interface/tty-level the mapping
+   CAN be used, however it's possible to use single tty's/interfaces with
+   real MSN's (more digits) also, in which case the mapping will be ignored.
+   Here is an example:
+
+   You have a 1TR6-type line with base-nr. 1234567 and a Euro-line with
+   MSN's 987654, 987655 and 987656. The DriverId for the Euro-line is "EURO".
+
+   isdnctrl mapping EURO -,987654,987655,987656,-,987655
+   ...
+   isdnctrl eaz isdn0 1      # listen on 12345671(1tr6) and 987654(euro)
+   ...
+   isdnctrl eaz isdn1 4      # listen on 12345674(1tr6) only.
+   ...
+   isdnctrl eaz isdn2 987654 # listen on 987654(euro) only.
+   
+   Same scheme is used with AT&E...  at the tty's.
+  
+6. If you want to write a new low-level-driver, you are welcome.
+   The interface to the link-level-module is described in the file INTERFACE.
+   If the interface should be expanded for any reason, don't do it
+   on your own, send me a mail containing the proposed changes and
+   some reasoning about them.
+   If other drivers will not be affected, I will include the changes
+   in the next release.
+   For developers only, there is a second mailing-list. Write to me
+   (fritz@wuemaus.franken.de), if you want to join that list.
+
+Have fun!
+
+ -Fritz
+
diff -u --recursive --new-file v1.3.68/linux/Documentation/isdn/README.icn linux/Documentation/isdn/README.icn
--- v1.3.68/linux/Documentation/isdn/README.icn	Thu Jan  1 02:00:00 1970
+++ linux/Documentation/isdn/README.icn	Mon Feb 26 11:58:03 1996
@@ -0,0 +1,61 @@
+You can get the ICN-ISDN-card from:
+
+Thinking Objects Software GmbH
+Obere Heerbergstr. 17
+97078 Würzburg
+Tel: +49 931 2877950
+Fax: +49 931 2877951
+
+email uhl@think.de
+WWW   http:/www.think.de
+
+
+The card communicates with the PC by two interfaces:
+  1. A range of 4 successive port-addresses, whos base address can be 
+     configured with the switches.
+  2. A memory window with 16KB-256KB size, which can be setup in 16k steps
+     over the whole range of 16MB. Isdn4linux only uses a 16k window.
+     The base address of the window can be configured when loading
+     the lowlevel-module (see README).
+
+Setting up the IO-address dipswitches for the ICN-ISDN-card:
+
+  Two types of cards exist, one with dip-switches and one with
+  hook-switches.
+
+  1. Setting for the card with hook-switches:
+
+     (0 = switch closed, 1 = switch open)
+
+     S3 S2 S1  Basie-address
+      0  0  0  0x300
+      0  0  1  0x310
+      0  1  0  0x320 (Default for isdn4linux)
+      0  1  1  0x330
+      1  0  0  0x340
+      1  0  1  0x350
+      1  1  0  0x360
+      1  1  1  NOT ALLOWED!
+    
+  2. Setting for the card with dip-switches:
+
+     (0 = switch closed, 1 = switch open)
+
+     S1 S2 S3 S4  Basis-Adresse
+      0  0  0  0  0x300
+      0  0  0  1  0x310
+      0  0  1  0  0x320 (Default for isdn4linux)
+      0  0  1  1  0x330
+      0  1  0  0  0x340
+      0  1  0  1  0x350
+      0  1  1  0  0x360
+      0  1  1  1  NOT ALLOWED!
+      1  0  0  0  0x308
+      1  0  0  1  0x318
+      1  0  1  0  0x328
+      1  0  1  1  0x338
+      1  1  0  0  0x348
+      1  1  0  1  0x358
+      1  1  1  0  0x368
+      1  1  1  1  NOT ALLOWED!
+
diff -u --recursive --new-file v1.3.68/linux/Documentation/isdn/README.syncppp linux/Documentation/isdn/README.syncppp
--- v1.3.68/linux/Documentation/isdn/README.syncppp	Thu Jan  1 02:00:00 1970
+++ linux/Documentation/isdn/README.syncppp	Mon Feb 26 11:58:03 1996
@@ -0,0 +1,52 @@
+Some additional information for setting up a syncPPP
+connection using network interfaces.
+---------------------------------------------------------------
+
+You need one thing beside the isdn4linux package:
+
+  a patched pppd .. (I called it ipppd to show the difference)
+
+Compiling isdn4linux with sync PPP:
+-----------------------------------
+To compile isdn4linux with the sync PPP part, you have
+to answer the apropriate question when doing a "make config"
+Don't forget to load the slhc.o
+module before the isdn.o module, if VJ-compression support
+is not compiled into your kernel. 
+
+Using isdn4linux with sync PPP:
+-------------------------------
+Sync PPP is just another encapsulation for isdn4linux. The
+name to enable sync PPP encapsualtion is 'syncppp' .. e.g:
+
+  isdn/isdnctrl encap ippp0 syncppp
+
+The name of the interface is here 'ippp0'. You need 
+one interface with the name 'ippp0' to saturate the
+ipppd, which checks the ppp version via this interface.
+
+To set up a PPP connection you need the ipppd .. You must start 
+the ipppd once after installing the modules. The ipppd 
+communicates with the isdn4linux link-level driver using the
+/dev/ippp0 to /dev/ippp15 devices. One ipppd can handle
+all devices at once. If you want to use two PPP connections
+at the same time, you have to connect the ipppd to two
+devices .. and so on. 
+I've implemented one additonal option for the ipppd:
+ 'useifip' will get (if set to not 0.0.0.0) the IP address 
+ for the negotiation from the attached network-interface. 
+You must disable BSD-compression, this implementation can't
+handle compressed packets.
+
+Check the rc.isdn.syncppp file for an example setup script.
+
+enjoy it,
+    michael
+     
+
+PS: I also implemented generic MP (RFC 1717). But in the 
+current isdn4linux link-level driver there is no 
+(or better say: another) way of doing channel bundling. So,
+don't call the ipppd with the `+mp` option to enable 
+MP negotiation.
+
diff -u --recursive --new-file v1.3.68/linux/Documentation/isdn/README.teles linux/Documentation/isdn/README.teles
--- v1.3.68/linux/Documentation/isdn/README.teles	Thu Jan  1 02:00:00 1970
+++ linux/Documentation/isdn/README.teles	Mon Feb 26 11:58:03 1996
@@ -0,0 +1,72 @@
+This is my Linux hardware level driver for Teles compatible ISDN cards. It is 
+meant to be used with isdn4isdn4linux, an ISDN Link-level module for Linux written
+by Fritz Elfert.
+
+Isdn4linux can be obtained from ftp.franken.de:/pub/isdn4linux. The most recent 
+Teles driver can be found on my homepage, http://www.xs4all.nl:/~jdo.
+
+Warning
+-------
+Teles4isdn4linux is a work in progress and may crash your machine. It has not 
+been certified and therefore operation on your PTT's ISDN network is probably 
+illegal.
+
+Limitations
+-----------
+Teles4isdn4linux only works on Euro ISDN lines and german 1TR6-lines.
+
+For the B channel transparent (HDLC) protocol and X.75 have been implemented. 
+
+Running
+-------
+When you insmod isdn.o and teles.o (or with the kernel-version, during boottime)
+a few lines should appear in your syslog. Look for something like:
+
+Oct 11 16:53:30 jedi kernel: channels 2
+Oct 11 16:53:31 jedi kernel: Teles module installed
+
+Remember, that according to the new strategy for accessing Low-level-drivers
+from within isdn4linux you should also define a driver-id while doing
+insmod: Simply append id=<SomeString> to the insmod-commandline. This
+string MUST NOT start with a digit or a small 'x'!
+
+At this point you can run a 'cat /dev/isdnctrl0' and view debugging 
+messages. Debugging messages are enabled with the telesctrl tool: 
+
+	teles/telesctrl <DriverId> 1 <debugging_flags>
+
+where <debugging_flags> is the integer sum of the following debugging
+options you wish enabled:
+
+	1   Link-level <--> Hardware-level communication
+	2   Top state machine 
+	4   D channel Q.931 (call control messages)
+	8   D channel Q.921 
+	16  B channel X.75
+
+For example 'teles/telesctrl MyTeles 1 31' enables full 
+debugging.
+
+Questions
+---------
+Check out the FAQ (ftp.franken.de).
+
+Bugs 
+----
+If you find any please let me know. 
+
+Thanks
+------
+Special thanks to:
+
+	Erik Bos,Beat Doebeli,Fritz Elfert,	
+	Pauline Middelink,Paula de Nie, 	
+	Bernd Oerding,Stephan Seidl,Matthias Urlichs, 
+	Rogier Wolff
+
+
+
+Enjoy,
+
+Jan den Ouden   denouden@groovin.xs4all.nl
+
diff -u --recursive --new-file v1.3.68/linux/MAGIC linux/MAGIC
--- v1.3.68/linux/MAGIC	Tue Jan 23 21:15:36 1996
+++ linux/MAGIC	Mon Feb 26 13:51:45 1996
@@ -72,11 +72,13 @@
 0x03	linux/hdreg.h
 0x04	linux/umsdos_fs.h
 0x06	linux/lp.h
+0x09	linux/md.h
 0x12	linux/fs.h
 0x20	linux/cm206.h
 0x22	linux/scc.h
 'A'	linux/apm_bios.h
 'C'	linux/soundcard.h
+'I'	linux/isdn.h
 'K'	linux/kd.h
 'M'	linux/soundcard.h
 'P'	linux/soundcard.h
diff -u --recursive --new-file v1.3.68/linux/MAINTAINERS linux/MAINTAINERS
--- v1.3.68/linux/MAINTAINERS	Fri Feb 23 13:54:33 1996
+++ linux/MAINTAINERS	Sun Feb 25 11:17:58 1996
@@ -102,6 +102,12 @@
 L:	linux-smp@vger.rutgers.edu
 S:	Maintained
 
+SPARC:
+P:	David S. Miller
+M:	davem@caip.rutgers.edu
+L:	sparclinux@vger.rutgers.edu
+S:	Maintained
+
 REST:
 P:	Linus Torvalds
 S:	Buried alive in email
diff -u --recursive --new-file v1.3.68/linux/Makefile linux/Makefile
--- v1.3.68/linux/Makefile	Fri Feb 23 13:54:33 1996
+++ linux/Makefile	Mon Feb 26 12:06:50 1996
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 3
-SUBLEVEL = 68
+SUBLEVEL = 69
 
 ARCH = i386
 
@@ -130,6 +130,10 @@
 LIBS		=$(TOPDIR)/lib/lib.a
 SUBDIRS		=kernel drivers mm fs net ipc lib
 
+ifeq ($(CONFIG_ISDN),y)
+DRIVERS	:= $(DRIVERS) drivers/isdn/isdn.a
+endif
+
 ifdef CONFIG_CD_NO_IDESCSI
 DRIVERS := $(DRIVERS) drivers/cdrom/cdrom.a
 endif
@@ -339,7 +343,7 @@
 
 
 backup: mrproper
-	cd .. && tar cf - linux | gzip -9 > backup.gz
+	cd .. && tar cf - linux/ | gzip -9 > backup.gz
 	sync
 
 dep-files: archdep .hdepend include/linux/version.h
diff -u --recursive --new-file v1.3.68/linux/arch/alpha/defconfig linux/arch/alpha/defconfig
--- v1.3.68/linux/arch/alpha/defconfig	Sat Feb 17 16:02:44 1996
+++ linux/arch/alpha/defconfig	Mon Feb 26 11:58:03 1996
@@ -176,6 +176,7 @@
 #
 # Character devices
 #
+CONFIG_SERIAL=y
 # CONFIG_CYCLADES is not set
 # CONFIG_STALDRV is not set
 # CONFIG_PRINTER is not set
diff -u --recursive --new-file v1.3.68/linux/arch/i386/config.in linux/arch/i386/config.in
--- v1.3.68/linux/arch/i386/config.in	Fri Feb  9 17:52:57 1996
+++ linux/arch/i386/config.in	Mon Feb 26 11:58:03 1996
@@ -64,6 +64,15 @@
 fi
 
 mainmenu_option next_comment
+comment 'ISDN subsystem'
+
+tristate 'ISDN support' CONFIG_ISDN
+if [ "$CONFIG_ISDN" != "n" ]; then
+  source drivers/isdn/Config.in
+fi
+endmenu
+
+mainmenu_option next_comment
 comment 'CD-ROM drivers (not for SCSI or IDE/ATAPI drives)'
 
 bool 'Support non-SCSI/IDE/ATAPI CDROM drives' CONFIG_CD_NO_IDESCSI
diff -u --recursive --new-file v1.3.68/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v1.3.68/linux/arch/i386/defconfig	Fri Feb 23 13:54:33 1996
+++ linux/arch/i386/defconfig	Mon Feb 26 13:51:45 1996
@@ -45,7 +45,7 @@
 # CONFIG_BLK_DEV_RAM is not set
 # CONFIG_BLK_DEV_LOOP is not set
 # CONFIG_BLK_DEV_XD is not set
-
+# CONFIG_BLK_DEV_MD is not set
 #
 # Networking options
 #
@@ -104,6 +104,11 @@
 # CONFIG_ARCNET is not set
 
 #
+# ISDN subsystem
+#
+# CONFIG_ISDN is not set
+
+#
 # CD-ROM drivers (not for SCSI or IDE/ATAPI drives)
 #
 # CONFIG_CD_NO_IDESCSI is not set
@@ -131,6 +136,7 @@
 #
 # Character devices
 #
+CONFIG_SERIAL=y
 # CONFIG_CYCLADES is not set
 # CONFIG_STALDRV is not set
 # CONFIG_PRINTER is not set
diff -u --recursive --new-file v1.3.68/linux/arch/i386/kernel/process.c linux/arch/i386/kernel/process.c
--- v1.3.68/linux/arch/i386/kernel/process.c	Wed Feb  7 15:11:21 1996
+++ linux/arch/i386/kernel/process.c	Sun Feb 25 11:17:59 1996
@@ -328,16 +328,15 @@
 	dump->magic = CMAGIC;
 	dump->start_code = 0;
 	dump->start_stack = regs->esp & ~(PAGE_SIZE - 1);
-	dump->u_tsize = ((unsigned long) current->mm->end_code) >> 12;
-	dump->u_dsize = ((unsigned long) (current->mm->brk + (PAGE_SIZE-1))) >> 12;
+	dump->u_tsize = ((unsigned long) current->mm->end_code) >> PAGE_SHIFT;
+	dump->u_dsize = ((unsigned long) (current->mm->brk + (PAGE_SIZE-1))) >> PAGE_SHIFT;
 	dump->u_dsize -= dump->u_tsize;
 	dump->u_ssize = 0;
 	for (i = 0; i < 8; i++)
 		dump->u_debugreg[i] = current->debugreg[i];  
 
-	if (dump->start_stack < TASK_SIZE) {
-		dump->u_ssize = ((unsigned long) (TASK_SIZE - dump->start_stack)) >> 12;
-	}
+	if (dump->start_stack < TASK_SIZE)
+		dump->u_ssize = ((unsigned long) (TASK_SIZE - dump->start_stack)) >> PAGE_SHIFT;
 
 	dump->regs = *regs;
 
diff -u --recursive --new-file v1.3.68/linux/arch/i386/kernel/smp.c linux/arch/i386/kernel/smp.c
--- v1.3.68/linux/arch/i386/kernel/smp.c	Sat Feb 17 16:02:45 1996
+++ linux/arch/i386/kernel/smp.c	Mon Feb 26 11:58:04 1996
@@ -27,6 +27,7 @@
 #include <linux/mm.h>
 #include <linux/kernel_stat.h>
 #include <linux/delay.h>
+#include <linux/mc146818rtc.h>
 #include <asm/i82489.h>
 #include <linux/smp.h>
 #include <asm/pgtable.h>
@@ -469,7 +470,7 @@
 	/*
 	 *	Allow the master to continue.
 	 */	
-	set_bit(cpuid, &cpu_callin_map[0]);
+	set_bit(cpuid, (unsigned long *)&cpu_callin_map[0]);
 	/*
 	 *	Until we are ready for SMP scheduling
 	 */
@@ -573,21 +574,38 @@
 			 *	the targeted processor.
 			 */
 
-#ifdef EEK
 			SMP_PRINTK(("Setting warm reset code and vector.\n"));
 
+			/*
+			 *	Needed to boot a 486 board.
+			 */
+			 
 			CMOS_WRITE(0xa, 0xf);
-			*((volatile unsigned short *) 0x467) = (unsigned short)(stack>>4);
+			pg0[0]=7;
+			*((volatile unsigned short *) 0x467) = ((unsigned long)stack)>>4;
 			*((volatile unsigned short *) 0x469) = 0;
-#endif
+			pg0[0]= pte_val(mk_pte(0, PAGE_READONLY));
+
+			/*
+			 *	Clean up the errors
+			 */
 
 			apic_write(APIC_ESR, 0);
 			accept_status = (apic_read(APIC_ESR) & 0xEF);
+			
+			/*
+			 *	Status is now clean
+			 */
+			 
 			send_status = 0;
 			accept_status = 0;
 
 			SMP_PRINTK(("Asserting INIT.\n"));
 
+			/*
+			 *	Turn INIT on
+			 */
+			 
 			cfg=apic_read(APIC_ICR2);
 			cfg&=0x00FFFFFF;
 			apic_write(APIC_ICR2, cfg|SET_APIC_DEST_FIELD(i)); 			/* Target chip     	*/
@@ -610,6 +628,10 @@
 			}
 #endif
 
+			/*
+			 *	And off again
+			 */
+			 
 			if (send_status && !accept_status)
 			{
 				SMP_PRINTK(("Deasserting INIT.\n"));
@@ -706,7 +728,6 @@
 				printk("APIC never delivered???\n");
 			else if (accept_status)		/* Send accept error */
 				printk("APIC delivery error (%lx).\n", accept_status);
-			else
 			{
 				for(timeout=0;timeout<50000;timeout++)
 				{
@@ -1096,9 +1117,9 @@
 		 */
 		 
 		case MSG_INVALIDATE_TLB:
-			if(clear_bit(i,&smp_invalidate_needed))
+			if(clear_bit(i,(unsigned long *)&smp_invalidate_needed))
 				local_invalidate();
-			set_bit(i, &cpu_callin_map[0]);
+			set_bit(i, (unsigned long *)&cpu_callin_map[0]);
 			cpu_callin_map[0]|=1<<smp_processor_id();
 			break;
 			
diff -u --recursive --new-file v1.3.68/linux/drivers/Makefile linux/drivers/Makefile
--- v1.3.68/linux/drivers/Makefile	Sun Dec 17 11:43:11 1995
+++ linux/drivers/Makefile	Mon Feb 26 11:58:04 1996
@@ -9,7 +9,7 @@
 
 SUB_DIRS     := block char net #streams
 MOD_SUB_DIRS := $(SUB_DIRS)
-ALL_SUB_DIRS := $(SUB_DIRS) pci scsi sound cdrom
+ALL_SUB_DIRS := $(SUB_DIRS) pci scsi sound cdrom isdn
 
 ifdef CONFIG_PCI
 SUB_DIRS += pci
@@ -37,6 +37,15 @@
 ifdef CONFIG_CD_NO_IDESCSI
 SUB_DIRS += cdrom
 MOD_SUB_DIRS += cdrom
+endif
+
+ifeq ($(CONFIG_ISDN),y)
+SUB_DIRS += isdn
+MOD_SUB_DIRS += isdn
+else
+  ifeq ($(CONFIG_ISDN),m)
+  MOD_SUB_DIRS += isdn
+  endif
 endif
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v1.3.68/linux/drivers/block/Config.in linux/drivers/block/Config.in
--- v1.3.68/linux/drivers/block/Config.in	Fri Feb 23 13:54:33 1996
+++ linux/drivers/block/Config.in	Mon Feb 26 13:51:45 1996
@@ -37,4 +37,9 @@
 fi
 
 bool 'XT harddisk support' CONFIG_BLK_DEV_XD
+bool 'Multiple devices driver support' CONFIG_BLK_DEV_MD
+if [ "$CONFIG_BLK_DEV_MD" = "y" ]; then
+  tristate '   Linear (append) mode' CONFIG_MD_LINEAR
+  tristate '   RAID-0 (striping) mode' CONFIG_MD_STRIPED
+fi
 endmenu
diff -u --recursive --new-file v1.3.68/linux/drivers/block/Makefile linux/drivers/block/Makefile
--- v1.3.68/linux/drivers/block/Makefile	Fri Feb 23 13:54:33 1996
+++ linux/drivers/block/Makefile	Mon Feb 26 13:51:45 1996
@@ -19,6 +19,7 @@
 L_OBJS   := ll_rw_blk.o genhd.o 
 M_OBJS   :=
 MOD_LIST_NAME := BLOCK_MODULES
+LX_OBJS :=
 
 ifeq ($(CONFIG_BLK_DEV_FD),y)
 L_OBJS += floppy.o
@@ -94,6 +95,47 @@
 
 ifeq ($(CONFIG_BLK_DEV_XD),y)
 L_OBJS += xd.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_MD),y)
+LX_OBJS += md.o
+
+ifeq ($(CONFIG_MD_LINEAR),y)
+L_OBJS += linear.o
+else
+  ifeq ($(CONFIG_MD_LINEAR),m)
+  M_OBJS += linear.o
+  endif
+endif
+
+ifeq ($(CONFIG_MD_STRIPED),y)
+L_OBJS += raid0.o
+else
+  ifeq ($(CONFIG_MD_STRIPED),m)
+  M_OBJS += raid0.o
+  endif
+endif
+
+#ifeq ($(CONFIG_MD_RAID1),y)
+#L_OBJS += raid1.o
+#else
+#  ifeq ($(CONFIG_MD_SUPPORT_RAID1),y)
+#    ifeq ($(CONFIG_MD_RAID1),m)
+#    M_OBJS += raid1.o
+#    endif
+#  endif
+#endif
+#
+#ifeq ($(CONFIG_MD_RAID5),y)
+#L_OBJS += raid5.o
+#else
+#  ifeq ($(CONFIG_MD_SUPPORT_RAID5),y)
+#    ifeq ($(CONFIG_MD_RAID5),m)
+#    M_OBJS += raid5.o
+#    endif
+#  endif
+#endif
+
 endif
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v1.3.68/linux/drivers/block/README.md linux/drivers/block/README.md
--- v1.3.68/linux/drivers/block/README.md	Thu Jan  1 02:00:00 1970
+++ linux/drivers/block/README.md	Mon Feb 26 13:51:45 1996
@@ -0,0 +1,4 @@
+Tools that manage md devices can be found at sweet-smoke.ufr-info-p7.ibp.fr
+in public/Linux/md034.tar.gz.
+
+	Marc ZYNGIER <zyngier@ufr-info-p7.ibp.fr>
diff -u --recursive --new-file v1.3.68/linux/drivers/block/linear.c linux/drivers/block/linear.c
--- v1.3.68/linux/drivers/block/linear.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/block/linear.c	Mon Feb 26 13:51:45 1996
@@ -0,0 +1,241 @@
+
+/*
+   linear.c : Multiple Devices driver for Linux
+              Copyright (C) 1994-96 Marc ZYNGIER
+	      <zyngier@ufr-info-p7.ibp.fr> or
+	      <maz@gloups.fdn.fr>
+
+   Linear mode management functions.
+
+   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.
+   
+   You should have received a copy of the GNU General Public License
+   (for example /usr/src/linux/COPYING); if not, write to the Free
+   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
+*/
+
+#include <linux/module.h>
+
+#include <linux/md.h>
+#include <linux/linear.h>
+#include <linux/malloc.h>
+
+#define MAJOR_NR MD_MAJOR
+#define MD_DRIVER
+#define MD_PERSONALITY
+
+#include <linux/blk.h>
+
+static int linear_run (int minor, struct md_dev *mddev)
+{
+  int cur=0, i, size, dev0_size, nb_zone;
+  struct linear_data *data;
+
+  MOD_INC_USE_COUNT;
+  
+  mddev->private=kmalloc (sizeof (struct linear_data), GFP_KERNEL);
+  data=(struct linear_data *) mddev->private;
+
+  /*
+     Find out the smallest device. This was previously done
+     at registery time, but since it violates modularity,
+     I moved it here... Any comment ? ;-)
+   */
+
+  data->smallest=devices[minor];
+  for (i=1; i<mddev->nb_dev; i++)
+    if (data->smallest->size > devices[minor][i].size)
+      data->smallest=devices[minor]+i;
+  
+  nb_zone=data->nr_zones=
+    md_size[minor]/data->smallest->size +
+    (md_size[minor]%data->smallest->size ? 1 : 0);
+  
+  data->hash_table=kmalloc (sizeof (struct linear_hash)*nb_zone, GFP_KERNEL);
+
+  size=devices[minor][cur].size;
+
+  i=0;
+  while (cur<mddev->nb_dev)
+  {
+    data->hash_table[i].dev0=devices[minor]+cur;
+
+    if (size>=data->smallest->size) /* If we completly fill the slot */
+    {
+      data->hash_table[i++].dev1=NULL;
+      size-=data->smallest->size;
+
+      if (!size)
+      {
+	if (++cur==mddev->nb_dev) continue;
+	size=devices[minor][cur].size;
+      }
+
+      continue;
+    }
+
+    if (++cur==mddev->nb_dev) /* Last dev, set dev1 as NULL */
+    {
+      data->hash_table[i].dev1=NULL;
+      continue;
+    }
+
+    dev0_size=size;		/* Here, we use a 2nd dev to fill the slot */
+    size=devices[minor][cur].size;
+    data->hash_table[i++].dev1=devices[minor]+cur;
+    size-=(data->smallest->size - dev0_size);
+  }
+
+  return 0;
+}
+
+static int linear_stop (int minor, struct md_dev *mddev)
+{
+  struct linear_data *data=(struct linear_data *) mddev->private;
+  
+  kfree (data->hash_table);
+  kfree (data);
+
+  MOD_DEC_USE_COUNT;
+
+  return 0;
+}
+
+
+static int linear_map (int minor, struct md_dev *mddev, struct request *req)
+{
+  struct linear_data *data=(struct linear_data *) mddev->private;
+  struct linear_hash *hash;
+  struct real_dev *tmp_dev;
+  long block, rblock;
+  struct buffer_head *bh, *bh2;
+  int queue, nblk;
+  static struct request pending[MAX_REAL]={{0, }, };
+
+  while (req->nr_sectors)
+  {
+    block=req->sector >> 1;
+    hash=data->hash_table+(block/data->smallest->size);
+    
+    if (block >= (hash->dev0->size + hash->dev0->offset))
+    {
+      if (!hash->dev1)
+	printk ("linear_map : hash->dev1==NULL for block %ld\n", block);
+      tmp_dev=hash->dev1;
+    }
+    else
+      tmp_dev=hash->dev0;
+    
+    if (block >= (tmp_dev->size + tmp_dev->offset) || block < tmp_dev->offset)
+      printk ("Block %ld out of bounds on dev %04x size %d offset %d\n", block, tmp_dev->dev, tmp_dev->size, tmp_dev->offset);
+    
+    rblock=(block-(tmp_dev->offset));
+    
+    if (req->sem)				/* This is a paging request */
+    {
+      req->rq_dev=tmp_dev->dev;
+      req->sector=rblock << 1;
+      add_request (blk_dev+MAJOR (tmp_dev->dev), req);
+      
+      return REDIRECTED_REQ;
+    }
+
+    queue=tmp_dev - devices[minor];
+
+    for (nblk=0, bh=bh2=req->bh;
+	 bh && rblock + nblk + (bh->b_size >> 10) <= tmp_dev->size;
+	 nblk+=bh->b_size >> 10, bh2=bh, bh=bh->b_reqnext)
+    {
+      if (!buffer_locked(bh))
+	printk("md%d: block %ld not locked\n", minor, bh->b_blocknr);
+      
+      bh->b_rdev=tmp_dev->dev;
+    }
+
+    pending[queue].rq_dev=tmp_dev->dev;
+    pending[queue].cmd=req->cmd;
+    pending[queue].sector=rblock << 1;
+    pending[queue].nr_sectors=nblk << 1;
+    pending[queue].current_nr_sectors=req->bh->b_size >> 9;
+    pending[queue].bh=req->bh;
+    pending[queue].bhtail=bh2;
+    bh2->b_reqnext=NULL;
+    
+    req->bh=bh;
+    req->sector+=nblk << 1;
+    req->nr_sectors-=nblk << 1;
+  }
+
+  req->rq_status=RQ_INACTIVE;
+  wake_up (&wait_for_request);
+  make_md_request (pending, mddev->nb_dev);
+  return REDIRECTED_REQ;
+}
+
+
+static int linear_status (char *page, int minor, struct md_dev *mddev)
+{
+  int sz=0;
+
+#undef MD_DEBUG
+#ifdef MD_DEBUG
+  int j;
+  struct linear_data *data=(struct linear_data *) mddev->private;
+  
+  sz+=sprintf (page+sz, "      ");
+  for (j=0; j<data->nr_zones; j++)
+  {
+    sz+=sprintf (page+sz, "[%s",
+		 partition_name (data->hash_table[j].dev0->dev));
+
+    if (data->hash_table[j].dev1)
+      sz+=sprintf (page+sz, "/%s] ",
+		   partition_name(data->hash_table[j].dev1->dev));
+    else
+      sz+=sprintf (page+sz, "] ");
+  }
+
+  sz+=sprintf (page+sz, "\n");
+#endif
+  return sz;
+}
+
+
+static struct md_personality linear_personality=
+{
+  "linear",
+  linear_map,
+  linear_run,
+  linear_stop,
+  linear_status,
+  NULL,				/* no ioctls */
+  0
+};
+
+
+#ifndef MODULE
+
+void linear_init (void)
+{
+  register_md_personality (LINEAR, &linear_personality);
+}
+
+#else
+
+int init_module (void)
+{
+  return (register_md_personality (LINEAR, &linear_personality));
+}
+
+void cleanup_module (void)
+{
+  if (MOD_IN_USE)
+    printk ("md linear : module still busy...\n");
+  else
+    unregister_md_personality (LINEAR);
+}
+
+#endif
diff -u --recursive --new-file v1.3.68/linux/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
--- v1.3.68/linux/drivers/block/ll_rw_blk.c	Fri Feb 23 13:54:34 1996
+++ linux/drivers/block/ll_rw_blk.c	Mon Feb 26 13:51:45 1996
@@ -24,12 +24,7 @@
 /*
  * The request-struct contains all necessary data
  * to load a nr of sectors into memory
- *
- * NR_REQUEST is the number of entries in the request-queue.
- * NOTE that writes may use only the low 2/3 of these: reads
- * take precedence.
  */
-#define NR_REQUEST	64
 static struct request all_requests[NR_REQUEST];
 
 /*
@@ -232,11 +227,15 @@
  * By this point, req->cmd is always either READ/WRITE, never READA/WRITEA,
  * which is important for drive_stat_acct() above.
  */
-static void add_request(struct blk_dev_struct * dev, struct request * req)
+
+struct semaphore request_lock = MUTEX;
+
+void add_request(struct blk_dev_struct * dev, struct request * req)
 {
 	struct request * tmp;
 	short		 disk_index;
 
+	down (&request_lock);
 	switch (MAJOR(req->rq_dev)) {
 		case SCSI_DISK_MAJOR:
 			disk_index = (MINOR(req->rq_dev) & 0x0070) >> 4;
@@ -257,10 +256,11 @@
 
 	req->next = NULL;
 	cli();
-	if (req->bh)
+	if (req->bh && req->bh->b_dev==req->bh->b_rdev)
 		mark_buffer_clean(req->bh);
 	if (!(tmp = dev->current_request)) {
 		dev->current_request = req;
+		up (&request_lock);
 		(dev->request_fn)();
 		sti();
 		return;
@@ -274,8 +274,9 @@
 	req->next = tmp->next;
 	tmp->next = req;
 
+	up (&request_lock);
 /* for SCSI devices, call request_fn unconditionally */
-	if (scsi_major(MAJOR(req->rq_dev)))
+	if (scsi_major(MAJOR(req->rq_dev)) && MAJOR(req->rq_dev)!=MD_MAJOR)
 		(dev->request_fn)();
 
 	sti();
@@ -338,6 +339,7 @@
 
 /* look for a free request. */
 	cli();
+	down (&request_lock);
 
 /* The scsi disk and cdrom drivers completely remove the request
  * from the queue when they start processing an entry.  For this reason
@@ -345,6 +347,7 @@
  */
 	if ((   major == IDE0_MAJOR	/* same as HD_MAJOR */
 	     || major == IDE1_MAJOR
+	     || major == MD_MAJOR
 	     || major == FLOPPY_MAJOR
 	     || major == SCSI_DISK_MAJOR
 	     || major == SCSI_CDROM_MAJOR
@@ -354,6 +357,7 @@
 	{
 		if (major != SCSI_DISK_MAJOR && major != SCSI_CDROM_MAJOR)
 			req = req->next;
+
 		while (req) {
 			if (req->rq_dev == bh->b_dev &&
 			    !req->sem &&
@@ -365,6 +369,7 @@
 				req->bhtail = bh;
 				req->nr_sectors += count;
 				mark_buffer_clean(bh);
+				up (&request_lock);
 				sti();
 				return;
 			}
@@ -382,6 +387,7 @@
 			    	req->sector = sector;
 				mark_buffer_clean(bh);
 			    	req->bh = bh;
+				up (&request_lock);
 			    	sti();
 			    	return;
 			}    
@@ -390,6 +396,8 @@
 		}
 	}
 
+	up (&request_lock);
+	
 /* find an unused request. */
 	req = get_request(max_req, bh->b_dev);
 	sti();
@@ -417,6 +425,15 @@
 	add_request(major+blk_dev,req);
 }
 
+#ifdef CONFIG_BLK_DEV_MD
+
+struct request *get_md_request (int max_req, kdev_t dev)
+{
+  return (get_request_wait (max_req, dev));
+}
+
+#endif
+
 /*
  * Swap partitions are now read via brw_page.  ll_rw_page is an
  * asynchronous function now --- we must call wait_on_page afterwards
@@ -515,6 +532,10 @@
 	for (i = 0; i < nr; i++) {
 		if (bh[i]) {
 			set_bit(BH_Req, &bh[i]->b_state);
+
+			/* Md needs this for error recovery */
+			bh[i]->b_rdev = bh[i]->b_dev;
+
 			make_request(major, rw, bh[i]);
 		}
 	}
@@ -658,5 +679,8 @@
 #ifdef CONFIG_SJCD
 	sjcd_init();
 #endif CONFIG_SJCD
+#ifdef CONFIG_BLK_DEV_MD
+	md_init();
+#endif CONFIG_BLK_DEV_MD
 	return 0;
 }
diff -u --recursive --new-file v1.3.68/linux/drivers/block/loop.c linux/drivers/block/loop.c
--- v1.3.68/linux/drivers/block/loop.c	Fri Feb 23 13:54:34 1996
+++ linux/drivers/block/loop.c	Mon Feb 26 13:26:28 1996
@@ -29,7 +29,7 @@
 #include "des.h"
 #endif
 
-#define DEFAULT_MAJOR_NR 28
+#define DEFAULT_MAJOR_NR 10
 int loop_major = DEFAULT_MAJOR_NR;
 #define MAJOR_NR loop_major	/* not necessarily constant */
 
@@ -225,15 +225,8 @@
 			brelse(bh);
 			goto error_out;
 		}
-		if (CURRENT->cmd == WRITE) {
-			set_bit(BH_Dirty, &bh->b_state);
-			ll_rw_block(WRITE, 1, &bh);
-			wait_on_buffer(bh);
-			if (buffer_dirty(bh)) {
-				brelse(bh);
-				goto error_out;
-			}
-		}
+		if (CURRENT->cmd == WRITE)
+			mark_buffer_dirty(bh, 1);
 		brelse(bh);
 		dest_addr += size;
 		len -= size;
@@ -280,7 +273,7 @@
 		return -ENXIO;
 	if (lo->lo_refcnt > 1)
 		return -EBUSY;
-	lo->lo_inode->i_count--;
+	iput(lo->lo_inode);
 	lo->lo_device = 0;
 	lo->lo_inode = NULL;
 	lo->lo_encrypt_type = 0;
@@ -288,6 +281,7 @@
 	lo->lo_encrypt_key_size = 0;
 	memset(lo->lo_encrypt_key, 0, LO_KEY_SIZE);
 	memset(lo->lo_name, 0, LO_NAME_SIZE);
+	loop_sizes[lo->lo_number] = 0;
 	invalidate_buffers(dev);
 	return 0;
 }
diff -u --recursive --new-file v1.3.68/linux/drivers/block/loop.h linux/drivers/block/loop.h
--- v1.3.68/linux/drivers/block/loop.h	Fri Feb 23 13:54:34 1996
+++ linux/drivers/block/loop.h	Mon Feb 26 13:26:28 1996
@@ -62,7 +62,7 @@
 #define LO_CRYPT_NONE	0
 #define LO_CRYPT_XOR	1
 #define LO_CRYPT_DES	2
-#define LO_CRYPT_IDEA	5
+#define LO_CRYPT_IDEA	3
 #define MAX_LO_CRYPT	4
 
 /*
diff -u --recursive --new-file v1.3.68/linux/drivers/block/md.c linux/drivers/block/md.c
--- v1.3.68/linux/drivers/block/md.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/block/md.c	Mon Feb 26 13:51:45 1996
@@ -0,0 +1,814 @@
+
+/*
+   md.c : Multiple Devices driver for Linux
+          Copyright (C) 1994-96 Marc ZYNGIER
+	  <zyngier@ufr-info-p7.ibp.fr> or
+	  <maz@gloups.fdn.fr>
+
+   A lot of inspiration came from hd.c ...
+
+   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.
+   
+   You should have received a copy of the GNU General Public License
+   (for example /usr/src/linux/COPYING); if not, write to the Free
+   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
+*/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/md.h>
+#include <linux/hdreg.h>
+#include <linux/stat.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/blkdev.h>
+#include <errno.h>
+
+#define MAJOR_NR MD_MAJOR
+#define MD_DRIVER
+
+#include <linux/blk.h>
+
+#ifdef CONFIG_MD_SUPPORT_RAID5
+int support_for_raid5; /* So raid-5 module won't be inserted if support
+			  was not set in the kernel */
+#endif
+
+#ifdef CONFIG_MD_SUPPORT_RAID1
+int support_for_raid1; /* So raid-1 module won't be inserted if support
+			  was not set in the kernel */
+#endif
+
+static struct hd_struct md_hd_struct[MAX_MD_DEV];
+static int md_blocksizes[MAX_MD_DEV];
+
+int md_size[MAX_MD_DEV]={0, };
+
+static void md_geninit (struct gendisk *);
+
+static struct gendisk md_gendisk=
+{
+  MD_MAJOR,
+  "md",
+  0,
+  1,
+  MAX_MD_DEV,
+  md_geninit,
+  md_hd_struct,
+  md_size,
+  MAX_MD_DEV,
+  NULL,
+  NULL
+};
+
+static struct md_personality *pers[MAX_PERSONALITY]={NULL, };
+
+struct real_dev devices[MAX_MD_DEV][MAX_REAL];
+struct md_dev md_dev[MAX_MD_DEV];
+
+static struct gendisk *find_gendisk (kdev_t dev)
+{
+  struct gendisk *tmp=gendisk_head;
+
+  while (tmp != NULL)
+  {
+    if (tmp->major==MAJOR(dev))
+      return (tmp);
+    
+    tmp=tmp->next;
+  }
+
+  return (NULL);
+}
+
+
+/* Picked up from genhd.c */
+char *partition_name (kdev_t dev)
+{
+  static char name[10];		/* This should be long
+				   enough for a device name ! */
+  struct gendisk *hd=find_gendisk (dev);
+  char base_name;
+  int minor=MINOR(dev);
+
+  if (!hd)
+  {
+    printk ("No gendisk entry for dev %04x\n", dev);
+    sprintf (name, "dev %04x", dev);
+    return (name);
+  }
+
+  base_name = (hd->major == IDE1_MAJOR) ? 'c' : 'a';
+  sprintf(name, "%s%c%d",
+	  hd->major_name,
+	  base_name + (minor >> hd->minor_shift),
+	  minor & ((1 << hd->minor_shift) - 1));
+  return (name);
+}
+
+
+static void set_ra (void)
+{
+  int i, j, minra=INT_MAX;
+
+  for (i=0; i<MAX_MD_DEV; i++)
+  {
+    if (!md_dev[i].pers)
+      continue;
+    
+    for (j=0; j<md_dev[i].nb_dev; j++)
+      if (read_ahead[MAJOR(devices[i][j].dev)]<minra)
+	minra=read_ahead[MAJOR(devices[i][j].dev)];
+  }
+  
+  read_ahead[MD_MAJOR]=minra;
+}
+
+
+static int md_ioctl (struct inode *inode, struct file *file,
+                     unsigned int cmd, unsigned long arg)
+{
+  int minor, index, err, current_ra;
+  struct gendisk *gen_real;
+  struct hd_geometry *loc = (struct hd_geometry *) arg;
+  kdev_t dev;
+
+  if (!suser())
+    return -EACCES;
+
+  if (((minor=MINOR(inode->i_rdev)) & 0x80) &&
+      (minor & 0x7f) < MAX_PERSONALITY &&
+      pers[minor & 0x7f] &&
+      pers[minor & 0x7f]->ioctl)
+    return (pers[minor & 0x7f]->ioctl (inode, file, cmd, arg));
+  
+  if (minor >= MAX_MD_DEV)
+    return -EINVAL;
+
+  switch (cmd)
+  {
+    case REGISTER_DEV:
+    dev=to_kdev_t ((dev_t) arg);
+    if (MAJOR(dev)==MD_MAJOR || md_dev[minor].nb_dev==MAX_REAL)
+      return -EINVAL;
+
+    if (!fs_may_mount (dev) || md_dev[minor].pers)
+      return -EBUSY;
+
+    if (!(gen_real=find_gendisk (dev)))
+      return -ENOENT;
+
+    index=md_dev[minor].nb_dev++;
+    devices[minor][index].dev=dev;
+
+    /* Lock the device by inserting a dummy inode. This doesn't
+       smeel very good, but I need to be consistent with the
+       mount stuff, specially with fs_may_mount. If someone have
+       a better idea, please help ! */
+    
+    devices[minor][index].inode=get_empty_inode ();
+    devices[minor][index].inode->i_dev=dev; /* don't care about
+					       other fields */
+    insert_inode_hash (devices[minor][index].inode);
+    
+    /* Devices sizes are rounded to a multiple of page (needed for
+       paging). This is NOT done by fdisk when partitionning,
+       but that's a DOS thing anyway... */
+    
+    devices[minor][index].size=gen_real->sizes[MINOR(dev)] & (PAGE_MASK>>10);
+    devices[minor][index].offset=index ?
+      (devices[minor][index-1].offset + devices[minor][index-1].size) : 0;
+
+    if (!index)
+      md_size[minor]=devices[minor][index].size;
+    else
+      md_size[minor]+=devices[minor][index].size;
+
+    printk("REGISTER_DEV %s to md%x done\n", partition_name(dev), minor);
+    break;
+
+    case START_MD:
+    if (!md_dev[minor].nb_dev)
+      return -EINVAL;
+
+    if (md_dev[minor].pers)
+      return -EBUSY;
+
+    md_dev[minor].repartition=(int) arg;
+    
+    if ((index=PERSONALITY(md_dev+minor) >> (PERSONALITY_SHIFT))
+	>= MAX_PERSONALITY ||
+	!pers[index])
+      return -EINVAL;
+
+    md_dev[minor].pers=pers[index];
+
+    if ((err=md_dev[minor].pers->run (minor, md_dev+minor)))
+    {
+      md_dev[minor].pers=NULL;
+      return (err);
+    }
+
+    /* FIXME : We assume here we have blocks
+       that are twice as large as sectors.
+       THIS MAY NOT BE TRUE !!! */
+    md_hd_struct[minor].start_sect=0;
+    md_hd_struct[minor].nr_sects=md_size[minor]<<1;
+
+    /* It would be better to have a per-md-dev read_ahead. Currently,
+       we only use the smallest read_ahead among md-attached devices */
+
+    current_ra=read_ahead[MD_MAJOR];
+    
+    for (index=0; index<md_dev[minor].nb_dev; index++)
+    {
+      if (current_ra>read_ahead[MAJOR(devices[minor][index].dev)])
+	current_ra=read_ahead[MAJOR(devices[minor][index].dev)];
+
+      devices[minor][index].fault_count=0;
+      devices[minor][index].invalid=VALID;
+    }
+
+    read_ahead[MD_MAJOR]=current_ra;
+
+    printk ("START_DEV md%x %s\n", minor, md_dev[minor].pers->name);
+    break;
+
+    case STOP_MD:
+    if (inode->i_count>1 || md_dev[minor].busy>1) /* ioctl : one open channel */
+    {
+      printk ("STOP_MD md%x failed : i_count=%d, busy=%d\n", minor, inode->i_count, md_dev[minor].busy);
+      return -EBUSY;
+    }
+
+    if (md_dev[minor].pers)
+    {
+      /*  The device won't exist anymore -> flush it now */
+      fsync_dev (inode->i_rdev);
+      invalidate_buffers (inode->i_rdev);
+      md_dev[minor].pers->stop (minor, md_dev+minor);
+    }
+
+    /* Remove locks. */
+    for (index=0; index<md_dev[minor].nb_dev; index++)
+      clear_inode (devices[minor][index].inode);
+
+    md_dev[minor].nb_dev=md_size[minor]=0;
+    md_dev[minor].pers=NULL;
+
+    set_ra ();			/* calculate new read_ahead */
+    
+    printk ("STOP_DEV md%x\n", minor);
+    break;
+
+#if defined(CONFIG_MD_SUPPORT_RAID1) || defined(CONFIG_MD_SUPPORT_RAID5)
+    case MD_INVALID:
+    dev=to_kdev_t ((dev_t) arg);
+    if (!(err=md_valid_device (minor, dev, INVALID_ALWAYS)))
+      printk ("md%d : %s disabled\n", minor, partition_name (dev));
+
+    return (err);
+
+    case MD_VALID:
+    dev=to_kdev_t ((dev_t) arg);
+    if (!(err=md_valid_device (minor, dev, VALID)))
+      printk ("md%d : %s enabled\n", minor, partition_name (dev));
+
+    return (err);
+#endif
+    
+    case BLKGETSIZE:   /* Return device size */
+    if  (!arg)  return -EINVAL;
+    err=verify_area (VERIFY_WRITE, (long *) arg, sizeof(long));
+    if (err)
+      return err;
+    put_user (md_hd_struct[MINOR(inode->i_rdev)].nr_sects, (long *) arg);
+    break;
+
+    case BLKFLSBUF:
+    fsync_dev (inode->i_rdev);
+    invalidate_buffers (inode->i_rdev);
+    break;
+
+    case BLKRASET:
+    if (arg > 0xff)
+      return -EINVAL;
+    read_ahead[MAJOR(inode->i_rdev)] = arg;
+    return 0;
+    
+    case BLKRAGET:
+    if  (!arg)  return -EINVAL;
+    err=verify_area (VERIFY_WRITE, (long *) arg, sizeof(long));
+    if (err)
+      return err;
+    put_user (read_ahead[MAJOR(inode->i_rdev)], (long *) arg);
+    break;
+
+    case HDIO_GETGEO:
+    if (!loc)  return -EINVAL;
+    err = verify_area(VERIFY_WRITE, loc, sizeof(*loc));
+    if (err)
+      return err;
+    put_user (2, (char *) &loc->heads);
+    put_user (4, (char *) &loc->sectors);
+    put_user (md_hd_struct[minor].nr_sects/8, (short *) &loc->cylinders);
+    put_user (md_hd_struct[MINOR(inode->i_rdev)].start_sect,
+		(long *) &loc->start);
+    break;
+    
+    RO_IOCTLS(inode->i_rdev,arg);
+    
+    default:
+    printk ("Unknown md_ioctl %d\n", cmd);
+    return -EINVAL;
+  }
+
+  return (0);
+}
+
+
+static int md_open (struct inode *inode, struct file *file)
+{
+  int minor=MINOR(inode->i_rdev);
+
+  md_dev[minor].busy++;
+  return (0);			/* Always succed */
+}
+
+
+static void md_release (struct inode *inode, struct file *file)
+{
+  int minor=MINOR(inode->i_rdev);
+
+  sync_dev (inode->i_rdev);
+  md_dev[minor].busy--;
+}
+
+
+static struct file_operations md_fops=
+{
+  NULL,
+  block_read,
+  block_write,
+  NULL,
+  NULL,
+  md_ioctl,
+  NULL,
+  md_open,
+  md_release,
+  block_fsync
+};
+
+
+static inline int remap_request (int minor, struct request *req)
+{
+  if (!md_dev[minor].pers)
+  {
+    printk ("Oops ! md%d not running, giving up !\n", minor);
+    return -1;
+  }
+
+  return (md_dev[minor].pers->map(minor, md_dev+minor, req));
+}
+
+static void do_md_request (void)
+{
+  int minor;
+  struct request *req;
+    
+  while (1)
+  {
+#ifdef MD_COUNT_SIZE
+    int reqsize, chunksize;
+#endif
+    
+    cli ();
+    req = blk_dev[MD_MAJOR].current_request;
+    if (!req || (req->rq_status == RQ_INACTIVE))
+    {
+      sti ();
+      return;
+    }
+    
+#ifdef MD_COUNT_SIZE
+    reqsize=req->nr_sectors>>1;
+    chunksize=1 << FACTOR_SHIFT(FACTOR(md_dev+MINOR(req->rq_dev)));
+    if (reqsize==chunksize) (md_dev+MINOR(req->rq_dev))->equal_count++;
+    if (reqsize<chunksize) (md_dev+MINOR(req->rq_dev))->smallest_count++;
+    if (reqsize>chunksize) (md_dev+MINOR(req->rq_dev))->biggest_count++;
+#endif
+    
+    blk_dev[MD_MAJOR].current_request = req->next;
+    sti ();
+
+    minor = MINOR(req->rq_dev);
+    if ((MAJOR(req->rq_dev) != MD_MAJOR) || (minor >= MAX_REAL))
+    {
+      printk("md: bad device number: 0x%04x\n", req->rq_dev);
+      end_request(0, req);
+      continue;
+    }
+
+    switch (remap_request (minor, req))
+    {
+      case REDIRECTED_BHREQ:	/* Allright, redirection was succesful */
+      req->rq_status=RQ_INACTIVE;
+      wake_up (&wait_for_request);
+      break;
+
+      case REDIRECTED_REQ:
+      break;			/* Redirected whole request (for swaping) */
+      
+      case REDIRECT_FAILED:	/* Swap redirection failed in RAID-[15] */
+      end_request (0, req);
+      break;
+      
+      default:
+      printk ("remap_request returned strange value !\n");
+    }
+  }
+}
+
+extern struct semaphore request_lock;
+
+void make_md_request (struct request *pending, int n)
+{
+  int i, j, max_req, major=0, rw, found;
+  kdev_t dev;
+  struct buffer_head *bh;
+  struct request *req;
+  
+  down (&request_lock);
+
+  for (i=0; i<n; i++)
+  {
+    if (!pending[i].bh)
+      continue;
+
+    cli();
+
+    found=0;
+    rw=pending[i].cmd;
+    bh=pending[i].bh;
+    major=MAJOR(dev=pending[i].rq_dev);
+    
+    max_req = (rw == READ) ? NR_REQUEST : ((NR_REQUEST*2)/3);
+ 
+    if ((   major == IDE0_MAJOR	/* same as HD_MAJOR */
+	 || major == IDE1_MAJOR
+	 || major == SCSI_DISK_MAJOR
+	 || major == IDE2_MAJOR
+	 || major == IDE3_MAJOR)
+	&& (req = blk_dev[major].current_request))
+    {
+#ifdef CONFIG_BLK_DEV_HD
+      if (major == HD_MAJOR)
+	req = req->next;
+#endif CONFIG_BLK_DEV_HD
+
+      while (req && !found)
+      {
+	if (req->rq_status!=RQ_INACTIVE && req->rq_status!=RQ_ACTIVE)
+	  printk ("Saw bad status request !\n");
+
+	if (req->rq_dev == dev &&
+	    !req->sem &&
+	    req->cmd == rw &&
+	    req->sector + req->nr_sectors == pending[i].sector &&
+	    (req->nr_sectors + pending[i].nr_sectors) < 245)
+	{
+	  req->bhtail->b_reqnext = bh;
+	  req->bhtail = pending[i].bhtail;
+	  req->nr_sectors += pending[i].nr_sectors;
+	  found=1;
+	  continue;
+	}
+	
+	if (!found &&
+	    req->rq_dev == dev &&
+	    !req->sem &&
+	    req->cmd == rw &&
+	    req->sector - pending[i].nr_sectors == pending[i].sector &&
+	    (req->nr_sectors + pending[i].nr_sectors) < 245)
+	{
+	  req->nr_sectors += pending[i].nr_sectors;
+	  bh->b_reqnext = req->bh;
+	  req->buffer = bh->b_data;
+	  req->current_nr_sectors = bh->b_size >> 9;
+	  req->sector = pending[i].sector;
+	  req->bh = bh;
+	  found=1;
+	  continue;
+	}    
+
+	req = req->next;
+      }
+    }
+
+    if (found)
+      continue;
+
+    up (&request_lock);
+    req=get_md_request (max_req, dev);
+    
+    /* Build it up... */
+    req->cmd = rw;
+    req->errors = 0;
+#if defined (CONFIG_MD_SUPPORT_RAID1)
+    req->shared_count = 0;
+#endif
+    req->sector = pending[i].sector;
+    req->nr_sectors = pending[i].nr_sectors;
+    req->current_nr_sectors = bh->b_size >> 9;
+    req->buffer = bh->b_data;
+    req->sem = NULL;
+    req->bh = bh;
+    req->bhtail = pending[i].bhtail;
+    req->next = NULL;
+
+    add_request (blk_dev + MAJOR(dev), req);
+    down (&request_lock);
+  }
+
+  up (&request_lock);
+  for (j=0; j<n; j++)
+  {
+    if (!pending[j].bh)
+      continue;
+    
+    pending[j].bh=NULL;
+  }
+
+  sti ();
+}
+
+
+static struct symbol_table md_symbol_table=
+{
+#include <linux/symtab_begin.h>
+
+  X(devices),
+  X(md_size),
+  X(add_request),
+  X(make_md_request),
+
+#ifdef CONFIG_MD_SUPPORT_RAID1
+  X(support_for_raid1),
+#endif
+
+#ifdef CONFIG_MD_SUPPORT_RAID5
+  X(support_for_raid5),
+#endif
+
+  X(register_md_personality),
+  X(unregister_md_personality),
+  X(partition_name),
+
+#if defined(CONFIG_MD_SUPPORT_RAID1) || defined(CONFIG_MD_SUPPORT_RAID5)
+  X(md_valid_device),
+  X(md_can_reemit),
+#endif
+
+#include <linux/symtab_end.h>
+};
+
+
+static void md_geninit (struct gendisk *gdisk)
+{
+  int i;
+  
+  for(i=0;i<MAX_MD_DEV;i++)
+  {
+    md_blocksizes[i] = 1024;
+    md_gendisk.part[i].start_sect=-1;
+    md_dev[i].pers=NULL;
+#ifdef MD_COUNT_SIZES
+    md_dev[i].smallest_count=md_dev[i].biggest_count=md_dev[i].equal_count=0;
+#endif
+  }
+
+  blksize_size[MAJOR_NR] = md_blocksizes;
+  register_symtab (&md_symbol_table);
+
+  proc_register(&proc_root,
+		&(struct proc_dir_entry)
+	      {
+		PROC_MD, 6, "mdstat",
+		S_IFREG | S_IRUGO, 1, 0, 0,
+	      });
+}
+
+
+int get_md_status (char *page)
+{
+  int sz=0, i, j;
+
+  sz+=sprintf( page+sz, "Personalities : ");
+  for (i=0; i<MAX_PERSONALITY; i++)
+    if (pers[i])
+      sz+=sprintf (page+sz, "[%d %s] ", i, pers[i]->name);
+
+  page[sz-1]='\n';
+
+  sz+=sprintf (page+sz, "read_ahead ");
+  if (read_ahead[MD_MAJOR]==INT_MAX)
+    sz+=sprintf (page+sz, "not set\n");
+  else
+    sz+=sprintf (page+sz, "%d sectors\n", read_ahead[MD_MAJOR]);
+  
+  for (i=0; i<MAX_MD_DEV; i++)
+  {
+    sz+=sprintf (page+sz, "md%d : %sactive", i, md_dev[i].pers ? "" : "in");
+
+    if (md_dev[i].pers)
+      sz+=sprintf (page+sz, " %s", md_dev[i].pers->name);
+
+    for (j=0; j<md_dev[i].nb_dev; j++)
+      sz+=sprintf (page+sz, " %s%s%s",
+		   (devices[i][j].invalid==VALID) ? "" : "(",
+		   partition_name(devices[i][j].dev),
+		   (devices[i][j].invalid==VALID) ? "" : ")");
+    
+    if (md_dev[i].nb_dev)
+      sz+=sprintf (page+sz, " %d blocks", md_size[i]);
+
+    if (!md_dev[i].pers)
+    {
+      sz+=sprintf (page+sz, "\n");
+      continue;
+    }
+
+    if (md_dev[i].pers->max_invalid_dev)
+      sz+=sprintf (page+sz, " maxfault=%ld", MAX_FAULT(md_dev+i));
+
+    if (md_dev[i].pers != pers[(LINEAR>>PERSONALITY_SHIFT)])
+    {
+      sz+=sprintf (page+sz, " %dk chunks", 1<<FACTOR_SHIFT(FACTOR(md_dev+i)));
+#ifdef MD_COUNT_SIZES
+      sz+=sprintf (page+sz, " (%d/%d/%d)",
+		   md_dev[i].smallest_count,
+		   md_dev[i].equal_count,
+		   md_dev[i].biggest_count);
+#endif
+    }
+    sz+=sprintf (page+sz, "\n");
+    sz+=md_dev[i].pers->status (page+sz, i, md_dev+i);
+  }
+  
+  return (sz);
+}
+
+#if defined(CONFIG_MD_SUPPORT_RAID1) || defined(CONFIG_MD_SUPPORT_RAID5)
+
+int md_valid_device (int minor, kdev_t dev, int mode)
+{
+  int i;
+
+  for (i=0; i<md_dev[minor].nb_dev; i++)
+    if (devices[minor][i].dev==dev)
+      break;
+
+  if (i>md_dev[minor].nb_dev)
+  {
+    printk ("Oops, dev %04x not found in md_valid_device\n", dev);
+    return -EINVAL;
+  }
+
+  switch (mode)
+  {
+    case VALID:
+    /* Don't consider INVALID_NEXT as a real invalidation.
+       Maybe that's not the good way to treat such a thing,
+       we'll see. */
+    if (devices[minor][i].invalid==INVALID_ALWAYS)
+    {
+      devices[minor][i].fault_count=0; /* reset fault count */
+      if (md_dev[minor].invalid_dev_count)
+	md_dev[minor].invalid_dev_count--;
+    }
+    break;
+
+    case INVALID:
+    if (devices[minor][i].invalid != VALID )
+      return 0;			/* Don't invalidate twice */
+    
+    if (++devices[minor][i].fault_count > MAX_FAULT(md_dev+minor) &&
+	MAX_FAULT(md_dev+minor)!=0xFF)
+    {
+      /* We cannot tolerate this fault.
+	 So sing a song, and say GoodBye to this device... */
+      
+      mode=INVALID_ALWAYS;
+      md_dev[minor].invalid_dev_count++;
+    }
+    else
+      /* FIXME :
+	 If we reached the max_invalid_dev count, doing one
+	 more invalidation will kill the md_dev. So we choose
+	 not to invalid the physical dev in such a case. But
+	 next access will probably fail... */
+      if (md_dev[minor].invalid_dev_count<=md_dev[minor].pers->max_invalid_dev)
+	mode=INVALID_NEXT;
+      else
+	mode=VALID;
+    break;
+
+    case INVALID_ALWAYS:	/* Only used via MD_INVALID ioctl */
+    md_dev[minor].invalid_dev_count++;
+  }
+  
+  devices[minor][i].invalid=mode;
+  return 0;
+}
+
+
+int md_can_reemit (int minor)
+{
+  /* FIXME :
+     If the device is raid-1 (md_dev[minor].pers->max_invalid_dev=-1),
+     always pretend that we can reemit the request.
+     Problem : if the 2 devices in the pair are dead, will loop
+     forever. Maybe having a per-personality can_reemit function would
+     help. */
+
+  if (!md_dev[minor].pers)
+    return (0);
+  
+  return(md_dev[minor].pers->max_invalid_dev &&
+	 ((md_dev[minor].pers->max_invalid_dev==-1) ?
+	 1 :
+	 md_dev[minor].invalid_dev_count<=md_dev[minor].pers->max_invalid_dev));
+}
+
+#endif
+
+int register_md_personality (int p_num, struct md_personality *p)
+{
+  int i=(p_num >> PERSONALITY_SHIFT);
+
+  if (i >= MAX_PERSONALITY)
+    return -EINVAL;
+
+  if (pers[i])
+    return -EBUSY;
+  
+  pers[i]=p;
+  printk ("%s personality registered\n", p->name);
+  return 0;
+}
+
+int unregister_md_personality (int p_num)
+{
+  int i=(p_num >> PERSONALITY_SHIFT);
+
+  if (i >= MAX_PERSONALITY)
+    return -EINVAL;
+
+  printk ("%s personality unregistered\n", pers[i]->name);
+  pers[i]=NULL;
+  return 0;
+} 
+
+void linear_init (void);
+void raid0_init (void);
+void raid1_init (void);
+void raid5_init (void);
+
+int md_init (void)
+{
+  printk ("md driver %s MAX_MD_DEV=%d, MAX_REAL=%d\n", MD_VERSION, MAX_MD_DEV, MAX_REAL);
+
+  if (register_blkdev (MD_MAJOR, "md", &md_fops))
+  {
+    printk ("Unable to get major %d for md\n", MD_MAJOR);
+    return (-1);
+  }
+
+  blk_dev[MD_MAJOR].request_fn=DEVICE_REQUEST;
+  blk_dev[MD_MAJOR].current_request=NULL;
+  read_ahead[MD_MAJOR]=INT_MAX;
+  md_gendisk.next=gendisk_head;
+
+  gendisk_head=&md_gendisk;
+
+#ifdef CONFIG_MD_LINEAR
+  linear_init ();
+#endif
+#ifdef CONFIG_MD_STRIPED
+  raid0_init ();
+#endif
+#ifdef CONFIG_MD_RAID1
+  raid1_init ();
+#endif
+#ifdef CONFIG_MD_RAID5
+  raid5_init ();
+#endif
+  
+  return (0);
+}
diff -u --recursive --new-file v1.3.68/linux/drivers/block/raid0.c linux/drivers/block/raid0.c
--- v1.3.68/linux/drivers/block/raid0.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/block/raid0.c	Mon Feb 26 13:51:45 1996
@@ -0,0 +1,341 @@
+
+/*
+   raid0.c : Multiple Devices driver for Linux
+             Copyright (C) 1994-96 Marc ZYNGIER
+	     <zyngier@ufr-info-p7.ibp.fr> or
+	     <maz@gloups.fdn.fr>
+
+   RAID-0 management functions.
+
+   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.
+   
+   You should have received a copy of the GNU General Public License
+   (for example /usr/src/linux/COPYING); if not, write to the Free
+   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
+*/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/md.h>
+#include <linux/raid0.h>
+#include <linux/malloc.h>
+
+#define MAJOR_NR MD_MAJOR
+#define MD_DRIVER
+#define MD_PERSONALITY
+
+#include <linux/blk.h>
+
+static void create_strip_zones (int minor, struct md_dev *mddev)
+{
+  int i, j, c=0;
+  int current_offset=0;
+  struct real_dev *smallest_by_zone;
+  struct raid0_data *data=(struct raid0_data *) mddev->private;
+  
+  data->nr_strip_zones=1;
+  
+  for (i=1; i<mddev->nb_dev; i++)
+  {
+    for (j=0; j<i; j++)
+      if (devices[minor][i].size==devices[minor][j].size)
+      {
+	c=1;
+	break;
+      }
+
+    if (!c)
+      data->nr_strip_zones++;
+
+    c=0;
+  }
+
+  data->strip_zone=kmalloc (sizeof(struct strip_zone)*data->nr_strip_zones,
+			      GFP_KERNEL);
+
+  data->smallest=NULL;
+  
+  for (i=0; i<data->nr_strip_zones; i++)
+  {
+    data->strip_zone[i].dev_offset=current_offset;
+    smallest_by_zone=NULL;
+    c=0;
+
+    for (j=0; j<mddev->nb_dev; j++)
+      if (devices[minor][j].size>current_offset)
+      {
+	data->strip_zone[i].dev[c++]=devices[minor]+j;
+	if (!smallest_by_zone ||
+	    smallest_by_zone->size > devices[minor][j].size)
+	  smallest_by_zone=devices[minor]+j;
+      }
+
+    data->strip_zone[i].nb_dev=c;
+    data->strip_zone[i].size=(smallest_by_zone->size-current_offset)*c;
+
+    if (!data->smallest ||
+	data->smallest->size > data->strip_zone[i].size)
+      data->smallest=data->strip_zone+i;
+
+    data->strip_zone[i].zone_offset=i ? (data->strip_zone[i-1].zone_offset+
+					   data->strip_zone[i-1].size) : 0;
+    current_offset=smallest_by_zone->size;
+  }
+}
+
+static int raid0_run (int minor, struct md_dev *mddev)
+{
+  int cur=0, i=0, size, zone0_size, nb_zone, min;
+  struct raid0_data *data;
+
+  min=1 << FACTOR_SHIFT(FACTOR(mddev));
+
+  for (i=0; i<mddev->nb_dev; i++)
+    if (devices[minor][i].size<min)
+    {
+      printk ("Cannot use %dk chunks on dev %s\n", min,
+	      partition_name (devices[minor][i].dev));
+      return -EINVAL;
+    }
+  
+  MOD_INC_USE_COUNT;
+  
+  /* Resize devices according to the factor */
+  md_size[minor]=0;
+  
+  for (i=0; i<mddev->nb_dev; i++)
+  {
+    devices[minor][i].size &= ~((1 << FACTOR_SHIFT(FACTOR(mddev))) - 1);
+    md_size[minor] += devices[minor][i].size;
+  }
+
+  mddev->private=kmalloc (sizeof (struct raid0_data), GFP_KERNEL);
+  data=(struct raid0_data *) mddev->private;
+  
+  create_strip_zones (minor, mddev);
+
+  nb_zone=data->nr_zones=
+    md_size[minor]/data->smallest->size +
+    (md_size[minor]%data->smallest->size ? 1 : 0);
+  
+  data->hash_table=kmalloc (sizeof (struct raid0_hash)*nb_zone, GFP_KERNEL);
+
+  size=data->strip_zone[cur].size;
+
+  i=0;
+  while (cur<data->nr_strip_zones)
+  {
+    data->hash_table[i].zone0=data->strip_zone+cur;
+
+    if (size>=data->smallest->size)/* If we completly fill the slot */
+    {
+      data->hash_table[i++].zone1=NULL;
+      size-=data->smallest->size;
+
+      if (!size)
+      {
+	if (++cur==data->nr_strip_zones) continue;
+	size=data->strip_zone[cur].size;
+      }
+
+      continue;
+    }
+
+    if (++cur==data->nr_strip_zones) /* Last dev, set unit1 as NULL */
+    {
+      data->hash_table[i].zone1=NULL;
+      continue;
+    }
+
+    zone0_size=size;		/* Here, we use a 2nd dev to fill the slot */
+    size=data->strip_zone[cur].size;
+    data->hash_table[i++].zone1=data->strip_zone+cur;
+    size-=(data->smallest->size - zone0_size);
+  }
+
+  return (0);
+}
+
+
+static int raid0_stop (int minor, struct md_dev *mddev)
+{
+  struct raid0_data *data=(struct raid0_data *) mddev->private;
+
+  kfree (data->hash_table);
+  kfree (data->strip_zone);
+  kfree (data);
+
+  MOD_DEC_USE_COUNT;
+  return 0;
+}
+
+/*
+ * FIXME - We assume some things here :
+ * - requested buffers NEVER bigger than chunk size,
+ * - requested buffers NEVER cross stripes limits.
+ * Of course, those facts may not be valid anymore (and surely won't...)
+ * Hey guys, there's some work out there ;-)
+ */
+static int raid0_map (int minor, struct md_dev *mddev, struct request *req)
+{
+  struct raid0_data *data=(struct raid0_data *) mddev->private;
+  static struct raid0_hash *hash;
+  struct strip_zone *zone;
+  struct real_dev *tmp_dev;
+  int i, queue, blk_in_chunk, factor, chunk;
+  long block, rblock;
+  struct buffer_head *bh;
+  static struct request pending[MAX_REAL]={{0, }, };
+
+  factor=FACTOR(mddev);
+
+  while (req->bh || req->sem)
+  {
+    block=req->sector >> 1;
+    hash=data->hash_table+(block/data->smallest->size);
+    
+    if (block >= (hash->zone0->size +
+		  hash->zone0->zone_offset))
+    {
+      if (!hash->zone1)
+	printk ("raid0_map : hash->zone1==NULL for block %ld\n", block);
+      zone=hash->zone1;
+    }
+    else
+      zone=hash->zone0;
+    
+    blk_in_chunk=block & ((1UL << FACTOR_SHIFT(factor)) - 1);
+    chunk=(block - zone->zone_offset) / (zone->nb_dev<<FACTOR_SHIFT(factor));
+    tmp_dev=zone->dev[(block >> FACTOR_SHIFT(factor)) % zone->nb_dev];
+    rblock=(chunk << FACTOR_SHIFT(factor)) + blk_in_chunk + zone->dev_offset;
+
+    if (req->sem)		/* This is a paging request */
+    {
+      req->rq_dev=tmp_dev->dev;
+      req->sector=rblock << 1;
+      add_request (blk_dev+MAJOR (tmp_dev->dev), req);
+
+      return REDIRECTED_REQ;
+    }
+
+    queue=tmp_dev - devices[minor];
+    
+				/* This is a buffer request */
+    for (i=blk_in_chunk;
+	 i<(1UL << FACTOR_SHIFT(factor)) && req->bh;
+	 i+=bh->b_size >> 10)
+    {
+      bh=req->bh;
+      if (!buffer_locked(bh))
+	printk("md%d: block %ld not locked\n", minor, bh->b_blocknr);
+
+      bh->b_rdev=tmp_dev->dev;
+#if defined (CONFIG_MD_SUPPORT_RAID1)
+      bh->b_reqshared=NULL;
+      bh->b_sister_req=NULL;
+#endif
+      
+      if (!pending[queue].bh)
+      {
+	pending[queue].rq_dev=tmp_dev->dev;
+	pending[queue].bhtail=pending[queue].bh=bh;
+	pending[queue].sector=rblock << 1;
+	pending[queue].cmd=req->cmd;
+	pending[queue].current_nr_sectors=
+	  pending[queue].nr_sectors=bh->b_size >> 9;
+      }
+      else
+      {
+	pending[queue].bhtail->b_reqnext=bh;
+	pending[queue].bhtail=bh;
+	pending[queue].nr_sectors+=bh->b_size >> 9;
+      }
+
+      end_redirect (req);	/* Separate bh from the request */
+    }
+  }
+  
+  req->rq_status=RQ_INACTIVE;
+  wake_up (&wait_for_request);
+  make_md_request (pending, mddev->nb_dev);
+  return REDIRECTED_REQ;	/* Since we already set the request free */
+}
+
+
+static int raid0_status (char *page, int minor, struct md_dev *mddev)
+{
+  int sz=0;
+#undef MD_DEBUG
+#ifdef MD_DEBUG
+  int j, k;
+  struct raid0_data *data=(struct raid0_data *) mddev->private;
+  
+  sz+=sprintf (page+sz, "      ");
+  for (j=0; j<data->nr_zones; j++)
+  {
+    sz+=sprintf (page+sz, "[z%d",
+		 data->hash_table[j].zone0-data->strip_zone);
+    if (data->hash_table[j].zone1)
+      sz+=sprintf (page+sz, "/z%d] ",
+		   data->hash_table[j].zone1-data->strip_zone);
+    else
+      sz+=sprintf (page+sz, "] ");
+  }
+  
+  sz+=sprintf (page+sz, "\n");
+  
+  for (j=0; j<data->nr_strip_zones; j++)
+  {
+    sz+=sprintf (page+sz, "      z%d=[", j);
+    for (k=0; k<data->strip_zone[j].nb_dev; k++)
+      sz+=sprintf (page+sz, "%s/",
+		   partition_name(data->strip_zone[j].dev[k]->dev));
+    sz--;
+    sz+=sprintf (page+sz, "] zo=%d do=%d s=%d\n",
+		 data->strip_zone[j].zone_offset,
+		 data->strip_zone[j].dev_offset,
+		 data->strip_zone[j].size);
+  }
+#endif
+  return sz;
+}
+
+
+static struct md_personality raid0_personality=
+{
+  "raid0",
+  raid0_map,
+  raid0_run,
+  raid0_stop,
+  raid0_status,
+  NULL,				/* no ioctls */
+  0
+};
+
+
+#ifndef MODULE
+
+void raid0_init (void)
+{
+  register_md_personality (RAID0, &raid0_personality);
+}
+
+#else
+
+int init_module (void)
+{
+  return (register_md_personality (RAID0, &raid0_personality));
+}
+
+void cleanup_module (void)
+{
+  if (MOD_IN_USE)
+    printk ("md raid0 : module still busy...\n");
+  else
+    unregister_md_personality (RAID0);
+}
+
+#endif
diff -u --recursive --new-file v1.3.68/linux/drivers/cdrom/optcd.c linux/drivers/cdrom/optcd.c
--- v1.3.68/linux/drivers/cdrom/optcd.c	Wed Feb 14 14:37:09 1996
+++ linux/drivers/cdrom/optcd.c	Fri Feb 23 09:39:34 1996
@@ -1,5 +1,5 @@
 /*	linux/drivers/cdrom/optcd.c - Optics Storage 8000 AT CDROM driver
-	$Id: optcd.c,v 1.20 1996/01/17 19:44:39 root Exp root $
+	$Id: optcd.c,v 1.29 1996/02/22 22:38:30 root Exp $
 
 	Copyright (C) 1995 Leo Spiekman (spiekman@dutette.et.tudelft.nl)
 
@@ -536,13 +536,15 @@
 
 
 /* Linear block address to minute, second, frame form */
+#define CD_FPM	(CD_SECS * CD_FRAMES)	/* frames per minute */
+
 static void lba2msf(int lba, struct cdrom_msf *msf)
 {
 	DEBUG((DEBUG_CONV, "lba2msf %d", lba));
 	lba += CD_MSF_OFFSET;
-	msf->cdmsf_min0 = lba / 4500; lba %= 4500;
-	msf->cdmsf_sec0 = lba / 75;
-	msf->cdmsf_frame0 = lba % 75;
+	msf->cdmsf_min0 = lba / CD_FPM; lba %= CD_FPM;
+	msf->cdmsf_sec0 = lba / CD_FRAMES;
+	msf->cdmsf_frame0 = lba % CD_FRAMES;
 	msf->cdmsf_min1 = 0;
 	msf->cdmsf_sec1 = 0;
 	msf->cdmsf_frame1 = 0;
@@ -558,27 +560,17 @@
 }
 
 
-union cd_addr {
-	struct {
-		u_char  minute;
-		u_char  second;
-		u_char  frame;
-	} msf;
-	int     lba;
-};
-
-
-static void msf2lba(union cd_addr *addr)
+static void msf2lba(union cdrom_addr *addr)
 {
-	addr->lba = addr->msf.minute * 4500
-	            + addr->msf.second * 75
+	addr->lba = addr->msf.minute * CD_FPM
+	            + addr->msf.second * CD_FRAMES
 	            + addr->msf.frame - CD_MSF_OFFSET;
 }
 
 
 /* Minute, second, frame address BCD to binary or to linear address,
    depending on MODE */
-static void msf_bcd2bin(union cd_addr *addr)
+static void msf_bcd2bin(union cdrom_addr *addr)
 {
 	addr->msf.minute = bcd2bin(addr->msf.minute);
 	addr->msf.second = bcd2bin(addr->msf.second);
@@ -590,6 +582,7 @@
 
 static int audio_status = CDROM_AUDIO_NO_STATUS;
 static char toc_uptodate = 0;
+static char disk_changed = 1;
 
 /* Get drive status, flagging completion of audio play and disk changes. */
 static int drive_status(void)
@@ -610,6 +603,7 @@
 
 	if (status & ST_DSK_CHG) {
 		toc_uptodate = 0;
+		disk_changed = 1;
 		audio_status = CDROM_AUDIO_NO_STATUS;
 	}
 
@@ -687,11 +681,11 @@
 	DEBUG((DEBUG_TOC, "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x",
 		d1, d2, d3, d4, d5, d6, d7, d8, d9, d10));
 
-	msf_bcd2bin((union cd_addr *)/*%%*/&qp->cdsc_absaddr);
-	msf_bcd2bin((union cd_addr *)/*%%*/&qp->cdsc_reladdr);
+	msf_bcd2bin(&qp->cdsc_absaddr);
+	msf_bcd2bin(&qp->cdsc_reladdr);
 	if (qp->cdsc_format == CDROM_LBA) {
-		msf2lba((union cd_addr *)/*%%*/&qp->cdsc_absaddr);
-		msf2lba((union cd_addr *)/*%%*/&qp->cdsc_reladdr);
+		msf2lba(&qp->cdsc_absaddr);
+		msf2lba(&qp->cdsc_reladdr);
 	}
 
 	return 0;
@@ -705,24 +699,18 @@
 #define ERR_TOC_MISSINGENTRY	0x121
 
 
-struct msf {
-	u_char  minute;
-	u_char  second;
-	u_char  frame;
-};
-
 struct cdrom_disk_info {
-	unsigned char	first;
-	unsigned char	last;
-	struct msf	disk_length;
-	struct msf	first_track;
+	unsigned char		first;
+	unsigned char		last;
+	struct cdrom_msf0	disk_length;
+	struct cdrom_msf0	first_track;
 	/* Multisession info: */
-	unsigned char	next;
-	struct msf	next_session;
-	struct msf	last_session;
-	unsigned char	multi;
-	unsigned char	xa;
-	unsigned char	audio;
+	unsigned char		next;
+	struct cdrom_msf0	next_session;
+	struct cdrom_msf0	last_session;
+	unsigned char		multi;
+	unsigned char		xa;
+	unsigned char		audio;
 };
 static struct cdrom_disk_info disk_info;
 
@@ -980,15 +968,10 @@
 	 && CURRENT -> cmd == READ && CURRENT -> sector != -1)
 
 
-#define BLOCKSIZE	2048
-#define BLOCKSIZE_RAW	2336
-#define BLOCKSIZE_ALL	2646
-#define N_BUFS		16
+/* Buffers for block size conversion. */
 #define NOBUF		-1
 
-
-/* Buffer for block size conversion. */
-static char buf[BLOCKSIZE * N_BUFS];
+static char buf[CD_FRAMESIZE * N_BUFS];
 static volatile int buf_bn[N_BUFS], next_bn;
 static volatile int buf_in = 0, buf_out = NOBUF;
 
@@ -1066,13 +1049,28 @@
 #endif
 
 
+/* Used as mutex to keep do_optcd_request (and other processes calling
+   ioctl) out while some process is inside a VFS call.
+   Reverse is accomplished by checking if state = S_IDLE upon entry
+   of opt_ioctl and opt_media_change. */
+static int in_vfs = 0;
+
+
 static volatile int transfer_is_active = 0;
 static volatile int error = 0;	/* %% do something with this?? */
 static int tries;		/* ibid?? */
+static int timeout = 0;
+
+static struct timer_list req_timer = {NULL, NULL, 0, 0, NULL};
+
+#define SET_REQ_TIMER(func, jifs) \
+	req_timer.expires = jiffies+(jifs); \
+	req_timer.function = (void *) (func); \
+	add_timer(&req_timer);
+#define CLEAR_REQ_TIMER	del_timer(&req_timer)
 
 static void poll(void)
 {
-	static int timeout;
 	static volatile int read_count = 1;
 	int flags;
 	int loop_again = 1;
@@ -1117,8 +1115,14 @@
 		case S_IDLE:
 			return;
 		case S_START:
-			if (send_cmd(COMDRVST))
+			if (in_vfs)
+				break;
+			if (send_cmd(COMDRVST)) {
+				state = S_IDLE;
+				while (CURRENT_VALID)
+					end_request(0);
 				return;
+			}
 			state = S_READ;
 			timeout = READ_TIMEOUT;
 			break;
@@ -1136,14 +1140,10 @@
 			skip = 0;
 			if ((status & ST_DOOR_OPEN) || (status & ST_DRVERR)) {
 				toc_uptodate = 0;
+				opt_invalidate_buffers();
 				printk((status & ST_DOOR_OPEN)
 				       ? "optcd: door open\n"
 				       : "optcd: disk removed\n");
-				if (transfer_is_active) {
-					state = S_START;
-					loop_again = 1;
-					break;
-				}
 				state = S_IDLE;
 				while (CURRENT_VALID)
 					end_request(0);
@@ -1237,8 +1237,8 @@
 						break;
 					}
 					fetch_data(buf+
-					    BLOCKSIZE*buf_in,
-					    BLOCKSIZE);
+					    CD_FRAMESIZE*buf_in,
+					    CD_FRAMESIZE);
 					read_count--;
 
 					DEBUG((DEBUG_REQUEST,
@@ -1290,8 +1290,12 @@
 				printk("optcd: discard data=%x frames\n",
 					read_count);
 			flush_data();
-			if (send_cmd(COMDRVST))
+			if (send_cmd(COMDRVST)) {
+				state = S_IDLE;
+				while (CURRENT_VALID)
+					end_request(0);
 				return;
+			}
 			state = S_STOPPING;
 			timeout = STOP_TIMEOUT;
 			break;
@@ -1327,11 +1331,15 @@
 	if (!timeout--) {
 		printk("optcd: timeout in state %d\n", state);
 		state = S_STOP;
-		if (exec_cmd(COMSTOP) < 0)
+		if (exec_cmd(COMSTOP) < 0) {
+			state = S_IDLE;
+			while (CURRENT_VALID)
+				end_request(0);
 			return;
+		}
 	}
 
-	SET_TIMER(poll, HZ/100);
+	SET_REQ_TIMER(poll, HZ/100);
 }
 
 
@@ -1366,9 +1374,10 @@
 				}
 				/* Start state machine */
 				state = S_START;
+				timeout = READ_TIMEOUT;
 				tries = 5;
 				/* %% why not start right away?? */
-				SET_TIMER(poll, HZ/100);
+				SET_REQ_TIMER(poll, HZ/100);
 			}
 			break;
 		}
@@ -1536,7 +1545,7 @@
 	/* %% What should go into entry.cdte_datamode? */
 
 	if (entry.cdte_format == CDROM_LBA)
-		msf2lba((union cd_addr *)/*%%*/&entry.cdte_addr);
+		msf2lba(&entry.cdte_addr);
 	else if (entry.cdte_format != CDROM_MSF)
 		return -EINVAL;
 
@@ -1604,7 +1613,7 @@
 {
 	int status;
 	struct cdrom_msf msf;
-	char buf[BLOCKSIZE_ALL];
+	char buf[CD_FRAMESIZE_RAWER];
 
 	status = verify_area(VERIFY_READ, (void *) arg, sizeof msf);
 	if (status)
@@ -1674,7 +1683,7 @@
 	   && ms.addr_format != CDROM_MSF)
 		return -EINVAL;
 	if (ms.addr_format == CDROM_LBA)
-		msf2lba((union cd_addr *)/*%%*/&ms.addr);
+		msf2lba(&ms.addr);
 
 	ms.xa_flag = disk_info.xa;
 
@@ -1710,6 +1719,7 @@
 	}
 
 	toc_uptodate = 0;
+	disk_changed = 1;
 	opt_invalidate_buffers();
 	audio_status = CDROM_AUDIO_NO_STATUS;
 
@@ -1724,7 +1734,7 @@
 static int opt_ioctl(struct inode *ip, struct file *fp,
                      unsigned int cmd, unsigned long arg)
 {
-	int err;
+	int status, err, retval = 0;
 
 	DEBUG((DEBUG_VFS, "starting opt_ioctl"));
 
@@ -1734,86 +1744,118 @@
 	if (cmd == CDROMRESET)
 		return cdromreset();
 
-	if (state != S_IDLE)
+	/* is do_optcd_request or another ioctl busy? */
+	if (state != S_IDLE || in_vfs)
 		return -EBUSY;
 
-	err = drive_status();
-	if (err < 0) {
-		DEBUG((DEBUG_VFS, "drive_status: %02x", -err));
+	in_vfs = 1;
+
+	status = drive_status();
+	if (status < 0) {
+		DEBUG((DEBUG_VFS, "drive_status: %02x", -status));
+		in_vfs = 0;
 		return -EIO;
 	}
+
+	if (status & ST_DOOR_OPEN)
+		switch (cmd) {	/* Actions that can be taken with door open */
+		case CDROMCLOSETRAY:
+			/* We do this before trying to read the toc. */
+			err = exec_cmd(COMCLOSE);
+			if (err < 0) {
+				DEBUG((DEBUG_VFS,
+				       "exec_cmd COMCLOSE: %02x", -err));
+				in_vfs = 0;
+				return -EIO;
+			}
+			break;
+		default:	in_vfs = 0;
+				return -EBUSY;
+		}
+
 	err = update_toc();
 	if (err < 0) {
 		DEBUG((DEBUG_VFS, "update_toc: %02x", -err));
+		in_vfs = 0;
 		return -EIO;
 	}
 
 	DEBUG((DEBUG_VFS, "ioctl cmd 0x%x", cmd));
 
 	switch (cmd) {
-	case CDROMPAUSE:	return cdrompause();
-	case CDROMRESUME:	return cdromresume();
-	case CDROMPLAYMSF:	return cdromplaymsf(arg);
-	case CDROMPLAYTRKIND:	return cdromplaytrkind(arg);
-	case CDROMREADTOCHDR:	return cdromreadtochdr(arg);
-	case CDROMREADTOCENTRY:	return cdromreadtocentry(arg);
+	case CDROMPAUSE:	retval = cdrompause(); break;
+	case CDROMRESUME:	retval = cdromresume(); break;
+	case CDROMPLAYMSF:	retval = cdromplaymsf(arg); break;
+	case CDROMPLAYTRKIND:	retval = cdromplaytrkind(arg); break;
+	case CDROMREADTOCHDR:	retval = cdromreadtochdr(arg); break;
+	case CDROMREADTOCENTRY:	retval = cdromreadtocentry(arg); break;
 
 	case CDROMSTOP:		err = exec_cmd(COMSTOP);
 				if (err < 0) {
 					DEBUG((DEBUG_VFS,
 						"exec_cmd COMSTOP: %02x",
 						-err));
-					return -EIO;
-				}
-				audio_status = CDROM_AUDIO_NO_STATUS;
-				break;
-	case CDROMSTART:	err = exec_cmd(COMCLOSE);  /* What else? */
-				if (err < 0) {
-					DEBUG((DEBUG_VFS,
-						"exec_cmd COMCLOSE: %02x",
-						-err));
-					return -EIO;
-				}
+					retval = -EIO;
+				} else
+					audio_status = CDROM_AUDIO_NO_STATUS;
 				break;
+	case CDROMSTART:	break;	/* This is a no-op */
 	case CDROMEJECT:	err = exec_cmd(COMUNLOCK);
 				if (err < 0) {
 					DEBUG((DEBUG_VFS,
 						"exec_cmd COMUNLOCK: %02x",
 						-err));
-					return -EIO;
+					retval = -EIO;
+					break;
 				}
 				err = exec_cmd(COMOPEN);
 				if (err < 0) {
 					DEBUG((DEBUG_VFS,
 						"exec_cmd COMOPEN: %02x",
 						-err));
-					return -EIO;
+					retval = -EIO;
 				}
 				break;
 
-	case CDROMVOLCTRL:	return cdromvolctrl(arg);
-	case CDROMSUBCHNL:	return cdromsubchnl(arg);
+	case CDROMVOLCTRL:	retval = cdromvolctrl(arg); break;
+	case CDROMSUBCHNL:	retval = cdromsubchnl(arg); break;
+
+	/* The drive detects the mode and automatically delivers the
+	   correct 2048 bytes, so we don't need these IOCTLs */
+	case CDROMREADMODE2:	retval = -EINVAL; break;
+	case CDROMREADMODE1:	retval = -EINVAL; break;
+
+	/* Drive doesn't support reading audio */
+	case CDROMREADAUDIO:	retval = -EINVAL; break;
 
-	case CDROMREADAUDIO:	return -EINVAL; /* not implemented */
 	case CDROMEJECT_SW:	auto_eject = (char) arg;
 				break;
 
 #ifdef MULTISESSION
-	case CDROMMULTISESSION:	return cdrommultisession(arg);
+	case CDROMMULTISESSION:	retval = cdrommultisession(arg); break;
 #endif
 
-	case CDROM_GET_UPC:	return -EINVAL; /* not implemented */
-	case CDROMVOLREAD:	return -EINVAL; /* not implemented */
+	case CDROM_GET_UPC:	retval = -EINVAL; break; /* not implemented */
+	case CDROMVOLREAD:	retval = -EINVAL; break; /* not implemented */
 
 	case CDROMREADRAW:
-			return cdromread(arg, BLOCKSIZE_RAW, COMREADRAW);
+			/* this drive delivers 2340 bytes in raw mode */
+			retval = cdromread(arg, CD_FRAMESIZE_RAW1, COMREADRAW);
+			break;
 	case CDROMREADCOOKED:
-			return cdromread(arg, BLOCKSIZE, COMREAD);
-	case CDROMSEEK:		return cdromseek(arg);
-	case CDROMPLAYBLK:	return -EINVAL; /* not implemented */
-	default:		return -EINVAL;
+			retval = cdromread(arg, CD_FRAMESIZE, COMREAD);
+			break;
+	case CDROMREADALL:
+			retval = cdromread(arg, CD_FRAMESIZE_RAWER, COMREADALL);
+			break;
+
+	case CDROMSEEK:		retval = cdromseek(arg); break;
+	case CDROMPLAYBLK:	retval = -EINVAL; break; /* not implemented */
+	case CDROMCLOSETRAY:	break;	/* The action was taken earlier */
+	default:		retval = -EINVAL;
 	}
-	return 0;
+	in_vfs = 0;
+	return retval;
 }
 
 
@@ -1824,31 +1866,24 @@
 {
 	DEBUG((DEBUG_VFS, "starting opt_open"));
 
-	if (!open_count++ && state == S_IDLE) {
+	if (!open_count && state == S_IDLE) {
 		int status;
 
+		toc_uptodate = 0;
 		opt_invalidate_buffers();
+
+		status = exec_cmd(COMCLOSE);	/* close door */
+		if (status < 0) {
+			DEBUG((DEBUG_VFS, "exec_cmd COMCLOSE: %02x", -status));
+		}
+
 		status = drive_status();
 		if (status < 0) {
 			DEBUG((DEBUG_VFS, "drive_status: %02x", -status));
 			return -EIO;
 		}
 		DEBUG((DEBUG_VFS, "status: %02x", status));
-		if (status & ST_DOOR_OPEN) {
-			status = exec_cmd(COMCLOSE);	/* close door */
-			if (status < 0) {
-				DEBUG((DEBUG_VFS,
-					"exec_cmd COMCLOSE: %02x", -status));
-			}
-			status = drive_status();	/* try again */
-			if (status < 0) {
-				DEBUG((DEBUG_VFS,
-					"drive_status: %02x", -status));
-				return -EIO;
-			}
-			DEBUG((DEBUG_VFS, "status: %02x", status));
-		}
-		if (status & (ST_DOOR_OPEN|ST_DRVERR)) {
+		if ((status & ST_DOOR_OPEN) || (status & ST_DRVERR)) {
 			printk("optcd: no disk or door open\n");
 			return -EIO;
 		}
@@ -1859,8 +1894,14 @@
 		status = update_toc();	/* Read table of contents */
 		if (status < 0)	{
 			DEBUG((DEBUG_VFS, "update_toc: %02x", -status));
+	 		status = exec_cmd(COMUNLOCK);	/* Unlock door */
+			if (status < 0) {
+				DEBUG((DEBUG_VFS,
+				       "exec_cmd COMUNLOCK: %02x", -status));
+			}
 			return -EIO;
 		}
+		open_count++;
 	}
 	MOD_INC_USE_COUNT;
 
@@ -1880,6 +1921,7 @@
 		ip, ip -> i_rdev, fp));
 
 	if (!--open_count) {
+		toc_uptodate = 0;
 		opt_invalidate_buffers();
 		sync_dev(ip -> i_rdev);
 		invalidate_buffers(ip -> i_rdev);
@@ -1892,9 +1934,24 @@
 			DEBUG((DEBUG_VFS, "exec_cmd COMOPEN: %02x", -status));
 		}
 		CLEAR_TIMER;
+		CLEAR_REQ_TIMER;
 	}
 	MOD_DEC_USE_COUNT;
 }
+
+
+/* Check if disk has been changed */
+static int opt_media_change(kdev_t dev)
+{
+	DEBUG((DEBUG_VFS, "executing opt_media_change"));
+	DEBUG((DEBUG_VFS, "dev: 0x%x; disk_changed = %d\n", dev, disk_changed));
+
+	if (disk_changed) {
+		disk_changed = 0;
+		return 1;
+	}
+	return 0;
+}
 
 /* Driver initialisation */
 
@@ -1939,19 +1996,19 @@
 
 
 static struct file_operations opt_fops = {
-	NULL,		/* lseek - default */
-	block_read,	/* read - general block-dev read */
-	block_write,	/* write - general block-dev write */
-	NULL,		/* readdir - bad */
-	NULL,		/* select */
-	opt_ioctl,	/* ioctl */
-	NULL,		/* mmap */
-	opt_open,	/* open */
-	opt_release,	/* release */
-	NULL,		/* fsync */
-	NULL,		/* fasync */
-	NULL,		/* media change */
-	NULL		/* revalidate */
+	NULL,			/* lseek - default */
+	block_read,		/* read - general block-dev read */
+	block_write,		/* write - general block-dev write */
+	NULL,			/* readdir - bad */
+	NULL,			/* select */
+	opt_ioctl,		/* ioctl */
+	NULL,			/* mmap */
+	opt_open,		/* open */
+	opt_release,		/* release */
+	NULL,			/* fsync */
+	NULL,			/* fasync */
+	opt_media_change,	/* media change */
+	NULL			/* revalidate */
 };
 
 
diff -u --recursive --new-file v1.3.68/linux/drivers/char/Config.in linux/drivers/char/Config.in
--- v1.3.68/linux/drivers/char/Config.in	Fri Feb 23 13:54:34 1996
+++ linux/drivers/char/Config.in	Mon Feb 26 11:58:04 1996
@@ -4,6 +4,7 @@
 mainmenu_option next_comment
 comment 'Character devices'
 
+bool 'Standard/generic serial support' CONFIG_SERIAL
 tristate 'Cyclades async mux support' CONFIG_CYCLADES
 bool 'Stallion multiport serial support' CONFIG_STALDRV
 if [ "$CONFIG_STALDRV" = "y" ]; then
diff -u --recursive --new-file v1.3.68/linux/drivers/char/Makefile linux/drivers/char/Makefile
--- v1.3.68/linux/drivers/char/Makefile	Wed Feb  7 15:11:23 1996
+++ linux/drivers/char/Makefile	Mon Feb 26 11:58:04 1996
@@ -16,9 +16,13 @@
 
 L_TARGET := char.a
 M_OBJS   :=
-L_OBJS   := tty_io.o n_tty.o console.o keyboard.o serial.o \
+L_OBJS   := tty_io.o n_tty.o console.o keyboard.o \
 	tty_ioctl.o pty.o vt.o mem.o vc_screen.o random.o \
 	defkeymap.o consolemap.o selection.o
+
+ifeq ($(CONFIG_SERIAL),y)
+L_OBJS += serial.o
+endif
 
 ifeq ($(CONFIG_CYCLADES),y)
 L_OBJS += cyclades.o
diff -u --recursive --new-file v1.3.68/linux/drivers/char/mem.c linux/drivers/char/mem.c
--- v1.3.68/linux/drivers/char/mem.c	Wed Dec 27 09:12:13 1995
+++ linux/drivers/char/mem.c	Mon Feb 26 11:58:04 1996
@@ -25,6 +25,9 @@
 #ifdef CONFIG_SOUND
 void soundcard_init(void);
 #endif
+#ifdef CONFIG_ISDN
+void isdn_init(void);
+#endif
 
 static int read_ram(struct inode * inode, struct file * file, char * buf, int count)
 {
@@ -393,6 +396,9 @@
 #endif
 #if CONFIG_QIC02_TAPE
 	qic02_tape_init();
+#endif
+#if CONFIG_ISDN
+	isdn_init();
 #endif
 	return 0;
 }
diff -u --recursive --new-file v1.3.68/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- v1.3.68/linux/drivers/char/tty_io.c	Sat Feb 17 16:02:48 1996
+++ linux/drivers/char/tty_io.c	Mon Feb 26 11:58:04 1996
@@ -1823,7 +1823,9 @@
 	if (sizeof(struct tty_struct) > PAGE_SIZE)
 		panic("size of tty structure > PAGE_SIZE!");
 	kbd_init();
+#ifdef CONFIG_SERIAL
 	rs_init();
+#endif
 #ifdef CONFIG_SCC
 	scc_init();
 #endif
diff -u --recursive --new-file v1.3.68/linux/drivers/isdn/Config.in linux/drivers/isdn/Config.in
--- v1.3.68/linux/drivers/isdn/Config.in	Thu Jan  1 02:00:00 1970
+++ linux/drivers/isdn/Config.in	Mon Feb 26 11:58:04 1996
@@ -0,0 +1,12 @@
+#
+# ISDN device configuration
+#
+if [ "$CONFIG_INET" != "n" ]; then
+  bool 'Support synchronous PPP' CONFIG_ISDN_PPP
+  if [ "$CONFIG_ISDN_PPP" != "n" ]; then
+    bool 'Use VJ-compression with synchronous PPP' CONFIG_ISDN_PPP_VJ
+    bool 'Support generic MP (RFC 1717)' CONFIG_ISDN_MPP
+  fi
+fi
+dep_tristate 'Teles/NICCY1016PC/Creatix support' CONFIG_ISDN_DRV_TELES $CONFIG_ISDN
+dep_tristate 'ICN B1 and B2 support' CONFIG_ISDN_DRV_ICN $CONFIG_ISDN
diff -u --recursive --new-file v1.3.68/linux/drivers/isdn/Makefile linux/drivers/isdn/Makefile
--- v1.3.68/linux/drivers/isdn/Makefile	Thu Jan  1 02:00:00 1970
+++ linux/drivers/isdn/Makefile	Mon Feb 26 11:58:04 1996
@@ -0,0 +1,54 @@
+SUB_DIRS     :=
+MOD_SUB_DIRS :=
+ALL_SUB_DIRS := icn teles
+
+L_OBJS :=
+LX_OBJS :=
+M_OBJS :=
+MX_OBJS :=
+O_OBJS :=
+OX_OBJS :=
+L_TARGET :=
+O_TARGET :=
+
+ifeq ($(CONFIG_ISDN),y)
+  L_TARGET := isdn.a
+  L_OBJS += isdn_net.o isdn_tty.o isdn_cards.o
+  LX_OBJS += isdn_common.o
+  ifdef CONFIG_ISDN_PPP
+    L_OBJS += isdn_ppp.o
+  endif
+else
+  ifeq ($(CONFIG_ISDN),m)
+    M_OBJS += isdn.o
+    O_TARGET += isdn.o
+    O_OBJS += isdn_net.o isdn_tty.o
+    OX_OBJS += isdn_common.o
+    ifdef CONFIG_ISDN_PPP
+      O_OBJS += isdn_ppp.o
+    endif
+  endif
+endif
+
+ifeq ($(CONFIG_ISDN_DRV_TELES),y)
+  L_OBJS += teles/teles.o
+  SUB_DIRS += teles
+  MOD_SUB_DIRS += teles
+else
+  ifeq ($(CONFIG_ISDN_DRV_TELES),m)
+    MOD_SUB_DIRS += teles
+  endif
+endif
+
+ifeq ($(CONFIG_ISDN_DRV_ICN),y)
+  L_OBJS += icn/icn.o
+  SUB_DIRS += icn
+  MOD_SUB_DIRS += icn
+else
+  ifeq ($(CONFIG_ISDN_DRV_ICN),m)
+    MOD_SUB_DIRS += icn
+  endif
+endif
+
+include $(TOPDIR)/Rules.make
+
diff -u --recursive --new-file v1.3.68/linux/drivers/isdn/icn/Makefile linux/drivers/isdn/icn/Makefile
--- v1.3.68/linux/drivers/isdn/icn/Makefile	Thu Jan  1 02:00:00 1970
+++ linux/drivers/isdn/icn/Makefile	Mon Feb 26 11:58:04 1996
@@ -0,0 +1,11 @@
+L_OBJS :=
+M_OBJS :=
+
+ifeq ($(CONFIG_ISDN_DRV_ICN),y)
+  L_OBJS += icn.o
+else
+  M_OBJS += icn.o
+endif
+
+include $(TOPDIR)/Rules.make
+
diff -u --recursive --new-file v1.3.68/linux/drivers/isdn/icn/icn.c linux/drivers/isdn/icn/icn.c
--- v1.3.68/linux/drivers/isdn/icn/icn.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/isdn/icn/icn.c	Mon Feb 26 11:58:04 1996
@@ -0,0 +1,1503 @@
+/* $Id icn.c,v 1.15 1996/01/10 20:57:39 fritz Exp fritz $
+ *
+ * ISDN low-level module for the ICN active ISDN-Card.
+ *
+ * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * 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: icn.c,v $
+ * Revision 1.17  1996/02/11 02:39:04  fritz
+ * Increased Buffer for status-messages.
+ * Removed conditionals for HDLC-firmware.
+ *
+ * Revision 1.16  1996/01/22 05:01:55  fritz
+ * Revert to GPL.
+ *
+ * Revision 1.15  1996/01/10 20:57:39  fritz
+ * Bugfix: Loading firmware twice caused the device stop working.
+ *
+ * Revision 1.14  1995/12/18  18:23:37  fritz
+ * Support for ICN-2B Cards.
+ * Change for supporting user-setable service-octet.
+ *
+ * Revision 1.13  1995/10/29  21:41:07  fritz
+ * Added support for DriverId's, added Jan's patches for Kernelversions.
+ *
+ * Revision 1.12  1995/04/29  13:07:35  fritz
+ * Added support for new Euro-ISDN-firmware
+ *
+ * Revision 1.11  1995/04/23  13:40:45  fritz
+ * Added support for SPV's.
+ * Changed Dial-Command to support MSN's on DSS1-Lines.
+ *
+ * Revision 1.10  1995/03/25  23:23:24  fritz
+ * Changed configurable Ports, to allow settings for DIP-Switch Cardversions.
+ *
+ * Revision 1.9  1995/03/25  23:17:30  fritz
+ * Fixed race-condition in pollbchan_send
+ *
+ * Revision 1.8  1995/03/15  12:49:44  fritz
+ * Added support for SPV's
+ * Splitted pollbchan_work ifor calling send-routine directly
+ *
+ * Revision 1.7  1995/02/20  03:48:03  fritz
+ * Added support of new request_region-function.
+ * Minor bugfixes.
+ *
+ * Revision 1.6  1995/01/31  15:48:45  fritz
+ * Added Cause-Messages to be signaled to upper layers.
+ * Added Revision-Info on load.
+ *
+ * Revision 1.5  1995/01/29  23:34:59  fritz
+ * Added stopdriver() and appropriate calls.
+ * Changed printk-statements to support loglevels.
+ *
+ * Revision 1.4  1995/01/09  07:40:46  fritz
+ * Added GPL-Notice
+ *
+ * Revision 1.3  1995/01/04  05:15:18  fritz
+ * Added undocumented "bootload-finished"-command in download-code
+ * to satisfy some brain-damaged icn card-versions.
+ *
+ * Revision 1.2  1995/01/02  02:14:45  fritz
+ * Misc Bugfixes
+ *
+ * Revision 1.1  1994/12/14  17:56:06  fritz
+ * Initial revision
+ *
+ */
+
+#include "icn.h"
+
+
+
+/*
+ * Verbose bootcode- and protocol-downloading.
+ */
+#undef BOOT_DEBUG
+
+/*
+ * Verbose Shmem-Mapping.
+ */
+#undef MAP_DEBUG
+
+/* If defined, no bootcode- and protocol-downloading is supported and
+ * you must use an external loader
+ */
+#undef LOADEXTERN
+
+static char
+*revision = "$Revision: 1.17 $";
+
+static void icn_pollcard(unsigned long dummy);
+
+/* Try to allocate a new buffer, link it into queue. */
+static u_char *
+ icn_new_buf(pqueue ** queue, int length)
+{
+	pqueue *p;
+	pqueue *q;
+
+	if ((p = *queue)) {
+		while (p) {
+			q = p;
+			p = (pqueue *) p->next;
+		}
+		p = (pqueue *) kmalloc(sizeof(pqueue) + length, GFP_ATOMIC);
+		q->next = (u_char *) p;
+	} else
+		p = *queue = (pqueue *) kmalloc(sizeof(pqueue) + length, GFP_ATOMIC);
+	if (p) {
+		p->size = sizeof(pqueue) + length;
+		p->length = length;
+		p->next = NULL;
+		p->rptr = p->buffer;
+		return p->buffer;
+	} else {
+		return (u_char *) NULL;
+	}
+}
+
+#ifdef MODULE
+static void icn_free_queue(pqueue ** queue)
+{
+	pqueue *p;
+	pqueue *q;
+
+	p = *queue;
+	while (p) {
+		q = p;
+		p = (pqueue *) p->next;
+		kfree_s(q, q->size);
+	}
+	*queue = (pqueue *) 0;
+}
+#endif
+
+/* Put a value into a shift-register, highest bit first.
+ * Parameters:
+ *            port     = port for output (bit 0 is significant)
+ *            val      = value to be output
+ *            firstbit = Bit-Number of highest bit
+ *            bitcount = Number of bits to output
+ */
+static inline void icn_shiftout(unsigned short port,
+		     unsigned long val,
+		     int firstbit,
+		     int bitcount)
+{
+
+	register u_char s;
+	register u_char c;
+
+	for (s = firstbit, c = bitcount; c > 0; s--, c--)
+		OUTB_P((u_char) ((val >> s) & 1) ? 0xff : 0, port);
+}
+
+/*
+ * Map Cannel0 (Bank0/Bank8) or Channel1 (Bank4/Bank12)
+ */
+static inline void icn_map_channel(int channel)
+{
+	static u_char chan2bank[] =
+	{0, 4, 8, 12};
+
+#ifdef MAP_DEBUG
+	printk(KERN_DEBUG "icn_map_channel %d %d\n", dev->channel, channel);
+#endif
+	if (channel == dev->channel)
+		return;
+	OUTB_P(0, ICN_MAPRAM);	/* Disable RAM          */
+	icn_shiftout(ICN_BANK, chan2bank[channel], 3, 4);	/* Select Bank          */
+	OUTB_P(0xff, ICN_MAPRAM);	/* Enable RAM           */
+	dev->channel = channel;
+#ifdef MAP_DEBUG
+	printk(KERN_DEBUG "icn_map_channel done\n");
+#endif
+}
+
+static inline int icn_lock_channel(int channel)
+{
+	register int retval;
+	ulong flags;
+
+#ifdef MAP_DEBUG
+	printk(KERN_DEBUG "icn_lock_channel %d\n", channel);
+#endif
+	save_flags(flags);
+	cli();
+	if (dev->channel == channel) {
+		dev->chanlock++;
+		retval = 1;
+#ifdef MAP_DEBUG
+		printk(KERN_DEBUG "icn_lock_channel %d OK\n", channel);
+#endif
+	} else {
+		retval = 0;
+#ifdef MAP_DEBUG
+		printk(KERN_DEBUG "icn_lock_channel %d FAILED, dc=%d\n", channel, dev->channel);
+#endif
+	}
+	restore_flags(flags);
+	return retval;
+}
+
+static inline void icn_release_channel(void)
+{
+	ulong flags;
+
+#ifdef MAP_DEBUG
+	printk(KERN_DEBUG "icn_release_channel l=%d\n", dev->chanlock);
+#endif
+	save_flags(flags);
+	cli();
+	if (dev->chanlock)
+		dev->chanlock--;
+	restore_flags(flags);
+}
+
+static inline int icn_trymaplock_channel(int channel)
+{
+	ulong flags;
+
+	save_flags(flags);
+	cli();
+#ifdef MAP_DEBUG
+	printk(KERN_DEBUG "trymaplock c=%d dc=%d l=%d\n", channel, dev->channel,
+	       dev->chanlock);
+#endif
+	if ((!dev->chanlock) || (dev->channel == channel)) {
+		dev->chanlock++;
+		icn_map_channel(channel);
+		restore_flags(flags);
+#ifdef MAP_DEBUG
+		printk(KERN_DEBUG "trymaplock %d OK\n", channel);
+#endif
+		return 1;
+	}
+	restore_flags(flags);
+#ifdef MAP_DEBUG
+	printk(KERN_DEBUG "trymaplock %d FAILED\n", channel);
+#endif
+	return 0;
+}
+
+static inline void icn_maprelease_channel(int channel)
+{
+	ulong flags;
+
+	save_flags(flags);
+	cli();
+#ifdef MAP_DEBUG
+	printk(KERN_DEBUG "map_release c=%d l=%d\n", channel, dev->chanlock);
+#endif
+	if (dev->chanlock)
+		dev->chanlock--;
+	if (!dev->chanlock)
+		icn_map_channel(channel);
+	restore_flags(flags);
+}
+
+/* Get Data from the B-Channel, assemble fragmented packets and put them
+ * into receive-queue. Wake up any B-Channel-reading processes.
+ * This routine is called via timer-callback from pollbchan().
+ * It schedules itself while any B-Channel is open.
+ */
+
+#ifdef DEBUG_RCVCALLBACK
+static int max_pending[2] =
+{0, 0};
+#endif
+
+static void icn_pollbchan_receive(int channel, icn_dev * dev)
+{
+	int mch = channel + ((dev->secondhalf) ? 2 : 0);
+	int eflag;
+	int cnt;
+	int flags;
+#ifdef DEBUG_RCVCALLBACK
+	int rcv_pending1;
+	int rcv_pending2;
+	int akt_pending;
+#endif
+
+	if (icn_trymaplock_channel(mch)) {
+		while (rbavl) {
+			cnt = rbuf_l;
+			if ((dev->rcvidx[channel] + cnt) > 4000) {
+				printk(KERN_WARNING "icn: bogus packet on ch%d, dropping.\n",
+				       channel + 1);
+				dev->rcvidx[channel] = 0;
+				eflag = 0;
+			} else {
+				memcpy(&dev->rcvbuf[channel][dev->rcvidx[channel]], rbuf_d, cnt);
+				dev->rcvidx[channel] += cnt;
+				eflag = rbuf_f;
+			}
+			rbnext;
+			icn_maprelease_channel(mch & 2);
+			if (!eflag) {
+				save_flags(flags);
+				cli();
+#ifdef DEBUG_RCVCALLBACK
+				rcv_pending1 =
+				    (dev->shmem->data_control.ecnr > dev->shmem->data_control.ecns) ?
+				    0xf - dev->shmem->data_control.ecnr + dev->shmem->data_control.ecns :
+				    dev->shmem->data_control.ecns - dev->shmem->data_control.ecnr;
+#endif
+				dev->interface.rcvcallb(dev->myid, channel, dev->rcvbuf[channel],
+						   dev->rcvidx[channel]);
+				dev->rcvidx[channel] = 0;
+#ifdef DEBUG_RCVCALLBACK
+				rcv_pending2 =
+				    (dev->shmem->data_control.ecnr > dev->shmem->data_control.ecns) ?
+				    0xf - dev->shmem->data_control.ecnr + dev->shmem->data_control.ecns :
+				    dev->shmem->data_control.ecns - dev->shmem->data_control.ecnr;
+				akt_pending = rcv_pending2 - rcv_pending1;
+				if (akt_pending > max_pending[channel]) {
+					max_pending[channel] = akt_pending;
+					printk(KERN_DEBUG "ICN_DEBUG: pend: %d %d\n", max_pending[0], max_pending[1]);
+				}
+#endif
+				restore_flags(flags);
+			}
+			if (!icn_trymaplock_channel(mch))
+				break;
+		}
+		icn_maprelease_channel(mch & 2);
+	}
+}
+
+/* Send data-packet to B-Channel, split it up into fragments of
+ * ICN_FRAGSIZE length. If last fragment is sent out, signal
+ * success to upper layers via statcallb with ISDN_STAT_BSENT argument.
+ * This routine is called via timer-callback from pollbchan() or
+ * directly from sendbuf().
+ */
+
+static void icn_pollbchan_send(int channel, icn_dev * dev)
+{
+	int mch = channel + ((dev->secondhalf) ? 2 : 0);
+	int eflag = 0;
+	int cnt;
+	int left;
+	int flags;
+	pqueue *p;
+	isdn_ctrl cmd;
+
+	if (!dev->sndcount[channel])
+		return;
+	if (icn_trymaplock_channel(mch)) {
+		while (sbfree && dev->sndcount[channel]) {
+			left = dev->spqueue[channel]->length;
+			cnt =
+			    (sbuf_l =
+			     (left > ICN_FRAGSIZE) ? ((sbuf_f = 0xff), ICN_FRAGSIZE) : ((sbuf_f = 0), left));
+			memcpy(sbuf_d, dev->spqueue[channel]->rptr, cnt);
+			sbnext;	/* switch to next buffer        */
+			icn_maprelease_channel(mch & 2);
+			dev->spqueue[channel]->rptr += cnt;
+			eflag = ((dev->spqueue[channel]->length -= cnt) == 0);
+			save_flags(flags);
+			cli();
+			p = dev->spqueue[channel];
+			dev->sndcount[channel] -= cnt;
+			if (eflag)
+				dev->spqueue[channel] = (pqueue *) dev->spqueue[channel]->next;
+			restore_flags(flags);
+			if (eflag) {
+				kfree_s(p, p->size);
+				cmd.command = ISDN_STAT_BSENT;
+				cmd.driver = dev->myid;
+				cmd.arg = channel;
+				dev->interface.statcallb(&cmd);
+			}
+			if (!icn_trymaplock_channel(mch))
+				break;
+		}
+		icn_maprelease_channel(mch & 2);
+	}
+}
+
+/* Send/Receive Data to/from the B-Channel.
+ * This routine is called via timer-callback.
+ * It schedules itself while any B-Channel is open.
+ */
+
+static void icn_pollbchan(unsigned long dummy)
+{
+	unsigned long flags;
+
+	dev->flags |= ICN_FLAGS_RBTIMER;
+	if (dev->flags & ICN_FLAGS_B1ACTIVE) {
+		icn_pollbchan_receive(0, dev);
+		icn_pollbchan_send(0, dev);
+	}
+	if (dev->flags & ICN_FLAGS_B2ACTIVE) {
+		icn_pollbchan_receive(1, dev);
+		icn_pollbchan_send(1, dev);
+	}
+	if (dev->doubleS0) {
+		if (dev2->flags & ICN_FLAGS_B1ACTIVE) {
+			icn_pollbchan_receive(0, dev2);
+			icn_pollbchan_send(0, dev2);
+		}
+		if (dev2->flags & ICN_FLAGS_B2ACTIVE) {
+			icn_pollbchan_receive(1, dev2);
+			icn_pollbchan_send(1, dev2);
+		}
+	}
+	if (dev->flags & (ICN_FLAGS_B1ACTIVE | ICN_FLAGS_B2ACTIVE)) {
+		/* schedule b-channel polling again */
+		save_flags(flags);
+		cli();
+		del_timer(&dev->rb_timer);
+		dev->rb_timer.function = icn_pollbchan;
+		dev->rb_timer.expires = jiffies + ICN_TIMER_BCREAD;
+		add_timer(&dev->rb_timer);
+		restore_flags(flags);
+	} else
+		dev->flags &= ~ICN_FLAGS_RBTIMER;
+	if (dev->doubleS0) {
+		if (dev2->flags & (ICN_FLAGS_B1ACTIVE | ICN_FLAGS_B2ACTIVE)) {
+			/* schedule b-channel polling again */
+			save_flags(flags);
+			cli();
+			del_timer(&dev2->rb_timer);
+			dev2->rb_timer.function = icn_pollbchan;
+			dev2->rb_timer.expires = jiffies + ICN_TIMER_BCREAD;
+			add_timer(&dev2->rb_timer);
+			restore_flags(flags);
+		} else
+			dev2->flags &= ~ICN_FLAGS_RBTIMER;
+	}
+}
+
+static void icn_pollit(icn_dev * dev)
+{
+	int mch = dev->secondhalf ? 2 : 0;
+	int avail = 0;
+	int dflag = 0;
+	int left;
+	u_char c;
+	int ch;
+	int flags;
+	int i;
+	u_char *p;
+	isdn_ctrl cmd;
+
+	if (icn_trymaplock_channel(mch)) {
+		avail = msg_avail;
+		for (left = avail, i = msg_o; left > 0; i++, left--) {
+			c = dev->shmem->comm_buffers.iopc_buf[i & 0xff];
+			save_flags(flags);
+			cli();
+			*dev->msg_buf_write++ = (c == 0xff) ? '\n' : c;
+			/* No checks for buffer overflow for raw-status-device */
+			if (dev->msg_buf_write > dev->msg_buf_end)
+				dev->msg_buf_write = dev->msg_buf;
+			restore_flags(flags);
+			if (c == 0xff) {
+				dev->imsg[dev->iptr] = 0;
+				dev->iptr = 0;
+				if (dev->imsg[0] == '0' && dev->imsg[1] >= '0' &&
+				    dev->imsg[1] <= '2' && dev->imsg[2] == ';') {
+					ch = dev->imsg[1] - '0';
+					p = &dev->imsg[3];
+					if (!strncmp(p, "BCON_", 5)) {
+						switch (ch) {
+						case 1:
+							dev->flags |= ICN_FLAGS_B1ACTIVE;
+							break;
+						case 2:
+							dev->flags |= ICN_FLAGS_B2ACTIVE;
+							break;
+						}
+						cmd.command = ISDN_STAT_BCONN;
+						cmd.driver = dev->myid;
+						cmd.arg = ch - 1;
+						dev->interface.statcallb(&cmd);
+						continue;
+					}
+					if (!strncmp(p, "TEI OK", 6)) {
+						cmd.command = ISDN_STAT_RUN;
+						cmd.driver = dev->myid;
+						cmd.arg = ch - 1;
+						dev->interface.statcallb(&cmd);
+						continue;
+					}
+					if (!strncmp(p, "BDIS_", 5)) {
+						switch (ch) {
+						case 1:
+							dev->flags &= ~ICN_FLAGS_B1ACTIVE;
+							dflag |= 1;
+							break;
+						case 2:
+							dev->flags &= ~ICN_FLAGS_B2ACTIVE;
+							dflag |= 2;
+							break;
+						}
+						cmd.command = ISDN_STAT_BHUP;
+						cmd.arg = ch - 1;
+						cmd.driver = dev->myid;
+						dev->interface.statcallb(&cmd);
+						continue;
+					}
+					if (!strncmp(p, "DCON_", 5)) {
+						cmd.command = ISDN_STAT_DCONN;
+						cmd.arg = ch - 1;
+						cmd.driver = dev->myid;
+						dev->interface.statcallb(&cmd);
+						continue;
+					}
+					if (!strncmp(p, "DDIS_", 5)) {
+						cmd.command = ISDN_STAT_DHUP;
+						cmd.arg = ch - 1;
+						cmd.driver = dev->myid;
+						dev->interface.statcallb(&cmd);
+						continue;
+					}
+					if (!strncmp(p, "E_L1: ACT FAIL", 14)) {
+						cmd.command = ISDN_STAT_BHUP;
+						cmd.arg = 0;
+						cmd.driver = dev->myid;
+						dev->interface.statcallb(&cmd);
+						cmd.command = ISDN_STAT_DHUP;
+						cmd.arg = 0;
+						cmd.driver = dev->myid;
+						dev->interface.statcallb(&cmd);
+						continue;
+					}
+					if (!strncmp(p, "CIF", 3)) {
+						cmd.command = ISDN_STAT_CINF;
+						cmd.arg = ch - 1;
+						strncpy(cmd.num, p + 3, sizeof(cmd.num) - 1);
+						cmd.driver = dev->myid;
+						dev->interface.statcallb(&cmd);
+						continue;
+					}
+					if (!strncmp(p, "CAU", 3)) {
+						cmd.command = ISDN_STAT_CAUSE;
+						cmd.arg = ch - 1;
+						strncpy(cmd.num, p + 3, sizeof(cmd.num) - 1);
+						cmd.driver = dev->myid;
+						dev->interface.statcallb(&cmd);
+						continue;
+					}
+					if (!strncmp(p, "DCAL_I", 6)) {
+						cmd.command = ISDN_STAT_ICALL;
+						cmd.driver = dev->myid;
+						cmd.arg = ch - 1;
+						strncpy(cmd.num, p + 6, sizeof(cmd.num) - 1);
+						dev->interface.statcallb(&cmd);
+						continue;
+					}
+					if (!strncmp(p, "FCALL", 5)) {
+						cmd.command = ISDN_STAT_ICALL;
+						cmd.driver = dev->myid;
+						cmd.arg = ch - 1;
+						strcpy(cmd.num, "LEASED,07,00,1");
+						dev->interface.statcallb(&cmd);
+						continue;
+					}
+					if (!strncmp(p, "DSCA_I", 6)) {
+						cmd.command = ISDN_STAT_ICALL;
+						cmd.driver = dev->myid;
+						cmd.arg = ch - 1;
+						strncpy(cmd.num, p + 6, sizeof(cmd.num) - 1);
+						dev->interface.statcallb(&cmd);
+						continue;
+					}
+					if (!strncmp(p, "NO D-CHAN", 9)) {
+						cmd.command = ISDN_STAT_NODCH;
+						cmd.driver = dev->myid;
+						cmd.arg = ch - 1;
+						strncpy(cmd.num, p + 6, sizeof(cmd.num) - 1);
+						dev->interface.statcallb(&cmd);
+						continue;
+					}
+				} else {
+					p = dev->imsg;
+					if (!strncmp(p, "DRV1.", 5)) {
+                                                printk(KERN_INFO "icn: %s\n",p);
+						if (!strncmp(p + 7, "TC", 2)) {
+							dev->ptype = ISDN_PTYPE_1TR6;
+							dev->interface.features |= ISDN_FEATURE_P_1TR6;
+							printk(KERN_INFO "icn: 1TR6-Protocol loaded and running\n");
+						}
+						if (!strncmp(p + 7, "EC", 2)) {
+							dev->ptype = ISDN_PTYPE_EURO;
+							dev->interface.features |= ISDN_FEATURE_P_EURO;
+							printk(KERN_INFO "icn: Euro-Protocol loaded and running\n");
+						}
+						continue;
+					}
+				}
+			} else {
+				dev->imsg[dev->iptr] = c;
+				if (dev->iptr < 59)
+					dev->iptr++;
+			}
+		}
+		msg_o = (msg_o + avail) & 0xff;
+		icn_release_channel();
+	}
+	if (avail) {
+		cmd.command = ISDN_STAT_STAVAIL;
+		cmd.driver = dev->myid;
+		cmd.arg = avail;
+		dev->interface.statcallb(&cmd);
+	}
+	if (dflag & 1)
+		dev->interface.rcvcallb(dev->myid, 0, dev->rcvbuf[0], 0);
+	if (dflag & 2)
+		dev->interface.rcvcallb(dev->myid, 1, dev->rcvbuf[1], 0);
+	if (dev->flags & (ICN_FLAGS_B1ACTIVE | ICN_FLAGS_B2ACTIVE))
+		if (!(dev->flags & ICN_FLAGS_RBTIMER)) {
+			/* schedule b-channel polling */
+			dev->flags |= ICN_FLAGS_RBTIMER;
+			save_flags(flags);
+			cli();
+			del_timer(&dev->rb_timer);
+			dev->rb_timer.function = icn_pollbchan;
+			dev->rb_timer.expires = jiffies + ICN_TIMER_BCREAD;
+			add_timer(&dev->rb_timer);
+			restore_flags(flags);
+		}
+}
+
+/*
+ * Check Statusqueue-Pointer from isdn-card.
+ * If there are new status-replies from the interface, check
+ * them against B-Channel-connects/disconnects and set flags arrcordingly.
+ * Wake-Up any processes, who are reading the status-device.
+ * If there are B-Channels open, initiate a timer-callback to
+ * icn_pollbchan().
+ * This routine is called periodically via timer.
+ */
+
+static void icn_pollcard(unsigned long dummy)
+{
+	ulong flags;
+
+	icn_pollit(dev);
+	if (dev->doubleS0)
+		icn_pollit(dev2);
+	/* schedule again */
+	save_flags(flags);
+	cli();
+	del_timer(&dev->st_timer);
+	dev->st_timer.function = icn_pollcard;
+	dev->st_timer.expires = jiffies + ICN_TIMER_DCREAD;
+	add_timer(&dev->st_timer);
+	restore_flags(flags);
+}
+
+/* Send a packet to the transmit-buffers, handle fragmentation if necessary.
+ * Parameters:
+ *            channel = Number of B-channel
+ *            buffer  = pointer to packet
+ *            len     = size of packet (max 4000)
+ *            dev     = pointer to device-struct
+ *            user    = 1 = call from userproc, 0 = call from kernel
+ * Return:
+ *        Number of bytes transferred, -E??? on error
+ */
+
+static int icn_sendbuf(int channel, const u_char * buffer, int len, int user, icn_dev * dev)
+{
+	register u_char *p;
+	int flags;
+
+	if (len > 4000)
+		return -EINVAL;
+	if (len) {
+		if (dev->sndcount[channel] > ICN_MAX_SQUEUE)
+			return 0;
+		save_flags(flags);
+		cli();
+		p = icn_new_buf(&dev->spqueue[channel], len);
+		if (!p) {
+		        restore_flags(flags);
+			return 0;
+		}
+		if (user) {
+			memcpy_fromfs(p, buffer, len);
+		} else {
+			memcpy(p, buffer, len);
+		}
+		dev->sndcount[channel] += len;
+		icn_pollbchan_send(channel, dev);
+		restore_flags(flags);
+	}
+	return len;
+}
+
+#ifndef LOADEXTERN
+static int icn_check_loader(int cardnumber)
+{
+	int timer = 0;
+
+	while (1) {
+#ifdef BOOT_DEBUG
+		printk(KERN_DEBUG "Loader %d ?\n", cardnumber);
+#endif
+		if (dev->shmem->data_control.scns ||
+		    dev->shmem->data_control.scnr) {
+			if (timer++ > 5) {
+				printk(KERN_WARNING "icn: Boot-Loader %d timed out.\n", cardnumber);
+				icn_release_channel();
+				return -EIO;
+			}
+#ifdef BOOT_DEBUG
+			printk(KERN_DEBUG "Loader %d TO?\n", cardnumber);
+#endif
+			current->state = TASK_INTERRUPTIBLE;
+			current->timeout = jiffies + ICN_BOOT_TIMEOUT1;
+			schedule();
+		} else {
+#ifdef BOOT_DEBUG
+			printk(KERN_DEBUG "Loader %d OK\n", cardnumber);
+#endif
+			icn_release_channel();
+			return 0;
+		}
+	}
+}
+
+/* Load the boot-code into the interface-card's memory and start it.
+ * Always called from user-process.
+ * 
+ * Parameters:
+ *            buffer = pointer to packet
+ * Return:
+ *        0 if successfully loaded
+ */
+
+#ifdef BOOT_DEBUG
+#define SLEEP(sec) { \
+int slsec = sec; \
+  printk(KERN_DEBUG "SLEEP(%d)\n",slsec); \
+  while (slsec) { \
+    current->state = TASK_INTERRUPTIBLE; \
+    current->timeout = jiffies + HZ; \
+    schedule(); \
+    slsec--; \
+  } \
+}
+#else
+#define SLEEP(sec)
+#endif
+
+static int icn_loadboot(u_char * buffer, icn_dev * dev)
+{
+	int ret;
+	ulong flags;
+
+#ifdef BOOT_DEBUG
+	printk(KERN_DEBUG "icn_loadboot called, buffaddr=%08lx\n", (ulong) buffer);
+#endif
+	if ((ret = verify_area(VERIFY_READ, (void *) buffer, ICN_CODE_STAGE1)))
+		return ret;
+	save_flags(flags);
+	cli();
+	if (!dev->rvalid) {
+		if (check_region(dev->port, ICN_PORTLEN)) {
+			printk(KERN_WARNING "icn: ports 0x%03x-0x%03x in use.\n", dev->port,
+			       dev->port + ICN_PORTLEN);
+			restore_flags(flags);
+			return -EBUSY;
+		}
+		request_region(dev->port, ICN_PORTLEN, regname);
+		dev->rvalid = 1;
+	}
+	if (!dev->mvalid) {
+		if (check_shmem((ulong) dev->shmem, 0x4000)) {
+			printk(KERN_WARNING "icn: memory at 0x%08lx in use.\n", (ulong) dev->shmem);
+			restore_flags(flags);
+			return -EBUSY;
+		}
+		request_shmem((ulong) dev->shmem, 0x4000, regname);
+		dev->mvalid = 1;
+	}
+	restore_flags(flags);
+	OUTB_P(0, ICN_RUN);	/* Reset Controler */
+	OUTB_P(0, ICN_MAPRAM);	/* Disable RAM     */
+	icn_shiftout(ICN_CFG, 0x0f, 3, 4);	/* Windowsize= 16k */
+	icn_shiftout(ICN_CFG, (unsigned long) dev->shmem, 23, 10);	/* Set RAM-Addr.   */
+#ifdef BOOT_DEBUG
+	printk(KERN_DEBUG "shmem=%08lx\n", (ulong) dev->shmem);
+#endif
+	SLEEP(1);
+	save_flags(flags);
+	cli();
+	dev->channel = 1;	/* Force Mapping   */
+#ifdef BOOT_DEBUG
+	printk(KERN_DEBUG "Map Bank 0\n");
+#endif
+	icn_map_channel(0);		/* Select Bank 0   */
+	icn_lock_channel(0);	/* Lock Bank 0     */
+	restore_flags(flags);
+	SLEEP(1);
+	memcpy_fromfs(dev->shmem, buffer, ICN_CODE_STAGE1);	/* Copy code       */
+#ifdef BOOT_DEBUG
+	printk(KERN_DEBUG "Bootloader transfered\n");
+#endif
+	if (dev->doubleS0) {
+		SLEEP(1);
+		save_flags(flags);
+		cli();
+		icn_release_channel();
+#ifdef BOOT_DEBUG
+		printk(KERN_DEBUG "Map Bank 8\n");
+#endif
+		icn_map_channel(2);	/* Select Bank 8   */
+		icn_lock_channel(2);	/* Lock Bank 8     */
+		restore_flags(flags);
+		SLEEP(1);
+		memcpy_fromfs(dev->shmem, buffer, ICN_CODE_STAGE1);	/* Copy code       */
+#ifdef BOOT_DEBUG
+		printk(KERN_DEBUG "Bootloader transfered\n");
+#endif
+	}
+	SLEEP(1);
+	OUTB_P(0xff, ICN_RUN);	/* Start Boot-Code */
+	if ((ret = icn_check_loader(dev->doubleS0 ? 2 : 1)))
+		return ret;
+	if (!dev->doubleS0)
+		return 0;
+	/* reached only, if we have a Double-S0-Card */
+	save_flags(flags);
+	cli();
+#ifdef BOOT_DEBUG
+	printk(KERN_DEBUG "Map Bank 0\n");
+#endif
+	icn_map_channel(0);		/* Select Bank 0   */
+	icn_lock_channel(0);	/* Lock Bank 0     */
+	restore_flags(flags);
+	SLEEP(1);
+	return (icn_check_loader(1));
+}
+
+static int icn_loadproto(u_char * buffer, icn_dev * ldev)
+{
+	register u_char *p = buffer;
+	uint left = ICN_CODE_STAGE2;
+	uint cnt;
+	int timer;
+	int ret;
+	unsigned long flags;
+
+#ifdef BOOT_DEBUG
+	printk(KERN_DEBUG "icn_loadproto called\n");
+#endif
+	if ((ret = verify_area(VERIFY_READ, (void *) buffer, ICN_CODE_STAGE2)))
+		return ret;
+	timer = 0;
+	save_flags(flags);
+	cli();
+	if (ldev->secondhalf) {
+		icn_map_channel(2);
+		icn_lock_channel(2);
+	} else {
+		icn_map_channel(0);
+		icn_lock_channel(0);
+	}
+	restore_flags(flags);
+	while (left) {
+		if (sbfree) {	/* If there is a free buffer...  */
+			cnt = MIN(256, left);
+			memcpy_fromfs(&sbuf_l, p, cnt);		/* copy data                     */
+			sbnext;	/* switch to next buffer         */
+			p += cnt;
+			left -= cnt;
+			timer = 0;
+		} else {
+#ifdef BOOT_DEBUG
+			printk(KERN_DEBUG "boot 2 !sbfree\n");
+#endif
+			if (timer++ > 5) {
+				icn_maprelease_channel(0);
+				return -EIO;
+			}
+			current->state = TASK_INTERRUPTIBLE;
+			current->timeout = jiffies + 10;
+			schedule();
+		}
+	}
+	sbuf_n = 0x20;
+	timer = 0;
+	while (1) {
+		if (cmd_o || cmd_i) {
+#ifdef BOOT_DEBUG
+			printk(KERN_DEBUG "Proto?\n");
+#endif
+			if (timer++ > 5) {
+				printk(KERN_WARNING "icn: Protocol timed out.\n");
+#ifdef BOOT_DEBUG
+				printk(KERN_DEBUG "Proto TO!\n");
+#endif
+				icn_maprelease_channel(0);
+				return -EIO;
+			}
+#ifdef BOOT_DEBUG
+			printk(KERN_DEBUG "Proto TO?\n");
+#endif
+			current->state = TASK_INTERRUPTIBLE;
+			current->timeout = jiffies + ICN_BOOT_TIMEOUT1;
+			schedule();
+		} else {
+			if ((ldev->secondhalf) || (!dev->doubleS0)) {
+				save_flags(flags);
+				cli();
+#ifdef BOOT_DEBUG
+				printk(KERN_DEBUG "Proto loaded, install poll-timer %d\n",
+				       ldev->secondhalf);
+#endif
+				init_timer(&dev->st_timer);
+				dev->st_timer.expires = jiffies + ICN_TIMER_DCREAD;
+				dev->st_timer.function = icn_pollcard;
+				add_timer(&dev->st_timer);
+				restore_flags(flags);
+			}
+			icn_maprelease_channel(0);
+			return 0;
+		}
+	}
+}
+#endif				/* !LOADEXTERN */
+
+/* Read the Status-replies from the Interface */
+static int icn_readstatus(u_char * buf, int len, int user, icn_dev * dev)
+{
+	int count;
+	u_char *p;
+
+	for (p = buf, count = 0; count < len; p++, count++) {
+		if (user)
+			put_fs_byte(*dev->msg_buf_read++, p);
+		else
+			*p = *dev->msg_buf_read++;
+		if (dev->msg_buf_read > dev->msg_buf_end)
+			dev->msg_buf_read = dev->msg_buf;
+	}
+	return count;
+}
+
+/* Put command-strings into the command-queue of the Interface */
+static int icn_writecmd(const u_char * buf, int len, int user, icn_dev * dev, int waitflg)
+{
+	int mch = dev->secondhalf ? 2 : 0;
+	int avail;
+	int pp;
+	int i;
+	int count;
+	int ocount;
+	unsigned long flags;
+	u_char *p;
+	isdn_ctrl cmd;
+	u_char msg[0x100];
+
+	while (1) {
+		if (icn_trymaplock_channel(mch)) {
+			avail = cmd_free;
+			count = MIN(avail, len);
+			if (user)
+				memcpy_fromfs(msg, buf, count);
+			else
+				memcpy(msg, buf, count);
+			save_flags(flags);
+			cli();
+			ocount = 1;
+			*dev->msg_buf_write++ = '>';
+			if (dev->msg_buf_write > dev->msg_buf_end)
+				dev->msg_buf_write = dev->msg_buf;
+			for (p = msg, pp = cmd_i, i = count; i > 0; i--, p++, pp++) {
+				dev->shmem->comm_buffers.pcio_buf[pp & 0xff] = (*p == '\n') ? 0xff : *p;
+				*dev->msg_buf_write++ = *p;
+				if ((*p == '\n') && (i > 1)) {
+					*dev->msg_buf_write++ = '>';
+					if (dev->msg_buf_write > dev->msg_buf_end)
+						dev->msg_buf_write = dev->msg_buf;
+					ocount++;
+				}
+				/* No checks for buffer overflow of raw-status-device */
+				if (dev->msg_buf_write > dev->msg_buf_end)
+					dev->msg_buf_write = dev->msg_buf;
+				ocount++;
+			}
+			restore_flags(flags);
+			cmd.command = ISDN_STAT_STAVAIL;
+			cmd.driver = dev->myid;
+			cmd.arg = ocount;
+			dev->interface.statcallb(&cmd);
+			cmd_i = (cmd_i + count) & 0xff;
+			icn_release_channel();
+			waitflg = 0;
+		} else
+			count = 0;
+		if (!waitflg)
+			break;
+		current->timeout = jiffies + 10;
+		schedule();
+	}
+	return count;
+}
+
+static void icn_stopdriver(icn_dev * ldev)
+{
+	unsigned long flags;
+	isdn_ctrl cmd;
+
+	save_flags(flags);
+	cli();
+	del_timer(&dev->st_timer);
+	del_timer(&ldev->rb_timer);
+	cmd.command = ISDN_STAT_STOP;
+	cmd.driver = ldev->myid;
+	ldev->interface.statcallb(&cmd);
+	restore_flags(flags);
+}
+
+static int my_atoi(char *s)
+{
+	int i, n;
+
+	n = 0;
+	if (!s)
+		return -1;
+	for (i = 0; *s >= '0' && *s <= '9'; i++, s++)
+		n = 10 * n + (*s - '0');
+	return n;
+}
+
+static int icn_command(isdn_ctrl * c, icn_dev * ldev)
+{
+	ulong a;
+	ulong flags;
+	int i;
+	char cbuf[60];
+	isdn_ctrl cmd;
+
+	switch (c->command) {
+	case ISDN_CMD_IOCTL:
+		memcpy(&a, c->num, sizeof(ulong));
+		switch (c->arg) {
+		case ICN_IOCTL_SETMMIO:
+			if ((unsigned long) dev->shmem != (a & 0x0ffc000)) {
+				if (check_shmem((ulong) (a & 0x0ffc000), 0x4000)) {
+					printk(KERN_WARNING "icn: memory at 0x%08lx in use.\n",
+					       (ulong) (a & 0x0ffc000));
+					return -EINVAL;
+				}
+				icn_stopdriver(dev);
+				if (dev->doubleS0)
+					icn_stopdriver(dev2);
+				save_flags(flags);
+				cli();
+				if (dev->mvalid)
+					release_shmem((ulong) dev->shmem, 0x4000);
+				dev->mvalid = 0;
+				dev->shmem = (icn_shmem *) (a & 0x0ffc000);
+				if (dev->doubleS0)
+					dev2->shmem = (icn_shmem *) (a & 0x0ffc000);
+				restore_flags(flags);
+				printk(KERN_INFO "icn: mmio set to 0x%08lx\n",
+				       (unsigned long) dev->shmem);
+			}
+			break;
+		case ICN_IOCTL_GETMMIO:
+			return (int) dev->shmem;
+		case ICN_IOCTL_SETPORT:
+			if (a == 0x300 || a == 0x310 || a == 0x320 || a == 0x330
+			    || a == 0x340 || a == 0x350 || a == 0x360 ||
+			    a == 0x308 || a == 0x318 || a == 0x328 || a == 0x338
+			    || a == 0x348 || a == 0x358 || a == 0x368) {
+				if (dev->port != (unsigned short) a) {
+					if (check_region((unsigned short) a, ICN_PORTLEN)) {
+						printk(KERN_WARNING "icn: ports 0x%03x-0x%03x in use.\n",
+						       (int) a, (int) a + ICN_PORTLEN);
+						return -EINVAL;
+					}
+					icn_stopdriver(dev);
+					if (dev->doubleS0)
+						icn_stopdriver(dev2);
+					save_flags(flags);
+					cli();
+					if (dev->rvalid)
+						release_region(dev->port, ICN_PORTLEN);
+					dev->port = (unsigned short) a;
+					dev->rvalid = 0;
+					if (dev->doubleS0) {
+						dev2->port = (unsigned short) a;
+						dev2->rvalid = 0;
+					}
+					restore_flags(flags);
+					printk(KERN_INFO "icn: port set to 0x%03x\n", dev->port);
+				}
+			} else
+				return -EINVAL;
+			break;
+		case ICN_IOCTL_GETPORT:
+			return (int) dev->port;
+		case ICN_IOCTL_GETDOUBLE:
+			return (int) dev->doubleS0;
+		case ICN_IOCTL_DEBUGVAR:
+			return (ulong) ldev;
+#ifndef LOADEXTERN
+		case ICN_IOCTL_LOADBOOT:
+			icn_stopdriver(dev);
+			if (dev->doubleS0)
+				icn_stopdriver(dev2);
+			return (icn_loadboot((u_char *) a, dev));
+		case ICN_IOCTL_LOADPROTO:
+			icn_stopdriver(dev);
+			if (dev->doubleS0)
+				icn_stopdriver(dev2);
+			if ((i = (icn_loadproto((u_char *) a, dev))))
+				return i;
+			if (dev->doubleS0)
+				i = icn_loadproto((u_char *) (a + ICN_CODE_STAGE2), dev2);
+			return i;
+#endif
+		case ICN_IOCTL_LEASEDCFG:
+			if (a) {
+				if (!ldev->leased) {
+					ldev->leased = 1;
+					while (ldev->ptype == ISDN_PTYPE_UNKNOWN) {
+						current->timeout = jiffies + ICN_BOOT_TIMEOUT1;
+						schedule();
+					}
+					current->timeout = jiffies + ICN_BOOT_TIMEOUT1;
+					schedule();
+					sprintf(cbuf, "00;FV2ON\n01;EAZ1\n");
+					i = icn_writecmd(cbuf, strlen(cbuf), 0, ldev, 1);
+					printk(KERN_INFO "icn: Leased-line mode enabled\n");
+					cmd.command = ISDN_STAT_RUN;
+					cmd.driver = ldev->myid;
+					cmd.arg = 0;
+					ldev->interface.statcallb(&cmd);
+				}
+			} else {
+				if (ldev->leased) {
+					ldev->leased = 0;
+					sprintf(cbuf, "00;FV2OFF\n");
+					i = icn_writecmd(cbuf, strlen(cbuf), 0, ldev, 1);
+					printk(KERN_INFO "icn: Leased-line mode disabled\n");
+					cmd.command = ISDN_STAT_RUN;
+					cmd.driver = ldev->myid;
+					cmd.arg = 0;
+					ldev->interface.statcallb(&cmd);
+				}
+			}
+			return 0;
+		default:
+			return -EINVAL;
+		}
+		break;
+	case ISDN_CMD_DIAL:
+		if (ldev->leased)
+			break;
+		if ((c->arg & 255) < ICN_BCH) {
+			char *p;
+			char *p2;
+			char dial[50];
+			char sis[50];
+			char dcode[4];
+			int si1, si2;
+
+			a = c->arg;
+			strcpy(sis, c->num);
+			p = strrchr(sis, ',');
+			*p++ = '\0';
+			si2 = my_atoi(p);
+			p = strrchr(sis, ',') + 1;
+			si1 = my_atoi(p);
+			p = c->num;
+			if (*p == 's' || *p == 'S') {
+				/* Dial for SPV */
+				p++;
+				strcpy(dcode, "SCA");
+			} else
+				/* Normal Dial */
+				strcpy(dcode, "CAL");
+			strcpy(dial, p);
+			p = strchr(dial, ',');
+			*p++ = '\0';
+			p2 = strchr(p, ',');
+			*p2 = '\0';
+			sprintf(cbuf, "%02d;D%s_R%s,%02d,%02d,%s\n", (int) (a + 1), dcode, dial, si1,
+				si2, p);
+			i = icn_writecmd(cbuf, strlen(cbuf), 0, ldev, 1);
+		}
+		break;
+	case ISDN_CMD_ACCEPTD:
+		if (c->arg < ICN_BCH) {
+			a = c->arg + 1;
+			sprintf(cbuf, "%02d;DCON_R\n", (int) a);
+			i = icn_writecmd(cbuf, strlen(cbuf), 0, ldev, 1);
+		}
+		break;
+	case ISDN_CMD_ACCEPTB:
+		if (c->arg < ICN_BCH) {
+			a = c->arg + 1;
+			sprintf(cbuf, "%02d;BCON_R\n", (int) a);
+			i = icn_writecmd(cbuf, strlen(cbuf), 0, ldev, 1);
+		}
+		break;
+	case ISDN_CMD_HANGUP:
+		if (c->arg < ICN_BCH) {
+			a = c->arg + 1;
+			sprintf(cbuf, "%02d;BDIS_R\n%02d;DDIS_R\n", (int) a, (int) a);
+			i = icn_writecmd(cbuf, strlen(cbuf), 0, ldev, 1);
+		}
+		break;
+	case ISDN_CMD_SETEAZ:
+		if (ldev->leased)
+			break;
+		if (c->arg < ICN_BCH) {
+			a = c->arg + 1;
+			if (ldev->ptype == ISDN_PTYPE_EURO) {
+				sprintf(cbuf, "%02d;MS%s%s\n", (int) a, c->num[0] ? "N" : "ALL", c->num);
+			} else
+				sprintf(cbuf, "%02d;EAZ%s\n", (int) a, c->num[0] ? c->num : "0123456789");
+			i = icn_writecmd(cbuf, strlen(cbuf), 0, ldev, 1);
+		}
+		break;
+	case ISDN_CMD_CLREAZ:
+		if (ldev->leased)
+			break;
+		if (c->arg < ICN_BCH) {
+			a = c->arg + 1;
+			if (ldev->ptype == ISDN_PTYPE_EURO)
+				sprintf(cbuf, "%02d;MSNC\n", (int) a);
+			else
+				sprintf(cbuf, "%02d;EAZC\n", (int) a);
+			i = icn_writecmd(cbuf, strlen(cbuf), 0, ldev, 1);
+		}
+		break;
+	case ISDN_CMD_SETL2:
+		if ((c->arg & 255) < ICN_BCH) {
+			a = c->arg;
+			switch (a >> 8) {
+			case ISDN_PROTO_L2_X75I:
+				sprintf(cbuf, "%02d;BX75\n", (int) (a & 255) + 1);
+				break;
+			case ISDN_PROTO_L2_HDLC:
+				sprintf(cbuf, "%02d;BTRA\n", (int) (a & 255) + 1);
+				break;
+			default:
+				return -EINVAL;
+			}
+			i = icn_writecmd(cbuf, strlen(cbuf), 0, ldev, 1);
+			ldev->l2_proto[a & 255] = (a >> 8);
+		}
+		break;
+	case ISDN_CMD_GETL2:
+		if ((c->arg & 255) < ICN_BCH)
+			return ldev->l2_proto[c->arg & 255];
+		else
+			return -ENODEV;
+	case ISDN_CMD_SETL3:
+		return 0;
+	case ISDN_CMD_GETL3:
+		if ((c->arg & 255) < ICN_BCH)
+			return ISDN_PROTO_L3_TRANS;
+		else
+			return -ENODEV;
+	case ISDN_CMD_GETEAZ:
+		break;
+	case ISDN_CMD_SETSIL:
+		break;
+	case ISDN_CMD_GETSIL:
+		break;
+	case ISDN_CMD_LOCK:
+		MOD_INC_USE_COUNT;
+		break;
+	case ISDN_CMD_UNLOCK:
+		MOD_DEC_USE_COUNT;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+/*
+ * For second half of doubleS0-Card add channel-offset.
+ */
+static int if_command1(isdn_ctrl * c)
+{
+	return (icn_command(c, dev));
+}
+
+static int if_command2(isdn_ctrl * c)
+{
+	return (icn_command(c, dev2));
+}
+
+static int if_writecmd1(const u_char * buf, int len, int user)
+{
+	return (icn_writecmd(buf, len, user, dev, 0));
+}
+
+static int if_writecmd2(const u_char * buf, int len, int user)
+{
+	return (icn_writecmd(buf, len, user, dev2, 0));
+}
+
+static int if_readstatus1(u_char * buf, int len, int user)
+{
+	return (icn_readstatus(buf, len, user, dev));
+}
+
+static int if_readstatus2(u_char * buf, int len, int user)
+{
+	return (icn_readstatus(buf, len, user, dev2));
+}
+
+static int if_sendbuf1(int id, int channel, const u_char * buffer, int len,
+		       int user)
+{
+	return (icn_sendbuf(channel, buffer, len, user, dev));
+}
+
+static int if_sendbuf2(int id, int channel, const u_char * buffer, int len,
+		       int user)
+{
+	return (icn_sendbuf(channel, buffer, len, user, dev2));
+}
+
+#ifdef MODULE
+#define icn_init init_module
+#else
+void icn_setup(char *str, int *ints)
+{
+	char *p;
+	static char sid[20];
+	static char sid2[20];
+
+	if (ints[0])
+		portbase = ints[1];
+	if (ints[0]>1)
+		membase  = ints[2];
+	if (strlen(str)) {
+		strcpy(sid,str);
+		icn_id = sid;
+		if ((p = strchr(sid,','))) {
+			*p++ = 0;
+			strcpy(sid2,p);
+			icn_id2 = sid2;
+		}
+	}
+}
+#endif
+
+int icn_init(void)
+{
+#ifdef LOADEXTERN
+	unsigned long flags;
+#endif
+	char *p;
+	isdn_ctrl cmd;
+	char rev[10];
+
+	if (!(dev = (icn_devptr) kmalloc(sizeof(icn_dev), GFP_KERNEL))) {
+		printk(KERN_WARNING "icn: Could not allocate device-struct.\n");
+		return -EIO;
+	}
+	memset((char *) dev, 0, sizeof(icn_dev));
+	dev->port = portbase;
+	dev->shmem = (icn_shmem *) (membase & 0x0ffc000);
+	if (strlen(icn_id2))
+		dev->doubleS0 = 1;
+	dev->interface.channels = ICN_BCH;
+	dev->interface.maxbufsize = 4000;
+	dev->interface.command = if_command1;
+	dev->interface.writebuf = if_sendbuf1;
+	dev->interface.writecmd = if_writecmd1;
+	dev->interface.readstat = if_readstatus1;
+	dev->interface.features = ISDN_FEATURE_L2_X75I |
+                ISDN_FEATURE_L2_HDLC |
+                ISDN_FEATURE_L3_TRANS |
+                ISDN_FEATURE_P_UNKNOWN;
+	dev->ptype = ISDN_PTYPE_UNKNOWN;
+	strncpy(dev->interface.id, icn_id, sizeof(dev->interface.id) - 1);
+	dev->msg_buf_write = dev->msg_buf;
+	dev->msg_buf_read = dev->msg_buf;
+	dev->msg_buf_end = &dev->msg_buf[sizeof(dev->msg_buf) - 1];
+	memset((char *) dev->l2_proto, ISDN_PROTO_L2_X75I, sizeof(dev->l2_proto));
+	if (strlen(icn_id2)) {
+		if (!(dev2 = (icn_devptr) kmalloc(sizeof(icn_dev), GFP_KERNEL))) {
+			printk(KERN_WARNING "icn: Could not allocate device-struct.\n");
+			kfree(dev);
+			kfree(dev2);
+			return -EIO;
+		}
+		memcpy((char *) dev2, (char *) dev, sizeof(icn_dev));
+		dev2->interface.command = if_command2;
+		dev2->interface.writebuf = if_sendbuf2;
+		dev2->interface.writecmd = if_writecmd2;
+		dev2->interface.readstat = if_readstatus2;
+		strncpy(dev2->interface.id, icn_id2,
+                        sizeof(dev->interface.id) - 1);
+		dev2->msg_buf_write = dev2->msg_buf;
+		dev2->msg_buf_read = dev2->msg_buf;
+		dev2->msg_buf_end = &dev2->msg_buf[sizeof(dev2->msg_buf) - 1];
+		dev2->secondhalf = 1;
+	}
+	if (!register_isdn(&dev->interface)) {
+		printk(KERN_WARNING "icn: Unable to register\n");
+		kfree(dev);
+		if (dev->doubleS0)
+			kfree(dev2);
+		return -EIO;
+	}
+	dev->myid = dev->interface.channels;
+	sprintf(regname, "icn-isdn (%s)", dev->interface.id);
+	if (dev->doubleS0) {
+		if (!register_isdn(&dev2->interface)) {
+			printk(KERN_WARNING "icn: Unable to register\n");
+			kfree(dev2);
+			if (dev->doubleS0) {
+				icn_stopdriver(dev);
+				cmd.command = ISDN_STAT_UNLOAD;
+				cmd.driver = dev->myid;
+				dev->interface.statcallb(&cmd);
+				kfree(dev);
+			}
+			return -EIO;
+		}
+		dev2->myid = dev2->interface.channels;
+	}
+	
+	/* No symbols to export, hide all symbols */
+	register_symtab(NULL);
+
+	if ((p = strchr(revision, ':'))) {
+		strcpy(rev, p + 1);
+		p = strchr(rev, '$');
+		*p = 0;
+	} else
+		strcpy(rev, " ??? ");
+	printk(KERN_NOTICE "ICN-ISDN-driver Rev%sport=0x%03x mmio=0x%08x id='%s'\n",
+	       rev, dev->port, (uint) dev->shmem, dev->interface.id);
+#ifdef LOADEXTERN
+	save_flags(flags);
+	cli();
+	init_timer(&dev->st_timer);
+	dev->st_timer.expires = jiffies + ICN_TIMER_DCREAD;
+	dev->st_timer.function = icn_pollcard;
+	add_timer(&dev->st_timer);
+	restore_flags(flags);
+#endif
+	return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+	isdn_ctrl cmd;
+	int i;
+
+	icn_stopdriver(dev);
+	cmd.command = ISDN_STAT_UNLOAD;
+	cmd.driver = dev->myid;
+	dev->interface.statcallb(&cmd);
+	if (dev->doubleS0) {
+		icn_stopdriver(dev2);
+		cmd.command = ISDN_STAT_UNLOAD;
+		cmd.driver = dev2->myid;
+		dev2->interface.statcallb(&cmd);
+	}
+	if (dev->rvalid) {
+		OUTB_P(0, ICN_RUN);	/* Reset Controler      */
+		OUTB_P(0, ICN_MAPRAM);	/* Disable RAM          */
+		release_region(dev->port, ICN_PORTLEN);
+	}
+	if (dev->mvalid)
+		release_shmem((ulong) dev->shmem, 0x4000);
+	if (dev->doubleS0) {
+		for (i = 0; i < ICN_BCH; i++)
+			icn_free_queue(&dev2->spqueue[1]);
+		kfree(dev2);
+	}
+	for (i = 0; i < ICN_BCH; i++)
+		icn_free_queue(&dev->spqueue[1]);
+	kfree(dev);
+	printk(KERN_NOTICE "ICN-ISDN-driver unloaded\n");
+}
+#endif
diff -u --recursive --new-file v1.3.68/linux/drivers/isdn/icn/icn.h linux/drivers/isdn/icn/icn.h
--- v1.3.68/linux/drivers/isdn/icn/icn.h	Thu Jan  1 02:00:00 1970
+++ linux/drivers/isdn/icn/icn.h	Mon Feb 26 12:29:22 1996
@@ -0,0 +1,294 @@
+/* $Id: icn.h,v 1.12 1996/01/22 05:01:22 fritz Exp fritz $
+ *
+ * ISDN lowlevel-module for the ICN active ISDN-Card.
+ *
+ * Copyright 1994 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: icn.h,v $
+ * Revision 1.12  1996/01/22 05:01:22  fritz
+ * Revert to GPL.
+ *
+ * Revision 1.11  1995/12/18  18:25:00  fritz
+ * Support for ICN-2B Cards.
+ * Change for supporting user-setable service-octet.
+ *
+ * Revision 1.10  1995/10/29  21:43:10  fritz
+ * Added support for leased lines.
+ *
+ * Revision 1.9  1995/04/23  13:42:10  fritz
+ * Added some constants for distingushing 1TR6 and DSS1
+ *
+ * Revision 1.8  1995/03/25  23:18:55  fritz
+ * Changed ICN_PORTLEN to reflect correct number of ports.
+ *
+ * Revision 1.7  1995/03/15  12:52:06  fritz
+ * Some cleanup
+ *
+ * Revision 1.6  1995/02/20  03:49:22  fritz
+ * Fixed ICN_MAX_SQUEUE to correctly reflect outstanding bytes, not number
+ * of buffers.
+ *
+ * Revision 1.5  1995/01/29  23:36:50  fritz
+ * Minor cleanup.
+ *
+ * Revision 1.4  1995/01/09  07:41:20  fritz
+ * Added GPL-Notice
+ *
+ * Revision 1.3  1995/01/04  05:14:20  fritz
+ * removed include of linux/asm/string.h for compiling with Linux 1.1.76
+ *
+ * Revision 1.2  1995/01/02  02:15:57  fritz
+ * Misc. Bugfixes
+ *
+ * Revision 1.1  1994/12/14  18:02:38  fritz
+ * Initial revision
+ *
+ */
+
+#ifndef icn_h
+#define icn_h
+
+#define ICN_IOCTL_SETMMIO   0
+#define ICN_IOCTL_GETMMIO   1
+#define ICN_IOCTL_SETPORT   2
+#define ICN_IOCTL_GETPORT   3
+#define ICN_IOCTL_LOADBOOT  4
+#define ICN_IOCTL_LOADPROTO 5
+#define ICN_IOCTL_LEASEDCFG 6
+#define ICN_IOCTL_GETDOUBLE 7
+#define ICN_IOCTL_DEBUGVAR  8
+
+#if defined(__KERNEL__) || defined(__DEBUGVAR__)
+
+#ifdef __KERNEL__
+/* Kernel includes */
+
+#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__ */
+
+/* some useful macros for debugging */
+#ifdef ICN_DEBUG_PORT
+#define OUTB_P(v,p) {printk(KERN_DEBUG "icn: outb_p(0x%02x,0x%03x)\n",v,p); outb_p(v,p);}
+#else
+#define OUTB_P outb
+#endif
+
+/* Defaults for Port-Address and shared-memory */
+#define ICN_BASEADDR 0x320
+#define ICN_PORTLEN (0x04)
+#define ICN_MEMADDR 0x0d0000
+
+/* Macros for accessing ports */
+#define ICN_CFG    (dev->port)
+#define ICN_MAPRAM (dev->port+1)
+#define ICN_RUN    (dev->port+2)
+#define ICN_BANK   (dev->port+3)
+
+#define ICN_FLAGS_B1ACTIVE 1	/* B-Channel-1 is open                 */
+#define ICN_FLAGS_B2ACTIVE 2	/* B-Channel-2 is open                 */
+#define ICN_FLAGS_RBTIMER  8	/* cyclic scheduling of B-Channel-poll */
+
+#define ICN_BOOT_TIMEOUT1  100	/* Delay for Boot-download (jiffies)   */
+#define ICN_CHANLOCK_DELAY  10	/* Delay for Channel-mapping (jiffies) */
+
+#define ICN_TIMER_BCREAD 3	/* B-Channel poll-cycle                */
+#define ICN_TIMER_DCREAD 50	/* D-Channel poll-cycle                */
+
+#define ICN_CODE_STAGE1 4096	/* Size of bootcode                    */
+#define ICN_CODE_STAGE2 65536	/* Size of protocol-code               */
+
+#define ICN_MAX_SQUEUE 65536	/* Max. outstanding send-data          */
+#define ICN_FRAGSIZE (250)	/* Max. size of send-fragments         */
+#define ICN_BCH 2		/* Number of supported channels        */
+
+/* type-definitions for accessing the mmap-io-areas */
+
+#define SHM_DCTL_OFFSET (0)	/* Offset to data-controlstructures in shm */
+#define SHM_CCTL_OFFSET (0x1d2)	/* Offset to comm-controlstructures in shm */
+#define SHM_CBUF_OFFSET (0x200)	/* Offset to comm-buffers in shm           */
+#define SHM_DBUF_OFFSET (0x2000)	/* Offset to data-buffers in shm           */
+
+typedef struct {
+	unsigned char length;	/* Bytecount of fragment (max 250)     */
+	unsigned char endflag;	/* 0=last frag., 0xff=frag. continued  */
+	unsigned char data[ICN_FRAGSIZE];	/* The data                            */
+	/* Fill to 256 bytes */
+	char unused[0x100 - ICN_FRAGSIZE - 2];
+} frag_buf;
+
+typedef union {
+	struct {
+		unsigned char scns;	/* Index to free SendFrag.             */
+		unsigned char scnr;	/* Index to active SendFrag   READONLY */
+		unsigned char ecns;	/* Index to free RcvFrag.     READONLY */
+		unsigned char ecnr;	/* Index to valid RcvFrag              */
+		char unused[6];
+		unsigned short fuell1;	/* Internal Buf Bytecount              */
+	} data_control;
+	struct {
+		char unused[SHM_CCTL_OFFSET];
+		unsigned char iopc_i;	/* Read-Ptr Status-Queue      READONLY */
+		unsigned char iopc_o;	/* Write-Ptr Status-Queue              */
+		unsigned char pcio_i;	/* Write-Ptr Command-Queue             */
+		unsigned char pcio_o;	/* Read-Ptr Command Queue     READONLY */
+	} comm_control;
+	struct {
+		char unused[SHM_CBUF_OFFSET];
+		unsigned char pcio_buf[0x100];	/* Ring-Buffer Command-Queue           */
+		unsigned char iopc_buf[0x100];	/* Ring-Buffer Status-Queue            */
+	} comm_buffers;
+	struct {
+		char unused[SHM_DBUF_OFFSET];
+		frag_buf receive_buf[0x10];
+		frag_buf send_buf[0x10];
+	} data_buffers;
+} icn_shmem;
+
+/* Sendbuffer-queue-element */
+typedef struct {
+	char *next;
+	short length;
+	short size;
+	u_char *rptr;
+	u_char buffer[1];
+} pqueue;
+
+typedef struct {
+	unsigned short port;	/* Base-port-adress                 */
+	icn_shmem *shmem;	/* Pointer to memory-mapped-buffers */
+	int myid;		/* Driver-Nr. assigned by linklevel */
+	int rvalid;		/* IO-portregion has been requested */
+	int mvalid;		/* IO-shmem has been requested      */
+	int leased;		/* Flag: This Adapter is connected  */
+	/*       to a leased line           */
+	unsigned short flags;	/* Statusflags                      */
+	int doubleS0;		/* Flag: Double-S0-Card             */
+	int secondhalf;		/* Flag: Second half of a doubleS0  */
+	int ptype;		/* Protocoltype (1TR6 or Euro)      */
+	struct timer_list st_timer;	/* Timer for Status-Polls           */
+	struct timer_list rb_timer;	/* Timer for B-Channel-Polls        */
+	int channel;		/* Currently mapped Channel         */
+	int chanlock;		/* Semaphore for Channel-Mapping    */
+	u_char rcvbuf[ICN_BCH][4096];	/* B-Channel-Receive-Buffers      */
+	int rcvidx[ICN_BCH];	/* Index for above buffers          */
+	int l2_proto[ICN_BCH];	/* Current layer-2-protocol         */
+	isdn_if interface;	/* Interface to upper layer         */
+	int iptr;		/* Index to imsg-buffer             */
+	char imsg[60];		/* Internal buf for status-parsing  */
+	char msg_buf[2048];	/* Buffer for status-messages       */
+	char *msg_buf_write;	/* Writepointer for statusbuffer    */
+	char *msg_buf_read;	/* Readpointer for statusbuffer     */
+	char *msg_buf_end;	/* Pointer to end of statusbuffer   */
+	int sndcount[ICN_BCH];	/* Byte-counters for B-Ch.-send     */
+	pqueue *spqueue[ICN_BCH];	/* Pointers to start of Send-Queue  */
+#ifdef DEBUG_RCVCALLBACK
+	int akt_pending[ICN_BCH];
+	int max_pending[ICN_BCH];
+#endif
+} icn_dev;
+
+typedef icn_dev *icn_devptr;
+
+#ifdef __KERNEL__
+static icn_dev *dev = (icn_dev *) 0;
+static icn_dev *dev2 = (icn_dev *) 0;
+
+/* With modutils >= 1.1.67 Integers can be changed while loading a
+ * module. For this reason define the Port-Base an Shmem-Base as
+ * integers.
+ */
+int portbase = ICN_BASEADDR;
+int membase = ICN_MEMADDR;
+char *icn_id = "\0";
+char *icn_id2 = "\0";
+static char regname[35];	/* Name used for port/mem-registration */
+
+#endif				/* __KERNEL__ */
+
+/* Utility-Macros */
+
+/* Return true, if there is a free transmit-buffer */
+#define sbfree (((dev->shmem->data_control.scns+1) & 0xf) != \
+                dev->shmem->data_control.scnr)
+
+/* Switch to next transmit-buffer */
+#define sbnext (dev->shmem->data_control.scns = \
+               ((dev->shmem->data_control.scns+1) & 0xf))
+
+/* Shortcuts for transmit-buffer-access */
+#define sbuf_n dev->shmem->data_control.scns
+#define sbuf_d dev->shmem->data_buffers.send_buf[sbuf_n].data
+#define sbuf_l dev->shmem->data_buffers.send_buf[sbuf_n].length
+#define sbuf_f dev->shmem->data_buffers.send_buf[sbuf_n].endflag
+
+/* Return true, if there is receive-data is available */
+#define rbavl  (dev->shmem->data_control.ecnr != \
+                dev->shmem->data_control.ecns)
+
+/* Switch to next receive-buffer */
+#define rbnext (dev->shmem->data_control.ecnr = \
+               ((dev->shmem->data_control.ecnr+1) & 0xf))
+
+/* Shortcuts for receive-buffer-access */
+#define rbuf_n dev->shmem->data_control.ecnr
+#define rbuf_d dev->shmem->data_buffers.receive_buf[rbuf_n].data
+#define rbuf_l dev->shmem->data_buffers.receive_buf[rbuf_n].length
+#define rbuf_f dev->shmem->data_buffers.receive_buf[rbuf_n].endflag
+
+/* Shortcuts for command-buffer-access */
+#define cmd_o (dev->shmem->comm_control.pcio_o)
+#define cmd_i (dev->shmem->comm_control.pcio_i)
+
+/* Return free space in command-buffer */
+#define cmd_free ((cmd_i>=cmd_o)?0x100-cmd_i+cmd_o:cmd_o-cmd_i)
+
+/* Shortcuts for message-buffer-access */
+#define msg_o (dev->shmem->comm_control.iopc_o)
+#define msg_i (dev->shmem->comm_control.iopc_i)
+
+/* Return length of Message, if avail. */
+#define msg_avail ((msg_o>msg_i)?0x100-msg_o+msg_i:msg_i-msg_o)
+
+#define MIN(a,b) ((a<b)?a:b)
+#define MAX(a,b) ((a>b)?a:b)
+
+/* Hopefully, a separate resource-registration-scheme for shared-memory
+ * will be introduced into the kernel. Until then, we use the normal
+ * routines, designed for port-registration.
+ */
+#define check_shmem   check_region
+#define release_shmem release_region
+#define request_shmem request_region
+
+#endif				/* defined(__KERNEL__) || defined(__DEBUGVAR__) */
+#endif				/* icn_h */
diff -u --recursive --new-file v1.3.68/linux/drivers/isdn/isdn_cards.c linux/drivers/isdn/isdn_cards.c
--- v1.3.68/linux/drivers/isdn/isdn_cards.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/isdn/isdn_cards.c	Mon Feb 26 11:58:04 1996
@@ -0,0 +1,21 @@
+
+#include <linux/config.h>
+
+#ifdef CONFIG_ISDN_DRV_ICN
+extern void icn_init(void);
+#endif
+
+#ifdef CONFIG_ISDN_DRV_TELES
+extern void teles_init(void);
+#endif
+
+void isdn_cards_init(void)
+{
+#if CONFIG_ISDN_DRV_ICN
+        icn_init();
+#endif
+#if CONFIG_ISDN_DRV_TELES
+        teles_init();
+#endif
+}
+
diff -u --recursive --new-file v1.3.68/linux/drivers/isdn/isdn_cards.h linux/drivers/isdn/isdn_cards.h
--- v1.3.68/linux/drivers/isdn/isdn_cards.h	Thu Jan  1 02:00:00 1970
+++ linux/drivers/isdn/isdn_cards.h	Mon Feb 26 11:58:04 1996
@@ -0,0 +1,3 @@
+
+extern void isdn_cards_init(void);
+
diff -u --recursive --new-file v1.3.68/linux/drivers/isdn/isdn_common.c linux/drivers/isdn/isdn_common.c
--- v1.3.68/linux/drivers/isdn/isdn_common.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/isdn/isdn_common.c	Mon Feb 26 11:58:05 1996
@@ -0,0 +1,1887 @@
+/* $Id: isdn_common.c,v 1.4 1996/02/11 02:33:26 fritz Exp fritz $
+ *
+ * Linux ISDN subsystem, common used functions (linklevel).
+ *
+ * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1995,96    Thinking Objects Software GmbH Wuerzburg
+ * Copyright 1995,96    by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * 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_common.c,v $
+ * Revision 1.4  1996/02/11 02:33:26  fritz
+ * Fixed bug in main timer-dispatcher.
+ * Bugfix: Lot of tty-callbacks got called regardless of the events already
+ * been handled by network-devices.
+ * Changed ioctl-names.
+ *
+ * Revision 1.3  1996/01/22 05:16:11  fritz
+ * Changed ioctl-names.
+ * Fixed bugs in isdn_open and isdn_close regarding PPP_MINOR.
+ *
+ * Revision 1.2  1996/01/21 16:52:40  fritz
+ * Support for sk_buffs added, changed header-stuffing.
+ *
+ * Revision 1.1  1996/01/09 04:12:52  fritz
+ * Initial revision
+ *
+ */
+
+#ifndef STANDALONE
+#include <linux/config.h>
+#endif
+#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"
+#include "isdn_tty.h"
+#include "isdn_net.h"
+#include "isdn_ppp.h"
+#include "isdn_cards.h"
+
+
+
+/* Debugflags */
+#undef  ISDN_DEBUG_STATCALLB
+
+isdn_dev *dev = (isdn_dev *) 0;
+
+static int  has_exported = 0;
+static char *isdn_revision      = "$Revision: 1.4 $";
+
+extern char *isdn_net_revision;
+extern char *isdn_tty_revision;
+#ifdef CONFIG_ISDN_PPP
+extern char *isdn_ppp_revision;
+#else
+static char *isdn_ppp_revision = ": none $";
+#endif
+
+void isdn_MOD_INC_USE_COUNT(void)
+{
+	MOD_INC_USE_COUNT;
+}
+
+void isdn_MOD_DEC_USE_COUNT(void)
+{
+	MOD_DEC_USE_COUNT;
+}
+
+#if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP)
+void isdn_dumppkt(char *s, u_char * p, int len, int dumplen)
+{
+	int dumpc;
+
+	printk(KERN_DEBUG "%s(%d) ", s, len);
+	for (dumpc = 0; (dumpc < dumplen) && (len); len--, dumpc++)
+		printk(" %02x", *p++);
+	printk("\n");
+}
+#endif
+
+/* Try to allocate a new buffer, link it into queue. */
+u_char *
+ isdn_new_buf(pqueue ** queue, int length)
+{
+	pqueue *p;
+	pqueue *q;
+
+	if ((p = *queue)) {
+		while (p) {
+			q = p;
+			p = (pqueue *) p->next;
+		}
+		p = (pqueue *) kmalloc(sizeof(pqueue) + length, GFP_ATOMIC);
+		q->next = (u_char *) p;
+	} else
+		p = *queue = (pqueue *) kmalloc(sizeof(pqueue) + length, GFP_ATOMIC);
+	if (p) {
+		p->size = sizeof(pqueue) + length;
+		p->length = length;
+		p->next = NULL;
+		p->rptr = p->buffer;
+		return p->buffer;
+	} else {
+		return (u_char *) NULL;
+	}
+}
+
+static void isdn_free_queue(pqueue ** queue)
+{
+	pqueue *p;
+	pqueue *q;
+
+	p = *queue;
+	while (p) {
+		q = p;
+		p = (pqueue *) p->next;
+		kfree_s(q, q->size);
+	}
+	*queue = (pqueue *) 0;
+}
+
+int isdn_dc2minor(int di, int ch)
+{
+	int i;
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+		if (dev->chanmap[i] == ch && dev->drvmap[i] == di)
+			return i;
+	return -1;
+}
+
+static int isdn_timer_cnt1 = 0;
+static int isdn_timer_cnt2 = 0;
+
+static void isdn_timer_funct(ulong dummy)
+{
+	int tf = dev->tflags;
+
+	if (tf & ISDN_TIMER_FAST) {
+		if (tf & ISDN_TIMER_MODEMREAD)
+			isdn_tty_readmodem();
+		if (tf & ISDN_TIMER_MODEMPLUS)
+			isdn_tty_modem_escape();
+		if (tf & ISDN_TIMER_MODEMXMIT)
+			isdn_tty_modem_xmit();
+	}
+	if (tf & ISDN_TIMER_SLOW) {
+		if (++isdn_timer_cnt1 > ISDN_TIMER_02SEC) {
+			isdn_timer_cnt1 = 0;
+			if (tf & ISDN_TIMER_NETDIAL)
+				isdn_net_dial();
+		}
+                if (++isdn_timer_cnt2 > ISDN_TIMER_1SEC) {
+                        isdn_timer_cnt2 = 0;
+                        if (tf & ISDN_TIMER_NETHANGUP)
+                                isdn_net_autohup();
+                        if (tf & ISDN_TIMER_MODEMRING)
+                                isdn_tty_modem_ring();
+#if (defined CONFIG_ISDN_PPP ) && (defined ISDN_CONFIG_MPP)
+                        if (tf & ISDN_TIMER_IPPP)
+                                isdn_ppp_timer_timeout();
+#endif
+                }
+	}
+	if (tf) {
+                int flags;
+
+		save_flags(flags);
+		cli();
+		del_timer(&dev->timer);
+		dev->timer.function = isdn_timer_funct;
+		dev->timer.expires = jiffies + ISDN_TIMER_RES;
+		add_timer(&dev->timer);
+		restore_flags(flags);
+	}
+}
+
+void isdn_timer_ctrl(int tf, int onoff)
+{
+	int flags;
+
+	save_flags(flags);
+	cli();
+	if ((tf & ISDN_TIMER_SLOW) && (!(dev->tflags & ISDN_TIMER_SLOW))) {
+		/* If the slow-timer wasn't activated until now */
+		isdn_timer_cnt1 = 0;
+		isdn_timer_cnt2 = 0;
+	}
+	if (onoff)
+		dev->tflags |= tf;
+	else
+		dev->tflags &= ~tf;
+	if (dev->tflags) {
+		del_timer(&dev->timer);
+		dev->timer.function = isdn_timer_funct;
+		dev->timer.expires = jiffies + ISDN_TIMER_RES;
+		add_timer(&dev->timer);
+	}
+	restore_flags(flags);
+}
+
+/* Receive a packet from B-Channel. (Called from low-level-module)
+ * 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 di, int channel, u_char * buf, int len)
+{
+	ulong flags;
+	char *p;
+	int i;
+	int midx;
+
+	if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+		return;
+        if ((i = isdn_dc2minor(di,channel))==-1)
+                return;
+	/* First, try to deliver data to network-device */
+	if (isdn_net_receive_callback(i, buf, len))
+		return;
+	/* No network-device found, deliver to tty or raw-channel */
+	if (len) {
+		save_flags(flags);
+		cli();
+                midx = dev->m_idx[i];
+                if (dev->mdm.atmodem[midx].mdmreg[13] & 2)
+                        /* T.70 decoding: Simply throw away the T.70 header (4 bytes) */
+                        if ((buf[0] == 1) && ((buf[1] == 0) || (buf[1] == 1))) {
+#ifdef ISDN_DEBUG_MODEM_DUMP
+                                isdn_dumppkt("T70strip1:", buf, len, len);
+#endif
+                                buf += 4;
+                                len -= 4;
+#ifdef ISDN_DEBUG_MODEM_DUMP
+                                isdn_dumppkt("T70strip2:", buf, len, len);
+#endif
+                        }
+                /* Try to deliver directly via tty-flip-buf if queue is empty */
+                if (!dev->drv[di]->rpqueue[channel])
+                        if (isdn_tty_try_read(midx, buf, len)) {
+                                restore_flags(flags);
+                                return;
+                        }
+                /* Direct deliver failed or queue wasn't empty.
+                 * Queue up for later dequeueing via timer-irq.
+                 */
+                p = isdn_new_buf(&dev->drv[di]->rpqueue[channel], len);
+                if (!p) {
+                        printk(KERN_WARNING "isdn: malloc of rcvbuf failed, dropping.\n");
+                        dev->drv[di]->rcverr[channel]++;
+                        restore_flags(flags);
+                        return;
+                } else {
+                        memcpy(p, buf, len);
+                        dev->drv[di]->rcvcount[channel] += len;
+                }
+                /* Schedule dequeuing */
+                if ((dev->modempoll) && (midx >= 0)) {
+                        if (dev->mdm.rcvsched[midx])
+                                isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
+                }
+                wake_up_interruptible(&dev->drv[di]->rcv_waitq[channel]);
+		restore_flags(flags);
+	}
+}
+
+void isdn_all_eaz(int di, int ch)
+{
+	isdn_ctrl cmd;
+
+	cmd.driver = di;
+	cmd.arg = ch;
+	cmd.command = ISDN_CMD_SETEAZ;
+	cmd.num[0] = '\0';
+	(void) dev->drv[di]->interface->command(&cmd);
+}
+
+static int isdn_status_callback(isdn_ctrl * c)
+{
+	int di;
+	int mi;
+	ulong flags;
+	int i;
+	int r;
+	isdn_ctrl cmd;
+
+	di = c->driver;
+        i = isdn_dc2minor(di, c->arg);
+	switch (c->command) {
+                case ISDN_STAT_BSENT:
+			if (i<0)
+				return -1;
+                        if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+                                return 0;
+                        if (isdn_net_stat_callback(i, c->command))
+                                return 0;
+#if FUTURE
+                        isdn_tty_bsent(di, c->arg);
+#endif
+                        wake_up_interruptible(&dev->drv[di]->snd_waitq[c->arg]);
+                        break;
+                case ISDN_STAT_STAVAIL:
+                        save_flags(flags);
+                        cli();
+                        dev->drv[di]->stavail += c->arg;
+                        restore_flags(flags);
+                        wake_up_interruptible(&dev->drv[di]->st_waitq);
+                        break;
+                case ISDN_STAT_RUN:
+                        dev->drv[di]->running = 1;
+                        for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+                                if (dev->drvmap[i] == di)
+                                        isdn_all_eaz(di, dev->chanmap[i]);
+                        break;
+                case ISDN_STAT_STOP:
+                        dev->drv[di]->running = 0;
+                        break;
+                case ISDN_STAT_ICALL:
+			if (i<0)
+				return -1;
+#ifdef ISDN_DEBUG_STATCALLB
+                        printk(KERN_DEBUG "ICALL (net): %d %ld %s\n", di, c->arg, c->num);
+#endif
+                        if (dev->global_flags & ISDN_GLOBAL_STOPPED) {
+                                cmd.driver = di;
+                                cmd.arg = c->arg;
+                                cmd.command = ISDN_CMD_HANGUP;
+                                dev->drv[di]->interface->command(&cmd);
+                                return 0;
+                        }
+
+			/* Try to find a network-interface which will accept incoming call */
+			r = isdn_net_find_icall(di, c->arg, i, c->num);
+			switch (r) {
+                                case 0:
+                                        /* No network-device replies. Schedule RING-message to
+                                         * tty and set RI-bit of modem-status.
+                                         */
+                                        if ((mi = isdn_tty_find_icall(di, c->arg, c->num)) >= 0) {
+                                                dev->mdm.msr[mi] |= UART_MSR_RI;
+                                                isdn_tty_modem_result(2, &dev->mdm.info[mi]);
+                                                isdn_timer_ctrl(ISDN_TIMER_MODEMRING, 1);
+                                        } else if (dev->drv[di]->reject_bus) {
+                                                cmd.driver = di;
+                                                cmd.arg = c->arg;
+                                                cmd.command = ISDN_CMD_HANGUP;
+                                                dev->drv[di]->interface->command(&cmd);
+                                        }
+                                        break;
+                                case 1:
+                                        /* Schedule connection-setup */
+                                        isdn_net_dial();
+                                        cmd.driver = di;
+                                        cmd.arg = c->arg;
+                                        cmd.command = ISDN_CMD_ACCEPTD;
+                                        dev->drv[di]->interface->command(&cmd);
+                                        break;
+                                case 2:	/* For calling back, first reject incoming call ... */
+                                case 3:	/* Interface found, but down, reject call actively  */
+                                        cmd.driver = di;
+                                        cmd.arg = c->arg;
+                                        cmd.command = ISDN_CMD_HANGUP;
+                                        dev->drv[di]->interface->command(&cmd);
+                                        if (r == 2)
+                                                /* ... then start dialing. */
+                                                isdn_net_dial();
+                                        break;
+			}
+                        return 0;
+                        break;
+                case ISDN_STAT_CINF:
+			if (i<0)
+				return -1;
+#ifdef ISDN_DEBUG_STATCALLB
+                        printk(KERN_DEBUG "CINF: %ld %s\n", c->arg, c->num);
+#endif
+                        if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+                                return 0;
+                        if (strcmp(c->num, "0"))
+                                isdn_net_stat_callback(i, c->command);
+                        break;
+                case ISDN_STAT_CAUSE:
+#ifdef ISDN_DEBUG_STATCALLB
+                        printk(KERN_DEBUG "CAUSE: %ld %s\n", c->arg, c->num);
+#endif
+                        printk(KERN_INFO "isdn: cause: %s\n", c->num);
+                        break;
+                case ISDN_STAT_DCONN:
+			if (i<0)
+				return -1;
+#ifdef ISDN_DEBUG_STATCALLB
+                        printk(KERN_DEBUG "DCONN: %ld\n", c->arg);
+#endif
+                        if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+                                return 0;
+                        /* Find any network-device, waiting for D-channel setup */
+                        if (isdn_net_stat_callback(i, c->command))
+                                break;
+			if ((mi = dev->m_idx[i]) >= 0)
+				/* If any tty has just dialed-out, setup B-Channel */
+				if (dev->mdm.info[mi].flags &
+				    (ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE)) {
+					if (dev->mdm.dialing[mi] == 1) {
+						dev->mdm.dialing[mi] = 2;
+						cmd.driver = di;
+						cmd.arg = c->arg;
+						cmd.command = ISDN_CMD_ACCEPTB;
+						dev->drv[di]->interface->command(&cmd);
+						return 0;
+					}
+				}
+                        break;
+                case ISDN_STAT_DHUP:
+			if (i<0)
+				return -1;
+#ifdef ISDN_DEBUG_STATCALLB
+                        printk(KERN_DEBUG "DHUP: %ld\n", c->arg);
+#endif
+                        if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+                                return 0;
+                        dev->drv[di]->flags &= ~(1 << (c->arg));
+                        isdn_info_update();
+                        /* Signal hangup to network-devices */
+                        if (isdn_net_stat_callback(i, c->command))
+                                break;
+			if ((mi = dev->m_idx[i]) >= 0) {
+				/* Signal hangup to tty-device */
+				if (dev->mdm.info[mi].flags &
+				    (ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE)) {
+					if (dev->mdm.dialing[mi] == 1) {
+						dev->mdm.dialing[mi] = 0;
+						isdn_tty_modem_result(7, &dev->mdm.info[mi]);
+					}
+					if (dev->mdm.online[mi])
+						isdn_tty_modem_result(3, &dev->mdm.info[mi]);
+#ifdef ISDN_DEBUG_MODEM_HUP
+					printk(KERN_DEBUG "Mhup in ISDN_STAT_DHUP\n");
+#endif
+					isdn_tty_modem_hup(&dev->mdm.info[mi]);
+					dev->mdm.msr[mi] &= ~(UART_MSR_DCD | UART_MSR_RI);
+					return 0;
+				}
+			}
+                        break;
+                case ISDN_STAT_BCONN:
+			if (i<0)
+				return -1;
+#ifdef ISDN_DEBUG_STATCALLB
+                        printk(KERN_DEBUG "BCONN: %ld\n", c->arg);
+#endif
+                        /* Signal B-channel-connect to network-devices */
+                        if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+                                return 0;
+                        dev->drv[di]->flags |= (1 << (c->arg));
+                        isdn_info_update();
+                        if (isdn_net_stat_callback(i, c->command))
+                                break;
+			if ((mi = dev->m_idx[i]) >= 0) {
+				/* Schedule CONNECT-Message to any tty, waiting for it and
+				 * set DCD-bit of it's modem-status.
+				 */
+				if (dev->mdm.info[mi].flags &
+				    (ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE)) {
+					dev->mdm.msr[mi] |= UART_MSR_DCD;
+					if (dev->mdm.dialing[mi])
+						dev->mdm.dialing[mi] = 0;
+					dev->mdm.rcvsched[mi] = 1;
+					isdn_tty_modem_result(5, &dev->mdm.info[mi]);
+				}
+			}
+                        break;
+                case ISDN_STAT_BHUP:
+			if (i<0)
+				return -1;
+#ifdef ISDN_DEBUG_STATCALLB
+                        printk(KERN_DEBUG "BHUP: %ld\n", c->arg);
+#endif
+                        if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+                                return 0;
+                        dev->drv[di]->flags &= ~(1 << (c->arg));
+                        isdn_info_update();
+			if ((mi = dev->m_idx[i]) >= 0) {
+				/* Signal hangup to tty-device, schedule NO CARRIER-message */
+				if (dev->mdm.info[mi].flags &
+				    (ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE)) {
+					dev->mdm.msr[mi] &= ~(UART_MSR_DCD | UART_MSR_RI);
+					if (dev->mdm.online[mi])
+						isdn_tty_modem_result(3, &dev->mdm.info[mi]);
+#ifdef ISDN_DEBUG_MODEM_HUP
+					printk(KERN_DEBUG "Mhup in ISDN_STAT_BHUP\n");
+#endif
+					isdn_tty_modem_hup(&dev->mdm.info[mi]);
+				}
+			}
+                        break;
+                case ISDN_STAT_NODCH:
+			if (i<0)
+				return -1;
+#ifdef ISDN_DEBUG_STATCALLB
+                        printk(KERN_DEBUG "NODCH: %ld\n", c->arg);
+#endif
+                        if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+                                return 0;
+                        if (isdn_net_stat_callback(i, c->command))
+                                break;
+			if ((mi = dev->m_idx[i]) >= 0) {
+				if (dev->mdm.info[mi].flags &
+				    (ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE)) {
+					if (dev->mdm.dialing[mi]) {
+						dev->mdm.dialing[mi] = 0;
+						isdn_tty_modem_result(6, &dev->mdm.info[mi]);
+					}
+					dev->mdm.msr[mi] &= ~UART_MSR_DCD;
+					if (dev->mdm.online[mi]) {
+						isdn_tty_modem_result(3, &dev->mdm.info[mi]);
+						dev->mdm.online[mi] = 0;
+					}
+				}
+			}
+                        break;
+                case ISDN_STAT_ADDCH:
+                        break;
+                case ISDN_STAT_UNLOAD:
+                        save_flags(flags);
+                        cli();
+                        for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+                                if (dev->drvmap[i] == di) {
+                                        dev->drvmap[i] = -1;
+                                        dev->chanmap[i] = -1;
+                                        dev->mdm.info[i].isdn_driver = -1;
+                                        dev->mdm.info[i].isdn_channel = -1;
+                                        isdn_info_update();
+                                }
+                        dev->drivers--;
+                        dev->channels -= dev->drv[di]->channels;
+                        kfree(dev->drv[di]->rcverr);
+                        kfree(dev->drv[di]->rcvcount);
+                        for (i = 0; i < dev->drv[di]->channels; i++)
+                                isdn_free_queue(&dev->drv[di]->rpqueue[i]);
+                        kfree(dev->drv[di]->rcv_waitq);
+                        kfree(dev->drv[di]->snd_waitq);
+                        kfree(dev->drv[di]);
+                        dev->drv[di] = NULL;
+                        dev->drvid[di][0] = '\0';
+                        isdn_info_update();
+                        restore_flags(flags);
+                        return 0;
+                default:
+                        return -1;
+	}
+	return 0;
+}
+
+/*
+ * Get integer from char-pointer, set pointer to end of number
+ */
+int isdn_getnum(char **p)
+{
+	int v = -1;
+
+	while (*p[0] >= '0' && *p[0] <= '9')
+		v = ((v < 0) ? 0 : (v * 10)) + (int) ((*p[0]++) - '0');
+	return v;
+}
+
+int isdn_readbchan(int di, int channel, u_char * buf, u_char * fp, int len, int user)
+{
+	int avail;
+	int left;
+	int count;
+	int copy_l;
+	int dflag;
+	int flags;
+	pqueue *p;
+	u_char *cp;
+
+	if (!dev->drv[di]->rpqueue[channel]) {
+		if (user)
+			interruptible_sleep_on(&dev->drv[di]->rcv_waitq[channel]);
+		else
+			return 0;
+	}
+	if (!dev->drv[di])
+		return 0;
+	save_flags(flags);
+	cli();
+	avail = dev->drv[di]->rcvcount[channel];
+	restore_flags(flags);
+	left = MIN(len, avail);
+	cp = buf;
+	count = 0;
+	while (left) {
+		if ((copy_l = dev->drv[di]->rpqueue[channel]->length) > left) {
+			copy_l = left;
+			dflag = 0;
+		} else
+			dflag = 1;
+		p = dev->drv[di]->rpqueue[channel];
+		if (user)
+			memcpy_tofs(cp, p->rptr, copy_l);
+		else
+			memcpy(cp, p->rptr, copy_l);
+		if (fp) {
+			memset(fp, 0, copy_l);
+			fp += copy_l;
+		}
+		left -= copy_l;
+		count += copy_l;
+		cp += copy_l;
+		if (dflag) {
+			if (fp)
+				*(fp - 1) = 0xff;
+			save_flags(flags);
+			cli();
+			dev->drv[di]->rpqueue[channel] = (pqueue *) p->next;
+			kfree_s(p, p->size);
+			restore_flags(flags);
+		} else {
+			p->rptr += copy_l;
+			p->length -= copy_l;
+		}
+		save_flags(flags);
+		cli();
+		dev->drv[di]->rcvcount[channel] -= copy_l;
+		restore_flags(flags);
+	}
+	return count;
+}
+
+static __inline int isdn_minor2drv(int minor)
+{
+	return (dev->drvmap[minor]);
+}
+
+static __inline int isdn_minor2chan(int minor)
+{
+	return (dev->chanmap[minor]);
+}
+
+static char *
+ isdn_statstr(void)
+{
+	static char istatbuf[2048];
+	char *p;
+	int i;
+
+	sprintf(istatbuf, "idmap:\t");
+	p = istatbuf + strlen(istatbuf);
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		sprintf(p, "%s ", (dev->drvmap[i] < 0) ? "-" : dev->drvid[dev->drvmap[i]]);
+		p = istatbuf + strlen(istatbuf);
+	}
+	sprintf(p, "\nchmap:\t");
+	p = istatbuf + strlen(istatbuf);
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		sprintf(p, "%d ", dev->chanmap[i]);
+		p = istatbuf + strlen(istatbuf);
+	}
+	sprintf(p, "\ndrmap:\t");
+	p = istatbuf + strlen(istatbuf);
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		sprintf(p, "%d ", dev->drvmap[i]);
+		p = istatbuf + strlen(istatbuf);
+	}
+	sprintf(p, "\nusage:\t");
+	p = istatbuf + strlen(istatbuf);
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		sprintf(p, "%d ", dev->usage[i]);
+		p = istatbuf + strlen(istatbuf);
+	}
+	sprintf(p, "\nflags:\t");
+	p = istatbuf + strlen(istatbuf);
+	for (i = 0; i < ISDN_MAX_DRIVERS; i++) {
+		if (dev->drv[i]) {
+			sprintf(p, "%ld ", dev->drv[i]->flags);
+			p = istatbuf + strlen(istatbuf);
+		} else {
+			sprintf(p, "? ");
+			p = istatbuf + strlen(istatbuf);
+		}
+	}
+	sprintf(p, "\nphone:\t");
+	p = istatbuf + strlen(istatbuf);
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		sprintf(p, "%s ", dev->num[i]);
+		p = istatbuf + strlen(istatbuf);
+	}
+	sprintf(p, "\n");
+	return istatbuf;
+}
+
+/* Module interface-code */
+
+void isdn_info_update(void)
+{
+	infostruct *p = dev->infochain;
+
+	while (p) {
+		*(p->private) = 1;
+		p = (infostruct *) p->next;
+	}
+	wake_up_interruptible(&(dev->info_waitq));
+}
+
+static int isdn_read(struct inode *inode, struct file *file, char *buf, int count)
+{
+	uint minor = MINOR(inode->i_rdev);
+	int len = 0;
+	ulong flags;
+	int drvidx;
+	int chidx;
+
+	if (minor == ISDN_MINOR_STATUS) {
+		char *p;
+		if (!file->private_data)
+			interruptible_sleep_on(&(dev->info_waitq));
+		save_flags(flags);
+		p = isdn_statstr();
+		restore_flags(flags);
+		file->private_data = 0;
+		if ((len = strlen(p)) <= count) {
+			memcpy_tofs(buf, p, len);
+			file->f_pos += len;
+			return len;
+		}
+		return 0;
+	}
+	if (!dev->drivers)
+		return -ENODEV;
+	if (minor < ISDN_MINOR_CTRL) {
+		drvidx = isdn_minor2drv(minor);
+		if (drvidx < 0)
+			return -ENODEV;
+		if (!dev->drv[drvidx]->running)
+			return -ENODEV;
+		chidx = isdn_minor2chan(minor);
+		len = isdn_readbchan(drvidx, chidx, buf, 0, count, 1);
+		file->f_pos += len;
+		return len;
+	}
+	if (minor <= ISDN_MINOR_CTRLMAX) {
+		drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
+		if (drvidx < 0)
+			return -ENODEV;
+		if (!dev->drv[drvidx]->stavail)
+			interruptible_sleep_on(&(dev->drv[drvidx]->st_waitq));
+		if (dev->drv[drvidx]->interface->readstat)
+			len = dev->drv[drvidx]->interface->
+			    readstat(buf, MIN(count, dev->drv[drvidx]->stavail), 1);
+		else
+			len = 0;
+		save_flags(flags);
+		cli();
+		dev->drv[drvidx]->stavail -= len;
+		restore_flags(flags);
+		file->f_pos += len;
+		return len;
+	}
+#ifdef CONFIG_ISDN_PPP
+	if (minor <= ISDN_MINOR_PPPMAX)
+		return (isdn_ppp_read(minor - ISDN_MINOR_PPP, file, buf, count));
+#endif
+	return -ENODEV;
+}
+
+static int isdn_lseek(struct inode *inode, struct file *file, off_t offset, int orig)
+{
+	return -ESPIPE;
+}
+
+static int isdn_write(struct inode *inode, struct file *file, const char *buf, int count)
+{
+	uint minor = MINOR(inode->i_rdev);
+	int drvidx;
+	int chidx;
+
+	if (minor == ISDN_MINOR_STATUS)
+		return -EPERM;
+	if (!dev->drivers)
+		return -ENODEV;
+	if (minor < ISDN_MINOR_CTRL) {
+		drvidx = isdn_minor2drv(minor);
+		if (drvidx < 0)
+			return -ENODEV;
+		if (!dev->drv[drvidx]->running)
+			return -ENODEV;
+		chidx = isdn_minor2chan(minor);
+		while (dev->drv[drvidx]->interface->writebuf(drvidx, chidx, buf, count, 1) != count)
+			interruptible_sleep_on(&dev->drv[drvidx]->snd_waitq[chidx]);
+		return count;
+	}
+	if (minor <= ISDN_MINOR_CTRLMAX) {
+		drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
+		if (drvidx < 0)
+			return -ENODEV;
+		/*
+		 * We want to use the isdnctrl device to load the firmware
+		 *
+		 if (!dev->drv[drvidx]->running)
+		 return -ENODEV;
+		 */
+		if (dev->drv[drvidx]->interface->writecmd)
+			return (dev->drv[drvidx]->interface->writecmd(buf, count, 1));
+		else
+			return count;
+	}
+#ifdef CONFIG_ISDN_PPP
+	if (minor <= ISDN_MINOR_PPPMAX)
+		return (isdn_ppp_write(minor - ISDN_MINOR_PPP, file, buf, count));
+#endif
+	return -ENODEV;
+}
+
+static int isdn_select(struct inode *inode, struct file *file, int type, select_table * st)
+{
+	uint minor = MINOR(inode->i_rdev);
+
+	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_CTRLMAX)
+		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;
+}
+
+static int isdn_set_allcfg(char *src)
+{
+	int ret;
+	int i;
+	ulong flags;
+	char buf[1024];
+	isdn_net_ioctl_cfg cfg;
+	isdn_net_ioctl_phone phone;
+
+	if ((ret = isdn_net_rmall()))
+		return ret;
+	save_flags(flags);
+	cli();
+	if ((ret = verify_area(VERIFY_READ, (void *) src, sizeof(int)))) {
+		restore_flags(flags);
+		return ret;
+	}
+	memcpy_tofs((char *) &i, src, sizeof(int));
+	while (i) {
+		char *c;
+		char *c2;
+
+		if ((ret = verify_area(VERIFY_READ, (void *) src, sizeof(cfg)))) {
+			restore_flags(flags);
+			return ret;
+		}
+		memcpy_tofs((char *) &cfg, src, sizeof(cfg));
+		src += sizeof(cfg);
+		if (!isdn_net_new(cfg.name, NULL)) {
+			restore_flags(flags);
+			return -EIO;
+		}
+		if ((ret = isdn_net_setcfg(&cfg))) {
+			restore_flags(flags);
+			return ret;
+		}
+		if ((ret = verify_area(VERIFY_READ, (void *) src, sizeof(buf)))) {
+			restore_flags(flags);
+			return ret;
+		}
+		memcpy_fromfs(buf, src, sizeof(buf));
+		src += sizeof(buf);
+		c = buf;
+		while (*c) {
+			if ((c2 = strchr(c, ' ')))
+				*c2++ = '\0';
+			strcpy(phone.phone, c);
+			strcpy(phone.name, cfg.name);
+			phone.outgoing = 0;
+			if ((ret = isdn_net_addphone(&phone))) {
+				restore_flags(flags);
+				return ret;
+			}
+			if (c2)
+				c = c2;
+			else
+				c += strlen(c);
+		}
+		if ((ret = verify_area(VERIFY_READ, (void *) src, sizeof(buf)))) {
+			restore_flags(flags);
+			return ret;
+		}
+		memcpy_fromfs(buf, src, sizeof(buf));
+		src += sizeof(buf);
+		c = buf;
+		while (*c) {
+			if ((c2 = strchr(c, ' ')))
+				*c2++ = '\0';
+			strcpy(phone.phone, c);
+			strcpy(phone.name, cfg.name);
+			phone.outgoing = 1;
+			if ((ret = isdn_net_addphone(&phone))) {
+				restore_flags(flags);
+				return ret;
+			}
+			if (c2)
+				c = c2;
+			else
+				c += strlen(c);
+		}
+		i--;
+	}
+	restore_flags(flags);
+	return 0;
+}
+
+static int isdn_get_allcfg(char *dest)
+{
+	isdn_net_ioctl_cfg cfg;
+	isdn_net_ioctl_phone phone;
+	isdn_net_dev *p;
+	ulong flags;
+	int ret;
+
+	/* Walk through netdev-chain */
+	save_flags(flags);
+	cli();
+	p = dev->netdev;
+	while (p) {
+		if ((ret = verify_area(VERIFY_WRITE, (void *) dest, sizeof(cfg) + 10))) {
+			restore_flags(flags);
+			return ret;
+		}
+		strcpy(cfg.eaz, p->local.msn);
+		cfg.exclusive = p->local.exclusive;
+		if (p->local.pre_device >= 0) {
+			sprintf(cfg.drvid, "%s,%d", dev->drvid[p->local.pre_device],
+				p->local.pre_channel);
+		} else
+			cfg.drvid[0] = '\0';
+		cfg.onhtime = p->local.onhtime;
+		cfg.charge = p->local.charge;
+		cfg.l2_proto = p->local.l2_proto;
+		cfg.l3_proto = p->local.l3_proto;
+		cfg.p_encap = p->local.p_encap;
+		cfg.secure = (p->local.flags & ISDN_NET_SECURE) ? 1 : 0;
+		cfg.callback = (p->local.flags & ISDN_NET_CALLBACK) ? 1 : 0;
+		cfg.chargehup = (p->local.hupflags & 4) ? 1 : 0;
+		cfg.ihup = (p->local.hupflags & 8) ? 1 : 0;
+		memcpy_tofs(dest, p->local.name, 10);
+		dest += 10;
+		memcpy_tofs(dest, (char *) &cfg, sizeof(cfg));
+		dest += sizeof(cfg);
+		strcpy(phone.name, p->local.name);
+		phone.outgoing = 0;
+		if ((ret = isdn_net_getphones(&phone, dest)) < 0) {
+			restore_flags(flags);
+			return ret;
+		} else
+			dest += ret;
+		strcpy(phone.name, p->local.name);
+		phone.outgoing = 1;
+		if ((ret = isdn_net_getphones(&phone, dest)) < 0) {
+			restore_flags(flags);
+			return ret;
+		} else
+			dest += ret;
+		p = p->next;
+	}
+	restore_flags(flags);
+	return 0;
+}
+
+static int isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
+{
+	uint minor = MINOR(inode->i_rdev);
+	isdn_ctrl c;
+	int drvidx;
+	int chidx;
+	int ret;
+	char *s;
+	char name[10];
+	char bname[21];
+	isdn_ioctl_struct iocts;
+	isdn_net_ioctl_phone phone;
+	isdn_net_ioctl_cfg cfg;
+
+	if (minor == ISDN_MINOR_STATUS)
+		return -EPERM;
+	if (!dev->drivers)
+		return -ENODEV;
+	if (minor < ISDN_MINOR_CTRL) {
+		drvidx = isdn_minor2drv(minor);
+		if (drvidx < 0)
+			return -ENODEV;
+		chidx = isdn_minor2chan(minor);
+		if (!dev->drv[drvidx]->running)
+			return -ENODEV;
+		return 0;
+	}
+	if (minor <= ISDN_MINOR_CTRLMAX) {
+		switch (cmd) {
+#ifdef CONFIG_NETDEVICES
+                        case IIOCNETAIF:
+                                /* Add a network-interface */
+                                if (arg) {
+                                        if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(name))))
+                                                return ret;
+                                        memcpy_fromfs(name, (char *) arg, sizeof(name));
+                                        s = name;
+                                } else
+                                        s = NULL;
+                                if ((s = isdn_net_new(s, NULL))) {
+                                        if ((ret = verify_area(VERIFY_WRITE, (void *) arg, strlen(s) + 1)))
+                                                return ret;
+                                        memcpy_tofs((char *) arg, s, strlen(s) + 1);
+                                        return 0;
+                                } else
+                                        return -ENODEV;
+                        case IIOCNETASL:
+                                /* Add a slave to a network-interface */
+                                if (arg) {
+                                        if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(bname))))
+                                                return ret;
+                                        memcpy_fromfs(bname, (char *) arg, sizeof(bname));
+                                } else
+                                        return -EINVAL;
+                                if ((s = isdn_net_newslave(bname))) {
+                                        if ((ret = verify_area(VERIFY_WRITE, (void *) arg, strlen(s) + 1)))
+                                                return ret;
+                                        memcpy_tofs((char *) arg, s, strlen(s) + 1);
+                                        return 0;
+                                } else
+                                        return -ENODEV;
+                        case IIOCNETDIF:
+                                /* Delete a network-interface */
+                                if (arg) {
+                                        if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(name))))
+                                                return ret;
+                                        memcpy_fromfs(name, (char *) arg, sizeof(name));
+                                        return isdn_net_rm(name);
+                                } else
+                                        return -EINVAL;
+                        case IIOCNETSCF:
+                                /* Set configurable parameters of a network-interface */
+                                if (arg) {
+                                        if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(cfg))))
+                                                return ret;
+                                        memcpy_fromfs((char *) &cfg, (char *) arg, sizeof(cfg));
+                                        return isdn_net_setcfg(&cfg);
+                                } else
+                                        return -EINVAL;
+                        case IIOCNETGCF:
+                                /* Get configurable parameters of a network-interface */
+                                if (arg) {
+                                        if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(cfg))))
+                                                return ret;
+                                        memcpy_fromfs((char *) &cfg, (char *) arg, sizeof(cfg));
+                                        if (!(ret = isdn_net_getcfg(&cfg))) {
+                                                if ((ret = verify_area(VERIFY_WRITE, (void *) arg, sizeof(cfg))))
+                                                        return ret;
+                                                memcpy_tofs((char *) arg, (char *) &cfg, sizeof(cfg));
+                                        }
+                                        return ret;
+                                } else
+                                        return -EINVAL;
+                        case IIOCNETANM:
+                                /* Add a phone-number to a network-interface */
+                                if (arg) {
+                                        if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(phone))))
+                                                return ret;
+                                        memcpy_fromfs((char *) &phone, (char *) arg, sizeof(phone));
+                                        return isdn_net_addphone(&phone);
+                                } else
+                                        return -EINVAL;
+                        case IIOCNETGNM:
+                                /* Get list of phone-numbers of a network-interface */
+                                if (arg) {
+                                        if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(phone))))
+                                                return ret;
+                                        memcpy_fromfs((char *) &phone, (char *) arg, sizeof(phone));
+                                        return isdn_net_getphones(&phone, (char *) arg);
+                                } else
+                                        return -EINVAL;
+                        case IIOCNETDNM:
+                                /* Delete a phone-number of a network-interface */
+                                if (arg) {
+                                        memcpy_fromfs((char *) &phone, (char *) arg, sizeof(phone));
+                                        if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(phone))))
+                                                return ret;
+                                        return isdn_net_delphone(&phone);
+                                } else
+                                        return -EINVAL;
+                        case IIOCNETDIL:
+                                /* Force dialing of a network-interface */
+                                if (arg) {
+                                        if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(name))))
+                                                return ret;
+                                        memcpy_fromfs(name, (char *) arg, sizeof(name));
+                                        return isdn_net_force_dial(name);
+                                } else
+                                        return -EINVAL;
+#ifdef CONFIG_ISDN_PPP
+                        case IIOCNETALN:
+                                if(arg) {
+                                        if ((ret = verify_area(VERIFY_READ,
+                                                               (void*)arg,
+                                                               sizeof(name))))
+                                                return ret;
+                                } else
+                                        return -EINVAL;
+                                memcpy_fromfs(name,(char*)arg,sizeof(name));
+                                return isdn_ppp_dial_slave(name);
+                        case IIOCNETDLN:
+                                /* remove one link from bundle; removed for i4l 0.7.1  */
+                                return 2;
+#endif
+                        case IIOCNETHUP:
+                                /* Force hangup of a network-interface */
+                                if (arg) {
+                                        if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(name))))
+                                                return ret;
+                                        memcpy_fromfs(name, (char *) arg, sizeof(name));
+                                        return isdn_net_force_hangup(name);
+                                } else
+                                        return -EINVAL;
+                                break;
+#endif				/* CONFIG_NETDEVICES */
+                        case IIOCSETVER:
+                                dev->net_verbose = arg;
+                                printk(KERN_INFO "isdn: Verbose-Level is %d\n", dev->net_verbose);
+                                return 0;
+                        case IIOCSETGST:
+                                if (arg)
+                                        dev->global_flags |= ISDN_GLOBAL_STOPPED;
+                                else
+                                        dev->global_flags &= ~ISDN_GLOBAL_STOPPED;
+                                printk(KERN_INFO "isdn: Global Mode %s\n",
+                                       (dev->global_flags & ISDN_GLOBAL_STOPPED) ? "stopped" : "running");
+                                return 0;
+                        case IIOCSETBRJ:
+                                drvidx = -1;
+                                if (arg) {
+                                        int i;
+                                        char *p;
+                                        if ((ret = verify_area(VERIFY_READ, (void *) arg,
+                                                               sizeof(isdn_ioctl_struct))))
+                                                return ret;
+                                        memcpy_fromfs((char *) &iocts, (char *) arg, sizeof(isdn_ioctl_struct));
+                                        if (strlen(iocts.drvid)) {
+                                                if ((p = strchr(iocts.drvid, ',')))
+                                                        *p = 0;
+                                                drvidx = -1;
+                                                for (i = 0; i < ISDN_MAX_DRIVERS; i++)
+                                                        if (!(strcmp(dev->drvid[i], iocts.drvid))) {
+                                                                drvidx = i;
+                                                                break;
+                                                        }
+                                        }
+                                }
+                                if (drvidx == -1)
+                                        return -ENODEV;
+                                dev->drv[drvidx]->reject_bus = iocts.arg;
+                                return 0;
+                        case IIOCGETSET:
+                                /* Get complete setup (all network-interfaces and profile-
+                                   settings of all tty-devices */
+                                if (arg)
+                                        return (isdn_get_allcfg((char *) arg));
+                                else
+                                        return -EINVAL;
+                                break;
+                        case IIOCSETSET:
+                                /* Set complete setup (all network-interfaces and profile-
+                                   settings of all tty-devices */
+                                if (arg)
+                                        return (isdn_set_allcfg((char *) arg));
+                                else
+                                        return -EINVAL;
+                                break;
+                        case IIOCSIGPRF:
+                                dev->profd = current;
+                                return 0;
+                                break;
+                        case IIOCGETPRF:
+                                /* Get all Modem-Profiles */
+                                if (arg) {
+                                        char *p = (char *) arg;
+                                        int i;
+                                        
+                                        if ((ret = verify_area(VERIFY_WRITE, (void *) arg,
+                                                               (ISDN_MODEM_ANZREG + ISDN_MSNLEN)
+                                                               * ISDN_MAX_CHANNELS)))
+                                                return ret;
+                                        
+                                        for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+                                                memcpy_tofs(p, dev->mdm.atmodem[i].profile, ISDN_MODEM_ANZREG);
+                                                p += ISDN_MODEM_ANZREG;
+                                                memcpy_tofs(p, dev->mdm.atmodem[i].pmsn, ISDN_MSNLEN);
+                                                p += ISDN_MSNLEN;
+                                        }
+                                        return (ISDN_MODEM_ANZREG + ISDN_MSNLEN) * ISDN_MAX_CHANNELS;
+                                } else
+                                        return -EINVAL;
+                                break;
+                        case IIOCSETPRF:
+                                /* Set all Modem-Profiles */
+                                if (arg) {
+                                        char *p = (char *) arg;
+                                        int i;
+                                        
+                                        if ((ret = verify_area(VERIFY_READ, (void *) arg,
+                                                               (ISDN_MODEM_ANZREG + ISDN_MSNLEN)
+                                                               * ISDN_MAX_CHANNELS)))
+                                                return ret;
+                                        
+                                        for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+                                                memcpy_fromfs(dev->mdm.atmodem[i].profile, p, ISDN_MODEM_ANZREG);
+                                                p += ISDN_MODEM_ANZREG;
+                                                memcpy_fromfs(dev->mdm.atmodem[i].pmsn, p, ISDN_MSNLEN);
+                                                p += ISDN_MSNLEN;
+                                        }
+                                        return 0;
+                                } else
+                                        return -EINVAL;
+                                break;
+                        case IIOCSETMAP:
+                        case IIOCGETMAP:
+                                /* Set/Get MSN->EAZ-Mapping for a driver */
+                                if (arg) {
+                                        int i;
+                                        char *p;
+                                        char nstring[255];
+                                        
+                                        if ((ret = verify_area(VERIFY_READ, (void *) arg,
+                                                               sizeof(isdn_ioctl_struct))))
+                                                return ret;
+                                        memcpy_fromfs((char *) &iocts, (char *) arg, sizeof(isdn_ioctl_struct));
+                                        if (strlen(iocts.drvid)) {
+                                                drvidx = -1;
+                                                for (i = 0; i < ISDN_MAX_DRIVERS; i++)
+                                                        if (!(strcmp(dev->drvid[i], iocts.drvid))) {
+                                                                drvidx = i;
+                                                                break;
+                                                        }
+                                        } else
+                                                drvidx = 0;
+                                        if (drvidx == -1)
+                                                return -ENODEV;
+                                        if (cmd == IIOCSETMAP) {
+                                                if ((ret = verify_area(VERIFY_READ, (void *) iocts.arg, 255)))
+                                                        return ret;
+                                                memcpy_fromfs(nstring, (char *) iocts.arg, 255);
+                                                memset(dev->drv[drvidx]->msn2eaz, 0,
+                                                       sizeof(dev->drv[drvidx]->msn2eaz));
+                                                p = strtok(nstring, ",");
+                                                i = 0;
+                                                while ((p) && (i < 10)) {
+                                                        strcpy(dev->drv[drvidx]->msn2eaz[i++], p);
+                                                        p = strtok(NULL, ",");
+                                                }
+                                        } else {
+                                                p = nstring;
+                                                for (i = 0; i < 10; i++)
+                                                        p += sprintf(p, "%s%s",
+                                                                     strlen(dev->drv[drvidx]->msn2eaz[i]) ?
+                                                                     dev->drv[drvidx]->msn2eaz[i] : "-",
+                                                                     (i < 9) ? "," : "\0");
+                                                if ((ret = verify_area(VERIFY_WRITE, (void *) iocts.arg,
+                                                                       strlen(nstring) + 1)))
+                                                        return ret;
+                                                memcpy_tofs((char *) iocts.arg, nstring, strlen(nstring) + 1);
+                                        }
+                                        return 0;
+                                } else
+                                        return -EINVAL;
+                        case IIOCDBGVAR:
+                                if (arg) {
+                                        if ((ret = verify_area(VERIFY_WRITE, (void *) arg, sizeof(ulong))))
+                                                return ret;
+                                        memcpy_tofs((char *) arg, (char *) &dev, sizeof(ulong));
+                                        return 0;
+                                } else
+                                        return -EINVAL;
+                                break;
+                        default:
+                                if ((cmd&IIOCDRVCTL)==IIOCDRVCTL)
+                                        cmd = ((cmd>>_IOC_NRSHIFT)&_IOC_NRMASK)& ISDN_DRVIOCTL_MASK;
+				else
+					return -EINVAL;
+                                if (arg) {
+                                        int i;
+                                        char *p;
+                                        if ((ret = verify_area(VERIFY_READ, (void *) arg,
+                                                               sizeof(isdn_ioctl_struct))))
+                                                return ret;
+                                        memcpy_fromfs((char *) &iocts, (char *) arg, sizeof(isdn_ioctl_struct));
+                                        if (strlen(iocts.drvid)) {
+                                                if ((p = strchr(iocts.drvid, ',')))
+                                                        *p = 0;
+                                                drvidx = -1;
+                                                for (i = 0; i < ISDN_MAX_DRIVERS; i++)
+                                                        if (!(strcmp(dev->drvid[i], iocts.drvid))) {
+                                                                drvidx = i;
+                                                                break;
+                                                        }
+                                        } else
+                                                drvidx = 0;
+                                        if (drvidx == -1)
+                                                return -ENODEV;
+                                        if ((ret = verify_area(VERIFY_WRITE, (void *) arg,
+                                                               sizeof(isdn_ioctl_struct))))
+                                                return ret;
+                                        c.driver = drvidx;
+                                        c.command = ISDN_CMD_IOCTL;
+                                        c.arg = cmd;
+                                        memcpy(c.num, (char *) &iocts.arg, sizeof(ulong));
+                                        ret = dev->drv[drvidx]->interface->command(&c);
+                                        memcpy((char *) &iocts.arg, c.num, sizeof(ulong));
+                                        memcpy_tofs((char *) arg, &iocts, sizeof(isdn_ioctl_struct));
+                                        return ret;
+                                } else
+                                        return -EINVAL;
+		}
+	}
+#ifdef CONFIG_ISDN_PPP
+	if (minor <= ISDN_MINOR_PPPMAX)
+		return (isdn_ppp_ioctl(minor - ISDN_MINOR_PPP, file, cmd, arg));
+#endif
+	return -ENODEV;
+}
+
+/*
+ * Open the device code.
+ * MOD_INC_USE_COUNT make sure that the driver memory is not freed
+ * while the device is in use.
+ */
+static int isdn_open(struct inode *ino, struct file *filep)
+{
+	uint minor = MINOR(ino->i_rdev);
+	int drvidx;
+	int chidx;
+	isdn_ctrl c;
+
+	if (minor == ISDN_MINOR_STATUS) {
+		infostruct *p;
+
+		if ((p = (infostruct *) kmalloc(sizeof(infostruct), GFP_KERNEL))) {
+			MOD_INC_USE_COUNT;
+			p->next = (char *) dev->infochain;
+			p->private = (char *) &(filep->private_data);
+			dev->infochain = p;
+			/* At opening we allow a single update */
+			filep->private_data = (char *) 1;
+			return 0;
+		} else
+			return -ENOMEM;
+	}
+	if (!dev->channels)
+		return -ENODEV;
+	if (minor < ISDN_MINOR_CTRL) {
+		drvidx = isdn_minor2drv(minor);
+		if (drvidx < 0)
+			return -ENODEV;
+		chidx = isdn_minor2chan(minor);
+		if (!dev->drv[drvidx]->running)
+			return -ENODEV;
+		if (!(dev->drv[drvidx]->flags & (1 << chidx)))
+			return -ENODEV;
+		c.command = ISDN_CMD_LOCK;
+		c.driver = drvidx;
+		(void) dev->drv[drvidx]->interface->command(&c);
+		MOD_INC_USE_COUNT;
+		return 0;
+	}
+	if (minor <= ISDN_MINOR_CTRLMAX) {
+		drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
+		if (drvidx < 0)
+			return -ENODEV;
+		c.command = ISDN_CMD_LOCK;
+		c.driver = drvidx;
+		MOD_INC_USE_COUNT;
+		(void) dev->drv[drvidx]->interface->command(&c);
+		return 0;
+	}
+#ifdef CONFIG_ISDN_PPP
+	if (minor <= ISDN_MINOR_PPPMAX) {
+		int ret;
+		if (!(ret = isdn_ppp_open(minor - ISDN_MINOR_PPP, filep)))
+		        MOD_INC_USE_COUNT;
+		return ret;
+	}
+#endif
+	return -ENODEV;
+}
+
+static void isdn_close(struct inode *ino, struct file *filep)
+{
+	uint minor = MINOR(ino->i_rdev);
+	int drvidx;
+	isdn_ctrl c;
+
+	if (!dev->channels)
+		return;
+	MOD_DEC_USE_COUNT;
+	if (minor == ISDN_MINOR_STATUS) {
+		infostruct *p = dev->infochain;
+		infostruct *q = NULL;
+		while (p) {
+			if (p->private == (char *) &(filep->private_data)) {
+				if (q)
+					q->next = p->next;
+				else
+					dev->infochain = (infostruct *) (p->next);
+				return;
+			}
+			q = p;
+			p = (infostruct *) (p->next);
+		}
+		printk(KERN_WARNING "isdn: No private data while closing isdnctrl\n");
+	}
+	if (minor < ISDN_MINOR_CTRL) {
+		drvidx = isdn_minor2drv(minor);
+		if (drvidx < 0)
+			return;
+		c.command = ISDN_CMD_UNLOCK;
+		c.driver = drvidx;
+		(void) dev->drv[drvidx]->interface->command(&c);
+		return;
+	}
+	if (minor <= ISDN_MINOR_CTRLMAX) {
+		drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
+		if (drvidx < 0)
+			return;
+		if (dev->profd == current)
+			dev->profd = NULL;
+		c.command = ISDN_CMD_UNLOCK;
+		c.driver = drvidx;
+		(void) dev->drv[drvidx]->interface->command(&c);
+		return;
+	}
+#ifdef CONFIG_ISDN_PPP
+	if (minor <= ISDN_MINOR_PPPMAX) {
+		isdn_ppp_release(minor - ISDN_MINOR_PPP, filep);
+        }
+#endif
+}
+
+static struct file_operations isdn_fops =
+{
+	isdn_lseek,
+	isdn_read,
+	isdn_write,
+	NULL,			/* isdn_readdir */
+	isdn_select,		/* isdn_select */
+	isdn_ioctl,		/* isdn_ioctl */
+	NULL,			/* isdn_mmap */
+	isdn_open,
+	isdn_close,
+	NULL			/* fsync */
+};
+
+char *
+ isdn_map_eaz2msn(char *msn, int di)
+{
+	driver *this = dev->drv[di];
+	int i;
+
+	if (strlen(msn) == 1) {
+		i = msn[0] - '0';
+		if ((i >= 0) && (i <= 9))
+			if (strlen(this->msn2eaz[i]))
+				return (this->msn2eaz[i]);
+	}
+	return (msn);
+}
+
+/*
+ * Find an unused ISDN-channel, whose feature-flags match the
+ * given L2- and L3-protocols.
+ */
+int isdn_get_free_channel(int usage, int l2_proto, int l3_proto, int pre_dev
+		     ,int pre_chan)
+{
+	int i;
+	ulong flags;
+	ulong features;
+
+	save_flags(flags);
+	cli();
+	features = (1 << l2_proto) | (0x100 << l3_proto);
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+		if (USG_NONE(dev->usage[i]) &&
+		    (dev->drvmap[i] != -1)) {
+			int d = dev->drvmap[i];
+			if ((dev->usage[i] & ISDN_USAGE_EXCLUSIVE) &&
+			((pre_dev != d) || (pre_chan != dev->chanmap[i])))
+				continue;
+			if ((dev->drv[d]->running)) {
+				if ((dev->drv[d]->interface->features & features) == features) {
+					if ((pre_dev < 0) || (pre_chan < 0)) {
+						dev->usage[i] &= ISDN_USAGE_EXCLUSIVE;
+						dev->usage[i] |= usage;
+						isdn_info_update();
+						restore_flags(flags);
+						return i;
+					} else {
+						if ((pre_dev == d) && (pre_chan == dev->chanmap[i])) {
+							dev->usage[i] &= ISDN_USAGE_EXCLUSIVE;
+							dev->usage[i] |= usage;
+							isdn_info_update();
+							restore_flags(flags);
+							return i;
+						}
+					}
+				}
+			}
+		}
+	restore_flags(flags);
+	return -1;
+}
+
+/*
+ * Set state of ISDN-channel to 'unused'
+ */
+void isdn_free_channel(int di, int ch, int usage)
+{
+	int i;
+	ulong flags;
+
+	save_flags(flags);
+	cli();
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+		if (((dev->usage[i] & ISDN_USAGE_MASK) == usage) &&
+		    (dev->drvmap[i] == di) &&
+		    (dev->chanmap[i] == ch)) {
+			dev->usage[i] &= (ISDN_USAGE_NONE | ISDN_USAGE_EXCLUSIVE);
+			strcpy(dev->num[i], "???");
+			isdn_info_update();
+			restore_flags(flags);
+			return;
+		}
+	restore_flags(flags);
+}
+
+/*
+ * Cancel Exclusive-Flag for ISDN-channel
+ */
+void isdn_unexclusive_channel(int di, int ch)
+{
+	int i;
+	ulong flags;
+
+	save_flags(flags);
+	cli();
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+		if ((dev->drvmap[i] == di) &&
+		    (dev->chanmap[i] == ch)) {
+			dev->usage[i] &= ~ISDN_USAGE_EXCLUSIVE;
+			isdn_info_update();
+			restore_flags(flags);
+			return;
+		}
+	restore_flags(flags);
+}
+
+/*
+ *  receive callback handler for drivers supporting sk_buff's.
+ */
+
+void isdn_receive_skb_callback(int drvidx, int chan, struct sk_buff *skb) 
+{
+        int i;
+
+	if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+		return;
+        if ((i = isdn_dc2minor(drvidx,chan))==-1)
+                return;
+	if (isdn_net_rcv_skb(i, skb) == 0) {
+		isdn_receive_callback(drvidx, chan, skb->data, skb->len);
+		skb->free = 1;
+		kfree_skb(skb, FREE_READ);
+	}
+}
+
+/*
+ *  writebuf replacement for SKB_ABLE drivers
+ */
+
+int isdn_writebuf_stub(int drvidx, int chan, const u_char *buf, int len, 
+		       int user)
+{
+        struct sk_buff * skb;
+        
+        skb = alloc_skb(dev->drv[drvidx]->interface->hl_hdrlen + len, GFP_ATOMIC);
+        if (skb == NULL)
+                return 0;
+
+        skb_reserve(skb, dev->drv[drvidx]->interface->hl_hdrlen);
+        skb->free = 1;
+
+        if (user)
+                memcpy_fromfs(skb_put(skb, len), buf, len);
+        else
+                memcpy(skb_put(skb, len), buf, len);
+
+        return dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, skb);
+}
+ 
+/*
+ * Low-level-driver registration
+ */
+
+
+int register_isdn(isdn_if * i)
+{
+	driver *d;
+	int n, j, k;
+	ulong flags;
+	int drvidx;
+
+	if (dev->drivers >= ISDN_MAX_DRIVERS) {
+		printk(KERN_WARNING "register_isdn: Max. %d drivers supported\n",
+		       ISDN_MAX_DRIVERS);
+		return 0;
+	}
+	n = i->channels;
+	if (dev->channels + n >= ISDN_MAX_CHANNELS) {
+		printk(KERN_WARNING "register_isdn: Max. %d channels supported\n",
+		       ISDN_MAX_CHANNELS);
+		return 0;
+	}
+	if (!(d = (driver *) kmalloc(sizeof(driver), GFP_KERNEL))) {
+		printk(KERN_WARNING "register_isdn: Could not alloc driver-struct\n");
+		return 0;
+	}
+	memset((char *) d, 0, sizeof(driver));
+	if (!(d->rcverr = (int *) kmalloc(sizeof(int) * n, GFP_KERNEL))) {
+		printk(KERN_WARNING "register_isdn: Could not alloc rcverr\n");
+		kfree(d);
+		return 0;
+	}
+	memset((char *) d->rcverr, 0, sizeof(int) * n);
+	if (!(d->rcvcount = (int *) kmalloc(sizeof(int) * n, GFP_KERNEL))) {
+		printk(KERN_WARNING "register_isdn: Could not alloc rcvcount\n");
+		kfree(d->rcverr);
+		kfree(d);
+		return 0;
+	}
+	memset((char *) d->rcvcount, 0, sizeof(int) * n);
+	if (!(d->rpqueue = (pqueue **) kmalloc(sizeof(pqueue *) * n, GFP_KERNEL))) {
+		printk(KERN_WARNING "register_isdn: Could not alloc rpqueue\n");
+		kfree(d->rcvcount);
+		kfree(d->rcverr);
+		kfree(d);
+		return 0;
+	}
+	memset((char *) d->rpqueue, 0, sizeof(pqueue *) * n);
+	if (!(d->rcv_waitq = (struct wait_queue **)
+	      kmalloc(sizeof(struct wait_queue *) * n, GFP_KERNEL))) {
+		printk(KERN_WARNING "register_isdn: Could not alloc rcv_waitq\n");
+		kfree(d->rpqueue);
+		kfree(d->rcvcount);
+		kfree(d->rcverr);
+		kfree(d);
+		return 0;
+	}
+	memset((char *) d->rcv_waitq, 0, sizeof(struct wait_queue *) * n);
+	if (!(d->snd_waitq = (struct wait_queue **)
+	      kmalloc(sizeof(struct wait_queue *) * n, GFP_KERNEL))) {
+		printk(KERN_WARNING "register_isdn: Could not alloc snd_waitq\n");
+		kfree(d->rcv_waitq);
+		kfree(d->rpqueue);
+		kfree(d->rcvcount);
+		kfree(d->rcverr);
+		kfree(d);
+		return 0;
+	}
+	memset((char *) d->snd_waitq, 0, sizeof(struct wait_queue *) * n);
+	d->channels = n;
+	d->loaded = 1;
+	d->maxbufsize = i->maxbufsize;
+	d->pktcount = 0;
+	d->stavail = 0;
+	d->running = 0;
+	d->flags = 0;
+	d->interface = i;
+	for (drvidx = 0; drvidx < ISDN_MAX_DRIVERS; drvidx++)
+		if (!dev->drv[drvidx])
+			break;
+	i->channels = drvidx;
+
+	if (i->writebuf_skb && (!i->writebuf))
+                i->writebuf = isdn_writebuf_stub;
+ 
+	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);
+	save_flags(flags);
+	cli();
+	for (j = 0; j < n; j++)
+		for (k = 0; k < ISDN_MAX_CHANNELS; k++)
+			if (dev->chanmap[k] < 0) {
+				dev->chanmap[k] = j;
+				dev->drvmap[k] = drvidx;
+				break;
+			}
+	dev->drv[drvidx] = d;
+	dev->channels += n;
+	strcpy(dev->drvid[drvidx], i->id);
+	isdn_info_update();
+	dev->drivers++;
+	restore_flags(flags);
+	return 1;
+}
+
+/*
+ *****************************************************************************
+ * And now the modules code.
+ *****************************************************************************
+ */
+
+extern int printk(const char *fmt,...);
+
+#ifdef MODULE
+#define isdn_init init_module
+#endif
+
+static char *isdn_getrev(const char *revision)
+{
+	static char rev[20];
+	char *p;
+
+	if ((p = strchr(revision, ':'))) {
+		strcpy(rev, p + 2);
+		p = strchr(rev, '$');
+		*--p = 0;
+	} else
+		strcpy(rev, "???");
+	return rev;
+}
+
+static struct symbol_table isdn_syms = {
+#include <linux/symtab_begin.h>
+        X(register_isdn),
+#include <linux/symtab_end.h>
+};
+
+static void isdn_export_syms(void)
+{
+        register_symtab(&isdn_syms);
+        has_exported = 1;
+}
+
+/*
+ * Allocate and initialize all data, register modem-devices
+ */
+int isdn_init(void)
+{
+	int i;
+
+	sti();
+	if (!(dev = (isdn_dev *) kmalloc(sizeof(isdn_dev), GFP_KERNEL))) {
+		printk(KERN_WARNING "isdn: Could not allocate device-struct.\n");
+		return -EIO;
+	}
+	memset((char *) dev, 0, sizeof(isdn_dev));
+	for (i = 0; i < ISDN_MAX_DRIVERS; i++)
+		dev->drvmap[i] = -1;
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		dev->chanmap[i] = -1;
+		dev->m_idx[i] = -1;
+		strcpy(dev->num[i], "???");
+	}
+	if (register_chrdev(ISDN_MAJOR, "isdn", &isdn_fops)) {
+		printk(KERN_WARNING "isdn: Could not register control devices\n");
+		kfree(dev);
+		return -EIO;
+	}
+	if ((i = isdn_tty_modem_init()) < 0) {
+		printk(KERN_WARNING "isdn: Could not register tty devices\n");
+		if (i == -3)
+			tty_unregister_driver(&dev->mdm.cua_modem);
+		if (i <= -2)
+			tty_unregister_driver(&dev->mdm.tty_modem);
+		kfree(dev);
+		unregister_chrdev(ISDN_MAJOR, "isdn");
+		return -EIO;
+	}
+#ifdef CONFIG_ISDN_PPP
+	if (isdn_ppp_init() < 0) {
+		printk(KERN_WARNING "isdn: Could not create PPP-device-structs\n");
+		tty_unregister_driver(&dev->mdm.tty_modem);
+		tty_unregister_driver(&dev->mdm.cua_modem);
+		for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+			kfree(dev->mdm.info[i].xmit_buf);
+		unregister_chrdev(ISDN_MAJOR, "isdn");
+		kfree(dev);
+		return -EIO;
+	}
+#endif				/* CONFIG_ISDN_PPP */
+
+        if (!has_exported)
+                isdn_export_syms();
+
+	printk(KERN_NOTICE "ISDN subsystem Rev: %s/%s/%s/%s",
+	       isdn_getrev(isdn_revision),
+	       isdn_getrev(isdn_tty_revision),
+	       isdn_getrev(isdn_net_revision),
+	       isdn_getrev(isdn_ppp_revision));
+
+#ifdef MODULE
+	printk(" loaded\n");
+#else
+	printk("\n");
+	isdn_cards_init();
+#endif
+	isdn_info_update();
+	return 0;
+}
+
+#ifdef MODULE
+/*
+ * Unload module
+ */
+void cleanup_module(void)
+{
+	int flags;
+	int i;
+
+#ifdef CONFIG_ISDN_PPP
+	isdn_ppp_cleanup();
+#endif
+	save_flags(flags);
+	cli();
+	if (isdn_net_rmall() < 0) {
+		printk(KERN_WARNING "isdn: net-device busy, remove cancelled\n");
+		restore_flags(flags);
+		return;
+	}
+	if (tty_unregister_driver(&dev->mdm.tty_modem)) {
+		printk(KERN_WARNING "isdn: ttyI-device busy, remove cancelled\n");
+		restore_flags(flags);
+		return;
+	}
+	if (tty_unregister_driver(&dev->mdm.cua_modem)) {
+		printk(KERN_WARNING "isdn: cui-device busy, remove cancelled\n");
+		restore_flags(flags);
+		return;
+	}
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+		kfree(dev->mdm.info[i].xmit_buf - 4);
+	if (unregister_chrdev(ISDN_MAJOR, "isdn") != 0) {
+		printk(KERN_WARNING "isdn: controldevice busy, remove cancelled\n");
+	} else {
+		del_timer(&dev->timer);
+		kfree(dev);
+		printk(KERN_NOTICE "ISDN-subsystem unloaded\n");
+	}
+	restore_flags(flags);
+}
+#endif
diff -u --recursive --new-file v1.3.68/linux/drivers/isdn/isdn_common.h linux/drivers/isdn/isdn_common.h
--- v1.3.68/linux/drivers/isdn/isdn_common.h	Thu Jan  1 02:00:00 1970
+++ linux/drivers/isdn/isdn_common.h	Mon Feb 26 11:58:05 1996
@@ -0,0 +1,58 @@
+/* $Id: isdn_common.h,v 1.1 1996/01/10 21:37:19 fritz Exp fritz $
+ *
+ * header for Linux ISDN subsystem, common used funtions and debugging-switches (linklevel).
+ *
+ * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
+ * Copyright 1995,96    by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * 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_common.h,v $
+ * Revision 1.1  1996/01/10 21:37:19  fritz
+ * Initial revision
+ *
+ */
+
+#undef  ISDN_DEBUG_MODEM_OPEN
+#undef  ISDN_DEBUG_MODEM_IOCTL
+#undef  ISDN_DEBUG_MODEM_WAITSENT
+#undef  ISDN_DEBUG_MODEM_HUP
+#undef  ISDN_DEBUG_MODEM_ICALL
+#undef  ISDN_DEBUG_MODEM_DUMP
+#undef  ISDN_DEBUG_AT
+#undef  ISDN_DEBUG_NET_DUMP
+#undef  ISDN_DEBUG_NET_DIAL
+#undef  ISDN_DEBUG_NET_BUILDHDR
+#undef  ISDN_DEBUG_NET_ICALL
+
+/* Prototypes */
+extern void  isdn_MOD_INC_USE_COUNT(void);
+extern void  isdn_MOD_DEC_USE_COUNT(void);
+extern void  isdn_free_channel(int di, int ch, int usage);
+extern void  isdn_all_eaz(int di, int ch);
+extern int   isdn_dc2minor(int di, int ch);
+extern void  isdn_info_update(void);
+extern char* isdn_map_eaz2msn(char *msn, int di);
+extern void  isdn_timer_ctrl(int tf, int onoff);
+extern void  isdn_unexclusive_channel(int di, int ch);
+extern int   isdn_getnum(char **);
+extern int   isdn_readbchan (int di, int channel, u_char *buf,
+			     u_char *fp, int len, int user);
+extern int   isdn_get_free_channel(int usage, int l2_proto, int l3_proto,
+				   int pre_dev, int pre_chan);
+#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 v1.3.68/linux/drivers/isdn/isdn_net.c linux/drivers/isdn/isdn_net.c
--- v1.3.68/linux/drivers/isdn/isdn_net.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/isdn/isdn_net.c	Mon Feb 26 11:58:05 1996
@@ -0,0 +1,2283 @@
+/* $Id: isdn_net.c,v 1.4 1996/02/19 15:23:38 fritz Exp fritz $
+ *
+ * Linux ISDN subsystem, network interfaces and related functions (linklevel).
+ *
+ * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
+ * Copyright 1995,96    by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * 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_net.c,v $
+ * Revision 1.4  1996/02/19 15:23:38  fritz
+ * Bugfix: Sync-PPP packets got compressed twice, when resent due to
+ *         send-queue-full reject.
+ *
+ * Revision 1.3  1996/02/11 02:22:28  fritz
+ * Changed status- receive-callbacks to use pointer-arrays for finding
+ * a corresponding interface instead of looping over all interfaces.
+ * Activate Auto-hangup-timer only when interface is online.
+ * Some bugfixes in the dialing-statemachine.
+ * Lot of bugfixes in sk_buff'ized encapsulation handling.
+ * For speedup connection-setup after dialing, remember sk_buf that triggered
+ * dialing.
+ * Fixed isdn_net_log_packet according to different encapsulations.
+ * Correct ARP-handling for ETHERNET-encapsulation.
+ *
+ * Revision 1.2  1996/01/22 05:05:12  fritz
+ * Changed returncode-logic for isdn_net_start_xmit() and it's
+ * helper-functions.
+ * Changed handling of buildheader for RAWIP and ETHERNET-encapsulation.
+ *
+ * Revision 1.1  1996/01/09 04:12:34  fritz
+ * Initial revision
+ *
+ */
+
+#ifndef STANDALONE
+#include <linux/config.h>
+#endif
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/isdn.h>
+#include <linux/if_arp.h>
+#include "isdn_common.h"
+#include "isdn_net.h"
+#ifdef CONFIG_ISDN_PPP
+#include "isdn_ppp.h"
+#endif
+
+/* In ksyms.c, but why not in some .h ??? */
+extern int arp_find(unsigned char *, u32, struct device *, u32,
+                    struct sk_buff *);
+
+/* Prototypes */
+
+int isdn_net_force_dial_lp(isdn_net_local *);
+static int isdn_net_wildmat(char *s, char *p);
+static int isdn_net_start_xmit(struct sk_buff *, struct device *);
+static int isdn_net_xmit(struct device *, isdn_net_local *, struct sk_buff *);
+ 
+char *isdn_net_revision = "$Revision: 1.4 $";
+
+ /*
+  * Code for raw-networking over ISDN
+  */
+
+static void
+isdn_net_reset(struct device *dev)
+{
+	ulong flags;
+
+	save_flags(flags);
+	cli();			/* Avoid glitch on writes to CMD regs */
+	dev->interrupt = 0;
+	dev->tbusy = 0;
+	restore_flags(flags);
+}
+
+/* Open/initialize the board. */
+static int
+isdn_net_open(struct device *dev)
+{
+	int i;
+	struct device *p;
+
+	isdn_net_reset(dev);
+	dev->start = 1;
+	/* Fill in the MAC-level header. */
+	for (i = 0; i < ETH_ALEN - sizeof(ulong); i++)
+		dev->dev_addr[i] = 0xfc;
+	memcpy(&(dev->dev_addr[i]), &dev->pa_addr, sizeof(ulong));
+
+        /* If this interface has slaves, start them also */
+
+	if ((p = (((isdn_net_local *) dev->priv)->slave))) {
+		while (p) {
+			isdn_net_reset(p);
+			p->start = 1;
+			p = (((isdn_net_local *) p->priv)->slave);
+		}
+	}
+
+	isdn_MOD_INC_USE_COUNT();
+	return 0;
+}
+
+/*
+ * Assign an ISDN-channel to a net-interface
+ */
+static void
+isdn_net_bind_channel(isdn_net_local * lp, int idx)
+{
+        ulong flags;
+
+        save_flags(flags);
+        cli();
+	lp->isdn_device = dev->drvmap[idx];
+	lp->isdn_channel = dev->chanmap[idx];
+        dev->rx_netdev[idx] = lp->netdev;
+        dev->st_netdev[idx] = lp->netdev;
+        restore_flags(flags);
+}
+
+/*
+ * Perform auto-hangup and cps-calculation for net-interfaces.
+ *
+ * auto-hangup:
+ * Increment idle-counter (this counter is reset on any incoming or
+ * outgoing packet), if counter exceeds configured limit either do a
+ * hangup immediately or - if configured - wait until just before the next
+ * charge-info.
+ *
+ * cps-calculation (needed for dynamic channel-bundling):
+ * Since this function is called every second, simply reset the
+ * byte-counter of the interface after copying it to the cps-variable.
+ */
+void
+isdn_net_autohup()
+{
+	isdn_net_dev *p = dev->netdev;
+        int anymore;
+	ulong flags;
+
+	save_flags(flags);
+	cli();
+        anymore = 0;
+	while (p) {
+		isdn_net_local *l = (isdn_net_local *) & (p->local);
+		l->cps = l->transcount;
+		l->transcount = 0;
+		if (dev->net_verbose > 3)
+			printk(KERN_DEBUG "%s: %d bogocps\n", l->name, l->cps);
+		if ((l->flags & ISDN_NET_CONNECTED) && (!l->dialstate)) {
+                        anymore = 1;
+			l->huptimer++;
+			if ((l->onhtime) && (l->huptimer > l->onhtime))
+				if (l->outgoing) {
+					if (l->hupflags & 4) {
+						if (l->hupflags & 1)
+							isdn_net_hangup(&p->dev);
+						else if (jiffies - l->chargetime > l->chargeint)
+							isdn_net_hangup(&p->dev);
+					} else
+						isdn_net_hangup(&p->dev);
+				} else if (l->hupflags & 8)
+					isdn_net_hangup(&p->dev);
+		}
+		p = (isdn_net_dev *) p->next;
+	}
+        isdn_timer_ctrl(ISDN_TIMER_NETHANGUP,anymore);
+	restore_flags(flags);
+}
+
+/*
+ * Handle status-messages from ISDN-interfacecard.
+ * This function is called from within the main-status-dispatcher
+ * isdn_status_callback, which itself is called from the lowlevel-driver.
+ * Return: 1 = Event handled, 0 = not for us or unknown Event.
+ */
+int
+isdn_net_stat_callback(int idx, int cmd)
+{
+	isdn_net_dev *p = dev->st_netdev[idx];
+
+	if (p) {
+		isdn_net_local *lp = &(p->local);
+                switch (cmd) {
+			case ISDN_STAT_BSENT:
+				/* A packet has successfully been sent out */
+				if ((lp->flags & ISDN_NET_CONNECTED) &&
+				    (!lp->dialstate)) {
+					lp->stats.tx_packets++;
+                                        if (clear_bit(0,(void*)&(p->dev.tbusy)))
+                                                mark_bh(NET_BH);
+				}
+				return 1;
+			case ISDN_STAT_DCONN:
+				/* D-Channel is up */
+				if (lp->dialstate == 4 || lp->dialstate == 7
+                                    || lp->dialstate == 8) {
+					lp->dialstate++;
+                                        return 1;
+                                }
+				break;
+			case ISDN_STAT_DHUP:
+				/* Either D-Channel-hangup or error during dialout */
+				if ((!lp->dialstate) && (lp->flags & ISDN_NET_CONNECTED)) {
+					lp->flags &= ~ISDN_NET_CONNECTED;
+					isdn_free_channel(lp->isdn_device, lp->isdn_channel,
+                                                          ISDN_USAGE_NET);
+#ifdef CONFIG_ISDN_PPP
+					isdn_ppp_free(lp);
+#endif
+					isdn_all_eaz(lp->isdn_device, lp->isdn_channel);
+					printk(KERN_INFO "%s: remote hangup\n", lp->name);
+					printk(KERN_INFO "%s: Chargesum is %d\n", lp->name,
+					       lp->charge);
+					lp->isdn_device = -1;
+					lp->isdn_channel = -1;
+					dev->st_netdev[idx] = NULL;
+					dev->rx_netdev[idx] = NULL;
+                                        return 1;
+				}
+                                break;
+			case ISDN_STAT_BCONN:
+				/* B-Channel is up */
+				if (lp->dialstate >= 5 && lp->dialstate <= 10) {
+					if (lp->dialstate <= 6) {
+                                                dev->usage[idx] |= ISDN_USAGE_OUTGOING;
+                                                isdn_info_update();
+					} else
+                                                dev->rx_netdev[idx] = p;
+					lp->dialstate = 0;
+                                        isdn_timer_ctrl(ISDN_TIMER_NETHANGUP,1);
+					printk(KERN_INFO "isdn_net: %s connected\n", lp->name);
+					/* If first Chargeinfo comes before B-Channel connect,
+					 * we correct the timestamp here.
+					 */
+					lp->chargetime = jiffies;
+                                        /* Immediately send first skb to speed up arp */
+                                        if (lp->first_skb) {
+                                                if (!(isdn_net_xmit(&p->dev,lp,lp->first_skb)))
+                                                        lp->first_skb = NULL;
+                                        }
+                                        return 1;
+				}
+				break;
+			case ISDN_STAT_NODCH:
+				/* No D-Channel avail. */
+				if (lp->dialstate == 4) {
+					lp->dialstate--;
+                                        return 1;
+                                }
+				break;
+			case ISDN_STAT_CINF:
+				/* Charge-info from TelCo. Calculate interval between
+				 * charge-infos and set timestamp for last info for
+				 * usage by isdn_net_autohup()
+				 */
+				lp->charge++;
+				if (lp->hupflags & 2) {
+					lp->hupflags &= ~1;
+					lp->chargeint = jiffies - lp->chargetime - (2 * HZ);
+				}
+				if (lp->hupflags & 1)
+					lp->hupflags |= 2;
+				lp->chargetime = jiffies;
+				return 1;
+                }
+	}
+        return 0;
+}
+
+/*
+ * Check, if a numer contains wilcard-characters, in which case it
+ * is for incoming purposes only.
+ */
+static int
+isdn_net_checkwild(char *num)
+{
+	return ((strchr(num, '?')) ||
+		(strchr(num, '*')) ||
+		(strchr(num, '[')) ||
+		(strchr(num, ']')) ||
+		(strchr(num, '^')));
+}
+
+/*
+ * Perform dialout for net-interfaces and timeout-handling for
+ * D-Channel-up and B-Channel-up Messages.
+ * This function is initially called from within isdn_net_start_xmit() or
+ * or isdn_net_find_icall() after initializing the dialstate for an
+ * interface. If further calls are needed, the function schedules itself
+ * for a timer-callback via isdn_timer_function().
+ * The dialstate is also affected by incoming status-messages from
+ * the ISDN-Channel which are handled in isdn_net_stat_callback() above.
+ */
+void
+isdn_net_dial(void)
+{
+	isdn_net_dev *p = dev->netdev;
+	int anymore = 0;
+	int i;
+	isdn_ctrl cmd;
+
+	while (p) {
+		switch (p->local.dialstate) {
+		case 0:
+			/* Nothing to do for this interface */
+			break;
+		case 1:
+			/* Initiate dialout. Set phone-number-pointer to first number
+			 * of interface.
+			 */
+			p->local.dial = p->local.phone[1];
+			anymore = 1;
+			p->local.dialstate++;
+			break;
+			/* Prepare dialing. Clear EAZ, then set EAZ. */
+		case 2:
+			cmd.driver = p->local.isdn_device;
+			cmd.arg = p->local.isdn_channel;
+			cmd.command = ISDN_CMD_CLREAZ;
+			dev->drv[p->local.isdn_device]->interface->command(&cmd);
+			sprintf(cmd.num, "%s", isdn_map_eaz2msn(p->local.msn, cmd.driver));
+			cmd.command = ISDN_CMD_SETEAZ;
+			dev->drv[p->local.isdn_device]->interface->command(&cmd);
+			p->local.dialretry = 0;
+			anymore = 1;
+			p->local.dialstate++;
+			break;
+		case 3:
+			/* Setup interface, dial current phone-number, switch to next number.
+			 * If list of phone-numbers is exhausted, increment
+			 * retry-counter.
+			 */
+			cmd.driver = p->local.isdn_device;
+			cmd.command = ISDN_CMD_SETL2;
+			cmd.arg = p->local.isdn_channel + (p->local.l2_proto << 8);
+			dev->drv[p->local.isdn_device]->interface->command(&cmd);
+			cmd.driver = p->local.isdn_device;
+			cmd.command = ISDN_CMD_SETL3;
+			cmd.arg = p->local.isdn_channel + (p->local.l3_proto << 8);
+			dev->drv[p->local.isdn_device]->interface->command(&cmd);
+			cmd.driver = p->local.isdn_device;
+			cmd.arg = p->local.isdn_channel;
+			p->local.huptimer = 0;
+			p->local.outgoing = 1;
+			p->local.hupflags |= 1;
+			if (!strcmp(p->local.dial->num, "LEASED")) {
+				p->local.dialstate = 4;
+				printk(KERN_INFO "%s: Open leased line ...\n", p->local.name);
+			} else {
+				cmd.command = ISDN_CMD_DIAL;
+				sprintf(cmd.num, "%s,%s,7,0", p->local.dial->num,
+				  isdn_map_eaz2msn(p->local.msn, cmd.driver));
+				i = isdn_dc2minor(p->local.isdn_device, p->local.isdn_channel);
+				if (i >= 0) {
+					strcpy(dev->num[i], p->local.dial->num);
+					isdn_info_update();
+				}
+				printk(KERN_INFO "%s: dialing %d %s...\n", p->local.name,
+				 p->local.dialretry, p->local.dial->num);
+				/*
+				 * Switch to next number or back to start if at end of list.
+				 */
+				if (!(p->local.dial = (isdn_net_phone *) p->local.dial->next)) {
+					p->local.dial = p->local.phone[1];
+					p->local.dialretry++;
+				}
+				p->local.dtimer = 0;
+#ifdef ISDN_DEBUG_NET_DIAL
+				printk(KERN_DEBUG "dial: d=%d c=%d\n", p->local.isdn_device,
+				       p->local.isdn_channel);
+#endif
+				dev->drv[p->local.isdn_device]->interface->command(&cmd);
+			}
+			anymore = 1;
+			p->local.dialstate++;
+			break;
+		case 4:
+			/* Wait for D-Channel-connect or incoming call, if passive
+			 * callback configured. If timeout and max retries not
+			 * reached, switch back to state 3.
+			 */
+			if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT10)
+				if (p->local.dialretry < p->local.dialmax) {
+					p->local.dialstate = 3;
+				} else
+					isdn_net_hangup(&p->dev);
+			anymore = 1;
+			break;
+		case 5:
+			/* Got D-Channel-Connect, send B-Channel-request */
+			cmd.driver = p->local.isdn_device;
+			cmd.arg = p->local.isdn_channel;
+			cmd.command = ISDN_CMD_ACCEPTB;
+			anymore = 1;
+			p->local.dtimer = 0;
+			p->local.dialstate++;
+			dev->drv[p->local.isdn_device]->interface->command(&cmd);
+			break;
+		case 6:
+			/* Wait for B- or D-Channel-connect. If timeout,
+                         * switch back to state 3.
+			 */
+#ifdef ISDN_DEBUG_NET_DIAL
+			printk(KERN_DEBUG "dialtimer2: %d\n", p->local.dtimer);
+#endif
+			if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT10)
+				p->local.dialstate = 3;
+			anymore = 1;
+			break;
+		case 7:
+			/* Got incoming Call, setup L2 and L3 protocols,
+                         * then wait for D-Channel-connect
+                         */
+#ifdef ISDN_DEBUG_NET_DIAL
+			printk(KERN_DEBUG "dialtimer4: %d\n", p->local.dtimer);
+#endif
+			cmd.driver = p->local.isdn_device;
+			cmd.command = ISDN_CMD_SETL2;
+			cmd.arg = p->local.isdn_channel + (p->local.l2_proto << 8);
+			dev->drv[p->local.isdn_device]->interface->command(&cmd);
+			cmd.driver = p->local.isdn_device;
+			cmd.command = ISDN_CMD_SETL3;
+			cmd.arg = p->local.isdn_channel + (p->local.l3_proto << 8);
+			dev->drv[p->local.isdn_device]->interface->command(&cmd);
+			if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT15)
+				isdn_net_hangup(&p->dev);
+			else {
+				anymore = 1;
+                                p->local.dialstate++;
+                        }
+			break;
+		case 9:
+			/* Got incoming D-Channel-Connect, send B-Channel-request */
+			cmd.driver = p->local.isdn_device;
+			cmd.arg = p->local.isdn_channel;
+			cmd.command = ISDN_CMD_ACCEPTB;
+			dev->drv[p->local.isdn_device]->interface->command(&cmd);
+			anymore = 1;
+			p->local.dtimer = 0;
+			p->local.dialstate++;
+			break;
+		case 8:
+		case 10:
+			/*  Wait for B- or D-channel-connect */
+#ifdef ISDN_DEBUG_NET_DIAL
+			printk(KERN_DEBUG "dialtimer4: %d\n", p->local.dtimer);
+#endif
+			if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT10)
+				isdn_net_hangup(&p->dev);
+			else
+				anymore = 1;
+			break;
+		default:
+			printk(KERN_WARNING "isdn_net: Illegal dialstate %d for device %s\n",
+			       p->local.dialstate, p->local.name);
+		}
+		p = (isdn_net_dev *) p->next;
+	}
+	isdn_timer_ctrl(ISDN_TIMER_NETDIAL, anymore);
+}
+
+/*
+ * Send-data-helpfunction for net-interfaces
+ */
+int
+isdn_net_send(u_char * buf, int di, int ch, int len)
+{
+	int l;
+
+	if ((l = dev->drv[di]->interface->writebuf(di, ch, buf, len, 0)) == len)
+		return 1;
+	/* Device driver queue full (or packet > 4000 bytes, should never
+	 * happen)
+	 */
+	if (l == -EINVAL)
+		printk(KERN_ERR "isdn_net: Huh, sending pkt too big!\n");
+	return 0;
+}
+
+/*
+ * Perform hangup for a net-interface.
+ */
+void
+isdn_net_hangup(struct device *d)
+{
+	isdn_net_local *lp = (isdn_net_local *) d->priv;
+	isdn_ctrl cmd;
+	ulong flags;
+
+	save_flags(flags);
+	cli();
+	if (lp->flags & ISDN_NET_CONNECTED) {
+		printk(KERN_INFO "isdn_net: local hangup %s\n", lp->name);
+		lp->dialstate = 0;
+                dev->rx_netdev[isdn_dc2minor(lp->isdn_device,lp->isdn_channel)] = NULL;
+                dev->st_netdev[isdn_dc2minor(lp->isdn_device,lp->isdn_channel)] = NULL;
+		isdn_free_channel(lp->isdn_device, lp->isdn_channel, ISDN_USAGE_NET);
+#ifdef CONFIG_ISDN_PPP
+		isdn_ppp_free(lp);
+#endif
+		lp->flags &= ~ISDN_NET_CONNECTED;
+		cmd.driver = lp->isdn_device;
+		cmd.command = ISDN_CMD_HANGUP;
+		cmd.arg = lp->isdn_channel;
+		(void) dev->drv[cmd.driver]->interface->command(&cmd);
+		printk(KERN_INFO "%s: Chargesum is %d\n", lp->name, lp->charge);
+		isdn_all_eaz(lp->isdn_device, lp->isdn_channel);
+		lp->isdn_device = -1;
+		lp->isdn_channel = -1;
+	}
+	restore_flags(flags);
+}
+
+typedef struct {
+	unsigned short source;
+	unsigned short dest;
+} ip_ports;
+
+static void
+isdn_net_log_packet(u_char * buf, isdn_net_local * lp)
+{
+        u_char *p = buf;
+	unsigned short proto = ETH_P_IP;
+        int data_ofs;
+        int len;
+	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;
+                case ISDN_NET_ENCAP_SYNCPPP:
+                        len = 4;
+#ifdef CONFIG_ISDN_MPP
+                        if (lp->ppp_minor!=-1) {
+                                if (ippp_table[lp->ppp_minor].mpppcfg &
+                                    SC_MP_PROT) {
+                                        if (ippp_table[lp->ppp_minor].mpppcfg &
+                                            SC_OUT_SHORT_SEQ)
+                                                len = 7;
+                                        else
+                                                len = 9;
+                                }
+                        }
+#endif
+                        p = &buf[len];
+                        break;
+        }
+	data_ofs = ((p[0] & 15) * 4);
+        switch (proto) {
+		case ETH_P_IP:
+			switch (p[9]) {
+                                case 1:
+                                        strcpy(addinfo, " ICMP");
+                                        break;
+                                case 2:
+                                        strcpy(addinfo, " IGMP");
+                                        break;
+                                case 4:
+                                        strcpy(addinfo, " IPIP");
+                                        break;
+                                case 6:
+                                        ipp = (ip_ports *) (&p[data_ofs]);
+                                        sprintf(addinfo, " TCP, port: %d -> %d", ntohs(ipp->source),
+                                                ntohs(ipp->dest));
+                                        break;
+                                case 8:
+                                        strcpy(addinfo, " EGP");
+                                        break;
+                                case 12:
+                                        strcpy(addinfo, " PUP");
+                                        break;
+                                case 17:
+                                        ipp = (ip_ports *) (&p[data_ofs]);
+                                        sprintf(addinfo, " UDP, port: %d -> %d", ntohs(ipp->source),
+                                                ntohs(ipp->dest));
+                                        break;
+                                case 22:
+                                        strcpy(addinfo, " IDP");
+                                        break;
+			}
+			printk(KERN_INFO "OPEN: %d.%d.%d.%d -> %d.%d.%d.%d%s\n",
+			       p[12], p[13], p[14], p[15],
+			       p[16], p[17], p[18], p[19],
+			       addinfo);
+			break;
+		case ETH_P_ARP:
+			printk(KERN_INFO "OPEN: ARP %d.%d.%d.%d -> *.*.*.* ?%d.%d.%d.%d\n",
+			       p[14], p[15], p[16], p[17],
+			       p[24], p[25], p[26], p[27]);
+			break;
+        }
+}
+
+/*
+ * Generic routine to send out an skbuf.
+ * If lowlevel-device does not support supports skbufs, use
+ * standard send-routine, else sind directly.
+ *
+ * Return: 0 on success, !0 on failure.
+ * Side-effects: ndev->tbusy is cleared on success.
+ */
+int
+isdn_net_send_skb(struct device *ndev, isdn_net_local *lp,
+                  struct sk_buff *skb)
+{
+	int ret;
+	
+	lp->transcount += skb->len;
+	if (dev->drv[lp->isdn_device]->interface->writebuf_skb) 
+		ret = dev->drv[lp->isdn_device]->interface->
+			writebuf_skb(lp->isdn_device, lp->isdn_channel, skb);
+	else {	      
+		if ((ret = isdn_net_send(skb->data, lp->isdn_device,
+                                    lp->isdn_channel, skb->len)))
+		        dev_kfree_skb(skb, FREE_WRITE);
+	}
+
+	if (ret)
+		clear_bit(0, (void *)&(ndev->tbusy));
+	return (!ret);
+}                                      
+	
+
+/*
+ *  Helper function for isdn_net_start_xmit.
+ *  When called, the connection is already established.
+ *  Based on cps-calculation, check if device is overloaded.
+ *  If so, and if a slave exists, trigger dialing for it.
+ *  If any slave is online, deliver packets using a simple round robin
+ *  scheme.
+ *
+ *  Return: 0 on success, !0 on failure.
+ */
+
+static int
+isdn_net_xmit(struct device *ndev, isdn_net_local *lp, struct sk_buff *skb) 
+{
+        int ret;
+
+	/* For the other encaps the header has allready been built */
+#ifdef CONFIG_ISDN_PPP
+	if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
+		return (isdn_ppp_xmit(skb, ndev));
+#endif		
+	/* Reset hangup-timeout */
+	lp->huptimer = 0;
+	if (lp->cps > 7000) {
+		/* Device overloaded */
+
+		/* 
+		 * Packet-delivery via round-robin over master 
+		 * and all connected slaves.
+		 */
+		if (lp->master)
+			/* Slaves always deliver themselves */
+			ret = isdn_net_send_skb(ndev, lp, skb);
+		else {
+			isdn_net_local *slp = (isdn_net_local *) (lp->srobin->priv);
+			/* Master delivers via srobin and maintains srobin */
+			if (lp->srobin == ndev)
+				ret = isdn_net_send_skb(ndev, lp, skb);
+			else
+				ret = ndev->tbusy = isdn_net_start_xmit(skb, lp->srobin);
+			lp->srobin = (slp->slave) ? slp->slave : ndev;
+			slp = (isdn_net_local *) (lp->srobin->priv);
+			if (!((slp->flags & ISDN_NET_CONNECTED) && (slp->dialstate == 0)))
+				lp->srobin = ndev;
+		}
+		/* Slave-startup using delay-variable */
+		if (lp->slave) {
+			if (!lp->sqfull) {
+				/* First time overload: set timestamp only */
+				lp->sqfull = 1;
+				lp->sqfull_stamp = jiffies;
+			} 
+			else {
+				/* subsequent overload: if slavedelay exceeded, start dialing */
+				if ((jiffies - lp->sqfull_stamp) > lp->slavedelay)
+					isdn_net_force_dial_lp((isdn_net_local *) lp->slave->priv);
+			}
+		}
+	} 
+	else {
+		/* Not overloaded, deliver locally */
+		ret = isdn_net_send_skb(ndev, lp, skb);
+		if (lp->sqfull && ((jiffies - lp->sqfull_stamp) > (lp->slavedelay + (10*HZ) )))
+			lp->sqfull = 0;
+	}
+	return ret;
+}
+
+/*
+ * Try sending a packet.
+ * If this interface isn't connected to a ISDN-Channel, find a free channel,
+ * and start dialing.
+ */
+int
+isdn_net_start_xmit(struct sk_buff *skb, struct device *ndev)
+{
+	isdn_net_local *lp = (isdn_net_local *) ndev->priv;
+
+
+	if (ndev->tbusy) {
+		if (jiffies - ndev->trans_start < 20)
+			return 1;
+                if (!lp->dialstate)
+                        lp->stats.tx_errors++;
+		ndev->tbusy = 0;
+		ndev->trans_start = jiffies;
+	}
+	if (skb == NULL) {
+		dev_tint(ndev);
+		return 0;
+	}
+	/* Avoid timer-based retransmission conflicts. */
+	if (set_bit(0, (void *) &ndev->tbusy) != 0)
+		printk(KERN_WARNING
+                       "%s: Transmitter access conflict.\n",
+                       ndev->name);
+	else {
+		u_char *buf = skb->data;
+#ifdef ISDN_DEBUG_NET_DUMP
+		isdn_dumppkt("S:", buf, skb->len, 40);
+#endif
+		if (!(lp->flags & ISDN_NET_CONNECTED)) {
+			int chi;
+			if (lp->phone[1]) {
+				ulong flags;
+				save_flags(flags);
+				cli();
+				/* Grab a free ISDN-Channel */
+				if ((chi = 
+                                     isdn_get_free_channel(ISDN_USAGE_NET,
+                                                           lp->l2_proto,
+                                                           lp->l3_proto,
+                                                           lp->pre_device,
+                                                           lp->pre_channel)) < 0) {
+                                        printk(KERN_WARNING
+                                               "isdn_net_start_xmit: No channel for %s\n",
+                                               ndev->name);
+					restore_flags(flags);
+					return 1;
+				}
+                                /* Log packet, which triggered dialing */
+				if (dev->net_verbose)
+                                        isdn_net_log_packet(buf, lp);
+				lp->dialstate = 1;
+				lp->flags |= ISDN_NET_CONNECTED;
+				/* Connect interface with channel */
+				isdn_net_bind_channel(lp, chi);
+#ifdef CONFIG_ISDN_PPP
+				if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
+					if (isdn_ppp_bind(lp) < 0) {
+						lp->dialstate = 0;
+						isdn_free_channel(lp->isdn_device,
+                                                                  lp->isdn_channel,
+                                                                  ISDN_USAGE_NET);
+                                                restore_flags(flags);
+						return 1;
+					}
+#endif
+                                /* remember first skb to speed up arp
+                                 * when using encap ETHER
+                                 */
+                                lp->first_skb = skb;
+				/* Initiate dialing */
+				isdn_net_dial();
+                                ndev->tbusy = 0;
+				restore_flags(flags);
+                                return 0;
+			} else {
+                                /*
+                                 * Having no phone-number is a permanent
+                                 * failure or misconfiguration.
+                                 * Instead of just dropping, we should also
+                                 * have the upper layers to respond
+                                 * with an ICMP No route to host in the
+                                 * future, however at the moment, i don't
+                                 * know a simple way to do that.
+                                 * The same applies, when the telecom replies
+                                 * "no destination" to our dialing-attempt.
+                                 */
+                                printk(KERN_WARNING
+                                       "isdn_net: No phone number for %s, packet dropped\n",
+                                       ndev->name);
+				dev_kfree_skb(skb, FREE_WRITE);
+				ndev->tbusy = 0;
+                                return 0;
+			}
+		} else {
+                        /* Connection is established, try sending */
+			ndev->trans_start = jiffies;
+			if (!lp->dialstate) {
+                                if (lp->first_skb) {
+                                        if (isdn_net_xmit(ndev,lp,lp->first_skb))
+                                                return 1;
+                                        lp->first_skb = NULL;
+                                }
+				return(isdn_net_xmit(ndev, lp, skb));
+			} else
+				ndev->tbusy = 1;
+		}
+	}
+	return 1;
+}
+
+/*
+ * Shutdown a net-interface.
+ */
+static int
+isdn_net_close(struct device *dev)
+{
+	struct device *p;
+
+	dev->tbusy = 1;
+	dev->start = 0;
+	isdn_net_hangup(dev);
+	if ((p = (((isdn_net_local *) dev->priv)->slave))) {
+		/* If this interface has slaves, stop them also */
+		while (p) {
+			isdn_net_hangup(p);
+			p->tbusy = 1;
+			p->start = 0;
+			p = (((isdn_net_local *) p->priv)->slave);
+		}
+	}
+	isdn_MOD_DEC_USE_COUNT();
+	return 0;
+}
+
+/*
+ * Get statistics
+ */
+static struct enet_statistics *
+ isdn_net_get_stats(struct device *dev)
+{
+	isdn_net_local *lp = (isdn_net_local *) dev->priv;
+	return &lp->stats;
+}
+
+/*      This is simply a copy from std. eth.c EXCEPT we pull ETH_HLEN
+ *      instead of dev->hard_header_len off. This is done, because the
+ *      lowlevel-driver has already pulled of it's stuff, when we get
+ *      here and this routine only get's called whit p_encap == ETHER.
+ *      Determine the packet's protocol ID. The rule here is that we
+ *      assume 802.3 if the type field is short enough to be a length.
+ *      This is normal practice and works for any 'now in use' protocol.
+ */
+
+unsigned short isdn_net_type_trans(struct sk_buff *skb, struct device *dev)
+{
+        struct ethhdr *eth;
+        unsigned char *rawp;
+        
+        skb_pull(skb,ETH_HLEN);
+        eth= skb->mac.ethernet;
+        
+        if(*eth->h_dest&1) {
+                if(memcmp(eth->h_dest,dev->broadcast, ETH_ALEN)==0)
+                        skb->pkt_type=PACKET_BROADCAST;
+                else
+                        skb->pkt_type=PACKET_MULTICAST;
+        }
+        
+        /*
+         *      This ALLMULTI check should be redundant by 1.4
+         *      so don't forget to remove it.
+         */
+        
+        else if (dev->flags&(IFF_PROMISC|IFF_ALLMULTI)) {
+                if (memcmp(eth->h_dest,dev->dev_addr, ETH_ALEN))
+                        skb->pkt_type=PACKET_OTHERHOST;
+        }
+
+        if (ntohs(eth->h_proto) >= 1536)
+                return eth->h_proto;
+
+        rawp = skb->data;
+
+        /*
+         *      This is a magic hack to spot IPX packets. Older Novell breaks
+         *      the protocol design and runs IPX over 802.3 without an 802.2 LLC
+         *      layer. We look for FFFF which isnt a used 802.2 SSAP/DSAP. This
+         *      won't work for fault tolerant netware but does for the rest.
+         */
+        if (*(unsigned short *)rawp == 0xFFFF)
+                return htons(ETH_P_802_3);
+        /*
+         *      Real 802.2 LLC
+         */
+        return htons(ETH_P_802_2);
+}
+
+/* 
+ * Got a packet from ISDN-Channel.
+ */
+static void
+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' */
+#endif
+
+	lp->transcount += skb->len;
+	lp->stats.rx_packets++;
+	lp->huptimer = 0;
+
+	if (lp->master) {
+		/* Bundling: If device is a slave-device, deliver to master, also
+		 * handle master's statistics and hangup-timeout
+		 */
+		ndev = lp->master;
+		lp = (isdn_net_local *) ndev->priv;
+		lp->stats.rx_packets++;
+		lp->huptimer = 0;
+	}
+
+	skb->dev = ndev;
+	skb->pkt_type = PACKET_HOST;
+        skb->mac.raw = skb->data;
+#ifdef ISDN_DEBUG_NET_DUMP
+        isdn_dumppkt("R:", skb->data, skb->len, 40);
+#endif
+	switch (lp->p_encap) {
+	case ISDN_NET_ENCAP_ETHER:
+		/* Ethernet over ISDN */
+		skb->protocol = isdn_net_type_trans(skb,ndev);
+		break;
+	case ISDN_NET_ENCAP_RAWIP:
+		/* RAW-IP without MAC-Header */
+		skb->protocol = htons(ETH_P_IP);
+		break;
+	case ISDN_NET_ENCAP_CISCOHDLC:
+		/* CISCO-HDLC IP with type field and  fake I-frame-header */
+		skb_pull(skb, 2);
+		/* Fall through */
+	case ISDN_NET_ENCAP_IPTYP:
+		/* IP with type field */
+		skb->protocol = *(unsigned short *)&(skb->data[0]);
+		skb_pull(skb, 2);
+                if (*(unsigned short *)skb->data == 0xFFFF)
+                        skb->protocol = htons(ETH_P_802_3);
+		break;
+#ifdef CONFIG_ISDN_PPP
+	case ISDN_NET_ENCAP_SYNCPPP:
+		isdn_ppp_receive(lp->netdev, olp, skb);
+		return;
+#endif
+        default:
+                printk(KERN_WARNING "%s: unknown encapsulation, dropping\n",
+                       lp->name);
+                kfree_skb(skb,FREE_READ);
+                return;
+	}
+	netif_rx(skb);
+	return;
+}
+
+/*
+ * A packet arrived via ISDN. Search interface-chain for a corresponding
+ * interface. If found, deliver packet to receiver-function and return 1,
+ * else return 0.
+ */
+int
+isdn_net_receive_callback(int idx, u_char * buf, int len)
+{
+	isdn_net_dev *p = dev->rx_netdev[idx];
+	struct sk_buff *skb;
+
+	if (p) {
+		isdn_net_local *lp = &p->local;
+		if ((lp->flags & ISDN_NET_CONNECTED) &&
+		    (!lp->dialstate)) {
+			skb = dev_alloc_skb(len);
+			if (skb == NULL) {
+				printk(KERN_WARNING "out of memory\n");
+				return 0;
+			}
+			memcpy(skb_put(skb, len), buf, len);
+			isdn_net_receive(&p->dev, skb);
+			return 1;
+		}
+	}
+	return 0;
+}
+
+/*
+ *  receive callback for lovlevel drivers, which support skb's
+ */
+
+int
+isdn_net_rcv_skb(int idx, struct sk_buff *skb) 
+{
+	isdn_net_dev *p = dev->rx_netdev[idx];
+
+	if (p) {
+		isdn_net_local *lp = &p->local;
+		if ((lp->flags & ISDN_NET_CONNECTED) &&
+		    (!lp->dialstate)) {
+			isdn_net_receive(&p->dev, skb);
+			return 1;
+		}
+	}
+	return 0;
+}
+
+static int
+my_eth_header(struct sk_buff *skb, struct device *dev, unsigned short type,
+              void *daddr, void *saddr, unsigned len)
+{
+	struct ethhdr *eth = (struct ethhdr *)skb_push(skb,ETH_HLEN);
+
+	/* 
+	 * Set the protocol type. For a packet of type ETH_P_802_3 we
+         * put the length here instead. It is up to the 802.2 layer to
+         * carry protocol information.
+	 */
+	
+	if(type!=ETH_P_802_3) 
+		eth->h_proto = htons(type);
+	else
+		eth->h_proto = htons(len);
+
+	/*
+	 *	Set the source hardware address. 
+	 */
+	if(saddr)
+		memcpy(eth->h_source,saddr,dev->addr_len);
+	else
+		memcpy(eth->h_source,dev->dev_addr,dev->addr_len);
+
+	/*
+	 *	Anyway, the loopback-device should never use this function... 
+	 */
+
+	if (dev->flags & IFF_LOOPBACK) {
+		memset(eth->h_dest, 0, dev->addr_len);
+		return(dev->hard_header_len);
+	}
+	
+	if(daddr) {
+		memcpy(eth->h_dest,daddr,dev->addr_len);
+		return dev->hard_header_len;
+	}
+	
+	return -dev->hard_header_len;
+}
+
+/*
+ *  build an header
+ *  depends on encaps that is beeing used.
+ */
+ 
+static int
+isdn_net_header(struct sk_buff *skb, struct device *dev, unsigned short type,
+                void *daddr, void *saddr, unsigned plen)
+{
+	isdn_net_local *lp = dev->priv;
+	ushort len = 0;
+	
+	switch (lp->p_encap) {
+                case ISDN_NET_ENCAP_ETHER:
+                        len = my_eth_header(skb, dev, type, daddr, saddr, plen);
+                        break;
+                case ISDN_NET_ENCAP_RAWIP:
+                        printk(KERN_WARNING "isdn_net_header called with RAW_IP!\n");
+			len = 0;
+                        break;
+                case ISDN_NET_ENCAP_IPTYP:
+                        /* ethernet type field */
+                        *((ushort*) skb_push(skb, 2)) = htons(type);
+                        len = 2;
+                        break;
+                case ISDN_NET_ENCAP_CISCOHDLC:
+			skb_push(skb, 4);
+                        skb->data[0] = 0x0f;
+                        skb->data[1] = 0x00;
+                        *((ushort*)&skb->data[2]) = htons(type);
+                        len = 4;
+                        break;
+#ifdef CONFIG_ISDN_PPP
+                case ISDN_NET_ENCAP_SYNCPPP:
+                        /* reserve space to be filled in isdn_ppp_xmit */
+                        len = 4;
+#ifdef CONFIG_ISDN_MPP
+                        if (lp->ppp_minor!=-1) {
+                                if (ippp_table[lp->ppp_minor].mpppcfg &
+                                    SC_MP_PROT) {
+                                        if (ippp_table[lp->ppp_minor].mpppcfg &
+                                            SC_OUT_SHORT_SEQ)
+                                                len = 7;
+                                        else
+                                                len = 9;
+                                }
+                        }
+#endif
+                        /* Initialize first 4 bytes to a value, which is
+                         * guaranteed to be invalid. Need that to check
+                         * for already compressed packets in isdn_ppp_xmit().
+                         */
+                        *((unsigned long *)skb_push(skb, len)) = 0;
+                        break;
+#endif
+	}
+	return len;
+}
+
+/* We don't need to send arp, because we have point-to-point connections. */
+
+static int
+isdn_net_rebuild_header(void *buff, struct device *dev, ulong 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_rebuild_header: Don't know how to resolve type %d addresses?\n",
+                               (int)eth->h_proto);
+                        memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
+                        return 0;
+                }
+                /*
+                 *      Try and get ARP to resolve the header.
+                 */
+#ifdef CONFIG_INET       
+                ret = arp_find((unsigned char *)&(eth->h_dest), dst, dev, dev->pa_addr,skb)? 1 : 0;
+#endif  
+        }
+	return ret;
+}
+
+/*
+ * Interface-setup. (called just after registering a new interface)
+ */
+static int
+isdn_net_init(struct device *ndev)
+{
+	ushort max_hlhdr_len = 0;
+        isdn_net_local *lp = (isdn_net_local *)ndev->priv;
+	int drvidx, i;
+
+	if (ndev == NULL) {
+		printk(KERN_WARNING "isdn_net_init: dev = NULL!\n");
+		return -ENODEV;
+	}
+	if (ndev->priv == NULL) {
+		printk(KERN_WARNING "isdn_net_init: dev->priv = NULL!\n");
+		return -ENODEV;
+	}
+
+        ether_setup(ndev);
+        lp->org_hcb               = ndev->header_cache_bind;
+        lp->org_hcu               = ndev->header_cache_update;
+
+	/* Setup the generic properties */
+
+        ndev->hard_header         = NULL;
+        ndev->header_cache_bind   = NULL;
+        ndev->header_cache_update = NULL;
+        ndev->mtu                 = 1500;
+        ndev->flags               = IFF_NOARP;
+        ndev->family              = AF_INET;
+        ndev->type                = ARPHRD_ETHER;  
+        ndev->addr_len            = ETH_ALEN;
+        ndev->pa_addr             = 0;
+        ndev->pa_brdaddr          = 0;
+        ndev->pa_mask             = 0;
+        ndev->pa_alen             = 4;
+
+        for (i = 0; i < ETH_ALEN; i++)
+                ndev->broadcast[i]=0xff;
+
+	for (i = 0; i < DEV_NUMBUFFS; i++)
+                skb_queue_head_init(&ndev->buffs[i]);
+	
+	/* The ISDN-specific entries in the device structure. */
+	ndev->open                = &isdn_net_open;
+	ndev->hard_start_xmit     = &isdn_net_start_xmit;
+
+	/* 
+	 *  up till binding we ask the protocol layer to reserve as much
+	 *  as we migth need for HL layer
+         */
+	
+	for (drvidx = 0; drvidx < ISDN_MAX_DRIVERS; drvidx++)
+		if (dev->drv[drvidx])
+			if (max_hlhdr_len < dev->drv[drvidx]->interface->hl_hdrlen)
+				max_hlhdr_len = dev->drv[drvidx]->interface->hl_hdrlen;
+
+	ndev->hard_header_len     = ETH_HLEN + max_hlhdr_len;
+
+	ndev->stop                = &isdn_net_close;
+	ndev->get_stats           = &isdn_net_get_stats;
+	ndev->rebuild_header      = &isdn_net_rebuild_header;
+
+#ifdef CONFIG_ISDN_PPP
+	ndev->do_ioctl            = isdn_ppp_dev_ioctl;
+#endif
+	return 0;
+}
+
+/*
+ * I picked the pattern-matching-functions from an old GNU-tar version (1.10)
+ * It was originaly written and put to PD by rs@mirror.TMC.COM (Rich Salz)
+ */
+
+static int
+isdn_net_Star(char *s, char *p)
+{
+	while (isdn_net_wildmat(s, p) == 0)
+		if (*++s == '\0')
+			return (0);
+	return (1);
+}
+
+/*
+ * Shell-type Pattern-matching for incoming caller-Ids
+ * This function gets a string in s and checks, if it matches the pattern
+ * given in p. It returns 1 on success, 0 otherwise.
+ *
+ * Posible Patterns:
+ *
+ * '?'     matches one character
+ * '*'     matches zero or more characters
+ * [xyz]   matches the set of charcters in brackets.
+ * [^xyz]  matches any single character not in the set of characters
+ */
+
+static int
+isdn_net_wildmat(char *s, char *p)
+{
+	register int last;
+	register int matched;
+	register int reverse;
+
+	for (; *p; s++, p++)
+		switch (*p) {
+                        case '\\':
+                                /*
+                                 * Literal match with following character,
+                                 * fall through.
+                                 */
+                                p++;
+                        default:
+                                if (*s != *p)
+                                        return (0);
+                                continue;
+                        case '?':
+                                /* Match anything. */
+                                if (*s == '\0')
+                                        return (0);
+                                continue;
+                        case '*':
+                                /* Trailing star matches everything. */
+                                return (*++p ? isdn_net_Star(s, p) : 1);
+                        case '[':
+                                /* [^....] means inverse character class. */
+                                if ((reverse = (p[1] == '^')))
+                                        p++;
+                                for (last = 0, matched = 0; *++p && (*p != ']'); last = *p)
+                                        /* This next line requires a good C compiler. */
+                                        if (*p == '-' ? *s <= *++p && *s >= last : *s == *p)
+                                                matched = 1;
+                                if (matched == reverse)
+                                        return (0);
+                                continue;
+		}
+	return (*s == '\0');
+}
+
+static void
+isdn_net_swapbind(int drvidx)
+{
+	isdn_net_dev *p;
+
+#ifdef ISDN_DEBUG_NET_ICALL
+	printk(KERN_DEBUG "n_fi: swapping ch of %d\n", drvidx);
+#endif
+	p = dev->netdev;
+	while (p) {
+		if (p->local.pre_device == drvidx)
+			switch (p->local.pre_channel) {
+			case 0:
+				p->local.pre_channel = 1;
+				break;
+			case 1:
+				p->local.pre_channel = 0;
+				break;
+			}
+		p = (isdn_net_dev *) p->next;
+	}
+}
+
+static void
+isdn_net_swap_usage(int i1, int i2)
+{
+	int u1 = dev->usage[i1] & ISDN_USAGE_EXCLUSIVE;
+	int u2 = dev->usage[i2] & ISDN_USAGE_EXCLUSIVE;
+
+#ifdef ISDN_DEBUG_NET_ICALL
+	printk(KERN_DEBUG "n_fi: usage of %d and %d\n", i1, i2);
+#endif
+	dev->usage[i1] &= ~ISDN_USAGE_EXCLUSIVE;
+	dev->usage[i1] |= u2;
+	dev->usage[i2] &= ~ISDN_USAGE_EXCLUSIVE;
+	dev->usage[i2] |= u1;
+	isdn_info_update();
+}
+
+/*
+ * An incoming call-request has arrived.
+ * Search the interface-chain for an aproppriate interface.
+ * If found, connect the interface to the ISDN-channel and initiate
+ * D- and B-Channel-setup. If secure-flag is set, accept only
+ * configured phone-numbers. If callback-flag is set, initiate
+ * callback-dialing.
+ *
+ * Return-Value: 0 = No appropriate interface for this call.
+ *               1 = Call accepted
+ *               2 = Do callback
+ */
+int
+isdn_net_find_icall(int di, int ch, int idx, char *num)
+{
+	char *eaz;
+	int si1;
+	int si2;
+	int ematch;
+	int swapped;
+	int sidx = 0;
+	isdn_net_dev *p;
+	isdn_net_phone *n;
+	ulong flags;
+	char nr[31];
+	char *s;
+
+	/* Search name in netdev-chain */
+	save_flags(flags);
+	cli();
+	if (num[0] == ',') {
+		nr[0] = '0';
+		strncpy(&nr[1], num, 30);
+		printk(KERN_WARNING "isdn_net: Incoming call without OAD, assuming '0'\n");
+	} else
+		strncpy(nr, num, 30);
+	s = strtok(nr, ",");
+	s = strtok(NULL, ",");
+	if (!s) {
+		printk(KERN_WARNING "isdn_net: Incoming callinfo garbled, ignored: %s\n",
+		       num);
+		restore_flags(flags);
+		return 0;
+	}
+	si1 = (int)simple_strtoul(s,NULL,10);
+	s = strtok(NULL, ",");
+	if (!s) {
+		printk(KERN_WARNING "isdn_net: Incoming callinfo garbled, ignored: %s\n",
+		       num);
+		restore_flags(flags);
+		return 0;
+	}
+	si2 = (int)simple_strtoul(s,NULL,10);
+	eaz = strtok(NULL, ",");
+	if (!eaz) {
+		printk(KERN_WARNING "isdn_net: Incoming call without CPN, assuming '0'\n");
+		eaz = "0";
+	}
+	if (dev->net_verbose > 1)
+		printk(KERN_INFO "isdn_net: call from %s,%d,%d -> %s\n", nr, si1, si2, eaz);
+	/* Accept only calls with Si1 = 7 (Data-Transmission) */
+	if (si1 != 7) {
+		if (dev->net_verbose > 1)
+			printk(KERN_INFO "isdn_net: Service-Indicator not 7, ignored\n");
+		return 0;
+	}
+	n = (isdn_net_phone *) 0;
+	p = dev->netdev;
+	ematch = 0;
+#ifdef ISDN_DEBUG_NET_ICALL
+	printk(KERN_DEBUG "n_fi: di=%d ch=%d idx=%d usg=%d\n", di, ch, idx,
+	       dev->usage[idx]);
+#endif
+	swapped = 0;
+	while (p) {
+		/* If last check has trigered as binding-swap, revert it */
+		switch (swapped) {
+		case 2:
+			isdn_net_swap_usage(idx, sidx);
+			/* fall through */
+		case 1:
+			isdn_net_swapbind(di);
+			break;
+		}
+		swapped = 0;
+		if (!strcmp(isdn_map_eaz2msn(p->local.msn, di), eaz))
+			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);
+#endif
+		if ((!strcmp(isdn_map_eaz2msn(p->local.msn, di), eaz)) &&	/* EAZ is matching   */
+		    (((!(p->local.flags & ISDN_NET_CONNECTED)) &&	/* but not connected */
+		      (USG_NONE(dev->usage[idx]))) ||	/* and ch. unused or */
+		     (((p->local.dialstate == 4) &&	/* if dialing        */
+		       (!(p->local.flags & ISDN_NET_CALLBACK)))		/* but no callback   */
+		     ))) {
+#ifdef ISDN_DEBUG_NET_ICALL
+			printk(KERN_DEBUG "n_fi: match1, pdev=%d pch=%d\n",
+			       p->local.pre_device, p->local.pre_channel);
+#endif
+			if (dev->usage[idx] & ISDN_USAGE_EXCLUSIVE) {
+				if ((p->local.pre_channel != ch) ||
+				    (p->local.pre_device != di)) {
+					/* Here we got a problem:
+					   If using an ICN-Card, an incoming call is always signaled on
+					   on the first channel of the card, if both channels are
+					   down. However this channel may be bound exclusive. If the
+					   second channel is free, this call should be accepted.
+					   The solution is horribly but it runs, so what:
+					   We exchange the exclusive bindings of the two channels, the
+					   corresponding variables in the interface-structs.
+					 */
+					if (ch == 0) {
+						sidx = isdn_dc2minor(di, 1);
+#ifdef ISDN_DEBUG_NET_ICALL
+						printk(KERN_DEBUG "n_fi: ch is 0\n");
+#endif
+						if (USG_NONE(dev->usage[sidx])) {
+							/* Second Channel is free, now see if it is bound
+							   exclusive too. */
+							if (dev->usage[sidx] & ISDN_USAGE_EXCLUSIVE) {
+#ifdef ISDN_DEBUG_NET_ICALL
+								printk(KERN_DEBUG "n_fi: 2nd channel is down and bound\n");
+#endif
+								/* Yes, swap bindings only, if the original
+								   binding is bound to channel 1 of this driver */
+								if ((p->local.pre_device == di) &&
+								    (p->local.pre_channel == 1)) {
+									isdn_net_swapbind(di);
+									swapped = 1;
+								} else {
+									/* ... else iterate next device */
+									p = (isdn_net_dev *) p->next;
+									continue;
+								}
+							} else {
+#ifdef ISDN_DEBUG_NET_ICALL
+								printk(KERN_DEBUG "n_fi: 2nd channel is down and unbound\n");
+#endif
+								/* No, swap always and swap excl-usage also */
+								isdn_net_swap_usage(idx, sidx);
+								isdn_net_swapbind(di);
+								swapped = 2;
+							}
+							/* Now check for exclusive binding again */
+#ifdef ISDN_DEBUG_NET_ICALL
+							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))) {
+#ifdef ISDN_DEBUG_NET_ICALL
+								printk(KERN_DEBUG "n_fi: final check failed\n");
+#endif
+								p = (isdn_net_dev *) p->next;
+								continue;
+							}
+						}
+					} else {
+						/* We are already on the second channel, so nothing to do */
+#ifdef ISDN_DEBUG_NET_ICALL
+						printk(KERN_DEBUG "n_fi: already on 2nd channel\n");
+#endif
+						p = (isdn_net_dev *) p->next;
+						continue;
+					}
+				}
+			}
+#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) {
+				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);
+#ifdef ISDN_DEBUG_NET_ICALL
+				printk(KERN_DEBUG "n_fi: match3\n");
+#endif
+				/* Here we got an interface matched, now see if it is up.
+				 * If not, reject the call actively.
+				 */
+				if (!p->dev.start) {
+					restore_flags(flags);
+					printk(KERN_INFO "%s: incoming call, if down -> rejected\n",
+					       lp->name);
+					return 3;
+				}
+				/* Interface is up, now see if it's a slave. If so, see if
+				 * it's master and parent slave is online. If not, reject the call.
+				 */
+				if (lp->master) {
+					isdn_net_local *mlp = (isdn_net_local *) lp->master->priv;
+					printk(KERN_DEBUG "ICALLslv: %s\n", lp->name);
+					printk(KERN_DEBUG "master=%s\n", mlp->name);
+					if (mlp->flags & ISDN_NET_CONNECTED) {
+						printk(KERN_DEBUG "master online\n");
+						/* Master is online, find parent-slave (master if first slave) */
+						while (mlp->slave) {
+							if ((isdn_net_local *) mlp->slave->priv == lp)
+								break;
+							mlp = (isdn_net_local *) mlp->slave->priv;
+						}
+					} else
+						printk(KERN_DEBUG "master offline\n");
+					/* Found parent, if it's offline iterate next device */
+					printk(KERN_DEBUG "mlpf: %d\n", mlp->flags & ISDN_NET_CONNECTED);
+					if (!(mlp->flags & ISDN_NET_CONNECTED)) {
+						p = (isdn_net_dev *) p->next;
+						continue;
+					}
+				}
+				if (lp->flags & ISDN_NET_CALLBACK) {
+					int chi;
+					printk(KERN_DEBUG "%s: call from %s -> %s, start callback\n",
+					       lp->name, nr, eaz);
+					if (lp->phone[1]) {
+						/* Grab a free ISDN-Channel */
+						if ((chi = isdn_get_free_channel(ISDN_USAGE_NET, lp->l2_proto,
+							    lp->l3_proto,
+							  lp->pre_device,
+						 lp->pre_channel)) < 0) {
+							printk(KERN_WARNING "isdn_net_find_icall: No channel for %s\n", lp->name);
+							restore_flags(flags);
+							return 0;
+						}
+						/* Setup dialstate. */
+						lp->dialstate = 1;
+						lp->flags |= ISDN_NET_CONNECTED;
+						/* Connect interface with channel */
+						isdn_net_bind_channel(lp, chi);
+#ifdef CONFIG_ISDN_PPP
+						if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
+							if (isdn_ppp_bind(lp) < 0) {
+								isdn_free_channel(p->local.isdn_device, p->local.isdn_channel,
+									     ISDN_USAGE_NET);
+								lp->dialstate = 0;
+								restore_flags(flags);
+								return 0;
+							}
+#endif
+						/* Initiate dialing by returning 2 */
+						restore_flags(flags);
+						return 2;
+					} else
+						printk(KERN_WARNING "isdn_net: %s: No phone number\n", lp->name);
+					restore_flags(flags);
+					return 0;
+				} else {
+					printk(KERN_DEBUG "%s: call from %s -> %s accepted\n", lp->name, nr,
+					       eaz);
+#if 0
+/* why is this a CONFIG_ISDN_PPP feature ??? */
+#ifdef CONFIG_ISDN_PPP
+					if (p->local.isdn_device != -1) {
+						isdn_free_channel(p->local.isdn_device, p->local.isdn_channel,
+							 ISDN_USAGE_NET);
+					}
+#endif
+#endif
+					/* if this interface is dialing, it does it probably on a different
+					   device, so free this device */
+					if (p->local.dialstate == 4)
+						isdn_free_channel(p->local.isdn_device, p->local.isdn_channel,
+							 ISDN_USAGE_NET);
+					dev->usage[idx] &= ISDN_USAGE_EXCLUSIVE;
+					dev->usage[idx] |= ISDN_USAGE_NET;
+					strcpy(dev->num[idx], nr);
+					isdn_info_update();
+                                        dev->st_netdev[idx] = lp->netdev;
+					p->local.isdn_device = di;
+					p->local.isdn_channel = ch;
+					p->local.ppp_minor = -1;
+					p->local.flags |= ISDN_NET_CONNECTED;
+					p->local.dialstate = 7;
+					p->local.dtimer = 0;
+					p->local.outgoing = 0;
+					p->local.huptimer = 0;
+					p->local.hupflags |= 1;
+#ifdef CONFIG_ISDN_PPP
+					if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
+						if (isdn_ppp_bind(lp) < 0) {
+							isdn_free_channel(p->local.isdn_device, p->local.isdn_channel,
+							 ISDN_USAGE_NET);
+							lp->dialstate = 0;
+							restore_flags(flags);
+							return 0;
+						}
+#endif
+					restore_flags(flags);
+					return 1;
+				}
+			}
+		}
+		p = (isdn_net_dev *) p->next;
+	}
+	/* If none of configured EAZ/MSN matched and not verbose, be silent */
+	if (ematch || dev->net_verbose)
+		printk(KERN_INFO "isdn_net: call from %s -> %d %s ignored\n", nr, di, eaz);
+	restore_flags(flags);
+	return 0;
+}
+
+/*
+ * Search list of net-interfaces for an interface with given name.
+ */
+isdn_net_dev *
+ isdn_net_findif(char *name)
+{
+	isdn_net_dev *p = dev->netdev;
+
+	while (p) {
+		if (!strcmp(p->local.name, name))
+			return p;
+		p = (isdn_net_dev *) p->next;
+	}
+	return (isdn_net_dev *) NULL;
+}
+
+/*
+ * Force a net-interface to dial out.
+ * This is called from the userlevel-routine below or
+ * from isdn_net_start_xmit().
+ */
+int isdn_net_force_dial_lp(isdn_net_local * lp)
+{
+	if ((!(lp->flags & ISDN_NET_CONNECTED)) && !lp->dialstate) {
+		int chi;
+		if (lp->phone[1]) {
+			ulong flags;
+			save_flags(flags);
+			cli();
+			/* Grab a free ISDN-Channel */
+			if ((chi = isdn_get_free_channel(ISDN_USAGE_NET, lp->l2_proto,
+						    lp->l3_proto,
+						    lp->pre_device,
+						 lp->pre_channel)) < 0) {
+				printk(KERN_WARNING "isdn_net_force_dial: No channel for %s\n", lp->name);
+				restore_flags(flags);
+				return -EAGAIN;
+			}
+			lp->dialstate = 1;
+			lp->flags |= ISDN_NET_CONNECTED;
+			/* Connect interface with channel */
+			isdn_net_bind_channel(lp, chi);
+#ifdef CONFIG_ISDN_PPP
+			if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
+				if (isdn_ppp_bind(lp) < 0) {
+					lp->dialstate = 0;
+					isdn_free_channel(lp->isdn_device, lp->isdn_channel, ISDN_USAGE_NET);
+					return 1;
+				}
+#endif
+			/* Initiate dialing */
+			isdn_net_dial();
+			restore_flags(flags);
+			return 0;
+		} else
+			return -EINVAL;
+	} else
+		return -EBUSY;
+}
+
+/*
+ * Force a net-interface to dial out.
+ * This is always called from within userspace (ISDN_IOCTL_NET_DIAL).
+ */
+int 
+isdn_net_force_dial(char *name)
+{
+	isdn_net_dev *p = isdn_net_findif(name);
+
+	if (!p)
+		return -ENODEV;
+	return (isdn_net_force_dial_lp(&p->local));
+}
+
+/*
+ * Allocate a new network-interface and initialize it's data structures.
+ */
+char *
+ isdn_net_new(char *name, struct device *master)
+{
+	isdn_net_dev *netdev;
+
+	/* Avoid creating an existing interface */
+	if (isdn_net_findif(name)) {
+		printk(KERN_WARNING "isdn_net: interface %s already exists\n", name);
+		return NULL;
+	}
+	if (!(netdev = (isdn_net_dev *) kmalloc(sizeof(isdn_net_dev), GFP_KERNEL))) {
+		printk(KERN_WARNING "isdn_net: Could not allocate net-device\n");
+		return NULL;
+	}
+	memset(netdev, 0, sizeof(isdn_net_dev));
+	if (name == NULL)
+		strcpy(netdev->local.name, "         ");
+	else
+		strcpy(netdev->local.name, name);
+	netdev->dev.name      = netdev->local.name;
+	netdev->dev.priv      = &netdev->local;
+	netdev->dev.init      = isdn_net_init;
+	netdev->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;
+		/* Put device at end of slave-chain */
+		while (p) {
+			q = p;
+			p = (((isdn_net_local *) p->priv)->slave);
+		}
+		((isdn_net_local *) q->priv)->slave = &(netdev->dev);
+		q->interrupt = 0;
+		q->tbusy = 0;
+		q->start = master->start;
+	} else {
+		/* Device shall be a master */
+		if (register_netdev(&netdev->dev) != 0) {
+			printk(KERN_WARNING "isdn_net: Could not register net-device\n");
+			kfree(netdev);
+			return NULL;
+		}
+	}
+	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_minor = -1;
+	netdev->local.l2_proto = ISDN_PROTO_L2_X75I;
+	netdev->local.l3_proto = ISDN_PROTO_L3_TRANS;
+	netdev->local.slavedelay = 10 * HZ;
+	netdev->local.srobin = &netdev->dev;
+	netdev->local.hupflags = 8;	/* Do hangup even on incoming calls */
+	netdev->local.onhtime = 10;	/* Default hangup-time for saving costs
+					   of those who forget configuring this */
+	/* The following should be configurable via ioctl */
+	netdev->local.dialmax = 1;
+	/* Put into to netdev-chain */
+	netdev->next = (void *) dev->netdev;
+	dev->netdev = netdev;
+	return netdev->dev.name;
+}
+
+char *
+ isdn_net_newslave(char *parm)
+{
+	char *p = strchr(parm, ',');
+	isdn_net_dev *n;
+	char newname[10];
+
+	if (p) {
+		/* Slave-Name MUST not be empty */
+		if (!strlen(p + 1))
+			return NULL;
+		strcpy(newname, p + 1);
+		*p = 0;
+		/* Master must already exist */
+		if (!(n = isdn_net_findif(parm)))
+			return NULL;
+		/* Master must be a real interface, not a slave */
+		if (n->local.master)
+			return NULL;
+		return (isdn_net_new(newname, &(n->dev)));
+	}
+	return NULL;
+}
+
+/*
+ * Set interface-parameters.
+ * Allways set all parameters, so the user-level application is responsible
+ * for not overwriting existing setups. It has to get the current
+ * setup first, if only selected parameters are to be changed.
+ */
+int isdn_net_setcfg(isdn_net_ioctl_cfg * cfg)
+{
+	isdn_net_dev *p = isdn_net_findif(cfg->name);
+	ulong features;
+	int i;
+	int drvidx;
+	int chidx;
+	char drvid[25];
+
+	if (p) {
+		/* See if any registered driver supports the features we want */
+		features = (1 << cfg->l2_proto) | (256 << cfg->l3_proto);
+		for (i = 0; i < ISDN_MAX_DRIVERS; i++)
+			if (dev->drv[i])
+				if ((dev->drv[i]->interface->features & features) == features)
+					break;
+		if (i == ISDN_MAX_DRIVERS) {
+			printk(KERN_WARNING "isdn_net: No driver with selected features\n");
+			return -ENODEV;
+		}
+                if ((p->local.p_encap != cfg->p_encap) &&
+                    ((p->local.p_encap == ISDN_NET_ENCAP_RAWIP) ||
+                     (cfg->p_encap == ISDN_NET_ENCAP_RAWIP)        ))
+                        if (p->dev.start) {
+                                printk(KERN_WARNING
+                                       "%s: cannot change encap when if is up\n",
+                                       p->local.name);
+                                return -EBUSY;
+                        }
+#ifndef CONFIG_ISDN_PPP
+                if (cfg->p_encap == ISDN_NET_ENCAP_SYNCPPP) {
+                        printk(KERN_WARNING "%s: SyncPPP not configured\n",
+                               p->local.name);
+                        return -EINVAL;
+                }
+#endif
+		if (strlen(cfg->drvid)) {
+			/* A bind has been requested ... */
+			char *c,*e;
+
+			drvidx = -1;
+			chidx = -1;
+			strcpy(drvid, cfg->drvid);
+			if ((c = strchr(drvid, ','))) {
+				/* The channel-number is appended to the driver-Id with a comma */
+				chidx = (int)simple_strtoul(c + 1,&e,10);
+				if (e == c)
+					chidx = -1;
+				*c = '\0';
+			}
+			for (i = 0; i < ISDN_MAX_DRIVERS; i++)
+				/* Lookup driver-Id in array */
+				if (!(strcmp(dev->drvid[i], drvid))) {
+					drvidx = i;
+					break;
+				}
+			if ((drvidx == -1) || (chidx == -1))
+				/* Either driver-Id or channel-number invalid */
+				return -ENODEV;
+		} else {
+			/* Parameters are valid, so get them */
+			drvidx = p->local.pre_device;
+			chidx = p->local.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,
+						  drvidx,
+						  chidx)) < 0) {
+				/* Grab failed, because desired channel is in use */
+				p->local.exclusive = -1;
+				restore_flags(flags);
+				return -EBUSY;
+			}
+			/* All went ok, so update isdninfo */
+			dev->usage[i] = ISDN_USAGE_EXCLUSIVE;
+			isdn_info_update();
+			restore_flags(flags);
+			p->local.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);
+				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.slavedelay  = cfg->slavedelay * HZ;
+		p->local.p_encap     = cfg->p_encap;
+		if (cfg->secure)
+			p->local.flags |= ISDN_NET_SECURE;
+		else
+			p->local.flags &= ~ISDN_NET_SECURE;
+		if (cfg->callback)
+			p->local.flags |= ISDN_NET_CALLBACK;
+		else
+			p->local.flags &= ~ISDN_NET_CALLBACK;
+		if (cfg->chargehup)
+			p->local.hupflags |= 4;
+		else
+			p->local.hupflags &= ~4;
+		if (cfg->ihup)
+			p->local.hupflags |= 8;
+		else
+			p->local.hupflags &= ~8;
+                if (cfg->p_encap == ISDN_NET_ENCAP_RAWIP) {
+                        p->dev.hard_header         = NULL;
+                        p->dev.header_cache_bind   = NULL;
+                        p->dev.header_cache_update = NULL;
+                        p->dev.flags               = IFF_NOARP;
+                } else {
+                        p->dev.hard_header = isdn_net_header;
+                        if (cfg->p_encap == ISDN_NET_ENCAP_ETHER) {
+                                p->dev.header_cache_bind   = p->local.org_hcb;
+                                p->dev.header_cache_update = p->local.org_hcu;
+                                p->dev.flags = IFF_BROADCAST | IFF_MULTICAST;
+                        } else {
+                                p->dev.header_cache_bind   = NULL;
+                                p->dev.header_cache_update = NULL;
+                                p->dev.flags               = IFF_NOARP;
+                        }
+                }
+                return 0;
+	}
+	return -ENODEV;
+}
+
+/*
+ * Perform get-interface-parameters.ioctl
+ */
+int isdn_net_getcfg(isdn_net_ioctl_cfg * cfg)
+{
+	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);
+		} 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 & 4) ? 1 : 0;
+		cfg->ihup = (p->local.hupflags & 8) ? 1 : 0;
+		cfg->slavedelay = p->local.slavedelay / HZ;
+		if (p->local.slave)
+			strcpy(cfg->slave, ((isdn_net_local *) p->local.slave->priv)->name);
+		else
+			cfg->slave[0] = '\0';
+		if (p->local.master)
+			strcpy(cfg->master, ((isdn_net_local *) p->local.master->priv)->name);
+		else
+			cfg->master[0] = '\0';
+		return 0;
+	}
+	return -ENODEV;
+}
+
+/*
+ * Add a phone-number to an interface.
+ */
+int isdn_net_addphone(isdn_net_ioctl_phone * phone)
+{
+	isdn_net_dev *p = isdn_net_findif(phone->name);
+	isdn_net_phone *n;
+
+	if (isdn_net_checkwild(phone->phone) && (phone->outgoing & 1))
+		return -EINVAL;
+	if (p) {
+		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;
+		return 0;
+	}
+	return -ENODEV;
+}
+
+/*
+ * Return a string of all phone-numbers of an interface.
+ */
+int isdn_net_getphones(isdn_net_ioctl_phone * phone, char *phones)
+{
+	isdn_net_dev *p = isdn_net_findif(phone->name);
+	int inout = phone->outgoing & 1;
+	int more = 0;
+	int count = 0;
+	isdn_net_phone *n;
+	int flags;
+	int ret;
+
+	if (!p)
+		return -ENODEV;
+	save_flags(flags);
+	cli();
+	inout &= 1;
+	n = p->local.phone[inout];
+        if (n)
+                count++;
+	while (n) {
+		if (more) {
+			put_fs_byte(' ', phones++);
+			count++;
+		}
+		if ((ret = verify_area(VERIFY_WRITE, (void *) phones, strlen(n->num) + 1))) {
+			restore_flags(flags);
+			return ret;
+		}
+		memcpy_tofs(phones, n->num, strlen(n->num) + 1);
+		phones += strlen(n->num);
+		count += strlen(n->num);
+		n = n->next;
+		more = 1;
+	}
+	restore_flags(flags);
+	return count;
+}
+
+/*
+ * Delete a phone-number from an interface.
+ */
+
+int isdn_net_delphone(isdn_net_ioctl_phone * phone)
+{
+	isdn_net_dev *p = isdn_net_findif(phone->name);
+	int inout = phone->outgoing & 1;
+	isdn_net_phone *n;
+	isdn_net_phone *m;
+
+	if (p) {
+		n = p->local.phone[inout];
+		m = NULL;
+		while (n) {
+			if (!strcmp(n->num, phone->phone)) {
+				if (m)
+					m->next = n->next;
+				else
+					p->local.phone[inout] = n->next;
+				kfree(n);
+				return 0;
+			}
+			m = n;
+			n = (isdn_net_phone *) n->next;
+		}
+		return -EINVAL;
+	}
+	return -ENODEV;
+}
+
+/*
+ * Delete all phone-numbers of an interface.
+ */
+static int isdn_net_rmallphone(isdn_net_dev * p)
+{
+	isdn_net_phone *n;
+	isdn_net_phone *m;
+	int flags;
+	int i;
+
+	save_flags(flags);
+	cli();
+	for (i = 0; i < 2; i++) {
+		n = p->local.phone[i];
+		while (n) {
+			m = n->next;
+			kfree(n);
+			n = m;
+		}
+		p->local.phone[i] = NULL;
+	}
+	restore_flags(flags);
+	return 0;
+}
+
+/*
+ * Force a hangup of a network-interface.
+ */
+int isdn_net_force_hangup(char *name)
+{
+	isdn_net_dev *p = isdn_net_findif(name);
+	int flags;
+	struct device *q;
+
+	if (p) {
+		save_flags(flags);
+		cli();
+		if (p->local.isdn_device < 0) {
+			restore_flags(flags);
+			return 1;
+		}
+		isdn_net_hangup(&p->dev);
+		q = p->local.slave;
+		/* If this interface has slaves, do a hangup for them also. */
+		while (q) {
+			isdn_net_hangup(q);
+			q = (((isdn_net_local *) q->priv)->slave);
+		}
+		restore_flags(flags);
+		return 0;
+	}
+	return -ENODEV;
+}
+
+/*
+ * Helper-function for isdn_net_rm: Do the real work.
+ */
+static int isdn_net_realrm(isdn_net_dev * p, isdn_net_dev * q)
+{
+	int flags;
+
+	save_flags(flags);
+	cli();
+	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.
+		 */
+		isdn_net_hangup(&p->dev);
+		p->dev.start = 0;
+	}
+	if (p->dev.start) {
+		restore_flags(flags);
+		return -EBUSY;
+	}
+	/* 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) {
+		/* 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
+		/* Unregister only if it's a master-device */
+		unregister_netdev(&p->dev);
+	/* Unlink device from chain */
+	if (q)
+		q->next = p->next;
+	else
+		dev->netdev = p->next;
+	if (p->local.slave) {
+		/* If this interface has a slave, remove it also */
+		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)) {
+				isdn_net_realrm(n, q);
+				break;
+			}
+			q = n;
+			n = (isdn_net_dev *) n->next;
+		}
+	}
+	/* If no more net-devices remain, disable auto-hangup timer */
+	if (dev->netdev == NULL)
+		isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, 0);
+	restore_flags(flags);
+
+#ifdef CONFIG_ISDN_PPP
+	isdn_ppp_free_mpqueue(p);
+#endif
+	kfree(p);
+
+	return 0;
+}
+
+/*
+ * Remove a single network-interface.
+ */
+int isdn_net_rm(char *name)
+{
+	isdn_net_dev *p;
+	isdn_net_dev *q;
+
+	/* Search name in netdev-chain */
+	p = dev->netdev;
+	q = NULL;
+	while (p) {
+		if (!strcmp(p->local.name, name))
+			return (isdn_net_realrm(p, q));
+		q = p;
+		p = (isdn_net_dev *) p->next;
+	}
+	/* If no more net-devices remain, disable auto-hangup timer */
+	if (dev->netdev == NULL)
+		isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, 0);
+	return -ENODEV;
+}
+
+/*
+ * Remove all network-interfaces
+ */
+int isdn_net_rmall(void)
+{
+	int flags;
+	int ret;
+
+	/* Walk through netdev-chain */
+	save_flags(flags);
+	cli();
+	while (dev->netdev) {
+		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);
+			        return ret;
+			}
+		}
+	}
+	dev->netdev = NULL;
+	restore_flags(flags);
+	return 0;
+}
+
+
+
+
+
+
+
diff -u --recursive --new-file v1.3.68/linux/drivers/isdn/isdn_net.h linux/drivers/isdn/isdn_net.h
--- v1.3.68/linux/drivers/isdn/isdn_net.h	Thu Jan  1 02:00:00 1970
+++ linux/drivers/isdn/isdn_net.h	Mon Feb 26 11:58:05 1996
@@ -0,0 +1,49 @@
+/* $Id: isdn_net.h,v 1.1 1996/02/11 02:35:13 fritz Exp fritz $
+ *
+ * header for Linux ISDN subsystem, network releted funtions (linklevel).
+ *
+ * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
+ * Copyright 1995,96    by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * 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_net.h,v $
+ * Revision 1.1  1996/02/11 02:35:13  fritz
+ * Initial revision
+ *
+ */
+
+extern char*         isdn_net_new(char *, struct device *);
+extern char*         isdn_net_newslave(char *);
+extern int           isdn_net_rm(char *);
+extern int           isdn_net_rmall(void);
+extern int           isdn_net_stat_callback(int, int);
+extern int           isdn_net_receive_callback(int, u_char *, int);
+extern int           isdn_net_setcfg(isdn_net_ioctl_cfg *);
+extern int           isdn_net_getcfg(isdn_net_ioctl_cfg *);
+extern int           isdn_net_addphone(isdn_net_ioctl_phone *);
+extern int           isdn_net_getphones(isdn_net_ioctl_phone *, char *);
+extern int           isdn_net_delphone(isdn_net_ioctl_phone *);
+extern int           isdn_net_find_icall(int, int, int, char *);
+extern void          isdn_net_hangup(struct device *);
+extern void          isdn_net_dial(void);
+extern void          isdn_net_autohup(void);
+extern int           isdn_net_force_hangup(char *);
+extern int           isdn_net_force_dial(char *);
+extern isdn_net_dev* isdn_net_findif(char *);
+extern int           isdn_net_send_skb(struct device *, isdn_net_local *, 
+				      struct sk_buff *);
+extern int           isdn_net_rcv_skb(int, struct sk_buff *); 
diff -u --recursive --new-file v1.3.68/linux/drivers/isdn/isdn_ppp.c linux/drivers/isdn/isdn_ppp.c
--- v1.3.68/linux/drivers/isdn/isdn_ppp.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/isdn/isdn_ppp.c	Mon Feb 26 11:58:05 1996
@@ -0,0 +1,1231 @@
+/* $Id: isdn_ppp.c,v 1.4 1996/02/19 15:25:50 fritz Exp fritz $
+ *
+ * Linux ISDN subsystem, functions for synchronous PPP (linklevel).
+ *
+ * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * 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_ppp.c,v $
+ * Revision 1.4  1996/02/19 15:25:50  fritz
+ * Bugfix: Sync-PPP packets got compressed twice, when resent due to
+ * send-queue-full reject.
+ *
+ * Revision 1.3  1996/02/11 02:27:12  fritz
+ * Lot of Bugfixes my Michael.
+ * Moved calls to skb_push() into isdn_net_header()
+ * Fixed a possible race-condition in isdn_ppp_timer_timeout().
+ *
+ * Revision 1.2  1996/01/22 05:08:06  fritz
+ * Merged in Michael's patches for MP.
+ * Minor changes in isdn_ppp_xmit.
+ *
+ * Revision 1.1  1996/01/09 04:11:29  fritz
+ * Initial revision
+ *
+ */
+
+/* TODO: right tbusy handling when using MP */
+
+#ifndef STANDALONE
+#include <linux/config.h>
+#endif
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/isdn.h>
+#include "isdn_common.h"
+#include "isdn_ppp.h"
+#include "isdn_net.h"
+
+#ifndef PPP_IPX
+#define PPP_IPX 0x002b 
+#endif
+ 
+/* Prototypes */
+static int isdn_ppp_fill_rq(char *buf, int len, int minor);
+static int isdn_ppp_hangup(int);
+static void isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp,
+			struct sk_buff *skb, int proto);
+static int isdn_ppp_if_get_unit(char **namebuf);
+
+#ifdef CONFIG_ISDN_MPP
+static int isdn_ppp_bundle(int, int);
+static void isdn_ppp_mask_queue(isdn_net_dev * dev, long mask);
+static void isdn_ppp_cleanup_queue(isdn_net_dev * dev, long min);
+static int isdn_ppp_fill_mpqueue(isdn_net_dev *, struct sk_buff **skb,
+		int BEbyte, int *sqno, int min_sqno);
+#endif
+
+char *isdn_ppp_revision              = "$Revision: 1.4 $";
+struct ippp_struct *ippp_table = (struct ippp_struct *) 0;
+
+extern int isdn_net_force_dial_lp(isdn_net_local *);
+
+int isdn_ppp_free(isdn_net_local * lp)
+{
+	if (lp->ppp_minor < 0)
+		return 0;
+
+#ifdef CONFIG_ISDN_MPP
+	if(lp->master)
+	{
+		isdn_net_dev *p = dev->netdev;
+		lp->last->next = lp->next;
+		lp->next->last = lp->last;
+		if(lp->netdev->queue == lp)
+			lp->netdev->queue = lp->next;
+                lp->next = lp->last = lp;
+		while(p) {
+			if(lp == &p->local) {
+				lp->netdev = p;
+				break;
+			}
+			p=p->next;
+		}
+	} else {
+                lp->netdev->ib.bundled = 0;
+		/* last link: free mpqueue, free sqqueue ? */
+	}
+
+#endif
+
+	isdn_ppp_hangup(lp->ppp_minor);
+#if 0
+	printk(KERN_DEBUG "isdn_ppp_free %d %lx %lx\n", lp->ppp_minor, (long) lp,(long) ippp_table[lp->ppp_minor].lp);
+#endif
+	ippp_table[lp->ppp_minor].lp = NULL;
+	return 0;
+}
+
+int isdn_ppp_bind(isdn_net_local * lp)
+{
+	int i;
+	int unit = 0;
+	char *name;
+	long flags;
+
+	if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP)
+		return 0;
+
+	save_flags(flags);
+	cli();
+
+        /* 
+         * search a free device 
+         */
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		if (ippp_table[i].state == IPPP_OPEN) {		/* OPEN, but not connected! */
+#if 0
+			printk(KERN_DEBUG "find_minor, %d lp: %08lx\n", i, (long) lp);
+#endif
+			break;
+		}
+	}
+
+	if (i >= ISDN_MAX_CHANNELS) {
+		restore_flags(flags);
+		printk(KERN_WARNING "isdn_ppp_bind: Can't find usable ippp device.\n");
+		return -1;
+	}
+	lp->ppp_minor = i;
+	ippp_table[lp->ppp_minor].lp = lp;
+
+	name = lp->name;
+	unit = isdn_ppp_if_get_unit(&name); /* get unit number from interface name .. ugly! */
+	ippp_table[lp->ppp_minor].unit = unit;
+
+	ippp_table[lp->ppp_minor].state = IPPP_OPEN | IPPP_CONNECT | IPPP_NOBLOCK;
+
+	restore_flags(flags);
+
+        /*
+         * kick the ipppd on the new device 
+         */
+	if (ippp_table[lp->ppp_minor].wq)
+		wake_up_interruptible(&ippp_table[lp->ppp_minor].wq);
+
+	return lp->ppp_minor;
+}
+
+static int isdn_ppp_hangup(int minor)
+{
+	if (minor < 0 || minor >= ISDN_MAX_CHANNELS)
+		return 0;
+
+	if (ippp_table[minor].state && ippp_table[minor].wq)
+		wake_up_interruptible(&ippp_table[minor].wq);
+
+	ippp_table[minor].state = IPPP_CLOSEWAIT;
+	return 1;
+}
+
+/*
+ * isdn_ppp_open 
+ */
+
+int isdn_ppp_open(int minor, struct file *file)
+{
+#if 0
+	printk(KERN_DEBUG "ippp, open, minor: %d state: %04x\n", minor,ippp_table[minor].state);
+#endif
+	if (ippp_table[minor].state)
+		return -EBUSY;
+
+	ippp_table[minor].lp = 0;
+	ippp_table[minor].mp_seqno = 0;  /* MP sequence number */
+	ippp_table[minor].pppcfg = 0;    /* ppp configuration */
+	ippp_table[minor].mpppcfg = 0;   /* mppp configuration */
+	ippp_table[minor].range = 0x1000000;	/* MP: 24 bit range */
+	ippp_table[minor].last_link_seqno = -1;	/* MP: maybe set to Bundle-MIN, when joining a bundle ?? */
+	ippp_table[minor].unit = -1;	/* set, when we have our interface */
+	ippp_table[minor].mru = 1524;	/* MRU, default 1524 */
+	ippp_table[minor].maxcid = 16;	/* VJ: maxcid */
+	ippp_table[minor].tk = current;
+	ippp_table[minor].wq = NULL;    /* read() wait queue */
+	ippp_table[minor].wq1 = NULL;   /* select() wait queue */
+	ippp_table[minor].first = ippp_table[minor].rq + NUM_RCV_BUFFS - 1; /* receive queue */
+	ippp_table[minor].last = ippp_table[minor].rq;
+#ifdef CONFIG_ISDN_PPP_VJ
+        /*
+         * VJ header compression init
+         */
+	ippp_table[minor].cbuf = kmalloc(ippp_table[minor].mru + PPP_HARD_HDR_LEN + 2, GFP_KERNEL);
+
+	if (ippp_table[minor].cbuf == NULL) {
+		printk(KERN_DEBUG "ippp: Can't allocate memory buffer for VJ compression.\n");
+		return -ENOMEM;
+	}
+	ippp_table[minor].slcomp = slhc_init(16, 16);	/* not necessary for 2. link in bundle */
+#endif
+
+	ippp_table[minor].state = IPPP_OPEN;
+
+	return 0;
+}
+
+void isdn_ppp_release(int minor, struct file *file)
+{
+	int i;
+
+	if (minor < 0 || minor >= ISDN_MAX_CHANNELS)
+		return;
+
+#if 0
+	printk(KERN_DEBUG "ippp: release, minor: %d %lx\n", minor, (long) ippp_table[minor].lp);
+#endif
+
+	if (ippp_table[minor].lp) {	/* a lp address says: this link is still up */
+		isdn_net_dev *p = dev->netdev;
+		while(p) {	/* find interface for our lp; */
+			if(&p->local == ippp_table[minor].lp)
+				break;
+			p = p->next;
+		}
+		if(!p) {
+			printk(KERN_ERR "isdn_ppp_release: Can't find device for net_local\n");
+			p = ippp_table[minor].lp->netdev;
+		}
+		ippp_table[minor].lp->ppp_minor = -1;
+		isdn_net_hangup(&p->dev); /* lp->ppp_minor==-1 => no calling of isdn_ppp_hangup() */
+		ippp_table[minor].lp = NULL;
+	}
+	for (i = 0; i < NUM_RCV_BUFFS; i++) {
+		if (ippp_table[minor].rq[i].buf)
+			kfree(ippp_table[minor].rq[i].buf);
+	}
+
+#ifdef CONFIG_ISDN_PPP_VJ
+	slhc_free(ippp_table[minor].slcomp);
+	kfree(ippp_table[minor].cbuf);
+#endif
+
+	ippp_table[minor].state = 0;
+}
+
+static int get_arg(void *b, unsigned long *val)
+{
+	int r;
+	if ((r = verify_area(VERIFY_READ, (void *) b, sizeof(unsigned long))))
+		 return r;
+	memcpy_fromfs((void *) val, b, sizeof(unsigned long));
+	return 0;
+}
+
+static int set_arg(void *b, unsigned long val)
+{
+	int r;
+	if ((r = verify_area(VERIFY_WRITE, b, sizeof(unsigned long))))
+		 return r;
+	memcpy_tofs(b, (void *) &val, sizeof(unsigned long));
+	return 0;
+}
+
+int isdn_ppp_ioctl(int minor, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	unsigned long val;
+	int r;
+
+#if 0
+	printk(KERN_DEBUG "isdn_ppp_ioctl: minor: %d cmd: %x",minor,cmd);
+	printk(KERN_DEBUG " state: %x\n",ippp_table[minor].state);
+#endif
+
+	if (!(ippp_table[minor].state & IPPP_OPEN))
+		return -EINVAL;
+
+	switch (cmd) {
+#if 0
+	case PPPIOCSINPSIG:	/* obsolete: set input ready signal */
+		/* usual: sig = SIGIO *//* we always deliver a SIGIO */
+		break;
+#endif
+	case PPPIOCBUNDLE:
+#ifdef CONFIG_ISDN_MPP
+		if ((r = get_arg((void *) arg, &val)))
+			return r;
+		printk(KERN_DEBUG "iPPP-bundle: minor: %d, slave unit: %d, master unit: %d\n",
+                        (int) minor, (int) ippp_table[minor].unit, (int) val);
+		return isdn_ppp_bundle(minor, val);
+#else
+		return -1;
+#endif
+		break;
+	case PPPIOCGUNIT:	/* get ppp/isdn unit number */
+		if ((r = set_arg((void *) arg, ippp_table[minor].unit)))
+			return r;
+		break;
+	case PPPIOCGMPFLAGS:	/* get configuration flags */
+		if ((r = set_arg((void *) arg, ippp_table[minor].mpppcfg)))
+			return r;
+		break;
+	case PPPIOCSMPFLAGS:	/* set configuration flags */
+		if ((r = get_arg((void *) arg, &val)))
+			return r;
+		ippp_table[minor].mpppcfg = val;
+		break;
+	case PPPIOCGFLAGS:	/* get configuration flags */
+		if ((r = set_arg((void *) arg, ippp_table[minor].pppcfg)))
+			return r;
+		break;
+	case PPPIOCSFLAGS:	/* set configuration flags */
+		if ((r = get_arg((void *) arg, &val))) {
+			return r;
+		}
+		if (val & SC_ENABLE_IP && !(ippp_table[minor].pppcfg & SC_ENABLE_IP)) {
+			ippp_table[minor].lp->netdev->dev.tbusy = 0;
+			mark_bh(NET_BH); /* OK .. we are ready to send the first buffer */
+		}
+		ippp_table[minor].pppcfg = val;
+		break;
+#if 0
+	case PPPIOCGSTAT:	/* read PPP statistic information */
+		break;
+	case PPPIOCGTIME:	/* read time delta information */
+		break;
+#endif
+	case PPPIOCSMRU:	/* set receive unit size for PPP */
+		if ((r = get_arg((void *) arg, &val)))
+			return r;
+		ippp_table[minor].mru = val;
+		break;
+	case PPPIOCSMPMRU:
+		break;
+	case PPPIOCSMPMTU:
+		break;
+	case PPPIOCSMAXCID:	/* set the maximum compression slot id */
+		if ((r = get_arg((void *) arg, &val)))
+			return r;
+		ippp_table[minor].maxcid = val;
+		break;
+	case PPPIOCGDEBUG:
+		break;
+	case PPPIOCSDEBUG:
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+int isdn_ppp_select(int minor, struct file *file, int type, select_table * st)
+{
+	struct ippp_buf_queue *bf, *bl;
+	unsigned long flags;
+
+#if 0
+	printk(KERN_DEBUG "isdn_ppp_select: minor: %d, type: %d \n",minor,type);
+#endif
+
+	if (!(ippp_table[minor].state & IPPP_OPEN))
+		return -EINVAL;
+
+	switch (type) {
+	case SEL_IN:
+		save_flags(flags);
+		cli();
+		bl = ippp_table[minor].last;
+		bf = ippp_table[minor].first;
+		if (bf->next == bl && !(ippp_table[minor].state & IPPP_NOBLOCK)) {
+			select_wait(&ippp_table[minor].wq, st);
+			restore_flags(flags);
+			return 0;
+		}
+		ippp_table[minor].state &= ~IPPP_NOBLOCK;
+		restore_flags(flags);
+		return 1;
+	case SEL_OUT:
+                /* we're always ready to send .. */
+		return 1;
+	case SEL_EX:
+		select_wait(&ippp_table[minor].wq1, st);
+		return 0;
+	}
+	return 1;
+}
+
+/*
+ *  fill up isdn_ppp_read() queue ..
+ */
+
+static int isdn_ppp_fill_rq(char *buf, int len, int minor)
+{
+	struct ippp_buf_queue *bf, *bl;
+	unsigned long flags;
+
+	if (minor < 0 || minor >= ISDN_MAX_CHANNELS) {
+		printk(KERN_WARNING "ippp: illegal minor.\n");
+		return 0;
+	}
+	if (!(ippp_table[minor].state & IPPP_CONNECT)) {
+		printk(KERN_DEBUG "ippp: device not activated.\n");
+		return 0;
+	}
+	save_flags(flags);
+	cli();
+
+	bf = ippp_table[minor].first;
+	bl = ippp_table[minor].last;
+
+	if (bf == bl) {
+		printk(KERN_WARNING "ippp: Queue is full; discarding first buffer\n");
+		bf = bf->next;
+		kfree(bf->buf);
+		ippp_table[minor].first = bf;
+	}
+	bl->buf = (char *) kmalloc(len, GFP_ATOMIC);
+	if (!bl->buf) {
+		printk(KERN_WARNING "ippp: Can't alloc buf\n");
+		restore_flags(flags);
+		return 0;
+	}
+	bl->len = len;
+
+	memcpy(bl->buf, buf, len);
+
+	ippp_table[minor].last = bl->next;
+	restore_flags(flags);
+
+	if (ippp_table[minor].wq)
+		wake_up_interruptible(&ippp_table[minor].wq);
+
+	return len;
+}
+
+/*
+ * read() .. non-blocking: ipppd calls it only after select()
+ *           reports, that there is data
+ */
+
+int isdn_ppp_read(int minor, struct file *file, char *buf, int count)
+{
+	struct ippp_struct *c = &ippp_table[minor];
+	struct ippp_buf_queue *b;
+	int r;
+	unsigned long flags;
+
+	if (!(ippp_table[minor].state & IPPP_OPEN))
+		return 0;
+
+	if ((r = verify_area(VERIFY_WRITE, (void *) buf, count)))
+		return r;
+
+	save_flags(flags);
+	cli();
+
+	b = c->first->next;
+	if (!b->buf) {
+		restore_flags(flags);
+		return -EAGAIN;
+	}
+	if (b->len < count)
+		count = b->len;
+	memcpy_tofs(buf, b->buf, count);
+	kfree(b->buf);
+	b->buf = NULL;
+	c->first = b;
+	restore_flags(flags);
+
+	return count;
+}
+
+/*
+ * ipppd wanna write a packet to the card .. non-blocking
+ */
+ 
+int isdn_ppp_write(int minor, struct file *file,  const char *buf, int count)
+{
+	isdn_net_local *lp;
+
+	if (!(ippp_table[minor].state & IPPP_CONNECT))
+		return 0;
+
+	lp = ippp_table[minor].lp;
+
+	/* -> push it directly to the lowlevel interface */
+
+	if (!lp)
+		printk(KERN_DEBUG "isdn_ppp_write: lp == NULL\n");
+	else {
+		if (lp->isdn_device < 0 || lp->isdn_channel < 0)
+			return 0;
+
+		if (dev->drv[lp->isdn_device]->running && lp->dialstate == 0 &&
+		    (lp->flags & ISDN_NET_CONNECTED))
+			dev->drv[lp->isdn_device]->interface->writebuf(
+                                lp->isdn_device,lp->isdn_channel, buf, count, 1);
+	}
+
+	return count;
+}
+
+/*
+ * init memory, structures etc. 
+ */
+
+int isdn_ppp_init(void)
+{
+	int i, j;
+
+	if (!(ippp_table = (struct ippp_struct *)
+	      kmalloc(sizeof(struct ippp_struct) * ISDN_MAX_CHANNELS, GFP_KERNEL))) {
+		printk(KERN_WARNING "isdn_ppp_init: Could not alloc ippp_table\n");
+		return -1;
+	}
+	memset((char *) ippp_table, 0, sizeof(struct ippp_struct) * ISDN_MAX_CHANNELS);
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		ippp_table[i].state = 0;
+		ippp_table[i].first = ippp_table[i].rq + NUM_RCV_BUFFS - 1;
+		ippp_table[i].last = ippp_table[i].rq;
+
+		for (j = 0; j < NUM_RCV_BUFFS; j++) {
+			ippp_table[i].rq[j].buf = NULL;
+			ippp_table[i].rq[j].last = ippp_table[i].rq +
+			    (NUM_RCV_BUFFS + j - 1) % NUM_RCV_BUFFS;
+			ippp_table[i].rq[j].next = ippp_table[i].rq + (j + 1) % NUM_RCV_BUFFS;
+		}
+	}
+	return 0;
+}
+
+void isdn_ppp_cleanup(void)
+{
+	kfree(ippp_table);
+}
+
+/*
+ * 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)
+{
+#if 0
+	printk(KERN_DEBUG "recv, skb %d\n",skb->len);
+#endif
+
+	if(skb->data[0] == 0xff && skb->data[1] == 0x03)
+		skb_pull(skb,2);
+	else if (ippp_table[lp->ppp_minor].pppcfg & SC_REJ_COMP_AC)
+		return;		/* discard it silently */
+
+#ifdef CONFIG_ISDN_MPP
+	if (!(ippp_table[lp->ppp_minor].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_MP) {
+			isdn_net_local *lpq;
+			int sqno, min_sqno, tseq;
+			u_char BEbyte = skb->data[0];
+#if 0
+			printk(KERN_DEBUG "recv: %d/%04x/%d -> %02x %02x %02x %02x %02x %02x\n", lp->ppp_minor, proto ,
+				(int) skb->len, (int) skb->data[0], (int) skb->data[1], (int) skb->data[2], 
+				(int) skb->data[3], (int) skb->data[4], (int) skb->data[5]);
+#endif
+			if (!(ippp_table[lp->ppp_minor].mpppcfg & SC_IN_SHORT_SEQ)) {
+				sqno = ((int) skb->data[1] << 16) + ((int) skb->data[2] << 8) + (int) skb->data[3];
+				skb_pull(skb,4);
+			} else {
+				sqno = (((int) skb->data[0] & 0xf) << 8) + (int) skb->data[1];
+				skb_pull(skb,2);
+			}
+
+			if ((tseq = ippp_table[lp->ppp_minor].last_link_seqno) >= sqno) {
+				int range = ippp_table[lp->ppp_minor].range;
+				if (tseq + 1024 < range + sqno) /* redundancy check .. not MP conform */
+					printk(KERN_WARNING "isdn_ppp_receive, MP, detected overflow with sqno: %d, last: %d !!!\n", sqno, tseq);
+				else {
+					sqno += range;
+					ippp_table[lp->ppp_minor].last_link_seqno = sqno;
+				}
+			} else
+				ippp_table[lp->ppp_minor].last_link_seqno = sqno;
+
+			for (min_sqno = 0, lpq = net_dev->queue;;) {
+				if (ippp_table[lpq->ppp_minor].last_link_seqno > min_sqno)
+					min_sqno = ippp_table[lpq->ppp_minor].last_link_seqno;
+				lpq = lpq->next;
+				if (lpq == net_dev->queue)
+					break;
+			}
+			if (min_sqno >= ippp_table[lpq->ppp_minor].range) {	/* OK, every link overflowed */
+				int mask = ippp_table[lpq->ppp_minor].range - 1;	/* range is a power of 2 */
+				isdn_ppp_cleanup_queue(net_dev, min_sqno);
+				isdn_ppp_mask_queue(net_dev, mask);
+				net_dev->ib.next_num &= mask;
+				{
+					struct sqqueue *q = net_dev->ib.sq;
+					while (q) {
+						q->sqno_start &= mask;
+						q->sqno_end &= mask;
+					}
+				}
+				min_sqno &= mask;
+				for (lpq = net_dev->queue;;) {
+					ippp_table[lpq->ppp_minor].last_link_seqno &= mask;
+					lpq = lpq->next;
+					if (lpq == net_dev->queue)
+						break;
+				}
+			}
+			if ((BEbyte & (MP_BEGIN_FRAG | MP_END_FRAG)) != (MP_BEGIN_FRAG | MP_END_FRAG)) {
+				printk(KERN_DEBUG "ippp: trying ;) to fill mp_queue %d .. UNTESTED!!\n", lp->ppp_minor);
+				if ((sqno_end = isdn_ppp_fill_mpqueue(net_dev, &skb , BEbyte, &sqno, min_sqno)) < 0)
+					return;		/* no packet complete */
+			} else
+				sqno_end = sqno;
+
+			/*
+			 * MP buffer management .. reorders incoming packets ..
+			 * lotsa mem-copies and not heavily tested.
+			 *
+			 * first check whether there is more than one link in the bundle
+			 * then check whether the number is in order
+			 */
+			net_dev->ib.modify = 1;		/* block timeout-timer */
+			if (net_dev->ib.bundled && net_dev->ib.next_num != sqno) {
+				/*
+				 * packet is not 'in order'
+				 */
+				struct sqqueue *q;
+
+				q = (struct sqqueue *) kmalloc(sizeof(struct sqqueue), GFP_ATOMIC);
+				if (!q) {
+					printk(KERN_WARNING "ippp: err, no memory !!\n");
+					net_dev->ib.modify = 0;
+					return;		/* discard */
+				}
+				q->skb = skb;
+				q->sqno_end = sqno_end;
+				q->sqno_start = sqno;
+				q->timer = jiffies + (ISDN_TIMER_1SEC) * 5;	/* timeout after 5 seconds */
+
+				if (!net_dev->ib.sq) {
+					net_dev->ib.sq = q;
+					q->next = NULL;
+				} else {
+					struct sqqueue *ql = net_dev->ib.sq;
+					if (ql->sqno_start > q->sqno_start) {
+						q->next = ql;
+						net_dev->ib.sq = q;
+					} else {
+						while (ql->next && ql->next->sqno_start < q->sqno_start)
+							ql = ql->next;
+						q->next = ql->next;
+						ql->next = q;
+					}
+				}
+				net_dev->ib.modify = 0;
+				return;
+			} else {
+				/* 
+			 	 * packet was 'in order' .. push it higher
+				 */
+				struct sqqueue *q;
+
+				net_dev->ib.next_num = sqno_end + 1;
+				isdn_ppp_push_higher(net_dev, lp, skb, -1);
+
+                                /*
+                                 * check queue, whether we have still buffered the next packet(s)
+                                 */
+				while ((q = net_dev->ib.sq) && q->sqno_start == net_dev->ib.next_num) {
+					isdn_ppp_push_higher(net_dev, lp, q->skb, -1);
+					net_dev->ib.sq = q->next;
+					net_dev->ib.next_num = q->sqno_end + 1;
+					kfree(q);
+				}
+			}
+			net_dev->ib.modify = 0;
+
+		} else
+			isdn_ppp_push_higher(net_dev, lp, skb , proto);
+	} else
+#endif
+		isdn_ppp_push_higher(net_dev, lp, skb , -1);
+}
+
+
+static void isdn_ppp_push_higher(isdn_net_dev *net_dev, isdn_net_local *lp, struct sk_buff *skb,int proto)
+{
+	struct device *dev = &net_dev->dev;
+
+	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 0
+	printk(KERN_DEBUG "push, skb %d %04x\n",skb->len,proto);
+#endif
+
+	switch (proto) {
+	case PPP_IPX: /* untested */
+		skb->dev = dev;
+		skb->mac.raw = skb->data;
+		skb->protocol = htons(ETH_P_IPX);
+		break;
+#ifdef CONFIG_ISDN_PPP_VJ
+	case PPP_VJC_UNCOMP:
+		slhc_remember(ippp_table[net_dev->local.ppp_minor].slcomp, skb->data, skb->len);
+#endif
+	case PPP_IP:
+		skb->dev = dev;
+		skb->mac.raw = skb->data;
+		skb->protocol = htons(ETH_P_IP);
+		break;
+	case PPP_VJC_COMP:
+#ifdef CONFIG_ISDN_PPP_VJ
+		{
+			struct sk_buff *skb_old = skb;
+			int pkt_len;
+			skb = dev_alloc_skb(skb_old->len + 40);
+
+			if (!skb) {
+				printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", dev->name);
+				net_dev->local.stats.rx_dropped++;
+				return;
+			}
+			skb->dev = dev;
+			skb_put(skb,skb_old->len + 40);
+			memcpy(skb->data, skb_old->data, skb_old->len);
+			skb->mac.raw = skb->data;
+			pkt_len = slhc_uncompress(ippp_table[net_dev->local.ppp_minor].slcomp,
+						  skb->data, skb_old->len);
+			skb_trim(skb, pkt_len);
+			dev_kfree_skb(skb_old,FREE_WRITE);
+			skb->protocol = htons(ETH_P_IP);
+		}
+#else
+		printk(KERN_INFO "isdn: Ooopsa .. VJ-Compression support not compiled into isdn driver.\n");
+		lp->stats.rx_dropped++;
+		return;
+#endif
+		break;
+	default:
+		skb_push(skb,4);
+		skb->data[0] = 0xff;
+		skb->data[1] = 0x03;
+		skb->data[2] = (proto>>8);
+		skb->data[3] = proto & 0xff;
+		isdn_ppp_fill_rq(skb->data, skb->len, lp->ppp_minor);	/* push data to pppd device */
+		dev_kfree_skb(skb,FREE_WRITE);
+		return;
+	}
+
+	netif_rx(skb);
+	net_dev->local.stats.rx_packets++;
+	/* Reset hangup-timer */
+	lp->huptimer = 0;
+
+	return;
+}
+
+/*
+ * send ppp frame .. we expect a PIDCOMPable proto -- 
+ *  (here: currently always PPP_IP,PPP_VJC_COMP,PPP_VJC_UNCOMP)
+ */
+int isdn_ppp_xmit(struct sk_buff *skb, struct device *dev)
+{
+	isdn_net_dev *nd = ((isdn_net_local *) dev->priv)->netdev;
+	isdn_net_local *lp = nd->queue;
+	int proto = PPP_IP;	/* 0x21 */
+	struct ippp_struct *ipt = ippp_table + lp->ppp_minor;
+	struct ippp_struct *ipts = ippp_table + lp->netdev->local.ppp_minor;
+
+        /* If packet is to be resent, it has already been processed and
+         * therefore it's first bytes are already initialized. In this case
+         * send it immediately ...
+         */
+        if (*((unsigned long *)skb->data) != 0)
+          return (isdn_net_send_skb(dev , lp , skb));
+
+        /* ... else packet needs processing. */
+
+/* future: step to next 'lp' when this lp is 'tbusy' */
+
+#if 0
+	printk(KERN_DEBUG  "xmit, skb %d\n",skb->len);
+#endif
+
+#ifdef CONFIG_ISDN_PPP_VJ
+	if (ipt->pppcfg & SC_COMP_TCP) {
+		u_char *buf = skb->data;
+		int pktlen;
+		int len = 4;
+#ifdef CONFIG_ISDN_MPP
+		if (ipt->mpppcfg & SC_MP_PROT) /* sigh */ 
+			if (ipt->mpppcfg & SC_OUT_SHORT_SEQ)
+				len += 3;
+			else
+				len += 5;
+#endif
+		buf += len;
+		pktlen = slhc_compress(ipts->slcomp, buf, skb->len-len, ipts->cbuf,
+				&buf, !(ipts->pppcfg & SC_NO_TCP_CCID));
+		skb_trim(skb,pktlen+len);
+		if(buf != skb->data+len) { /* copied to new buffer ??? (btw: WHY must slhc copy it?? *sigh*)  */
+			memcpy(skb->data+len,buf,pktlen);
+		}
+		if (skb->data[len] & SL_TYPE_COMPRESSED_TCP) {	/* cslip? style -> PPP */
+			proto = PPP_VJC_COMP;
+			skb->data[len] ^= SL_TYPE_COMPRESSED_TCP;
+		} else {
+			if (skb->data[len] >= SL_TYPE_UNCOMPRESSED_TCP)
+				proto = PPP_VJC_UNCOMP;
+			skb->data[len] = (skb->data[len] & 0x0f) | 0x40;
+		}
+	}
+#endif
+
+#if 0
+	printk(KERN_DEBUG  "xmit, skb %d %04x\n",skb->len,proto);
+#endif
+
+#ifdef CONFIG_ISDN_MPP
+	if (ipt->mpppcfg & SC_MP_PROT) {
+		/* we get mp_seqno from static isdn_net_local */
+		long mp_seqno = ipts->mp_seqno;
+		ipts->mp_seqno++;
+		nd->queue = nd->queue->next;
+		if (ipt->mpppcfg & SC_OUT_SHORT_SEQ) {
+			/* skb_push(skb, 3); Done in isdn_net_header() */
+			mp_seqno &= 0xfff;
+			skb->data[4] = MP_BEGIN_FRAG | MP_END_FRAG | (mp_seqno >> 8);	/* (B)egin & (E)ndbit .. */
+			skb->data[5] = mp_seqno & 0xff;
+			skb->data[6] = proto;	/* PID compression */
+		} else {
+			/* skb_push(skb, 5); Done in isdn_net_header () */
+			skb->data[4] = MP_BEGIN_FRAG | MP_END_FRAG;	/* (B)egin & (E)ndbit .. */
+			skb->data[5] = (mp_seqno >> 16) & 0xff;	/* sequence nubmer: 24bit */
+			skb->data[6] = (mp_seqno >> 8) & 0xff;
+			skb->data[7] = (mp_seqno >> 0) & 0xff;
+			skb->data[8] = proto;	/* PID compression */
+		}
+		proto = PPP_MP; /* MP Protocol, 0x003d */
+	}
+#endif
+	skb->data[0] = 0xff;        /* All Stations */
+	skb->data[1] = 0x03;        /* Unumbered information */
+	skb->data[2] = proto >> 8;
+	skb->data[3] = proto & 0xff;
+
+	lp->huptimer = 0;
+	if (!(ipt->pppcfg & SC_ENABLE_IP)) {	/* PPP connected ? */
+		printk(KERN_INFO "isdn, xmit: Packet blocked: %d %d\n", lp->isdn_device, lp->isdn_channel);
+		return 1;
+	}
+        /* tx-stats are now updated via BSENT-callback */
+	return (isdn_net_send_skb(dev , lp , skb));
+}
+
+void isdn_ppp_free_mpqueue(isdn_net_dev * p)
+{
+	struct mpqueue *ql, *q = p->mp_last;
+	while (q) {
+		ql = q->next;
+ 		dev_kfree_skb(q->skb,FREE_WRITE);
+		kfree(q);
+		q = ql;
+	}
+}
+
+#ifdef CONFIG_ISDN_MPP
+
+static int isdn_ppp_bundle(int minor, int unit)
+{
+	char ifn[IFNAMSIZ + 1];
+	long flags;
+	isdn_net_dev *p;
+	isdn_net_local *lp,*nlp;
+
+	sprintf(ifn, "ippp%d", unit);
+	p = isdn_net_findif(ifn);
+	if (!p)
+		return -1;
+
+	isdn_timer_ctrl(ISDN_TIMER_IPPP, 1);	/* enable timer for ippp/MP */
+
+	save_flags(flags);
+	cli();
+
+	nlp = ippp_table[minor].lp;
+
+	lp = p->queue;
+	p->ib.bundled = 1;
+	nlp->last = lp->last;
+	lp->last->next = nlp;
+	lp->last = nlp;
+	nlp->next = lp;
+	p->queue = nlp;
+
+	nlp->netdev = lp->netdev;
+
+	ippp_table[nlp->ppp_minor].unit = ippp_table[lp->ppp_minor].unit;
+/* maybe also SC_CCP stuff */
+	ippp_table[nlp->ppp_minor].pppcfg |= ippp_table[lp->ppp_minor].pppcfg &
+	    (SC_ENABLE_IP | SC_NO_TCP_CCID | SC_REJ_COMP_TCP);
+
+	ippp_table[nlp->ppp_minor].mpppcfg |= ippp_table[lp->ppp_minor].mpppcfg &
+	    (SC_MP_PROT | SC_REJ_MP_PROT | SC_OUT_SHORT_SEQ | SC_IN_SHORT_SEQ);
+#if 0
+	if (ippp_table[nlp->ppp_minor].mpppcfg != ippp_table[lp->ppp_minor].mpppcfg) {
+		printk(KERN_WARNING "isdn_ppp_bundle: different MP options %04x and %04x\n",
+		       ippp_table[nlp->ppp_minor].mpppcfg, ippp_table[lp->ppp_minor].mpppcfg);
+	}
+#endif
+
+	restore_flags(flags);
+	return 0;
+}
+
+
+static void isdn_ppp_mask_queue(isdn_net_dev * dev, long mask)
+{
+	struct mpqueue *q = dev->mp_last;
+	while (q) {
+		q->sqno &= mask;
+		q = q->next;
+	}
+}
+
+
+static int isdn_ppp_fill_mpqueue(isdn_net_dev * dev, struct sk_buff ** skb, int BEbyte, int *sqnop, int min_sqno)
+{
+	struct mpqueue *qe, *q1, *q;
+	long cnt, flags;
+	int pktlen, sqno_end;
+	int sqno = *sqnop;
+
+	q1 = (struct mpqueue *) kmalloc(sizeof(struct mpqueue), GFP_KERNEL);
+	if (!q1) {
+		printk(KERN_WARNING "isdn_ppp_fill_mpqueue: Can't alloc struct memory.\n");
+		save_flags(flags);
+		cli();
+		isdn_ppp_cleanup_queue(dev, min_sqno);
+		restore_flags(flags);
+		return -1;
+	}
+	q1->skb = *skb;
+	q1->sqno = sqno;
+	q1->BEbyte = BEbyte;
+	q1->time = jiffies;
+
+	save_flags(flags);
+	cli();
+
+	if (!(q = dev->mp_last)) {
+		dev->mp_last = q1;
+		q1->next = NULL;
+		q1->last = NULL;
+		isdn_ppp_cleanup_queue(dev, min_sqno);	/* not necessary */
+		restore_flags(flags);
+		return -1;
+	}
+	for (;;) {		/* the faster way would be to step from the queue-end to the start */
+		if (sqno > q->sqno) {
+			if (q->next) {
+				q = q->next;
+				continue;
+			}
+			q->next = q1;
+			q1->next = NULL;
+			q1->last = q;
+			break;
+		}
+		if (sqno == q->sqno)
+			printk(KERN_WARNING "isdn_fill_mpqueue: illegal sqno received!!\n");
+		q1->last = q->last;
+		q1->next = q;
+		if (q->last) {
+			q->last->next = q1;
+		} else
+			dev->mp_last = q1;
+		q->last = q1;
+		break;
+	}
+
+/* now we check whether we completed a packet with this fragment */
+	pktlen = -q1->skb->len;
+	q = q1;
+	cnt = q1->sqno;
+	while (!(q->BEbyte & MP_END_FRAG)) {
+		cnt++;
+		if (!(q->next) || q->next->sqno != cnt) {
+			isdn_ppp_cleanup_queue(dev, min_sqno);
+			restore_flags(flags);
+			return -1;
+		}
+		pktlen += q->skb->len;
+		q = q->next;
+	}
+	pktlen += q->skb->len;
+	qe = q;
+
+	q = q1;
+	cnt = q1->sqno;
+	while (!(q->BEbyte & MP_BEGIN_FRAG)) {
+		cnt--;
+		if (!(q->last) || q->last->sqno != cnt) {
+			isdn_ppp_cleanup_queue(dev, min_sqno);
+			restore_flags(flags);
+			return -1;
+		}
+		pktlen += q->skb->len;
+		q = q->last;
+	}
+	pktlen += q->skb->len;
+
+	if (q->last)
+		q->last->next = qe->next;
+	else
+		dev->mp_last = qe->next;
+
+	if (qe->next)
+		qe->next->last = q->last;
+	qe->next = NULL;
+	sqno_end = qe->sqno;
+	*sqnop = q->sqno;
+
+	isdn_ppp_cleanup_queue(dev, min_sqno);
+	restore_flags(flags);
+
+	*skb = dev_alloc_skb(pktlen + 40); /* not needed: +40 for VJ compression .. */
+
+	if (!(*skb)) {
+		while (q) {
+			struct mpqueue *ql = q->next;
+			dev_kfree_skb(q->skb,FREE_WRITE);
+			kfree(q);
+			q = ql;
+		}
+		return -2;
+	}
+	cnt = 0;
+	skb_put(*skb,pktlen);
+	while (q) {
+		struct mpqueue *ql = q->next;
+		memcpy((*skb)->data + cnt, q->skb->data, q->skb->len);
+		cnt += q->skb->len;
+		dev_kfree_skb(q->skb,FREE_WRITE);
+		kfree(q);
+		q = ql;
+	}
+
+	return sqno_end;
+}
+
+/*
+ * remove stale packets from list
+ */
+
+static void isdn_ppp_cleanup_queue(isdn_net_dev * dev, long min_sqno)
+{
+/* z.z einfaches aussortieren gammeliger pakete. Fuer die Zukunft:
+   eventuell, solange vorne kein B-paket ist und sqno<=min_sqno: auch rauswerfen
+   wenn sqno<min_sqno und Luecken vorhanden sind: auch weg (die koennen nicht mehr gefuellt werden)
+   bei paketen groesser min_sqno: ueber mp_mrru: wenn summe ueber pktlen der rumhaengenden Pakete 
+   groesser als mrru ist: raus damit , Pakete muessen allerdings zusammenhaengen sonst koennte
+   ja ein Paket mit B und eins mit E dazwischenpassen */
+
+	struct mpqueue *ql, *q = dev->mp_last;
+	while (q) {
+		if (q->sqno < min_sqno) {
+			if (q->BEbyte & MP_END_FRAG) {
+				printk(KERN_DEBUG "ippp: freeing stale packet!\n");
+				if ((dev->mp_last = q->next))
+					q->next->last = NULL;
+				while (q) {
+					ql = q->last;
+					dev_kfree_skb(q->skb,FREE_WRITE);
+					kfree(q);
+					q = ql;
+				}
+				q = dev->mp_last;
+			} else
+				q = q->next;
+		} else
+			break;
+	}
+}
+
+/*
+ * a buffered packet timed-out?
+ */
+
+#endif
+
+void isdn_ppp_timer_timeout(void)
+{
+#ifdef CONFIG_ISDN_MPP
+	isdn_net_dev *net_dev = dev->netdev;
+	struct sqqueue *q, *ql = NULL, *qn;
+
+	while (net_dev) {
+		isdn_net_local *lp = &net_dev->local;
+		if (net_dev->ib.modify)	{	/* interface locked? */
+                        net_dev = net_dev->next;
+			continue;
+                }
+
+		q = net_dev->ib.sq;
+		while (q) {
+			if (q->sqno_start == net_dev->ib.next_num || q->timer < jiffies) {
+				ql = net_dev->ib.sq;
+				net_dev->ib.sq = q->next;
+				net_dev->ib.next_num = q->sqno_end + 1;
+				q->next = NULL;
+				for (; ql;) {
+					isdn_ppp_push_higher(net_dev, lp, ql->skb, -1);
+					qn = ql->next;
+					kfree(ql);
+					ql = qn;
+				}
+				q = net_dev->ib.sq;
+			} else
+				q = q->next;
+		}
+		net_dev = net_dev->next;
+	}
+#endif
+}
+
+int isdn_ppp_dev_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+{
+	int error;
+	char *r;
+	int len;
+	isdn_net_local *lp = (isdn_net_local *) dev->priv;
+
+	if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP)
+		return -EINVAL;
+
+	switch (cmd) {
+	case SIOCGPPPVER:
+		r = (char *) ifr->ifr_ifru.ifru_data;
+		len = strlen(PPP_VERSION) + 1;
+		error = verify_area(VERIFY_WRITE, r, len);
+		if (!error)
+			memcpy_tofs(r, PPP_VERSION, len);
+		break;
+	default:
+		error = -EINVAL;
+	}
+	return error;
+}
+
+static int isdn_ppp_if_get_unit(char **namebuf)
+{
+	char *name = *namebuf;
+	int len, i, unit = 0, deci;
+
+	len = strlen(name);
+	for (i = 0, deci = 1; i < len; i++, deci *= 10) {
+		if (name[len - 1 - i] >= '0' && name[len - 1 - i] <= '9')
+			unit += (name[len - 1 - i] - '0') * deci;
+		else
+			break;
+	}
+	if (!i)
+		unit = -1;
+
+	*namebuf = name + len - 1 - i;
+	return unit;
+
+}
+
+
+int isdn_ppp_dial_slave(char *name)
+{
+#ifdef CONFIG_ISDN_MPP
+	isdn_net_dev *ndev;
+	isdn_net_local *lp;
+	struct device *sdev;
+
+	if(!(ndev = isdn_net_findif(name)))
+		return 1;
+	lp = &ndev->local;
+	if(!(lp->flags & ISDN_NET_CONNECTED))
+		return 5;
+
+	sdev = lp->slave;
+	while(sdev)
+	{
+		isdn_net_local *mlp = (isdn_net_local *) sdev->priv;
+		if(!(mlp->flags & ISDN_NET_CONNECTED))
+			break;
+		sdev = mlp->slave;
+	}
+	if(!sdev)
+		return 2;
+
+	isdn_net_force_dial_lp((isdn_net_local *) sdev->priv);
+	return 0;
+#else
+	return -1;
+#endif
+}
+
+
diff -u --recursive --new-file v1.3.68/linux/drivers/isdn/isdn_ppp.h linux/drivers/isdn/isdn_ppp.h
--- v1.3.68/linux/drivers/isdn/isdn_ppp.h	Thu Jan  1 02:00:00 1970
+++ linux/drivers/isdn/isdn_ppp.h	Mon Feb 26 11:58:05 1996
@@ -0,0 +1,44 @@
+/* $Id: isdn_ppp.h,v 1.1 1996/01/10 21:39:10 fritz Exp fritz $
+ *
+ * header for Linux ISDN subsystem, functions for synchronous PPP (linklevel).
+ *
+ * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * 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_ppp.h,v $
+ * Revision 1.1  1996/01/10 21:39:10  fritz
+ * Initial revision
+ *
+ */
+
+extern void isdn_ppp_timer_timeout(void);
+extern int  isdn_ppp_read(int , struct file *, char *, int);
+extern int  isdn_ppp_write(int , struct file *, const char *, int);
+extern int  isdn_ppp_open(int , struct file *);
+extern int  isdn_ppp_init(void);
+extern void isdn_ppp_cleanup(void);
+extern int  isdn_ppp_free(isdn_net_local *);
+extern int  isdn_ppp_bind(isdn_net_local *);
+extern int  isdn_ppp_xmit(struct sk_buff *, struct device *);
+extern void isdn_ppp_receive(isdn_net_dev *, isdn_net_local *, struct sk_buff *);
+extern int  isdn_ppp_dev_ioctl(struct device *, struct ifreq *, int);
+extern void isdn_ppp_free_mpqueue(isdn_net_dev *);
+extern int  isdn_ppp_select(int, struct file *, int, select_table *);
+extern int  isdn_ppp_ioctl(int, struct file *, unsigned int, unsigned long);
+extern void isdn_ppp_release(int, struct file *);
+extern int  isdn_ppp_dial_slave(char *);
+
+extern struct ippp_struct *ippp_table;
diff -u --recursive --new-file v1.3.68/linux/drivers/isdn/isdn_tty.c linux/drivers/isdn/isdn_tty.c
--- v1.3.68/linux/drivers/isdn/isdn_tty.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/isdn/isdn_tty.c	Mon Feb 26 12:31:19 1996
@@ -0,0 +1,2068 @@
+/* $Id: isdn_tty.c,v 1.3 1996/02/11 02:12:32 fritz Exp fritz $
+ *
+ * Linux ISDN subsystem, tty functions and AT-command emulator (linklevel).
+ *
+ * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * 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_tty.c,v $
+ * Revision 1.3  1996/02/11 02:12:32  fritz
+ * Bugfixes according to similar fixes in standard serial.c of kernel.
+ *
+ * Revision 1.2  1996/01/22 05:12:25  fritz
+ * replaced my_atoi by simple_strtoul
+ *
+ * Revision 1.1  1996/01/09 04:13:18  fritz
+ * Initial revision
+ *
+ */
+
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/isdn.h>
+#include "isdn_common.h"
+#include "isdn_tty.h"
+
+/* Prototypes */
+
+static int  isdn_tty_edit_at(const char *, int, modem_info *, int);
+static void isdn_tty_check_esc(const u_char *, u_char, int, int *, int *, int);
+static void isdn_tty_modem_reset_regs(atemu *, int);
+
+/* Leave this unchanged unless you know what you do! */
+#define MODEM_PARANOIA_CHECK
+#define MODEM_DO_RESTART
+
+static char *isdn_ttyname_ttyI = "ttyI";
+static char *isdn_ttyname_cui  = "cui";
+char *isdn_tty_revision        = "$Revision: 1.3 $";
+
+int isdn_tty_try_read(int i, u_char * buf, int len)
+{
+        int c;
+        struct tty_struct *tty;
+
+	if (i < 0)
+		return 0;
+	if (dev->mdm.online[i]) {
+		if ((tty = dev->mdm.info[i].tty)) {
+			if (dev->mdm.info[i].MCR & UART_MCR_RTS) {
+				c = TTY_FLIPBUF_SIZE - tty->flip.count - 1;
+				if (c >= len) {
+					if (len > 1) {
+						memcpy(tty->flip.char_buf_ptr, buf, len);
+						tty->flip.count += len;
+						memset(tty->flip.flag_buf_ptr, 0, len);
+						if (dev->mdm.atmodem[i].mdmreg[12] & 128)
+							tty->flip.flag_buf_ptr[len - 1] = 0xff;
+						tty->flip.flag_buf_ptr += len;
+						tty->flip.char_buf_ptr += len;
+					} else
+						tty_insert_flip_char(tty, buf[0], 0);
+					queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
+					return 1;
+				}
+			}
+		}
+	}
+	return 0;
+}
+
+void isdn_tty_readmodem(void)
+{
+	int resched = 0;
+	int midx;
+	int i;
+	int c;
+	int r;
+	ulong flags;
+	struct tty_struct *tty;
+	modem_info *info;
+
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		if ((midx = dev->m_idx[i]) >= 0)
+			if (dev->mdm.online[midx]) {
+				save_flags(flags);
+				cli();
+				r = 0;
+				info = &dev->mdm.info[midx];
+				if ((tty = info->tty)) {
+					if (info->MCR & UART_MCR_RTS) {
+						c = TTY_FLIPBUF_SIZE - tty->flip.count - 1;
+						if (c > 0) {
+							r = isdn_readbchan(info->isdn_driver, info->isdn_channel,
+								      tty->flip.char_buf_ptr,
+								      tty->flip.flag_buf_ptr, c, 0);
+							if (!(dev->mdm.atmodem[midx].mdmreg[12] & 128))
+								memset(tty->flip.flag_buf_ptr, 0, r);
+							tty->flip.count += r;
+							tty->flip.flag_buf_ptr += r;
+							tty->flip.char_buf_ptr += r;
+							if (r)
+								queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
+						}
+					} else
+						r = 1;
+				} else
+					r = 1;
+				restore_flags(flags);
+				if (r) {
+					dev->mdm.rcvsched[midx] = 0;
+					resched = 1;
+				} else
+					dev->mdm.rcvsched[midx] = 1;
+			}
+	}
+	if (!resched)
+		isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 0);
+}
+
+/************************************************************
+ *
+ * Modem-functions
+ *
+ * mostly "stolen" from original Linux-serial.c and friends.
+ *
+ ************************************************************/
+
+static void isdn_tty_dial(char *n, modem_info * info, atemu * m)
+{
+	isdn_ctrl cmd;
+	ulong flags;
+	int i;
+
+	save_flags(flags);
+	cli();
+	i = isdn_get_free_channel(ISDN_USAGE_MODEM, m->mdmreg[14], m->mdmreg[15], -1, -1);
+	if (i < 0) {
+		restore_flags(flags);
+		isdn_tty_modem_result(6, info);
+	} else {
+		if (strlen(m->msn)) {
+			info->isdn_driver = dev->drvmap[i];
+			info->isdn_channel = dev->chanmap[i];
+			info->drv_index = i;
+			dev->m_idx[i] = info->line;
+			dev->usage[i] |= ISDN_USAGE_OUTGOING;
+			isdn_info_update();
+			restore_flags(flags);
+			cmd.driver = info->isdn_driver;
+			cmd.arg = info->isdn_channel;
+			cmd.command = ISDN_CMD_CLREAZ;
+			dev->drv[info->isdn_driver]->interface->command(&cmd);
+			strcpy(cmd.num, isdn_map_eaz2msn(m->msn, info->isdn_driver));
+			cmd.driver = info->isdn_driver;
+			cmd.command = ISDN_CMD_SETEAZ;
+			dev->drv[info->isdn_driver]->interface->command(&cmd);
+			cmd.driver = info->isdn_driver;
+			cmd.command = ISDN_CMD_SETL2;
+			cmd.arg = info->isdn_channel + (m->mdmreg[14] << 8);
+			dev->drv[info->isdn_driver]->interface->command(&cmd);
+			cmd.driver = info->isdn_driver;
+			cmd.command = ISDN_CMD_SETL3;
+			cmd.arg = info->isdn_channel + (m->mdmreg[15] << 8);
+			dev->drv[info->isdn_driver]->interface->command(&cmd);
+			cmd.driver = info->isdn_driver;
+			cmd.arg = info->isdn_channel;
+			sprintf(cmd.num, "%s,%s,%d,%d", n, isdn_map_eaz2msn(m->msn, info->isdn_driver),
+				m->mdmreg[18], m->mdmreg[19]);
+			cmd.command = ISDN_CMD_DIAL;
+			dev->mdm.dialing[info->line] = 1;
+			strcpy(dev->num[i], n);
+			isdn_info_update();
+			dev->drv[info->isdn_driver]->interface->command(&cmd);
+		} else
+			restore_flags(flags);
+	}
+}
+
+void isdn_tty_modem_hup(modem_info * info)
+{
+	isdn_ctrl cmd;
+
+	dev->mdm.rcvsched[info->line] = 0;
+	dev->mdm.online[info->line] = 0;
+	if (info->isdn_driver >= 0) {
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_HANGUP;
+		cmd.arg = info->isdn_channel;
+		dev->drv[info->isdn_driver]->interface->command(&cmd);
+		isdn_all_eaz(info->isdn_driver, info->isdn_channel);
+		isdn_free_channel(info->isdn_driver, info->isdn_channel, ISDN_USAGE_MODEM);
+	}
+	dev->m_idx[info->drv_index] = -1;
+	info->isdn_driver = -1;
+	info->isdn_channel = -1;
+	info->drv_index = -1;
+}
+
+static inline int isdn_tty_paranoia_check(modem_info * info, dev_t device, const char *routine)
+{
+#ifdef MODEM_PARANOIA_CHECK
+	if (!info) {
+		printk(KERN_WARNING "isdn: null info_struct for (%d, %d) in %s\n",
+		       MAJOR(device), MINOR(device), routine);
+		return 1;
+	}
+	if (info->magic != ISDN_ASYNC_MAGIC) {
+		printk(KERN_WARNING "isdn: bad magic for modem struct (%d, %d) in %s\n",
+		       MAJOR(device), MINOR(device), routine);
+		return 1;
+	}
+#endif
+	return 0;
+}
+
+/*
+ * This routine is called to set the UART divisor registers to match
+ * the specified baud rate for a serial port.
+ */
+static void isdn_tty_change_speed(modem_info * info)
+{
+	uint cflag, cval, fcr, quot;
+	int i;
+
+	if (!info->tty || !info->tty->termios)
+		return;
+	cflag = info->tty->termios->c_cflag;
+
+	quot = i = cflag & CBAUD;
+	if (i & CBAUDEX) {
+		i &= ~CBAUDEX;
+		if (i < 1 || i > 2)
+			info->tty->termios->c_cflag &= ~CBAUDEX;
+		else
+			i += 15;
+	}
+	if (quot) {
+		info->MCR |= UART_MCR_DTR;
+	} else {
+		info->MCR &= ~UART_MCR_DTR;
+		isdn_tty_modem_reset_regs(&dev->mdm.atmodem[info->line], 0);
+		if (dev->mdm.online[info->line]) {
+#ifdef ISDN_DEBUG_MODEM_HUP
+			printk(KERN_DEBUG "Mhup in changespeed\n");
+#endif
+			isdn_tty_modem_hup(info);
+			isdn_tty_modem_result(3, info);
+		}
+		return;
+	}
+	/* byte size and parity */
+	cval = cflag & (CSIZE | CSTOPB);
+	cval >>= 4;
+	if (cflag & PARENB)
+		cval |= UART_LCR_PARITY;
+	if (!(cflag & PARODD))
+		cval |= UART_LCR_EPAR;
+	fcr = 0;
+
+	/* CTS flow control flag and modem status interrupts */
+	if (cflag & CRTSCTS) {
+		info->flags |= ISDN_ASYNC_CTS_FLOW;
+	} else
+		info->flags &= ~ISDN_ASYNC_CTS_FLOW;
+	if (cflag & CLOCAL)
+		info->flags &= ~ISDN_ASYNC_CHECK_CD;
+	else {
+		info->flags |= ISDN_ASYNC_CHECK_CD;
+	}
+}
+
+static int isdn_tty_startup(modem_info * info)
+{
+	ulong flags;
+
+	if (info->flags & ISDN_ASYNC_INITIALIZED)
+		return 0;
+	if (!info->type) {
+		if (info->tty)
+			set_bit(TTY_IO_ERROR, &info->tty->flags);
+		return 0;
+	}
+	save_flags(flags);
+	cli();
+
+#ifdef ISDN_DEBUG_MODEM_OPEN
+	printk(KERN_DEBUG "starting up ttyi%d ...\n", info->line);
+#endif
+	/*
+	 * Now, initialize the UART 
+	 */
+	info->MCR = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2;
+	if (info->tty)
+		clear_bit(TTY_IO_ERROR, &info->tty->flags);
+	/*
+	 * and set the speed of the serial port
+	 */
+	isdn_tty_change_speed(info);
+
+	info->flags |= ISDN_ASYNC_INITIALIZED;
+	dev->mdm.msr[info->line] |= UART_MSR_DSR;
+#if FUTURE
+	info->send_outstanding = 0;
+#endif
+	restore_flags(flags);
+	return 0;
+}
+
+/*
+ * This routine will shutdown a serial port; interrupts are disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.
+ */
+static void isdn_tty_shutdown(modem_info * info)
+{
+	ulong flags;
+
+	if (!(info->flags & ISDN_ASYNC_INITIALIZED))
+		return;
+#ifdef ISDN_DEBUG_MODEM_OPEN
+	printk(KERN_DEBUG "Shutting down isdnmodem port %d ....\n", info->line);
+#endif
+	save_flags(flags);
+	cli();			/* Disable interrupts */
+	if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
+		info->MCR &= ~(UART_MCR_DTR | UART_MCR_RTS);
+		isdn_tty_modem_reset_regs(&dev->mdm.atmodem[info->line], 0);
+		if (dev->mdm.online[info->line]) {
+#ifdef ISDN_DEBUG_MODEM_HUP
+			printk(KERN_DEBUG "Mhup in isdn_tty_shutdown\n");
+#endif
+			isdn_tty_modem_hup(info);
+		}
+	}
+	if (info->tty)
+		set_bit(TTY_IO_ERROR, &info->tty->flags);
+
+	info->flags &= ~ISDN_ASYNC_INITIALIZED;
+	restore_flags(flags);
+}
+
+static int isdn_tty_write(struct tty_struct *tty, int from_user, const u_char * buf, int count)
+{
+	int c, total = 0;
+	modem_info *info = (modem_info *) tty->driver_data;
+	ulong flags;
+	int i;
+
+	if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_write"))
+		return 0;
+	if (!tty)
+		return 0;
+	save_flags(flags);
+	cli();
+	while (1) {
+		c = MIN(count, info->xmit_size - info->xmit_count - 1);
+		if (info->isdn_driver >= 0) {
+#if 0
+			if (info->isdn_driver != 0) {
+				printk(KERN_DEBUG "FIDO: Zwei HW-Treiber geladen? Ansonsten ist was faul.\n");
+				break;
+			}
+			int drvidx = info->isdn_driver;
+			driver *driv = dev->drv[drvidx];
+			i = driv->maxbufsize;
+#else
+			i = dev->drv[info->isdn_driver]->maxbufsize;
+#endif
+			c = MIN(c, i);
+		}
+		if (c <= 0)
+			break;
+		i = info->line;
+		if (dev->mdm.online[i]) {
+			isdn_tty_check_esc(buf, dev->mdm.atmodem[i].mdmreg[2], c,
+				       &(dev->mdm.atmodem[i].pluscount),
+			     &(dev->mdm.atmodem[i].lastplus), from_user);
+			if (from_user)
+				memcpy_fromfs(&(info->xmit_buf[info->xmit_count]), buf, c);
+			else
+				memcpy(&(info->xmit_buf[info->xmit_count]), buf, c);
+			info->xmit_count += c;
+			if (dev->mdm.atmodem[i].mdmreg[13] & 1) {
+				char *bufptr;
+				int buflen;
+#if 0
+				printk(KERN_DEBUG "WB1: %d\n", info->xmit_count);
+#endif
+				bufptr = info->xmit_buf;
+				buflen = info->xmit_count;
+				if (dev->mdm.atmodem[i].mdmreg[13] & 2) {
+					/* Add T.70 simplyfied header */
+
+#ifdef ISDN_DEBUG_MODEM_DUMP
+					isdn_dumppkt("T70pack1:", bufptr, buflen, 40);
+#endif
+					bufptr -= 4;
+					buflen += 4;
+					memcpy(bufptr, "\1\0\1\0", 4);
+#ifdef ISDN_DEBUG_MODEM_DUMP
+					isdn_dumppkt("T70pack2:", bufptr, buflen, 40);
+#endif
+				}
+				if (dev->drv[info->isdn_driver]->interface->
+				    writebuf(info->isdn_driver, info->isdn_channel, bufptr, buflen, 0) > 0) {
+					info->xmit_count = 0;
+					info->xmit_size = dev->mdm.atmodem[i].mdmreg[16] * 16;
+#if FUTURE
+					info->send_outstanding++;
+					dev->mdm.msr[i] &= ~UART_MSR_CTS;
+#endif
+					if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+					    tty->ldisc.write_wakeup)
+						(tty->ldisc.write_wakeup) (tty);
+					wake_up_interruptible(&tty->write_wait);
+				}
+			}
+		} else {
+			if (dev->mdm.dialing[i]) {
+				dev->mdm.dialing[i] = 0;
+#ifdef ISDN_DEBUG_MODEM_HUP
+				printk(KERN_DEBUG "Mhup in isdn_tty_write\n");
+#endif
+				isdn_tty_modem_hup(info);
+				isdn_tty_modem_result(3, info);
+			} else
+				c = isdn_tty_edit_at(buf, c, info, from_user);
+		}
+		buf += c;
+		count -= c;
+		total += c;
+	}
+	if (info->xmit_count)
+		isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, 1);
+	restore_flags(flags);
+	return total;
+}
+
+static int isdn_tty_write_room(struct tty_struct *tty)
+{
+	modem_info *info = (modem_info *) tty->driver_data;
+	int ret;
+
+	if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_write_room"))
+		return 0;
+	if (!dev->mdm.online[info->line])
+		return info->xmit_size - 1;
+	ret = info->xmit_size - info->xmit_count - 1;
+	return (ret < 0) ? 0 : ret;
+}
+
+static int isdn_tty_chars_in_buffer(struct tty_struct *tty)
+{
+	modem_info *info = (modem_info *) tty->driver_data;
+
+	if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_chars_in_buffer"))
+		return 0;
+	if (!dev->mdm.online[info->line])
+		return 0;
+	return (info->xmit_count);
+}
+
+static void isdn_tty_flush_buffer(struct tty_struct *tty)
+{
+	modem_info *info = (modem_info *) tty->driver_data;
+	uint flags;
+
+	if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_flush_buffer"))
+		return;
+	save_flags(flags);
+	cli();
+	info->xmit_count = 0;
+	restore_flags(flags);
+	wake_up_interruptible(&tty->write_wait);
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+	    tty->ldisc.write_wakeup)
+		(tty->ldisc.write_wakeup) (tty);
+}
+
+static void isdn_tty_flush_chars(struct tty_struct *tty)
+{
+	modem_info *info = (modem_info *) tty->driver_data;
+
+	if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_flush_chars"))
+		return;
+	if (info->xmit_count > 0)
+		isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, 1);
+}
+
+/*
+ * ------------------------------------------------------------
+ * isdn_tty_throttle()
+ * 
+ * This routine is called by the upper-layer tty layer to signal that
+ * incoming characters should be throttled.
+ * ------------------------------------------------------------
+ */
+static void isdn_tty_throttle(struct tty_struct *tty)
+{
+	modem_info *info = (modem_info *) tty->driver_data;
+
+	if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_throttle"))
+		return;
+	if (I_IXOFF(tty))
+		info->x_char = STOP_CHAR(tty);
+	info->MCR &= ~UART_MCR_RTS;
+}
+
+static void isdn_tty_unthrottle(struct tty_struct *tty)
+{
+	modem_info *info = (modem_info *) tty->driver_data;
+
+	if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_unthrottle"))
+		return;
+	if (I_IXOFF(tty)) {
+		if (info->x_char)
+			info->x_char = 0;
+		else
+			info->x_char = START_CHAR(tty);
+	}
+	info->MCR |= UART_MCR_RTS;
+}
+
+/*
+ * ------------------------------------------------------------
+ * isdn_tty_ioctl() and friends
+ * ------------------------------------------------------------
+ */
+
+/*
+ * isdn_tty_get_lsr_info - get line status register info
+ *
+ * Purpose: Let user call ioctl() to get info when the UART physically
+ *          is emptied.  On bus types like RS485, the transmitter must
+ *          release the bus after transmitting. This must be done when
+ *          the transmit shift register is empty, not be done when the
+ *          transmit holding register is empty.  This functionality
+ *          allows RS485 driver to be written in user space. 
+ */
+static int isdn_tty_get_lsr_info(modem_info * info, uint * value)
+{
+	u_char status;
+	uint result;
+	ulong flags;
+
+	save_flags(flags);
+	cli();
+	status = dev->mdm.msr[info->line];
+	restore_flags(flags);
+	result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0);
+	put_fs_long(result, (ulong *) value);
+	return 0;
+}
+
+
+static int isdn_tty_get_modem_info(modem_info * info, uint * value)
+{
+	u_char control, status;
+	uint result;
+	ulong flags;
+
+	control = info->MCR;
+	save_flags(flags);
+	cli();
+	status = dev->mdm.msr[info->line];
+	restore_flags(flags);
+	result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0)
+	    | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0)
+	    | ((status & UART_MSR_DCD) ? TIOCM_CAR : 0)
+	    | ((status & UART_MSR_RI) ? TIOCM_RNG : 0)
+	    | ((status & UART_MSR_DSR) ? TIOCM_DSR : 0)
+	    | ((status & UART_MSR_CTS) ? TIOCM_CTS : 0);
+	put_fs_long(result, (ulong *) value);
+	return 0;
+}
+
+static int isdn_tty_set_modem_info(modem_info * info, uint cmd, uint * value)
+{
+	uint arg = get_fs_long((ulong *) value);
+
+	switch (cmd) {
+	case TIOCMBIS:
+		if (arg & TIOCM_RTS) {
+			info->MCR |= UART_MCR_RTS;
+		}
+		if (arg & TIOCM_DTR) {
+			info->MCR |= UART_MCR_DTR;
+		}
+		break;
+	case TIOCMBIC:
+		if (arg & TIOCM_RTS) {
+			info->MCR &= ~UART_MCR_RTS;
+		}
+		if (arg & TIOCM_DTR) {
+			info->MCR &= ~UART_MCR_DTR;
+			isdn_tty_modem_reset_regs(&dev->mdm.atmodem[info->line], 0);
+			if (dev->mdm.online[info->line]) {
+#ifdef ISDN_DEBUG_MODEM_HUP
+				printk(KERN_DEBUG "Mhup in TIOCMBIC\n");
+#endif
+				isdn_tty_modem_hup(info);
+				isdn_tty_modem_result(3, info);
+			}
+		}
+		break;
+	case TIOCMSET:
+		info->MCR = ((info->MCR & ~(UART_MCR_RTS | UART_MCR_DTR))
+			     | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0)
+			     | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0));
+		if (!(info->MCR & UART_MCR_DTR)) {
+			isdn_tty_modem_reset_regs(&dev->mdm.atmodem[info->line], 0);
+			if (dev->mdm.online[info->line]) {
+#ifdef ISDN_DEBUG_MODEM_HUP
+				printk(KERN_DEBUG "Mhup in TIOCMSET\n");
+#endif
+				isdn_tty_modem_hup(info);
+				isdn_tty_modem_result(3, info);
+			}
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int isdn_tty_ioctl(struct tty_struct *tty, struct file *file,
+		       uint cmd, ulong arg)
+{
+	modem_info *info = (modem_info *) tty->driver_data;
+	int error;
+	int retval;
+
+	if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_ioctl"))
+		return -ENODEV;
+	switch (cmd) {
+	case TCSBRK:		/* SVID version: non-zero arg --> no break */
+		retval = tty_check_change(tty);
+		if (retval)
+			return retval;
+		tty_wait_until_sent(tty, 0);
+		return 0;
+	case TCSBRKP:		/* support for POSIX tcsendbreak() */
+		retval = tty_check_change(tty);
+		if (retval)
+			return retval;
+		tty_wait_until_sent(tty, 0);
+		return 0;
+	case TIOCGSOFTCAR:
+		error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long));
+		if (error)
+			return error;
+		put_fs_long(C_CLOCAL(tty) ? 1 : 0, (ulong *) arg);
+		return 0;
+	case TIOCSSOFTCAR:
+		arg = get_fs_long((ulong *) arg);
+		tty->termios->c_cflag =
+		    ((tty->termios->c_cflag & ~CLOCAL) |
+		     (arg ? CLOCAL : 0));
+		return 0;
+	case TIOCMGET:
+		error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(uint));
+		if (error)
+			return error;
+		return isdn_tty_get_modem_info(info, (uint *) arg);
+	case TIOCMBIS:
+	case TIOCMBIC:
+	case TIOCMSET:
+		return isdn_tty_set_modem_info(info, cmd, (uint *) arg);
+	case TIOCSERGETLSR:	/* Get line status register */
+		error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(uint));
+		if (error)
+			return error;
+		else
+			return isdn_tty_get_lsr_info(info, (uint *) arg);
+
+	case TIOCGSERIAL:
+		return -ENOIOCTLCMD;
+#if 0
+		error = verify_area(VERIFY_WRITE, (void *) arg,
+				    sizeof(struct serial_struct));
+		if (error)
+			return error;
+		return get_serial_info(info,
+				       (struct serial_struct *) arg);
+#endif
+	case TIOCSSERIAL:
+		return -ENOIOCTLCMD;
+#if 0
+		return set_serial_info(info,
+				       (struct serial_struct *) arg);
+#endif
+	case TIOCSERCONFIG:
+		return -ENOIOCTLCMD;
+#if 0
+		return do_autoconfig(info);
+#endif
+
+	case TIOCSERGWILD:
+		return -ENOIOCTLCMD;
+#if 0
+		error = verify_area(VERIFY_WRITE, (void *) arg,
+				    sizeof(int));
+		if (error)
+			return error;
+		put_fs_long(modem_wild_int_mask, (ulong *) arg);
+		return 0;
+#endif
+	case TIOCSERSWILD:
+		return -ENOIOCTLCMD;
+#if 0
+		if (!suser())
+			return -EPERM;
+		modem_wild_int_mask = get_fs_long((ulong *) arg);
+		if (modem_wild_int_mask < 0)
+			modem_wild_int_mask = check_wild_interrupts(0);
+		return 0;
+#endif
+	case TIOCSERGSTRUCT:
+		return -ENOIOCTLCMD;
+#if 0
+		error = verify_area(VERIFY_WRITE, (void *) arg,
+				    sizeof(modem_info));
+		if (error)
+			return error;
+		memcpy_tofs((modem_info *) arg,
+			    info, sizeof(modem_info));
+		return 0;
+#endif
+	default:
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+		printk(KERN_DEBUG "unsupp. ioctl 0x%08x on ttyi%d\n", cmd, info->line);
+#endif
+		return -ENOIOCTLCMD;
+	}
+	return 0;
+}
+
+static void isdn_tty_set_termios(struct tty_struct *tty, struct termios *old_termios)
+{
+	modem_info *info = (modem_info *) tty->driver_data;
+
+	if (tty->termios->c_cflag == old_termios->c_cflag)
+		return;
+	isdn_tty_change_speed(info);
+	if ((old_termios->c_cflag & CRTSCTS) &&
+	    !(tty->termios->c_cflag & CRTSCTS)) {
+		tty->hw_stopped = 0;
+	}
+}
+
+/*
+ * ------------------------------------------------------------
+ * isdn_tty_open() and friends
+ * ------------------------------------------------------------
+ */
+static int isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info * info)
+{
+	struct wait_queue wait = {current, NULL};
+	int do_clocal = 0;
+	unsigned long flags;
+	int retval;
+
+	/*
+	 * If the device is in the middle of being closed, then block
+	 * until it's done, and then try again.
+	 */
+	if (tty_hung_up_p(filp) ||
+	    (info->flags & ISDN_ASYNC_CLOSING)) {
+		if (info->flags & ISDN_ASYNC_CLOSING)
+                        interruptible_sleep_on(&info->close_wait);
+#ifdef MODEM_DO_RESTART
+		if (info->flags & ISDN_ASYNC_HUP_NOTIFY)
+			return -EAGAIN;
+		else
+			return -ERESTARTSYS;
+#else
+		return -EAGAIN;
+#endif
+	}
+	/*
+	 * If this is a callout device, then just make sure the normal
+	 * device isn't being used.
+	 */
+	if (tty->driver.subtype == ISDN_SERIAL_TYPE_CALLOUT) {
+		if (info->flags & ISDN_ASYNC_NORMAL_ACTIVE)
+			return -EBUSY;
+		if ((info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) &&
+		    (info->flags & ISDN_ASYNC_SESSION_LOCKOUT) &&
+		    (info->session != current->session))
+			return -EBUSY;
+		if ((info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) &&
+		    (info->flags & ISDN_ASYNC_PGRP_LOCKOUT) &&
+		    (info->pgrp != current->pgrp))
+			return -EBUSY;
+		info->flags |= ISDN_ASYNC_CALLOUT_ACTIVE;
+		return 0;
+	}
+	/*
+	 * If non-blocking mode is set, then make the check up front
+	 * and then exit.
+	 */
+	if (filp->f_flags & O_NONBLOCK) {
+		if (info->flags & ISDN_ASYNC_CALLOUT_ACTIVE)
+			return -EBUSY;
+		info->flags |= ISDN_ASYNC_NORMAL_ACTIVE;
+		return 0;
+	}
+	if (info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) {
+		if (info->normal_termios.c_cflag & CLOCAL)
+			do_clocal = 1;
+	} else {
+		if (tty->termios->c_cflag & CLOCAL)
+			do_clocal = 1;
+	}
+	/*
+	 * Block waiting for the carrier detect and the line to become
+	 * free (i.e., not in use by the callout).  While we are in
+	 * this loop, info->count is dropped by one, so that
+	 * isdn_tty_close() knows when to free things.  We restore it upon
+	 * exit, either normal or abnormal.
+	 */
+	retval = 0;
+	add_wait_queue(&info->open_wait, &wait);
+#ifdef ISDN_DEBUG_MODEM_OPEN
+	printk(KERN_DEBUG "isdn_tty_block_til_ready before block: ttyi%d, count = %d\n",
+	       info->line, info->count);
+#endif
+        save_flags(flags);
+        cli();
+        if (!(tty_hung_up_p(filp)))
+                info->count--;
+        restore_flags(flags);
+	info->blocked_open++;
+	while (1) {
+		current->state = TASK_INTERRUPTIBLE;
+		if (tty_hung_up_p(filp) ||
+		    !(info->flags & ISDN_ASYNC_INITIALIZED)) {
+#ifdef MODEM_DO_RESTART
+			if (info->flags & ISDN_ASYNC_HUP_NOTIFY)
+				retval = -EAGAIN;
+			else
+				retval = -ERESTARTSYS;
+#else
+			retval = -EAGAIN;
+#endif
+			break;
+		}
+		if (!(info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) &&
+		    !(info->flags & ISDN_ASYNC_CLOSING) &&
+		    (do_clocal || (
+					  dev->mdm.msr[info->line] &
+					  UART_MSR_DCD))) {
+			break;
+		}
+		if (current->signal & ~current->blocked) {
+			retval = -ERESTARTSYS;
+			break;
+		}
+#ifdef ISDN_DEBUG_MODEM_OPEN
+		printk(KERN_DEBUG "isdn_tty_block_til_ready blocking: ttyi%d, count = %d\n",
+		       info->line, info->count);
+#endif
+		schedule();
+	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&info->open_wait, &wait);
+	if (!tty_hung_up_p(filp))
+		info->count++;
+	info->blocked_open--;
+#ifdef ISDN_DEBUG_MODEM_OPEN
+	printk(KERN_DEBUG "isdn_tty_block_til_ready after blocking: ttyi%d, count = %d\n",
+	       info->line, info->count);
+#endif
+	if (retval)
+		return retval;
+	info->flags |= ISDN_ASYNC_NORMAL_ACTIVE;
+	return 0;
+}
+
+/*
+ * This routine is called whenever a serial port is opened.  It
+ * enables interrupts for a serial port, linking in its async structure into
+ * the IRQ chain.   It also performs the serial-specific
+ * initialization for the tty structure.
+ */
+static int isdn_tty_open(struct tty_struct *tty, struct file *filp)
+{
+	modem_info *info;
+	int retval, line;
+
+	line = MINOR(tty->device) - tty->driver.minor_start;
+	if (line < 0 || line > ISDN_MAX_CHANNELS)
+		return -ENODEV;
+	info = &dev->mdm.info[line];
+	if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_open"))
+		return -ENODEV;
+#ifdef ISDN_DEBUG_MODEM_OPEN
+	printk(KERN_DEBUG "isdn_tty_open %s%d, count = %d\n", tty->driver.name,
+	       info->line, info->count);
+#endif
+	info->count++;
+	tty->driver_data = info;
+	info->tty = tty;
+	/*
+	 * Start up serial port
+	 */
+	retval = isdn_tty_startup(info);
+	if (retval) {
+#ifdef ISDN_DEBUG_MODEM_OPEN
+		printk(KERN_DEBUG "isdn_tty_open return after startup\n");
+#endif
+		return retval;
+	}
+	retval = isdn_tty_block_til_ready(tty, filp, info);
+	if (retval) {
+#ifdef ISDN_DEBUG_MODEM_OPEN
+		printk(KERN_DEBUG "isdn_tty_open return after isdn_tty_block_til_ready \n");
+#endif
+		return retval;
+	}
+	if ((info->count == 1) && (info->flags & ISDN_ASYNC_SPLIT_TERMIOS)) {
+		if (tty->driver.subtype == ISDN_SERIAL_TYPE_NORMAL)
+			*tty->termios = info->normal_termios;
+		else
+			*tty->termios = info->callout_termios;
+		isdn_tty_change_speed(info);
+	}
+	info->session = current->session;
+	info->pgrp = current->pgrp;
+#ifdef ISDN_DEBUG_MODEM_OPEN
+	printk(KERN_DEBUG "isdn_tty_open ttyi%d successful...\n", info->line);
+#endif
+	dev->modempoll++;
+#ifdef ISDN_DEBUG_MODEM_OPEN
+	printk(KERN_DEBUG "isdn_tty_open normal exit\n");
+#endif
+	return 0;
+}
+
+static void isdn_tty_close(struct tty_struct *tty, struct file *filp)
+{
+	modem_info *info = (modem_info *) tty->driver_data;
+	ulong flags;
+	ulong timeout;
+
+	if (!info || isdn_tty_paranoia_check(info, tty->device, "isdn_tty_close"))
+		return;
+	save_flags(flags);
+	cli();
+	if (tty_hung_up_p(filp)) {
+		restore_flags(flags);
+#ifdef ISDN_DEBUG_MODEM_OPEN
+		printk(KERN_DEBUG "isdn_tty_close return after tty_hung_up_p\n");
+#endif
+		return;
+	}
+	dev->modempoll--;
+	if ((tty->count == 1) && (info->count != 1)) {
+		/*
+		 * Uh, oh.  tty->count is 1, which means that the tty
+		 * structure will be freed.  Info->count should always
+		 * be one in these conditions.  If it's greater than
+		 * one, we've got real problems, since it means the
+		 * serial port won't be shutdown.
+		 */
+		printk(KERN_ERR "isdn_tty_close: bad port count; tty->count is 1, "
+		       "info->count is %d\n", info->count);
+		info->count = 1;
+	}
+	if (--info->count < 0) {
+		printk(KERN_ERR "isdn_tty_close: bad port count for ttyi%d: %d\n",
+		       info->line, info->count);
+		info->count = 0;
+	}
+	if (info->count) {
+		restore_flags(flags);
+#ifdef ISDN_DEBUG_MODEM_OPEN
+		printk(KERN_DEBUG "isdn_tty_close after info->count != 0\n");
+#endif
+		return;
+	}
+	info->flags |= ISDN_ASYNC_CLOSING;
+	/*
+	 * Save the termios structure, since this port may have
+	 * separate termios for callout and dialin.
+	 */
+	if (info->flags & ISDN_ASYNC_NORMAL_ACTIVE)
+		info->normal_termios = *tty->termios;
+	if (info->flags & ISDN_ASYNC_CALLOUT_ACTIVE)
+		info->callout_termios = *tty->termios;
+
+	tty->closing = 1;
+
+	/*
+	 * At this point we stop accepting input.  To do this, we
+	 * disable the receive line status interrupts, and tell the
+	 * interrupt driver to stop checking the data ready bit in the
+	 * line status register.
+	 */
+	if (info->flags & ISDN_ASYNC_INITIALIZED) {
+		tty_wait_until_sent(tty, 3000);		/* 30 seconds timeout */
+		/*
+		 * Before we drop DTR, make sure the UART transmitter
+		 * has completely drained; this is especially
+		 * important if there is a transmit FIFO!
+		 */
+		timeout = jiffies + HZ;
+		while (!(dev->mdm.mlr[info->line]
+			 & UART_LSR_TEMT)) {
+			current->state = TASK_INTERRUPTIBLE;
+			current->timeout = jiffies + 20;
+			schedule();
+			if (jiffies > timeout)
+				break;
+		}
+	}
+	isdn_tty_shutdown(info);
+	if (tty->driver.flush_buffer)
+		tty->driver.flush_buffer(tty);
+	if (tty->ldisc.flush_buffer)
+		tty->ldisc.flush_buffer(tty);
+	info->tty = 0;
+	tty->closing = 0;
+#if 00
+	if (tty->ldisc.num != ldiscs[N_TTY].num) {
+		if (tty->ldisc.close)
+			(tty->ldisc.close) (tty);
+		tty->ldisc = ldiscs[N_TTY];
+		tty->termios->c_line = N_TTY;
+		if (tty->ldisc.open)
+			(tty->ldisc.open) (tty);
+	}
+#endif
+	if (info->blocked_open) {
+		if (info->close_delay) {
+			current->state = TASK_INTERRUPTIBLE;
+			current->timeout = jiffies + info->close_delay;
+			schedule();
+		}
+		wake_up_interruptible(&info->open_wait);
+	}
+	info->flags &= ~(ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE |
+			 ISDN_ASYNC_CLOSING);
+	wake_up_interruptible(&info->close_wait);
+	restore_flags(flags);
+#ifdef ISDN_DEBUG_MODEM_OPEN
+	printk(KERN_DEBUG "isdn_tty_close normal exit\n");
+#endif
+}
+
+/*
+ * isdn_tty_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+static void isdn_tty_hangup(struct tty_struct *tty)
+{
+	modem_info *info = (modem_info *) tty->driver_data;
+
+	if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_hangup"))
+		return;
+	isdn_tty_shutdown(info);
+	info->count = 0;
+	info->flags &= ~(ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE);
+	info->tty = 0;
+	wake_up_interruptible(&info->open_wait);
+}
+
+static void isdn_tty_reset_profile(atemu * m)
+{
+	m->profile[0] = 0;
+	m->profile[1] = 0;
+	m->profile[2] = 43;
+	m->profile[3] = 13;
+	m->profile[4] = 10;
+	m->profile[5] = 8;
+	m->profile[6] = 3;
+	m->profile[7] = 60;
+	m->profile[8] = 2;
+	m->profile[9] = 6;
+	m->profile[10] = 7;
+	m->profile[11] = 70;
+	m->profile[12] = 0x45;
+	m->profile[13] = 0;
+	m->profile[14] = ISDN_PROTO_L2_X75I;
+	m->profile[15] = ISDN_PROTO_L3_TRANS;
+	m->profile[16] = ISDN_SERIAL_XMIT_SIZE / 16;
+	m->profile[17] = ISDN_MODEM_WINSIZE;
+	m->profile[18] = 7;
+	m->profile[19] = 0;
+	m->pmsn[0] = '\0';
+}
+
+static void isdn_tty_modem_reset_regs(atemu * m, int force)
+{
+	if ((m->mdmreg[12] & 32) || force) {
+		memcpy(m->mdmreg, m->profile, ISDN_MODEM_ANZREG);
+		memcpy(m->msn, m->pmsn, ISDN_MSNLEN);
+	}
+	m->mdmcmdl = 0;
+}
+
+static void modem_write_profile(atemu * m)
+{
+	memcpy(m->profile, m->mdmreg, ISDN_MODEM_ANZREG);
+	memcpy(m->pmsn, m->msn, ISDN_MSNLEN);
+	if (dev->profd)
+		send_sig(SIGIO, dev->profd, 1);
+}
+
+int isdn_tty_modem_init(void)
+{
+	modem *m;
+	int i;
+	modem_info *info;
+
+	m = &dev->mdm;
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		isdn_tty_reset_profile(&(m->atmodem[i]));
+		isdn_tty_modem_reset_regs(&(m->atmodem[i]), 1);
+	}
+	memset(&m->tty_modem, 0, sizeof(struct tty_driver));
+	m->tty_modem.magic = TTY_DRIVER_MAGIC;
+	m->tty_modem.name = isdn_ttyname_ttyI;
+	m->tty_modem.major = ISDN_TTY_MAJOR;
+	m->tty_modem.minor_start = 0;
+	m->tty_modem.num = ISDN_MAX_CHANNELS;
+	m->tty_modem.type = TTY_DRIVER_TYPE_SERIAL;
+	m->tty_modem.subtype = ISDN_SERIAL_TYPE_NORMAL;
+	m->tty_modem.init_termios = tty_std_termios;
+	m->tty_modem.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+	m->tty_modem.flags = TTY_DRIVER_REAL_RAW;
+	m->tty_modem.refcount = &m->refcount;
+	m->tty_modem.table = m->modem_table;
+	m->tty_modem.termios = m->modem_termios;
+	m->tty_modem.termios_locked = m->modem_termios_locked;
+	m->tty_modem.open = isdn_tty_open;
+	m->tty_modem.close = isdn_tty_close;
+	m->tty_modem.write = isdn_tty_write;
+	m->tty_modem.put_char = NULL;
+	m->tty_modem.flush_chars = isdn_tty_flush_chars;
+	m->tty_modem.write_room = isdn_tty_write_room;
+	m->tty_modem.chars_in_buffer = isdn_tty_chars_in_buffer;
+	m->tty_modem.flush_buffer = isdn_tty_flush_buffer;
+	m->tty_modem.ioctl = isdn_tty_ioctl;
+	m->tty_modem.throttle = isdn_tty_throttle;
+	m->tty_modem.unthrottle = isdn_tty_unthrottle;
+	m->tty_modem.set_termios = isdn_tty_set_termios;
+	m->tty_modem.stop = NULL;
+	m->tty_modem.start = NULL;
+	m->tty_modem.hangup = isdn_tty_hangup;
+	/*
+	 * The callout device is just like normal device except for
+	 * major number and the subtype code.
+	 */
+	m->cua_modem = m->tty_modem;
+	m->cua_modem.name = isdn_ttyname_cui;
+	m->cua_modem.major = ISDN_TTYAUX_MAJOR;
+	m->tty_modem.minor_start = 0;
+	m->cua_modem.subtype = ISDN_SERIAL_TYPE_CALLOUT;
+
+	if (tty_register_driver(&m->tty_modem)) {
+		printk(KERN_WARNING "isdn: Unable to register modem-device\n");
+		return -1;
+	}
+	if (tty_register_driver(&m->cua_modem)) {
+		printk(KERN_WARNING "Couldn't register modem-callout-device\n");
+		return -2;
+	}
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		info = &(m->info[i]);
+		info->magic = ISDN_ASYNC_MAGIC;
+		info->line = i;
+		info->tty = 0;
+		info->close_delay = 50;
+		info->x_char = 0;
+		info->count = 0;
+		info->blocked_open = 0;
+		info->callout_termios = m->cua_modem.init_termios;
+		info->normal_termios = m->tty_modem.init_termios;
+		info->open_wait = 0;
+		info->close_wait = 0;
+		info->type = ISDN_PORT_16550A;
+		info->isdn_driver = -1;
+		info->isdn_channel = -1;
+		info->drv_index = -1;
+		info->xmit_size = ISDN_SERIAL_XMIT_SIZE;
+		if (!(info->xmit_buf = kmalloc(ISDN_SERIAL_XMIT_SIZE + 5, GFP_KERNEL))) {
+			printk(KERN_ERR "Could not allocate modem xmit-buffer\n");
+			return -3;
+		}
+		info->xmit_buf += 4;	/* Make room for T.70 header */
+	}
+	return 0;
+}
+
+/*
+ * An incoming call-request has arrived.
+ * Search the tty-devices for an aproppriate device and bind
+ * it to the ISDN-Channel.
+ * Return Index to dev->mdm or -1 if none found.
+ */
+int isdn_tty_find_icall(int di, int ch, char *num)
+{
+	char *eaz;
+	int i;
+	int idx;
+	int si1;
+	int si2;
+	char *s;
+	char nr[31];
+	ulong flags;
+
+	save_flags(flags);
+	cli();
+	if (num[0] == ',') {
+		nr[0] = '0';
+		strncpy(&nr[1], num, 29);
+		printk(KERN_WARNING "isdn: Incoming call without OAD, assuming '0'\n");
+	} else
+		strncpy(nr, num, 30);
+	s = strtok(nr, ",");
+	s = strtok(NULL, ",");
+	if (!s) {
+		printk(KERN_WARNING "isdn: Incoming callinfo garbled, ignored: %s\n",
+		       num);
+		restore_flags(flags);
+		return -1;
+	}
+	si1 = (int)simple_strtoul(s,NULL,10);
+	s = strtok(NULL, ",");
+	if (!s) {
+		printk(KERN_WARNING "isdn: Incoming callinfo garbled, ignored: %s\n",
+		       num);
+		restore_flags(flags);
+		return -1;
+	}
+	si2 = (int)simple_strtoul(s,NULL,10);
+	eaz = strtok(NULL, ",");
+	if (!eaz) {
+		printk(KERN_WARNING "isdn: Incoming call without CPN, assuming '0'\n");
+		eaz = "0";
+	}
+#ifdef ISDN_DEBUG_MODEM_ICALL
+	printk(KERN_DEBUG "m_fi: eaz=%s si1=%d si2=%d\n", eaz, si1, si2);
+#endif
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+#ifdef ISDN_DEBUG_MODEM_ICALL
+		printk(KERN_DEBUG "m_fi: i=%d msn=%s mmsn=%s mreg18=%d mreg19=%d\n", i,
+		       dev->mdm.atmodem[i].msn, isdn_map_eaz2msn(dev->mdm.atmodem[i].msn, di),
+		       dev->mdm.atmodem[i].mdmreg[18], dev->mdm.atmodem[i].mdmreg[19]);
+#endif
+		if ((!strcmp(isdn_map_eaz2msn(dev->mdm.atmodem[i].msn, di)
+			     ,eaz)) &&	/* EAZ is matching   */
+		    (dev->mdm.atmodem[i].mdmreg[18] == si1) &&	/* SI1 is matching   */
+		    (dev->mdm.atmodem[i].mdmreg[19] == si2)) {	/* SI2 is matching   */
+			modem_info *info = &dev->mdm.info[i];
+			idx = isdn_dc2minor(di, ch);
+#ifdef ISDN_DEBUG_MODEM_ICALL
+			printk(KERN_DEBUG "m_fi: match1\n");
+			printk(KERN_DEBUG "m_fi: idx=%d flags=%08lx drv=%d ch=%d usg=%d\n", idx,
+			       info->flags, info->isdn_driver, info->isdn_channel,
+			       dev->usage[idx]);
+#endif
+			if ((info->flags & ISDN_ASYNC_NORMAL_ACTIVE) &&
+			    (info->isdn_driver == -1) &&
+			    (info->isdn_channel == -1) &&
+			    (USG_NONE(dev->usage[idx]))) {
+				info->isdn_driver = di;
+				info->isdn_channel = ch;
+				info->drv_index = idx;
+				dev->m_idx[idx] = info->line;
+				dev->usage[idx] &= ISDN_USAGE_EXCLUSIVE;
+				dev->usage[idx] |= ISDN_USAGE_MODEM;
+				strcpy(dev->num[idx], nr);
+				isdn_info_update();
+				restore_flags(flags);
+				printk(KERN_INFO "isdn_tty: call from %s, -> RING on ttyI%d\n", nr,
+				       info->line);
+				return info->line;
+			}
+		}
+	}
+	printk(KERN_INFO "isdn_tty: call from %s -> %s %s\n", nr, eaz,
+	       dev->drv[di]->reject_bus ? "rejected" : "ignored");
+	restore_flags(flags);
+	return -1;
+}
+
+/*********************************************************************
+ Modem-Emulator-Routines
+ *********************************************************************/
+
+#define cmdchar(c) ((c>' ')&&(c<=0x7f))
+
+/*
+ * Put a message from the AT-emulator into receive-buffer of tty,
+ * convert CR, LF, and BS to values in modem-registers 3, 4 and 5.
+ */
+static void isdn_tty_at_cout(char *msg, modem_info * info)
+{
+	struct tty_struct *tty;
+	atemu *m = &(dev->mdm.atmodem[info->line]);
+	char *p;
+	char c;
+	ulong flags;
+
+	if (!msg) {
+		printk(KERN_WARNING "isdn: Null-Message in isdn_tty_at_cout\n");
+		return;
+	}
+	save_flags(flags);
+	cli();
+	tty = info->tty;
+	for (p = msg; *p; p++) {
+		switch (*p) {
+		case '\r':
+			c = m->mdmreg[3];
+			break;
+		case '\n':
+			c = m->mdmreg[4];
+			break;
+		case '\b':
+			c = m->mdmreg[5];
+			break;
+		default:
+			c = *p;
+		}
+		if ((info->flags & ISDN_ASYNC_CLOSING) || (!tty)) {
+			restore_flags(flags);
+			return;
+		}
+		if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+			break;
+		tty_insert_flip_char(tty, c, 0);
+	}
+	restore_flags(flags);
+	queue_task(&tty->flip.tqueue, &tq_timer);
+}
+
+/*
+ * Perform ATH Hangup
+ */
+static void isdn_tty_on_hook(modem_info * info)
+{
+	if (info->isdn_channel >= 0) {
+#ifdef ISDN_DEBUG_MODEM_HUP
+		printk(KERN_DEBUG "Mhup in isdn_tty_on_hook\n");
+#endif
+		isdn_tty_modem_hup(info);
+		isdn_tty_modem_result(3, info);
+	}
+}
+
+static void isdn_tty_off_hook(void)
+{
+	printk(KERN_DEBUG "isdn_tty_off_hook\n");
+}
+
+#define PLUSWAIT1 (HZ/2)	/* 0.5 sec. */
+#define PLUSWAIT2 (HZ*3/2)	/* 1.5 sec */
+
+/*
+ * Check Buffer for Modem-escape-sequence, activate timer-callback to
+ * isdn_tty_modem_escape() if sequence found.
+ *
+ * Parameters:
+ *   p          pointer to databuffer
+ *   plus       escape-character
+ *   count      length of buffer
+ *   pluscount  count of valid escape-characters so far
+ *   lastplus   timestamp of last character
+ */
+static void isdn_tty_check_esc(const u_char * p, u_char plus, int count, int *pluscount,
+			   int *lastplus, int from_user)
+{
+	char cbuf[3];
+
+	if (plus > 127)
+		return;
+	if (count > 3) {
+		p += count - 3;
+		count = 3;
+		*pluscount = 0;
+	}
+	if (from_user) {
+		memcpy_fromfs(cbuf, p, count);
+		p = cbuf;
+	}
+	while (count > 0) {
+		if (*(p++) == plus) {
+			if ((*pluscount)++) {
+				/* Time since last '+' > 0.5 sec. ? */
+				if ((jiffies - *lastplus) > PLUSWAIT1)
+					*pluscount = 1;
+			} else {
+				/* Time since last non-'+' < 1.5 sec. ? */
+				if ((jiffies - *lastplus) < PLUSWAIT2)
+					*pluscount = 0;
+			}
+			if ((*pluscount == 3) && (count = 1))
+				isdn_timer_ctrl(ISDN_TIMER_MODEMPLUS, 1);
+			if (*pluscount > 3)
+				*pluscount = 1;
+		} else
+			*pluscount = 0;
+		*lastplus = jiffies;
+		count--;
+	}
+}
+
+/*
+ * Return result of AT-emulator to tty-receive-buffer, depending on
+ * modem-register 12, bit 0 and 1.
+ * For CONNECT-messages also switch to online-mode.
+ * For RING-message handle auto-ATA if register 0 != 0
+ */
+void isdn_tty_modem_result(int code, modem_info * info)
+{
+	atemu *m = &dev->mdm.atmodem[info->line];
+	static char *msg[] =
+	{"OK", "CONNECT", "RING", "NO CARRIER", "ERROR",
+	 "CONNECT 64000", "NO DIALTONE", "BUSY", "NO ANSWER",
+	 "RINGING", "NO MSN/EAZ"};
+	ulong flags;
+	char s[4];
+
+	switch (code) {
+	case 2:
+		m->mdmreg[1]++;	/* RING */
+		if (m->mdmreg[1] == m->mdmreg[0]) {
+			/* Accept incoming call */
+			isdn_ctrl cmd;
+			m->mdmreg[1] = 0;
+			dev->mdm.msr[info->line] &= ~UART_MSR_RI;
+			cmd.driver = info->isdn_driver;
+			cmd.arg = info->isdn_channel;
+			cmd.command = ISDN_CMD_ACCEPTD;
+			dev->drv[info->isdn_driver]->interface->command(&cmd);
+		}
+		break;
+	case 3:
+		/* NO CARRIER */
+		save_flags(flags);
+		cli();
+		dev->mdm.msr[info->line] &= ~(UART_MSR_DCD | UART_MSR_RI);
+		if ((info->flags & ISDN_ASYNC_CLOSING) || (!info->tty)) {
+			restore_flags(flags);
+			return;
+		}
+		restore_flags(flags);
+		break;
+	case 1:
+	case 5:
+		dev->mdm.online[info->line] = 1;
+		break;
+	}
+	if (m->mdmreg[12] & 1) {
+		/* Show results */
+		isdn_tty_at_cout("\r\n", info);
+		if (m->mdmreg[12] & 2) {
+			/* Show numeric results */
+			sprintf(s, "%d", code);
+			isdn_tty_at_cout(s, info);
+		} else {
+			if (code == 2) {
+				isdn_tty_at_cout("CALLER NUMBER: ", info);
+				isdn_tty_at_cout(dev->num[info->drv_index], info);
+				isdn_tty_at_cout("\r\n", info);
+			}
+			isdn_tty_at_cout(msg[code], info);
+			if (code == 5) {
+				/* Append Protocol to CONNECT message */
+				isdn_tty_at_cout((m->mdmreg[14] != 3) ? "/X.75" : "/HDLC", info);
+				if (m->mdmreg[13] & 2)
+					isdn_tty_at_cout("/T.70", info);
+			}
+		}
+		isdn_tty_at_cout("\r\n", info);
+	}
+	if (code == 3) {
+		save_flags(flags);
+		cli();
+		if ((info->flags & ISDN_ASYNC_CLOSING) || (!info->tty)) {
+			restore_flags(flags);
+			return;
+		}
+		if ((info->flags & ISDN_ASYNC_CHECK_CD) &&
+		    (!((info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) &&
+		       (info->flags & ISDN_ASYNC_CALLOUT_NOHUP))))
+			tty_hangup(info->tty);
+		restore_flags(flags);
+	}
+}
+
+/*
+ * Display a modem-register-value.
+ */
+static void isdn_tty_show_profile(int ridx, modem_info * info)
+{
+	char v[6];
+
+	sprintf(v, "%d\r\n", dev->mdm.atmodem[info->line].mdmreg[ridx]);
+	isdn_tty_at_cout(v, info);
+}
+
+/*
+ * Get MSN-string from char-pointer, set pointer to end of number
+ */
+static void isdn_tty_get_msnstr(char *n, char **p)
+{
+	while ((*p[0] >= '0' && *p[0] <= '9') || (*p[0] == ','))
+		*n++ = *p[0]++;
+	*n = '\0';
+}
+
+/*
+ * Get phone-number from modem-commandbuffer
+ */
+static void isdn_tty_getdial(char *p, char *q)
+{
+	int first = 1;
+
+	while (strchr("0123456789,#.*WPTS-", *p) && *p) {
+		if ((*p >= '0' && *p <= '9') || ((*p == 'S') && first))
+			*q++ = *p;
+		p++;
+		first = 0;
+	}
+	*q = 0;
+}
+
+/*
+ * Parse and perform an AT-command-line.
+ *
+ * Parameter:
+ *   channel   index to line (minor-device)
+ */
+static void isdn_tty_parse_at(modem_info * info)
+{
+	atemu *m = &dev->mdm.atmodem[info->line];
+	char *p;
+	int mreg;
+	int mval;
+	int i;
+	char rb[100];
+	char ds[40];
+	isdn_ctrl cmd;
+
+#ifdef ISDN_DEBUG_AT
+	printk(KERN_DEBUG "AT: '%s'\n", m->mdmcmd);
+#endif
+	for (p = &m->mdmcmd[2]; *p;) {
+		switch (*p) {
+		case 'A':
+			/* A - Accept incoming call */
+			p++;
+			if (m->mdmreg[1]) {
+#define FIDOBUG
+#ifdef FIDOBUG
+/* Variables fido... defined temporarily for finding a strange bug */
+				driver *fido_drv;
+				isdn_if *fido_if;
+				int fido_isdn_driver;
+				modem_info *fido_modem_info;
+				int (*fido_command) (isdn_ctrl *);
+#endif
+				/* Accept incoming call */
+				m->mdmreg[1] = 0;
+				dev->mdm.msr[info->line] &= ~UART_MSR_RI;
+				cmd.driver = info->isdn_driver;
+				cmd.command = ISDN_CMD_SETL2;
+				cmd.arg = info->isdn_channel + (m->mdmreg[14] << 8);
+				dev->drv[info->isdn_driver]->interface->command(&cmd);
+				cmd.driver = info->isdn_driver;
+				cmd.command = ISDN_CMD_SETL3;
+				cmd.arg = info->isdn_channel + (m->mdmreg[15] << 8);
+				dev->drv[info->isdn_driver]->interface->command(&cmd);
+				cmd.driver = info->isdn_driver;
+				cmd.arg = info->isdn_channel;
+				cmd.command = ISDN_CMD_ACCEPTD;
+#ifdef FIDOBUG
+				fido_modem_info = info;
+				fido_isdn_driver = fido_modem_info->isdn_driver;
+				fido_drv = dev->drv[fido_isdn_driver];
+				fido_if = fido_drv->interface;
+				fido_command = fido_if->command;
+				fido_command(&cmd);
+#else
+				dev->drv[info->isdn_driver]->interface->command(&cmd);
+#endif
+			} else {
+				isdn_tty_modem_result(8, info);
+				return;
+			}
+			break;
+		case 'D':
+			/* D - Dial */
+			isdn_tty_getdial(++p, ds);
+			p += strlen(p);
+			if (!strlen(m->msn))
+				isdn_tty_modem_result(10, info);
+			else if (strlen(ds))
+				isdn_tty_dial(ds, info, m);
+			else
+				isdn_tty_modem_result(4, info);
+			return;
+		case 'E':
+			/* E - Turn Echo on/off */
+			p++;
+			switch (*p) {
+			case '0':
+				p++;
+				m->mdmreg[12] &= ~4;
+				break;
+			case '1':
+				p++;
+				m->mdmreg[12] |= 4;
+				break;
+			default:
+				isdn_tty_modem_result(4, info);
+				return;
+			}
+			break;
+		case 'H':
+			/* H - On/Off-hook */
+			p++;
+			switch (*p) {
+			case '0':
+				p++;
+				isdn_tty_on_hook(info);
+				break;
+			case '1':
+				p++;
+				isdn_tty_off_hook();
+				break;
+			default:
+				isdn_tty_on_hook(info);
+				break;
+			}
+			break;
+		case 'I':
+			/* I - Information */
+			p++;
+			isdn_tty_at_cout("ISDN for Linux  (c) by Fritz Elfert\r\n", info);
+			switch (*p) {
+			case '0':
+			case '1':
+				p++;
+				break;
+			default:
+			}
+			break;
+		case 'O':
+			/* O - Go online */
+			p++;
+			if (dev->mdm.msr[info->line] & UART_MSR_DCD)	/* if B-Channel is up */
+				isdn_tty_modem_result(5, info);
+			else
+				isdn_tty_modem_result(3, info);
+			return;
+		case 'Q':
+			/* Q - Turn Emulator messages on/off */
+			p++;
+			switch (*p) {
+			case '0':
+				p++;
+				m->mdmreg[12] |= 1;
+				break;
+			case '1':
+				p++;
+				m->mdmreg[12] &= ~1;
+				break;
+			default:
+				isdn_tty_modem_result(4, info);
+				return;
+			}
+			break;
+		case 'S':
+			/* S - Set/Get Register */
+			p++;
+			mreg = isdn_getnum(&p);
+			if (mreg < 0 || mreg > ISDN_MODEM_ANZREG) {
+				isdn_tty_modem_result(4, info);
+				return;
+			}
+			switch (*p) {
+			case '=':
+				p++;
+				mval = isdn_getnum(&p);
+				if (mval >= 0 && mval <= 255) {
+					if ((mreg == 16) && ((mval * 16) > ISDN_SERIAL_XMIT_SIZE)) {
+						isdn_tty_modem_result(4, info);
+						return;
+					}
+					m->mdmreg[mreg] = mval;
+				} else {
+					isdn_tty_modem_result(4, info);
+					return;
+				}
+				break;
+			case '?':
+				p++;
+				isdn_tty_show_profile(mreg, info);
+				return;
+				break;
+			default:
+				isdn_tty_modem_result(4, info);
+				return;
+			}
+			break;
+		case 'V':
+			/* V - Numeric or ASCII Emulator-messages */
+			p++;
+			switch (*p) {
+			case '0':
+				p++;
+				m->mdmreg[12] |= 2;
+				break;
+			case '1':
+				p++;
+				m->mdmreg[12] &= ~2;
+				break;
+			default:
+				isdn_tty_modem_result(4, info);
+				return;
+			}
+			break;
+		case 'Z':
+			/* Z - Load Registers from Profile */
+			p++;
+			isdn_tty_modem_reset_regs(m, 1);
+			break;
+		case '+':
+			p++;
+			switch (*p) {
+			case 'F':
+				break;
+			}
+			break;
+		case '&':
+			p++;
+			switch (*p) {
+			case 'B':
+				/* &B - Set Buffersize */
+				p++;
+				i = isdn_getnum(&p);
+				if ((i < 0) || (i > ISDN_SERIAL_XMIT_SIZE)) {
+					isdn_tty_modem_result(4, info);
+					return;
+				}
+				m->mdmreg[16] = i / 16;
+				break;
+			case 'D':
+				/* &D - Set DCD-Low-behavior */
+				p++;
+				switch (isdn_getnum(&p)) {
+				case 2:
+					m->mdmreg[12] &= ~32;
+					break;
+				case 3:
+					m->mdmreg[12] |= 32;
+					break;
+				default:
+					isdn_tty_modem_result(4, info);
+					return;
+				}
+				break;
+			case 'E':
+				/* &E -Set EAZ/MSN */
+				p++;
+				isdn_tty_get_msnstr(m->msn, &p);
+				break;
+			case 'F':
+				/* &F -Set Factory-Defaults */
+				p++;
+				isdn_tty_reset_profile(m);
+				isdn_tty_modem_reset_regs(m, 1);
+				break;
+			case 'S':
+				/* &S - Set Windowsize */
+				p++;
+				i = isdn_getnum(&p);
+				if ((i > 0) && (i < 9))
+					m->mdmreg[17] = i;
+				else {
+					isdn_tty_modem_result(4, info);
+					return;
+				}
+				break;
+			case 'V':
+				/* &V - Show registers */
+				p++;
+				for (i = 0; i < ISDN_MODEM_ANZREG; i++) {
+					sprintf(rb, "S%d=%d%s", i, m->mdmreg[i], (i == 6) ? "\r\n" : " ");
+					isdn_tty_at_cout(rb, info);
+				}
+				sprintf(rb, "\r\nEAZ/MSN: %s\r\n", strlen(m->msn) ? m->msn : "None");
+				isdn_tty_at_cout(rb, info);
+				break;
+			case 'W':
+				/* &W - Write Profile */
+				p++;
+				switch (*p) {
+				case '0':
+					p++;
+					modem_write_profile(m);
+					break;
+				default:
+					isdn_tty_modem_result(4, info);
+					return;
+				}
+				break;
+			case 'X':
+				/* &X - Switch to BTX-Mode */
+				p++;
+				switch (*p) {
+				case '0':
+					p++;
+					m->mdmreg[13] &= ~2;
+					break;
+				case '1':
+					p++;
+					m->mdmreg[13] |= 2;
+					m->mdmreg[14] = 0;
+					m->mdmreg[16] = 7;
+					m->mdmreg[18] = 7;
+					m->mdmreg[19] = 0;
+					break;
+				default:
+					isdn_tty_modem_result(4, info);
+					return;
+				}
+				break;
+			default:
+				isdn_tty_modem_result(4, info);
+				return;
+			}
+			break;
+		default:
+			isdn_tty_modem_result(4, info);
+			return;
+		}
+	}
+	isdn_tty_modem_result(0, info);
+}
+
+/* Need own toupper() because standard-toupper is not available
+ * within modules.
+ */
+#define my_toupper(c) (((c>='a')&&(c<='z'))?(c&0xdf):c)
+
+/*
+ * Perform line-editing of AT-commands
+ *
+ * Parameters:
+ *   p        inputbuffer
+ *   count    length of buffer
+ *   channel  index to line (minor-device)
+ *   user     flag: buffer is in userspace
+ */
+static int isdn_tty_edit_at(const char *p, int count, modem_info * info, int user)
+{
+	atemu *m = &dev->mdm.atmodem[info->line];
+	int total = 0;
+	u_char c;
+	char eb[2];
+	int cnt;
+
+	for (cnt = count; cnt > 0; p++, cnt--) {
+		if (user)
+			c = get_fs_byte(p);
+		else
+			c = *p;
+		total++;
+		if (c == m->mdmreg[3] || c == m->mdmreg[4]) {
+			/* Separator (CR oder LF) */
+			m->mdmcmd[m->mdmcmdl] = 0;
+			if (m->mdmreg[12] & 4) {
+				eb[0] = c;
+				eb[1] = 0;
+				isdn_tty_at_cout(eb, info);
+			}
+			if (m->mdmcmdl >= 2)
+				isdn_tty_parse_at(info);
+			m->mdmcmdl = 0;
+			continue;
+		}
+		if (c == m->mdmreg[5] && m->mdmreg[5] < 128) {
+			/* Backspace-Funktion */
+			if ((m->mdmcmdl > 2) || (!m->mdmcmdl)) {
+				if (m->mdmcmdl)
+					m->mdmcmdl--;
+				if (m->mdmreg[12] & 4)
+					isdn_tty_at_cout("\b", info);
+			}
+			continue;
+		}
+		if (cmdchar(c)) {
+			if (m->mdmreg[12] & 4) {
+				eb[0] = c;
+				eb[1] = 0;
+				isdn_tty_at_cout(eb, info);
+			}
+			if (m->mdmcmdl < 255) {
+				c = my_toupper(c);
+				switch (m->mdmcmdl) {
+				case 0:
+					if (c == 'A')
+						m->mdmcmd[m->mdmcmdl++] = c;
+					break;
+				case 1:
+					if (c == 'T')
+						m->mdmcmd[m->mdmcmdl++] = c;
+					break;
+				default:
+					m->mdmcmd[m->mdmcmdl++] = c;
+				}
+			}
+		}
+	}
+	return total;
+}
+
+/*
+ * Switch all modem-channels who are online and got a valid
+ * escape-sequence 1.5 seconds ago, to command-mode.
+ * This function is called every second via timer-interrupt from within 
+ * timer-dispatcher isdn_timer_function()
+ */
+void isdn_tty_modem_escape(void)
+{
+	int ton = 0;
+	int i;
+	int midx;
+
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+		if (USG_MODEM(dev->usage[i]))
+			if ((midx = dev->m_idx[i]) >= 0)
+				if (dev->mdm.online[midx]) {
+					ton = 1;
+					if ((dev->mdm.atmodem[midx].pluscount == 3) &&
+					    ((jiffies - dev->mdm.atmodem[midx].lastplus) > PLUSWAIT2)) {
+						dev->mdm.atmodem[midx].pluscount = 0;
+						dev->mdm.online[midx] = 0;
+						isdn_tty_modem_result(0, &dev->mdm.info[midx]);
+					}
+				}
+	isdn_timer_ctrl(ISDN_TIMER_MODEMPLUS, ton);
+}
+
+/*
+ * Put a RING-message to all modem-channels who have the RI-bit set.
+ * This function is called every second via timer-interrupt from within 
+ * timer-dispatcher isdn_timer_function()
+ */
+void isdn_tty_modem_ring(void)
+{
+	int ton = 0;
+	int i;
+	int midx;
+
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+		if (USG_MODEM(dev->usage[i]))
+			if ((midx = dev->m_idx[i]) >= 0)
+				if (dev->mdm.msr[midx] & UART_MSR_RI) {
+					ton = 1;
+					isdn_tty_modem_result(2, &dev->mdm.info[midx]);
+				}
+	isdn_timer_ctrl(ISDN_TIMER_MODEMRING, ton);
+}
+
+void isdn_tty_modem_xmit(void)
+{
+	int ton = 0;
+	int i;
+	int midx;
+	char *bufptr;
+	int buflen;
+
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+		if (USG_MODEM(dev->usage[i]))
+			if ((midx = dev->m_idx[i]) >= 0)
+				if (dev->mdm.online[midx]) {
+					modem_info *info = &(dev->mdm.info[midx]);
+					ulong flags;
+
+					save_flags(flags);
+					cli();
+					if (info->xmit_count > 0) {
+						struct tty_struct *tty = info->tty;
+						ton = 1;
+#if 0
+						printk(KERN_DEBUG "WB2: %d\n", info->xmit_count);
+#endif
+						bufptr = info->xmit_buf;
+						buflen = info->xmit_count;
+						if (dev->mdm.atmodem[midx].mdmreg[13] & 2) {
+							/* Add T.70 simplyfied header */
+#ifdef ISDN_DEBUG_MODEM_DUMP
+							isdn_dumppkt("T70pack3:", bufptr, buflen, 40);
+#endif
+							bufptr -= 4;
+							buflen += 4;
+							memcpy(bufptr, "\1\0\1\0", 4);
+#ifdef ISDN_DEBUG_MODEM_DUMP
+							isdn_dumppkt("T70pack4:", bufptr, buflen, 40);
+#endif
+						}
+						if (dev->drv[info->isdn_driver]->interface->
+						    writebuf(info->isdn_driver, info->isdn_channel, bufptr, buflen, 0) > 0) {
+							info->xmit_count = 0;
+							info->xmit_size = dev->mdm.atmodem[midx].mdmreg[16] * 16;
+#if FUTURE
+							info->send_outstanding++;
+							dev->mdm.msr[midx] &= ~UART_MSR_CTS;
+#endif
+							if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+							    tty->ldisc.write_wakeup)
+								(tty->ldisc.write_wakeup) (tty);
+							wake_up_interruptible(&tty->write_wait);
+						}
+					}
+					restore_flags(flags);
+				}
+	isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, ton);
+}
+
+#if FUTURE
+/*
+ * A packet has been output successfully.
+ * Search the tty-devices for an aproppriate device, decrement it's
+ * counter for outstanding packets, and set CTS if this counter reaches 0.
+ */
+void isdn_tty_bsent(int drv, int chan)
+{
+	int i;
+	ulong flags;
+
+	save_flags(flags);
+	cli();
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		modem_info *info = &dev->mdm.info[i];
+		if ((info->isdn_driver == drv) &&
+		    (info->isdn_channel == chan) &&
+		    (info->send_outstanding)) {
+			if (!(--info->send_outstanding))
+				dev->mdm.msr[i] |= UART_MSR_CTS;
+			restore_flags(flags);
+			return;
+		}
+	}
+	restore_flags(flags);
+	return;
+}
+#endif				/* FUTURE */
diff -u --recursive --new-file v1.3.68/linux/drivers/isdn/isdn_tty.h linux/drivers/isdn/isdn_tty.h
--- v1.3.68/linux/drivers/isdn/isdn_tty.h	Thu Jan  1 02:00:00 1970
+++ linux/drivers/isdn/isdn_tty.h	Mon Feb 26 11:58:05 1996
@@ -0,0 +1,39 @@
+/* $Id: isdn_tty.h,v 1.1 1996/01/10 21:39:22 fritz Exp fritz $
+ *
+ * header for Linux ISDN subsystem, tty related functions (linklevel).
+ *
+ * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * 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_tty.h,v $
+ * Revision 1.1  1996/01/10 21:39:22  fritz
+ * Initial revision
+ *
+ */
+
+extern void  isdn_tty_modem_result(int, modem_info *);
+extern void  isdn_tty_modem_escape(void);
+extern void  isdn_tty_modem_ring(void);
+extern void  isdn_tty_modem_xmit(void);
+extern void  isdn_tty_modem_hup(modem_info *);
+extern int   isdn_tty_modem_init(void);
+extern void  isdn_tty_readmodem(void);
+extern int   isdn_tty_try_read(int, u_char *, int);
+extern int   isdn_tty_find_icall(int, int, char *);
+#if FUTURE
+extern void  isdn_tty_bsent(int, int);
+#endif
diff -u --recursive --new-file v1.3.68/linux/drivers/isdn/teles/Makefile linux/drivers/isdn/teles/Makefile
--- v1.3.68/linux/drivers/isdn/teles/Makefile	Thu Jan  1 02:00:00 1970
+++ linux/drivers/isdn/teles/Makefile	Mon Feb 26 11:58:05 1996
@@ -0,0 +1,17 @@
+L_OBJS :=
+M_OBJS :=
+O_OBJS := mod.o card.o config.o buffers.o tei.o isdnl2.o isdnl3.o \
+llglue.o q931.o callc.o fsm.o
+
+O_TARGET :=
+ifeq ($(CONFIG_ISDN_DRV_TELES),y)
+  O_TARGET += teles.o
+else
+  ifeq ($(CONFIG_ISDN_DRV_TELES),m)
+    O_TARGET += teles.o
+    M_OBJS += teles.o
+  endif
+endif
+
+include $(TOPDIR)/Rules.make
+ 
diff -u --recursive --new-file v1.3.68/linux/drivers/isdn/teles/buffers.c linux/drivers/isdn/teles/buffers.c
--- v1.3.68/linux/drivers/isdn/teles/buffers.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/isdn/teles/buffers.c	Mon Feb 26 11:58:05 1996
@@ -0,0 +1,315 @@
+#define __NO_VERSION__
+#include "teles.h"
+#include <linux/mm.h>
+#include <linux/malloc.h>
+
+
+void
+BufPoolInit(struct BufPool *bp, int order, int bpps,
+	    int maxpages)
+{
+#ifdef DEBUG_MAGIC
+	generateerror
+	    bp->magic = 010167;
+#endif
+
+#if 0
+	printk(KERN_DEBUG "BufPoolInit bp %x\n", bp);
+#endif
+
+	bp->freelist = NULL;
+	bp->pageslist = NULL;
+	bp->pageorder = order;
+	bp->pagescount = 0;
+	bp->bpps = bpps;
+	bp->bufsize = BUFFER_SIZE(order, bpps);
+	bp->maxpages = maxpages;
+}
+
+int
+BufPoolAdd(struct BufPool *bp, int priority)
+{
+	struct Pages   *ptr;
+	long            flags;
+	byte           *bptr;
+	int             i;
+	struct BufHeader *bh = NULL, *prev, *first;
+
+#if 0
+	printk(KERN_DEBUG "BufPoolAdd bp %x\n", bp);
+#endif
+
+	ptr = (struct Pages *) __get_free_pages(priority,bp->pageorder,~0);
+	if (!ptr) {
+		printk(KERN_WARNING "BufPoolAdd couldn't get pages!\n");
+		return (-1);
+	}
+#if 0
+	printk(KERN_DEBUG "Order %d pages allocated at %x\n", bp->pageorder, ptr);
+#endif
+
+	ptr->next = bp->pageslist;
+	bp->pageslist = ptr;
+	bp->pagescount++;
+
+	bptr = (byte *) ptr + sizeof(struct Pages *);
+
+	i = bp->bpps;
+	first = (struct BufHeader *) bptr;
+	prev = NULL;
+	while (i--) {
+		bh = (struct BufHeader *) bptr;
+#ifdef DEBUG_MAGIC
+		bh->magic = 020167;
+#endif
+		bh->next = prev;
+		prev = bh;
+		bh->bp = bp;
+		bptr += PART_SIZE(bp->pageorder, bp->bpps);
+	}
+
+	save_flags(flags);
+	cli();
+	first->next = bp->freelist;
+	bp->freelist = bh;
+	restore_flags(flags);
+	return (0);
+}
+
+void
+BufPoolFree(struct BufPool *bp)
+{
+	struct Pages   *p;
+
+#if 0
+	printk(KERN_DEBUG "BufPoolFree bp %x\n", bp);
+#endif
+
+	while (bp->pagescount--) {
+		p = bp->pageslist->next;
+		free_pages((unsigned long) bp->pageslist, bp->pageorder);
+#if 0
+		printk(KERN_DEBUG "Free pages %x order %d\n", bp->pageslist, bp->pageorder);
+#endif
+		bp->pageslist = p;
+	}
+}
+
+int
+BufPoolGet(struct BufHeader **bh,
+	   struct BufPool *bp, int priority, void *heldby, int where)
+{
+	long            flags;
+	int             i;
+
+#ifdef DEBUG_MAGIC
+	if (bp->magic != 010167) {
+		printk(KERN_DEBUG "BufPoolGet: not a BufHeader\n");
+		return (-1);
+	}
+#endif
+
+	save_flags(flags);
+	cli();
+	i = 0;
+	while (!0) {
+		if (bp->freelist) {
+			*bh = bp->freelist;
+			bp->freelist = bp->freelist->next;
+			(*bh)->heldby = heldby;
+			(*bh)->where = where;
+			restore_flags(flags);
+			return (0);
+		}
+		if ((i == 0) && (bp->pagescount < bp->maxpages)) {
+                        if (BufPoolAdd(bp, priority)) {
+                                restore_flags(flags);
+                                return -1;
+                        }
+			i++;
+		} else {
+			*bh = NULL;
+			restore_flags(flags);
+			return (-1);
+		}
+	}
+
+}
+
+void
+BufPoolRelease(struct BufHeader *bh)
+{
+	struct BufPool *bp;
+	long            flags;
+
+#ifdef DEBUG_MAGIC
+	if (bh->magic != 020167) {
+		printk(KERN_DEBUG "BufPoolRelease: not a BufHeader\n");
+		printk(KERN_DEBUG "called from %x\n", __builtin_return_address(0));
+		return;
+	}
+#endif
+
+	bp = bh->bp;
+
+#ifdef DEBUG_MAGIC
+	if (bp->magic != 010167) {
+		printk(KERN_DEBUG "BufPoolRelease: not a BufPool\n");
+		return;
+	}
+#endif
+
+	save_flags(flags);
+	cli();
+	bh->next = bp->freelist;
+	bp->freelist = bh;
+	restore_flags(flags);
+}
+
+void
+BufQueueLink(struct BufQueue *bq,
+	     struct BufHeader *bh)
+{
+	unsigned long   flags;
+
+	save_flags(flags);
+	cli();
+	if (!bq->head)
+		bq->head = bh;
+	if (bq->tail)
+		bq->tail->next = bh;
+	bq->tail = bh;
+	bh->next = NULL;
+	restore_flags(flags);
+}
+
+void
+BufQueueLinkFront(struct BufQueue *bq,
+		  struct BufHeader *bh)
+{
+	unsigned long   flags;
+
+	save_flags(flags);
+	cli();
+	bh->next = bq->head;
+	bq->head = bh;
+	if (!bq->tail)
+		bq->tail = bh;
+	restore_flags(flags);
+}
+
+int
+BufQueueUnlink(struct BufHeader **bh, struct BufQueue *bq)
+{
+	long            flags;
+
+	save_flags(flags);
+	cli();
+
+	if (bq->head) {
+		if (bq->tail == bq->head)
+			bq->tail = NULL;
+		*bh = bq->head;
+		bq->head = (*bh)->next;
+		restore_flags(flags);
+		return (0);
+	} else {
+		restore_flags(flags);
+		return (-1);
+	}
+}
+
+void
+BufQueueInit(struct BufQueue *bq)
+{
+#ifdef DEBUG_MAGIC
+	bq->magic = 030167;
+#endif
+	bq->head = NULL;
+	bq->tail = NULL;
+}
+
+void
+BufQueueRelease(struct BufQueue *bq)
+{
+	struct BufHeader *bh;
+
+	while (bq->head) {
+		BufQueueUnlink(&bh, bq);
+		BufPoolRelease(bh);
+	}
+}
+
+int
+BufQueueLength(struct BufQueue *bq)
+{
+	int             i = 0;
+	struct BufHeader *bh;
+
+	bh = bq->head;
+	while (bh) {
+		i++;
+		bh = bh->next;
+	}
+	return (i);
+}
+
+void
+BufQueueDiscard(struct BufQueue *q, int pr, void *heldby,
+		int releasetoo)
+{
+	long            flags;
+	struct BufHeader *sp;
+
+	save_flags(flags);
+	cli();
+
+	while (!0) {
+		sp = q->head;
+		if (!sp)
+			break;
+		if ((sp->primitive == pr) && (sp->heldby == heldby)) {
+			q->head = sp->next;
+			if (q->tail == sp)
+				q->tail = NULL;
+			if (releasetoo)
+				BufPoolRelease(sp);
+		} else
+			break;
+	}
+
+	sp = q->head;
+	if (sp)
+		while (sp->next) {
+			if ((sp->next->primitive == pr) && (sp->next->heldby == heldby)) {
+				if (q->tail == sp->next)
+					q->tail = sp;
+				if (releasetoo)
+					BufPoolRelease(sp->next);
+				sp->next = sp->next->next;
+			} else
+				sp = sp->next;
+		}
+	restore_flags(flags);
+}
+
+void
+Sfree(byte * ptr)
+{
+#if 0
+	printk(KERN_DEBUG "Sfree %x\n", ptr);
+#endif
+	kfree(ptr);
+}
+
+byte           *
+Smalloc(int size, int pr, char *why)
+{
+	byte           *p;
+
+	p = (byte *) kmalloc(size, pr);
+#if 0
+	printk(KERN_DEBUG "Smalloc %s size %d res %x\n", why, size, p);
+#endif
+	return (p);
+}
diff -u --recursive --new-file v1.3.68/linux/drivers/isdn/teles/callc.c linux/drivers/isdn/teles/callc.c
--- v1.3.68/linux/drivers/isdn/teles/callc.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/isdn/teles/callc.c	Mon Feb 26 11:58:05 1996
@@ -0,0 +1,1316 @@
+#define __NO_VERSION__
+#include "teles.h"
+
+extern struct IsdnCard cards[];
+extern int      nrcards;
+extern int      drid;
+extern isdn_if  iif;
+extern void     teles_mod_dec_use_count(void);
+extern void     teles_mod_inc_use_count(void);
+
+static int      init_ds(int chan, int incoming);
+static void     release_ds(int chan);
+static char    *strcpyupto(char *dest, char *src, char upto);
+
+static struct Fsm callcfsm =
+{NULL, 0, 0},   lcfsm =
+{NULL, 0, 0};
+
+struct Channel *chanlist;
+static int      chancount = 0;
+unsigned int    debugflags = 0;
+
+#define TMR_DCHAN_EST 2000
+
+static void
+stat_debug(struct Channel *chanp, char *s)
+{
+	char            tmp[100], tm[32];
+
+	jiftime(tm, jiffies);
+	sprintf(tmp, "%s Channel %d HL->LL %s\n", tm, chanp->chan, s);
+	teles_putstatus(tmp);
+}
+
+#ifdef DEFINED_BUT_NOT_USED
+static void
+stat_error(struct Channel *chanp, char *s)
+{
+        char            tmp[100];
+
+        sprintf(tmp, "Channel %d: %s\n", chanp->chan, s);
+        teles_putstatus(tmp);
+}
+#endif
+
+enum {
+        ST_NULL,           /*  0 inactive                                               */
+        ST_OUT,            /*  1 outgoing, awaiting SETUP confirm                       */
+        ST_CLEAR,          /*  2 call release, awaiting RELEASE confirm                 */
+        ST_OUT_W,          /*  3 outgoing, awaiting d-channel establishment             */
+        ST_REL_W,          /*  4 awaiting d-channel release                             */
+        ST_IN_W,           /*  5 incoming, awaiting d-channel establishment             */
+        ST_IN,             /*  6 incoming call received                                 */
+        ST_IN_SETUP,       /*  7 incoming, SETUP response sent                          */
+        ST_IN_DACT,        /*  8 incoming connected, no b-channel prot.                 */
+        ST_OUT_ESTB,       /* 10 outgoing connected, awaiting b-channel prot. estbl.    */
+        ST_ACTIVE,         /* 11 active, b channel prot. established                    */
+        ST_BC_HANGUP,      /* 12 call clear. (initiator), awaiting b channel prot. rel. */
+        ST_PRO_W,          /* 13 call clear. (initiator), DISCONNECT req. sent          */
+        ST_ANT_W,          /* 14 call clear. (receiver), awaiting DISCONNECT ind.       */
+        ST_DISC_BC_HANGUP, /*    d channel gone, wait for b channel deactivation        */
+        ST_D_ERR,          /*    d channel released while active                        */
+};
+
+#define STATE_COUNT (ST_D_ERR+1)
+
+static char    *strState[] =
+{
+        "ST_NULL",
+        "ST_OUT",
+        "ST_CLEAR",
+        "ST_OUT_W",
+        "ST_REL_W",
+        "ST_IN_W",
+        "ST_IN",
+        "ST_IN_SETUP",
+        "ST_IN_DACT",
+        "ST_OUT_ESTB",
+        "ST_ACTIVE",
+        "ST_BC_HANGUP",
+        "ST_PRO_W",
+        "ST_ANT_W",
+        "ST_DISC_BC_HANGUP",
+        "ST_D_ERR",
+};
+
+enum {
+        EV_DIAL,           /*  0 */
+        EV_SETUP_CNF,      /*  1 */
+        EV_ACCEPTB,        /*  2 */
+        EV_DISCONNECT_CNF, /*  5 */
+        EV_DISCONNECT_IND, /*  6 */
+        EV_RELEASE_CNF,    /*  7 */
+        EV_DLEST,          /*  8 */
+        EV_DLRL,           /*  9 */
+        EV_SETUP_IND,      /* 10 */
+        EV_RELEASE_IND,    /* 11 */
+        EV_ACCEPTD,        /* 12 */
+        EV_SETUP_CMPL_IND, /* 13 */
+        EV_BC_EST,         /* 14 */
+        EV_WRITEBUF,       /* 15 */
+        EV_DATAIN,         /* 16 */
+        EV_HANGUP,         /* 17 */
+        EV_BC_REL,         /* 18 */
+        EV_CINF,           /* 19 */
+};
+
+#define EVENT_COUNT (EV_CINF+1)
+
+static char    *strEvent[] =
+{
+        "EV_DIAL",
+        "EV_SETUP_CNF",
+        "EV_ACCEPTB",
+        "EV_DISCONNECT_CNF",
+        "EV_DISCONNECT_IND",
+        "EV_RELEASE_CNF",
+        "EV_DLEST",
+        "EV_DLRL",
+        "EV_SETUP_IND",
+        "EV_RELEASE_IND",
+        "EV_ACCEPTD",
+        "EV_SETUP_CMPL_IND",
+        "EV_BC_EST",
+        "EV_WRITEBUF",
+        "EV_DATAIN",
+        "EV_HANGUP",
+        "EV_BC_REL",
+        "EV_CINF",
+};
+
+enum {
+        ST_LC_NULL,
+        ST_LC_ACTIVATE_WAIT,
+        ST_LC_DELAY,
+        ST_LC_ESTABLISH_WAIT,
+        ST_LC_CONNECTED,
+        ST_LC_RELEASE_WAIT,
+};
+
+#define LC_STATE_COUNT (ST_LC_RELEASE_WAIT+1)
+
+static char    *strLcState[] =
+{
+        "ST_LC_NULL",
+        "ST_LC_ACTIVATE_WAIT",
+        "ST_LC_DELAY",
+        "ST_LC_ESTABLISH_WAIT",
+        "ST_LC_CONNECTED",
+        "ST_LC_RELEASE_WAIT",
+};
+
+enum {
+        EV_LC_ESTABLISH,
+        EV_LC_PH_ACTIVATE,
+        EV_LC_PH_DEACTIVATE,
+        EV_LC_DL_ESTABLISH,
+        EV_LC_TIMER,
+        EV_LC_DL_RELEASE,
+        EV_LC_RELEASE,
+};
+
+#define LC_EVENT_COUNT (EV_LC_RELEASE+1)
+
+static char    *strLcEvent[] =
+{
+        "EV_LC_ESTABLISH",
+        "EV_LC_PH_ACTIVATE",
+        "EV_LC_PH_DEACTIVATE",
+        "EV_LC_DL_ESTABLISH",
+        "EV_LC_TIMER",
+        "EV_LC_DL_RELEASE",
+        "EV_LC_RELEASE",
+};
+
+#define LC_D  0
+#define LC_B  1
+
+static int
+my_atoi(char *s)
+{
+        int             i, n;
+
+        n = 0;
+        if (!s)
+                return -1;
+        for (i = 0; *s >= '0' && *s <= '9'; i++, s++)
+                n = 10 * n + (*s - '0');
+        return n;
+}
+
+/*
+ * Dial out
+ */
+static void
+r1(struct FsmInst *fi, int event, void *arg)
+{
+        isdn_ctrl      *ic = arg;
+        struct Channel *chanp = fi->userdata;
+        char           *ptr;
+        char            sis[3];
+
+        /* Destination Phone-Number */
+        ptr = strcpyupto(chanp->para.called, ic->num, ',');
+        /* Source Phone-Number */
+        ptr = strcpyupto(chanp->para.calling, ptr + 1, ',');
+        if (!strcmp(chanp->para.calling, "0"))
+                chanp->para.calling[0] = '\0';
+
+        /* Service-Indicator 1 */
+        ptr = strcpyupto(sis, ptr + 1, ',');
+        chanp->para.info = my_atoi(sis);
+
+        /* Service-Indicator 2 */
+        ptr = strcpyupto(sis, ptr + 1, '\0');
+        chanp->para.info2 = my_atoi(sis);
+
+        chanp->l2_active_protocol = chanp->l2_protocol;
+        chanp->incoming = 0;
+        chanp->lc_b.l2_start = !0;
+
+        switch (chanp->l2_active_protocol) {
+          case (ISDN_PROTO_L2_X75I):
+                  chanp->lc_b.l2_establish = !0;
+                  break;
+          case (ISDN_PROTO_L2_HDLC):
+                  chanp->lc_b.l2_establish = 0;
+                  break;
+          default:
+                  printk(KERN_WARNING "r1 unknown protocol\n");
+                  break;
+        }
+
+        FsmChangeState(fi, ST_OUT_W);
+        FsmEvent(&chanp->lc_d.lcfi, EV_LC_ESTABLISH, NULL);
+}
+
+static void
+ll_hangup(struct Channel *chanp, int bchantoo)
+{
+        isdn_ctrl       ic;
+
+        if (bchantoo) {
+                if (chanp->debug & 1)
+                        stat_debug(chanp, "STAT_BHUP");
+                ic.driver = drid;
+                ic.command = ISDN_STAT_BHUP;
+                ic.arg = chanp->chan;
+                iif.statcallb(&ic);
+        }
+        if (chanp->debug & 1)
+                stat_debug(chanp, "STAT_DHUP");
+        ic.driver = drid;
+        ic.command = ISDN_STAT_DHUP;
+        ic.arg = chanp->chan;
+        iif.statcallb(&ic);
+}
+
+static void
+r2(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        chanp->is.l4.l4l3(&chanp->is, CC_RELEASE_REQ, NULL);
+
+        FsmChangeState(fi, ST_CLEAR);
+        ll_hangup(chanp, 0);
+}
+
+static void
+r3(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL);
+        FsmChangeState(fi, ST_REL_W);
+}
+
+static void
+r4(struct FsmInst *fi, int event, void *arg)
+{
+        FsmChangeState(fi, ST_NULL);
+}
+
+static void
+r5(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        chanp->para.callref = chanp->outcallref;
+
+        chanp->outcallref++;
+        if (chanp->outcallref == 128)
+                chanp->outcallref = 64;
+
+        chanp->is.l4.l4l3(&chanp->is, CC_SETUP_REQ, NULL);
+
+        FsmChangeState(fi, ST_OUT);
+}
+
+static void
+r6(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        FsmChangeState(fi, ST_IN_W);
+        FsmEvent(&chanp->lc_d.lcfi, EV_LC_ESTABLISH, NULL);
+}
+
+static void
+r7(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+        isdn_ctrl       ic;
+
+        chanp->is.l4.l4l3(&chanp->is, CC_ALERTING_REQ, NULL);
+
+        FsmChangeState(fi, ST_IN);
+
+        /*
+         * Report incoming calls only once to linklevel, use octet 3 of
+         * channel identification information element. (it's value
+         * is copied to chanp->para.bchannel in l3s12(), file isdnl3.c)
+         */
+        if (((chanp->chan & 1) + 1) & chanp->para.bchannel) {
+                if (chanp->debug & 1)
+                        stat_debug(chanp, "STAT_ICALL");
+                ic.driver = drid;
+                ic.command = ISDN_STAT_ICALL;
+                ic.arg = chanp->chan;
+                /*
+                 * No need to return "unknown" for calls without OAD,
+                 * cause that's handled in linklevel now (replaced by '0')
+                 */
+                sprintf(ic.num, "%s,%d,0,%s", chanp->para.calling, chanp->para.info,
+                        chanp->para.called);
+                iif.statcallb(&ic);
+        }
+}
+
+static void
+r8(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        FsmChangeState(fi, ST_IN_SETUP);
+        chanp->is.l4.l4l3(&chanp->is, CC_SETUP_RSP, NULL);
+
+}
+
+static void
+r9(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        FsmChangeState(fi, ST_IN_DACT);
+
+        chanp->l2_active_protocol = chanp->l2_protocol;
+        chanp->incoming = !0;
+        chanp->lc_b.l2_start = 0;
+
+        switch (chanp->l2_active_protocol) {
+          case (ISDN_PROTO_L2_X75I):
+                  chanp->lc_b.l2_establish = !0;
+                  break;
+          case (ISDN_PROTO_L2_HDLC):
+                  chanp->lc_b.l2_establish = 0;
+                  break;
+          default:
+                  printk(KERN_WARNING "r9 unknown protocol\n");
+                  break;
+        }
+
+        init_ds(chanp->chan, !0);
+
+        FsmEvent(&chanp->lc_b.lcfi, EV_LC_ESTABLISH, NULL);
+}
+
+static void
+r10(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        FsmChangeState(fi, ST_OUT_ESTB);
+
+        init_ds(chanp->chan, 0);
+        FsmEvent(&chanp->lc_b.lcfi, EV_LC_ESTABLISH, NULL);
+
+}
+
+static void
+r12(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+        isdn_ctrl       ic;
+
+        FsmChangeState(fi, ST_ACTIVE);
+        chanp->data_open = !0;
+
+        if (chanp->debug & 1)
+                stat_debug(chanp, "STAT_DCONN");
+        ic.driver = drid;
+        ic.command = ISDN_STAT_DCONN;
+        ic.arg = chanp->chan;
+        iif.statcallb(&ic);
+
+        if (chanp->debug & 1)
+                stat_debug(chanp, "STAT_BCONN");
+        ic.driver = drid;
+        ic.command = ISDN_STAT_BCONN;
+        ic.arg = chanp->chan;
+        iif.statcallb(&ic);
+}
+
+#ifdef DEFINED_BUT_NOT_USED
+static void
+prp(byte * p, int size)
+{
+        while (size--)
+                printk("%2x ", *p++);
+        printk("\n");
+}
+#endif
+
+static void
+r15(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        chanp->data_open = 0;
+        FsmChangeState(fi, ST_BC_HANGUP);
+        FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL);
+}
+
+static void
+r16(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        release_ds(chanp->chan);
+
+        FsmChangeState(fi, ST_PRO_W);
+        chanp->is.l4.l4l3(&chanp->is, CC_DISCONNECT_REQ, NULL);
+}
+
+static void
+r17(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        chanp->data_open = 0;
+        release_ds(chanp->chan);
+
+        FsmChangeState(fi, ST_ANT_W);
+}
+
+static void
+r18(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        FsmChangeState(fi, ST_REL_W);
+        FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL);
+
+        ll_hangup(chanp, !0);
+}
+
+static void
+r19(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        FsmChangeState(fi, ST_CLEAR);
+
+        chanp->is.l4.l4l3(&chanp->is, CC_RELEASE_REQ, NULL);
+
+        ll_hangup(chanp, !0);
+}
+
+static void
+r20(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        FsmChangeState(fi, ST_NULL);
+
+        ll_hangup(chanp, 0);
+}
+
+static void
+r21(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        chanp->data_open = 0;
+        FsmChangeState(fi, ST_DISC_BC_HANGUP);
+        FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL);
+}
+
+static void
+r22(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        release_ds(chanp->chan);
+
+        FsmChangeState(fi, ST_CLEAR);
+
+        chanp->is.l4.l4l3(&chanp->is, CC_RELEASE_REQ, NULL);
+
+        ll_hangup(chanp, !0);
+}
+
+static void
+r23(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        release_ds(chanp->chan);
+
+        FsmChangeState(fi, ST_PRO_W);
+        chanp->is.l4.l4l3(&chanp->is, CC_DISCONNECT_REQ, NULL);
+}
+
+static void
+r24(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        chanp->data_open = 0;
+        FsmChangeState(fi, ST_D_ERR);
+        FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL);
+}
+
+static void
+r25(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+
+        release_ds(chanp->chan);
+
+        FsmChangeState(fi, ST_NULL);
+
+        ll_hangup(chanp, !0);
+}
+
+static void
+r26(struct FsmInst *fi, int event, void *arg)
+{
+        struct Channel *chanp = fi->userdata;
+        isdn_ctrl       ic;
+
+
+        ic.driver = drid;
+        ic.command = ISDN_STAT_CINF;
+        ic.arg = chanp->chan;
+        sprintf(ic.num, "%d", chanp->para.chargeinfo);
+        iif.statcallb(&ic);
+}
+
+
+
+static struct FsmNode fnlist[] =
+{
+        {ST_NULL, EV_DIAL, r1},
+        {ST_OUT_W, EV_DLEST, r5},
+        {ST_OUT_W, EV_DLRL, r20},
+        {ST_OUT, EV_DISCONNECT_IND, r2},
+        {ST_CLEAR, EV_RELEASE_CNF, r3},
+        {ST_REL_W, EV_DLRL, r4},
+        {ST_NULL, EV_SETUP_IND, r6},
+        {ST_IN_W, EV_DLEST, r7},
+        {ST_IN, EV_RELEASE_IND, r3},
+        {ST_IN, EV_ACCEPTD, r8},
+        {ST_IN_SETUP, EV_SETUP_CMPL_IND, r9},
+        {ST_OUT, EV_SETUP_CNF, r10},
+        {ST_OUT_ESTB, EV_BC_EST, r12},
+        {ST_OUT_ESTB, EV_BC_REL, r23},
+        {ST_IN_DACT, EV_BC_EST, r12},
+        {ST_ACTIVE, EV_HANGUP, r15},
+        {ST_BC_HANGUP, EV_BC_REL, r16},
+        {ST_BC_HANGUP, EV_DISCONNECT_IND, r21},
+        {ST_ACTIVE, EV_BC_REL, r17},
+        {ST_ACTIVE, EV_DISCONNECT_IND, r21},
+        {ST_ACTIVE, EV_DLRL, r24},
+        {ST_ACTIVE, EV_CINF, r26},
+        {ST_PRO_W, EV_RELEASE_IND, r18},
+        {ST_ANT_W, EV_DISCONNECT_IND, r19},
+        {ST_DISC_BC_HANGUP, EV_BC_REL, r22},
+        {ST_D_ERR, EV_BC_REL, r25},
+};
+
+#define FNCOUNT (sizeof(fnlist)/sizeof(struct FsmNode))
+
+static void
+lc_r1(struct FsmInst *fi, int event, void *arg)
+{
+        struct LcFsm   *lf = fi->userdata;
+
+        FsmChangeState(fi, ST_LC_ACTIVATE_WAIT);
+        FsmAddTimer(&lf->act_timer, 1000, EV_LC_TIMER, NULL, 50);
+        lf->st->ma.manl1(lf->st, PH_ACTIVATE, NULL);
+
+}
+
+static void
+lc_r6(struct FsmInst *fi, int event, void *arg)
+{
+        struct LcFsm   *lf = fi->userdata;
+
+        FsmDelTimer(&lf->act_timer, 50);
+        FsmChangeState(fi, ST_LC_DELAY);
+        FsmAddTimer(&lf->act_timer, 40, EV_LC_TIMER, NULL, 51);
+}
+
+static void
+lc_r2(struct FsmInst *fi, int event, void *arg)
+{
+        struct LcFsm   *lf = fi->userdata;
+
+        if (lf->l2_establish) {
+                FsmChangeState(fi, ST_LC_ESTABLISH_WAIT);
+                if (lf->l2_start)
+                        lf->st->ma.manl2(lf->st, DL_ESTABLISH, NULL);
+        } else {
+                FsmChangeState(fi, ST_LC_CONNECTED);
+                lf->lccall(lf, LC_ESTABLISH, NULL);
+        }
+}
+
+static void
+lc_r3(struct FsmInst *fi, int event, void *arg)
+{
+        struct LcFsm   *lf = fi->userdata;
+
+        FsmChangeState(fi, ST_LC_CONNECTED);
+        lf->lccall(lf, LC_ESTABLISH, NULL);
+}
+
+static void
+lc_r4(struct FsmInst *fi, int event, void *arg)
+{
+        struct LcFsm   *lf = fi->userdata;
+
+        if (lf->l2_establish) {
+                FsmChangeState(fi, ST_LC_RELEASE_WAIT);
+                lf->st->ma.manl2(lf->st, DL_RELEASE, NULL);
+        } else {
+                FsmChangeState(fi, ST_LC_NULL);
+                lf->st->ma.manl1(lf->st, PH_DEACTIVATE, NULL);
+                lf->lccall(lf, LC_RELEASE, NULL);
+        }
+}
+
+static void
+lc_r5(struct FsmInst *fi, int event, void *arg)
+{
+        struct LcFsm   *lf = fi->userdata;
+
+        FsmChangeState(fi, ST_LC_NULL);
+        lf->st->ma.manl1(lf->st, PH_DEACTIVATE, NULL);
+        lf->lccall(lf, LC_RELEASE, NULL);
+}
+
+static struct FsmNode LcFnList[] =
+{
+        {ST_LC_NULL, EV_LC_ESTABLISH, lc_r1},
+        {ST_LC_ACTIVATE_WAIT, EV_LC_PH_ACTIVATE, lc_r6},
+        {ST_LC_DELAY, EV_LC_TIMER, lc_r2},
+        {ST_LC_ESTABLISH_WAIT, EV_LC_DL_ESTABLISH, lc_r3},
+        {ST_LC_CONNECTED, EV_LC_RELEASE, lc_r4},
+        {ST_LC_CONNECTED, EV_LC_DL_RELEASE, lc_r5},
+        {ST_LC_RELEASE_WAIT, EV_LC_DL_RELEASE, lc_r5},
+        {ST_LC_ACTIVATE_WAIT, EV_LC_TIMER, lc_r5},
+        {ST_LC_ESTABLISH_WAIT, EV_LC_DL_RELEASE, lc_r5},
+};
+
+#define LC_FN_COUNT (sizeof(LcFnList)/sizeof(struct FsmNode))
+
+void
+CallcNew(void)
+{
+        callcfsm.state_count = STATE_COUNT;
+        callcfsm.event_count = EVENT_COUNT;
+        callcfsm.strEvent = strEvent;
+        callcfsm.strState = strState;
+        FsmNew(&callcfsm, fnlist, FNCOUNT);
+
+        lcfsm.state_count = LC_STATE_COUNT;
+        lcfsm.event_count = LC_EVENT_COUNT;
+        lcfsm.strEvent = strLcEvent;
+        lcfsm.strState = strLcState;
+        FsmNew(&lcfsm, LcFnList, LC_FN_COUNT);
+}
+
+void
+CallcFree(void)
+{
+        FsmFree(&lcfsm);
+        FsmFree(&callcfsm);
+}
+
+static void
+release_ds(int chan)
+{
+        struct PStack  *st = &chanlist[chan].ds;
+        struct IsdnCardState *sp;
+        struct HscxState *hsp;
+
+        sp = st->l1.hardware;
+        hsp = sp->hs + chanlist[chan].hscx;
+
+        close_hscxstate(hsp);
+
+        switch (chanlist[chan].l2_active_protocol) {
+          case (ISDN_PROTO_L2_X75I):
+                  releasestack_isdnl2(st);
+                  break;
+          case (ISDN_PROTO_L2_HDLC):
+                  releasestack_transl2(st);
+                  break;
+        }
+}
+
+static void
+cc_l1man(struct PStack *st, int pr, void *arg)
+{
+        struct Channel *chanp = (struct Channel *) st->l4.userdata;
+
+        switch (pr) {
+          case (PH_ACTIVATE):
+                  FsmEvent(&chanp->lc_d.lcfi, EV_LC_PH_ACTIVATE, NULL);
+                  break;
+          case (PH_DEACTIVATE):
+                  FsmEvent(&chanp->lc_d.lcfi, EV_LC_PH_DEACTIVATE, NULL);
+                  break;
+        }
+}
+
+static void
+cc_l2man(struct PStack *st, int pr, void *arg)
+{
+        struct Channel *chanp = (struct Channel *) st->l4.userdata;
+
+        switch (pr) {
+          case (DL_ESTABLISH):
+                  FsmEvent(&chanp->lc_d.lcfi, EV_LC_DL_ESTABLISH, NULL);
+                  break;
+          case (DL_RELEASE):
+                  FsmEvent(&chanp->lc_d.lcfi, EV_LC_DL_RELEASE, NULL);
+                  break;
+        }
+}
+
+static void
+dcc_l1man(struct PStack *st, int pr, void *arg)
+{
+        struct Channel *chanp = (struct Channel *) st->l4.userdata;
+
+        switch (pr) {
+          case (PH_ACTIVATE):
+                  FsmEvent(&chanp->lc_b.lcfi, EV_LC_PH_ACTIVATE, NULL);
+                  break;
+          case (PH_DEACTIVATE):
+                  FsmEvent(&chanp->lc_b.lcfi, EV_LC_PH_DEACTIVATE, NULL);
+                  break;
+        }
+}
+
+static void
+dcc_l2man(struct PStack *st, int pr, void *arg)
+{
+        struct Channel *chanp = (struct Channel *) st->l4.userdata;
+
+        switch (pr) {
+          case (DL_ESTABLISH):
+                  FsmEvent(&chanp->lc_b.lcfi, EV_LC_DL_ESTABLISH, NULL);
+                  break;
+          case (DL_RELEASE):
+                  FsmEvent(&chanp->lc_b.lcfi, EV_LC_DL_RELEASE, NULL);
+                  break;
+        }
+}
+
+static void
+ll_handler(struct PStack *st, int pr,
+           struct BufHeader *ibh)
+{
+        struct Channel *chanp = (struct Channel *) st->l4.userdata;
+
+        switch (pr) {
+          case (CC_DISCONNECT_IND):
+                  FsmEvent(&chanp->fi, EV_DISCONNECT_IND, NULL);
+                  break;
+          case (CC_RELEASE_CNF):
+                  FsmEvent(&chanp->fi, EV_RELEASE_CNF, NULL);
+                  break;
+          case (CC_SETUP_IND):
+                  FsmEvent(&chanp->fi, EV_SETUP_IND, NULL);
+                  break;
+          case (CC_RELEASE_IND):
+                  FsmEvent(&chanp->fi, EV_RELEASE_IND, NULL);
+                  break;
+          case (CC_SETUP_COMPLETE_IND):
+                  FsmEvent(&chanp->fi, EV_SETUP_CMPL_IND, NULL);
+                  break;
+          case (CC_SETUP_CNF):
+                  FsmEvent(&chanp->fi, EV_SETUP_CNF, NULL);
+                  break;
+          case (CC_INFO_CHARGE):
+                  FsmEvent(&chanp->fi, EV_CINF, NULL);
+                  break;
+        }
+}
+
+static void
+init_is(int chan, unsigned int ces)
+{
+        struct PStack  *st = &(chanlist[chan].is);
+        struct IsdnCardState *sp = chanlist[chan].sp;
+        char            tmp[128];
+
+        setstack_teles(st, sp);
+
+        st->l2.sap = 0;
+
+        st->l2.tei = 255;
+
+        st->l2.ces = ces;
+        st->l2.extended = !0;
+        st->l2.laptype = LAPD;
+        st->l2.window = 1;
+        st->l2.orig = !0;
+        st->l2.t200 = 1000;               /* 1000 milliseconds  */
+        if (st->protocol == ISDN_PTYPE_1TR6) {
+                st->l2.n200 = 3;          /* try 3 times        */
+                st->l2.t203 = 10000;      /* 10000 milliseconds */
+        } else {
+                st->l2.n200 = 4;          /* try 4 times        */
+                st->l2.t203 = 5000;       /* 5000 milliseconds  */
+        }
+
+        sprintf(tmp, "Channel %d q.921", chan);
+        setstack_isdnl2(st, tmp);
+        setstack_isdnl3(st);
+        st->l2.debug = 2;
+        st->l3.debug = 2;
+        st->l2.debug = 0xff;
+        st->l3.debug = 0xff;
+        st->l4.userdata = chanlist + chan;
+        st->l4.l2writewakeup = NULL;
+
+        st->l3.l3l4 = ll_handler;
+        st->l1.l1man = cc_l1man;
+        st->l2.l2man = cc_l2man;
+
+        st->pa = &chanlist[chan].para;
+        teles_addlist(sp, st);
+}
+
+static void
+callc_debug(struct FsmInst *fi, char *s)
+{
+        char            str[80], tm[32];
+        struct Channel *chanp = fi->userdata;
+
+        jiftime(tm, jiffies);
+        sprintf(str, "%s Channel %d callc %s\n", tm, chanp->chan, s);
+        teles_putstatus(str);
+}
+
+static void
+lc_debug(struct FsmInst *fi, char *s)
+{
+        char            str[256], tm[32];
+        struct LcFsm   *lf = fi->userdata;
+
+        jiftime(tm, jiffies);
+        sprintf(str, "%s Channel %d lc %s\n", tm, lf->ch->chan, s);
+        teles_putstatus(str);
+}
+
+static void
+dlc_debug(struct FsmInst *fi, char *s)
+{
+        char            str[256], tm[32];
+        struct LcFsm   *lf = fi->userdata;
+
+        jiftime(tm, jiffies);
+        sprintf(str, "%s Channel %d dlc %s\n", tm, lf->ch->chan, s);
+        teles_putstatus(str);
+}
+
+static void
+lccall_d(struct LcFsm *lf, int pr, void *arg)
+{
+        struct Channel *chanp = lf->ch;
+
+        switch (pr) {
+          case (LC_ESTABLISH):
+                  FsmEvent(&chanp->fi, EV_DLEST, NULL);
+                  break;
+          case (LC_RELEASE):
+                  FsmEvent(&chanp->fi, EV_DLRL, NULL);
+                  break;
+        }
+}
+
+static void
+lccall_b(struct LcFsm *lf, int pr, void *arg)
+{
+        struct Channel *chanp = lf->ch;
+
+        switch (pr) {
+          case (LC_ESTABLISH):
+                  FsmEvent(&chanp->fi, EV_BC_EST, NULL);
+                  break;
+          case (LC_RELEASE):
+                  FsmEvent(&chanp->fi, EV_BC_REL, NULL);
+                  break;
+        }
+}
+
+static void
+init_chan(int chan, int cardnr, int hscx,
+          unsigned int ces)
+{
+        struct IsdnCard *card = cards + cardnr;
+        struct Channel *chanp = chanlist + chan;
+
+        chanp->sp = card->sp;
+        chanp->hscx = hscx;
+        chanp->chan = chan;
+        chanp->incoming = 0;
+        chanp->debug = 0;
+        init_is(chan, ces);
+
+        chanp->fi.fsm = &callcfsm;
+        chanp->fi.state = ST_NULL;
+        chanp->fi.debug = 0;
+        chanp->fi.userdata = chanp;
+        chanp->fi.printdebug = callc_debug;
+
+        chanp->lc_d.lcfi.fsm = &lcfsm;
+        chanp->lc_d.lcfi.state = ST_LC_NULL;
+        chanp->lc_d.lcfi.debug = 0;
+        chanp->lc_d.lcfi.userdata = &chanp->lc_d;
+        chanp->lc_d.lcfi.printdebug = lc_debug;
+        chanp->lc_d.type = LC_D;
+        chanp->lc_d.ch = chanp;
+        chanp->lc_d.st = &chanp->is;
+        chanp->lc_d.l2_establish = !0;
+        chanp->lc_d.l2_start = !0;
+        chanp->lc_d.lccall = lccall_d;
+        FsmInitTimer(&chanp->lc_d.lcfi, &chanp->lc_d.act_timer);
+
+        chanp->lc_b.lcfi.fsm = &lcfsm;
+        chanp->lc_b.lcfi.state = ST_LC_NULL;
+        chanp->lc_b.lcfi.debug = 0;
+        chanp->lc_b.lcfi.userdata = &chanp->lc_b;
+        chanp->lc_b.lcfi.printdebug = dlc_debug;
+        chanp->lc_b.type = LC_B;
+        chanp->lc_b.ch = chanp;
+        chanp->lc_b.st = &chanp->ds;
+        chanp->lc_b.l2_establish = !0;
+        chanp->lc_b.l2_start = !0;
+        chanp->lc_b.lccall = lccall_b;
+        FsmInitTimer(&chanp->lc_b.lcfi, &chanp->lc_b.act_timer);
+
+        chanp->outcallref = 64;
+        chanp->data_open = 0;
+}
+
+int
+CallcNewChan(void)
+{
+        int             i, ces, c;
+
+        chancount = 0;
+        for (i = 0; i < nrcards; i++)
+                if (cards[i].sp)
+                        chancount += 2;
+
+        chanlist = (struct Channel *) Smalloc(sizeof(struct Channel) *
+                                      chancount, GFP_KERNEL, "chanlist");
+
+        c = 0;
+        ces = randomces();
+        for (i = 0; i < nrcards; i++)
+                if (cards[i].sp) {
+                        init_chan(c++, i, 1, ces++);
+                        ces %= 0xffff;
+                        init_chan(c++, i, 0, ces++);
+                        ces %= 0xffff;
+                }
+        printk(KERN_INFO "channels %d\n", chancount);
+        return (chancount);
+
+}
+
+static void
+release_is(int chan)
+{
+        struct PStack  *st = &chanlist[chan].is;
+
+        releasestack_isdnl2(st);
+        teles_rmlist(st->l1.hardware, st);
+        BufQueueRelease(&st->l2.i_queue);
+}
+
+static void
+release_chan(int chan)
+{
+#if 0
+        release_ds(chan);
+#endif
+        release_is(chan);
+}
+
+void
+CallcFreeChan(void)
+{
+        int             i;
+
+        for (i = 0; i < chancount; i++)
+                release_chan(i);
+        Sfree((void *) chanlist);
+}
+
+static void
+lldata_handler(struct PStack *st, int pr,
+               void *arg)
+{
+        struct Channel *chanp = (struct Channel *) st->l4.userdata;
+        byte           *ptr;
+        int             size;
+        struct BufHeader *ibh = arg;
+
+        switch (pr) {
+          case (DL_DATA):
+                  if (chanp->data_open) {
+                          ptr = DATAPTR(ibh);
+                          ptr += chanp->ds.l2.ihsize;
+                          size = ibh->datasize - chanp->ds.l2.ihsize;
+                          iif.rcvcallb(drid, chanp->chan, ptr, size);
+                  }
+                  BufPoolRelease(ibh);
+                  break;
+          default:
+                  printk(KERN_WARNING "lldata_handler unknown primitive\n");
+                  break;
+        }
+}
+
+static void
+lltrans_handler(struct PStack *st, int pr,
+                struct BufHeader *ibh)
+{
+        struct Channel *chanp = (struct Channel *) st->l4.userdata;
+        byte           *ptr;
+
+        switch (pr) {
+          case (PH_DATA):
+                  if (chanp->data_open) {
+                          ptr = DATAPTR(ibh);
+                          iif.rcvcallb(drid, chanp->chan, ptr, ibh->datasize);
+                  }
+                  BufPoolRelease(ibh);
+                  break;
+          default:
+                  printk(KERN_WARNING "lltrans_handler unknown primitive\n");
+                  break;
+        }
+}
+
+static void
+ll_writewakeup(struct PStack *st)
+{
+        struct Channel *chanp = st->l4.userdata;
+        isdn_ctrl       ic;
+
+        ic.driver = drid;
+        ic.command = ISDN_STAT_BSENT;
+        ic.arg = chanp->chan;
+        iif.statcallb(&ic);
+}
+
+static int
+init_ds(int chan, int incoming)
+{
+        struct PStack  *st = &(chanlist[chan].ds);
+        struct IsdnCardState *sp = (struct IsdnCardState *)
+        chanlist[chan].is.l1.hardware;
+        struct HscxState *hsp = sp->hs + chanlist[chan].hscx;
+        char            tmp[128];
+
+        st->l1.hardware = sp;
+
+        hsp->mode = 2;
+        hsp->transbufsize = 4000;
+
+        if (setstack_hscx(st, hsp))
+                return (-1);
+
+        st->l2.extended = 0;
+        st->l2.laptype = LAPB;
+        st->l2.orig = !incoming;
+        st->l2.t200 = 1000;        /* 1000 milliseconds */
+        st->l2.window = 3;
+        st->l2.n200 = 4;           /* try 4 times       */
+        st->l2.t203 = 5000;        /* 5000 milliseconds */
+
+        st->l2.debug = 0xff;
+        st->l3.debug = 0xff;
+        switch (chanlist[chan].l2_active_protocol) {
+          case (ISDN_PROTO_L2_X75I):
+                  sprintf(tmp, "Channel %d x.75", chan);
+                  setstack_isdnl2(st, tmp);
+                  st->l2.l2l3 = lldata_handler;
+                  st->l1.l1man = dcc_l1man;
+                  st->l2.l2man = dcc_l2man;
+                  st->l4.userdata = chanlist + chan;
+                  st->l4.l1writewakeup = NULL;
+                  st->l4.l2writewakeup = ll_writewakeup;
+                  st->l2.l2m.debug = debugflags & 16;
+                  st->ma.manl2(st, MDL_NOTEIPROC, NULL);
+                  st->l1.hscxmode = 2;        /* Packet-Mode ? */
+                  st->l1.hscxchannel = chanlist[chan].para.bchannel - 1;
+                  break;
+          case (ISDN_PROTO_L2_HDLC):
+                  st->l1.l1l2 = lltrans_handler;
+                  st->l1.l1man = dcc_l1man;
+                  st->l4.userdata = chanlist + chan;
+                  st->l4.l1writewakeup = ll_writewakeup;
+                  st->l1.hscxmode = 2;
+                  st->l1.hscxchannel = chanlist[chan].para.bchannel - 1;
+                  break;
+        }
+
+        return (0);
+
+}
+
+static void
+channel_report(int i)
+{
+}
+
+static void
+command_debug(struct Channel *chanp, char *s)
+{
+        char            tmp[64], tm[32];
+
+        jiftime(tm, jiffies);
+        sprintf(tmp, "%s Channel %d LL->HL %s\n", tm, chanp->chan, s);
+        teles_putstatus(tmp);
+}
+
+static void
+distr_debug(void)
+{
+        int             i;
+
+        for (i = 0; i < chancount; i++) {
+                chanlist[i].debug = debugflags & 1;
+                chanlist[i].fi.debug = debugflags & 2;
+                chanlist[i].is.l2.l2m.debug = debugflags & 8;
+                chanlist[i].ds.l2.l2m.debug = debugflags & 16;
+        }
+        for (i = 0; i < nrcards; i++)
+                if (cards[i].sp)
+                        cards[i].sp->dlogflag = debugflags & 4;
+}
+
+int
+teles_command(isdn_ctrl * ic)
+{
+        struct Channel *chanp;
+        char            tmp[64];
+        int             i;
+        unsigned int    num;
+
+        switch (ic->command) {
+          case (ISDN_CMD_SETEAZ):
+                  chanp = chanlist + ic->arg;
+                  if (chanp->debug & 1)
+                          command_debug(chanp, "SETEAZ");
+                  return (0);
+          case (ISDN_CMD_DIAL):
+                  chanp = chanlist + (ic->arg & 0xff);
+                  if (chanp->debug & 1) {
+                          sprintf(tmp, "DIAL %s", ic->num);
+                          command_debug(chanp, tmp);
+                  }
+                  FsmEvent(&chanp->fi, EV_DIAL, ic);
+                  return (0);
+          case (ISDN_CMD_ACCEPTB):
+                  chanp = chanlist + ic->arg;
+                  if (chanp->debug & 1)
+                          command_debug(chanp, "ACCEPTB");
+                  FsmEvent(&chanp->fi, EV_ACCEPTB, NULL);
+                  break;
+          case (ISDN_CMD_ACCEPTD):
+                  chanp = chanlist + ic->arg;
+                  if (chanp->debug & 1)
+                          command_debug(chanp, "ACCEPTD");
+                  FsmEvent(&chanp->fi, EV_ACCEPTD, NULL);
+                  break;
+          case (ISDN_CMD_HANGUP):
+                  chanp = chanlist + ic->arg;
+                  if (chanp->debug & 1)
+                          command_debug(chanp, "HANGUP");
+                  FsmEvent(&chanp->fi, EV_HANGUP, NULL);
+                  break;
+          case (ISDN_CMD_LOCK):
+                  teles_mod_inc_use_count();
+                  break;
+          case (ISDN_CMD_UNLOCK):
+                  teles_mod_dec_use_count();
+                  break;
+          case (ISDN_CMD_IOCTL):
+                  switch (ic->arg) {
+                    case (0):
+                            for (i = 0; i < nrcards; i++)
+                                    if (cards[i].sp)
+                                            teles_reportcard(i);
+                            for (i = 0; i < chancount; i++)
+                                    channel_report(i);
+                            break;
+                    case (1):
+                            debugflags = *(unsigned int *) ic->num;
+                            distr_debug();
+                            sprintf(tmp, "debugging flags set to %x\n", debugflags);
+                            teles_putstatus(tmp);
+                            break;
+                    case (2):
+                            num = *(unsigned int *) ic->num;
+                            i = num >> 8;
+                            if (i >= chancount)
+                                    break;
+                            chanp = chanlist + i;
+                            chanp->impair = num & 0xff;
+                            if (chanp->debug & 1) {
+                                    sprintf(tmp, "IMPAIR %x", chanp->impair);
+                                    command_debug(chanp, tmp);
+                            }
+                            break;
+                  }
+                  break;
+          case (ISDN_CMD_SETL2):
+                  chanp = chanlist + (ic->arg & 0xff);
+                  if (chanp->debug & 1) {
+                          sprintf(tmp, "SETL2 %ld", ic->arg >> 8);
+                          command_debug(chanp, tmp);
+                  }
+                  chanp->l2_protocol = ic->arg >> 8;
+                  break;
+          default:
+                  break;
+        }
+
+        return (0);
+}
+
+int
+teles_writebuf(int id, int chan, const u_char * buf, int count, int user)
+{
+        struct Channel *chanp = chanlist + chan;
+        struct PStack  *st = &chanp->ds;
+        struct BufHeader *ibh;
+        int             err, i;
+        byte           *ptr;
+
+        err = BufPoolGet(&ibh, st->l1.sbufpool, GFP_ATOMIC, st, 21);
+        if (err)
+                return (0);
+
+        if (count > BUFFER_SIZE(HSCX_SBUF_ORDER, HSCX_SBUF_BPPS)) {
+                printk(KERN_WARNING "teles_writebuf: packet too large!\n");
+                return (-EINVAL);
+        }
+        ptr = DATAPTR(ibh);
+        if (chanp->lc_b.l2_establish)
+                i = st->l2.ihsize;
+        else
+                i = 0;
+
+        ptr += i;
+
+        if (user)
+                memcpy_fromfs(ptr, buf, count);
+        else
+                memcpy(ptr, buf, count);
+        ibh->datasize = count + i;
+
+        if (chanp->data_open) {
+                if (chanp->lc_b.l2_establish)
+                        chanp->ds.l3.l3l2(&chanp->ds, DL_DATA, ibh);
+                else
+                        chanp->ds.l2.l2l1(&chanp->ds, PH_DATA, ibh);
+                return (count);
+        } else {
+                BufPoolRelease(ibh);
+                return (0);
+        }
+
+}
+
+static char    *
+strcpyupto(char *dest, char *src, char upto)
+{
+        while (*src && (*src != upto) && (*src != '\0'))
+                *dest++ = *src++;
+        *dest = '\0';
+        return (src);
+}
diff -u --recursive --new-file v1.3.68/linux/drivers/isdn/teles/card.c linux/drivers/isdn/teles/card.c
--- v1.3.68/linux/drivers/isdn/teles/card.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/isdn/teles/card.c	Mon Feb 26 11:58:05 1996
@@ -0,0 +1,1772 @@
+/*
+ * card.c     low level stuff for the Teles S0 isdn card
+ * 
+ * Author     Jan den Ouden
+ * 
+ * 
+ * 
+ * Changelog:
+ * 
+ * Beat Doebeli         log all D channel traffic
+ * 
+ */
+
+#define __NO_VERSION__
+#include "teles.h"
+
+#define INCLUDE_INLINE_FUNCS
+#include <linux/tqueue.h>
+#include <linux/interrupt.h>
+
+#undef DCHAN_VERBOSE
+
+extern void     tei_handler(struct PStack *st, byte pr,
+			    struct BufHeader *ibh);
+extern struct   IsdnCard cards[];
+extern int      nrcards;
+
+#define byteout(addr,val) outb_p(val,addr)
+#define bytein(addr) inb_p(addr)
+
+static inline   byte
+readisac_0(byte * cardm, byte offset)
+{
+	return *(byte *) (cardm + 0x100 + ((offset & 1) ? 0x1ff : 0) + offset);
+}
+
+static inline   byte
+readisac_3(int iobase, byte offset)
+{
+        return (bytein(iobase - 0x420 + offset));
+}
+
+#define READISAC(mbase,ibase,ofs) \
+        ((mbase)?readisac_0(mbase,ofs):readisac_3(ibase,ofs))
+
+static inline void
+writeisac_0(byte * cardm, byte offset, byte value)
+{
+	*(byte *) (cardm + 0x100 + ((offset & 1) ? 0x1ff : 0) + offset) = value;
+}
+
+static inline void
+writeisac_3(int iobase, byte offset, byte value)
+{
+	byteout(iobase - 0x420 + offset, value);
+}
+
+#define WRITEISAC(mbase,ibase,ofs,val) \
+        ((mbase)?writeisac_0(mbase,ofs,val):writeisac_3(ibase,ofs,val))
+
+static inline void
+readisac_s(int iobase, byte offset, byte * dest, int count)
+{
+	insb(iobase - 0x420 + offset, dest, count);
+}
+
+static inline void
+writeisac_s(int iobase, byte offset, byte * src, int count)
+{
+	outsb(iobase - 0x420 + offset, src, count);
+}
+
+static inline   byte
+readhscx_0(byte * base, byte hscx, byte offset)
+{
+	return *(byte *) (base + 0x180 + ((offset & 1) ? 0x1FF : 0) +
+			  ((hscx & 1) ? 0x40 : 0) + offset);
+}
+
+static inline   byte
+readhscx_3(int iobase, byte hscx, byte offset)
+{
+	return (bytein(iobase - (hscx ? 0x820 : 0xc20) + offset));
+}
+
+#define READHSCX(mbase,ibase,hscx,ofs) \
+        ((mbase)?readhscx_0(mbase,hscx,ofs):readhscx_3(ibase,hscx,ofs))
+
+static inline void
+writehscx_0(byte * base, byte hscx, byte offset, byte data)
+{
+	*(byte *) (base + 0x180 + ((offset & 1) ? 0x1FF : 0) +
+		   ((hscx & 1) ? 0x40 : 0) + offset) = data;
+}
+
+static inline void
+writehscx_3(int iobase, byte hscx, byte offset, byte data)
+{
+	byteout(iobase - (hscx ? 0x820 : 0xc20) + offset, data);
+}
+
+static inline void
+readhscx_s(int iobase, byte hscx, byte offset, byte * dest, int count)
+{
+	insb(iobase - (hscx ? 0x820 : 0xc20) + offset, dest, count);
+}
+
+static inline void
+writehscx_s(int iobase, byte hscx, byte offset, byte * src, int count)
+{
+	outsb(iobase - (hscx ? 0x820 : 0xc20) + offset, src, count);
+}
+
+#define ISAC_MASK 0x20
+#define ISAC_ISTA 0x20
+#define ISAC_STAR 0x21
+#define ISAC_CMDR 0x21
+#define ISAC_EXIR 0x24
+
+#define ISAC_RBCH 0x2a
+
+#define ISAC_ADF2 0x39
+#define ISAC_SPCR 0x30
+#define ISAC_ADF1 0x38
+#define ISAC_CIX0 0x31
+#define ISAC_STCR 0x37
+#define ISAC_MODE 0x22
+#define ISAC_RSTA 0x27
+#define ISAC_RBCL 0x25
+#define ISAC_TIMR 0x23
+#define ISAC_SQXR 0x3b
+
+#define HSCX_ISTA 0x20
+#define HSCX_CCR1 0x2f
+#define HSCX_CCR2 0x2c
+#define HSCX_TSAR 0x31
+#define HSCX_TSAX 0x30
+#define HSCX_XCCR 0x32
+#define HSCX_RCCR 0x33
+#define HSCX_MODE 0x22
+#define HSCX_CMDR 0x21
+#define HSCX_EXIR 0x24
+#define HSCX_XAD1 0x24
+#define HSCX_XAD2 0x25
+#define HSCX_RAH2 0x27
+#define HSCX_RSTA 0x27
+#define HSCX_TIMR 0x23
+#define HSCX_STAR 0x21
+#define HSCX_RBCL 0x25
+#define HSCX_XBCH 0x2d
+#define HSCX_VSTR 0x2e
+#define HSCX_RLCR 0x2e
+#define HSCX_MASK 0x20
+
+static inline void
+waitforCEC_0(byte * base, byte hscx)
+{
+	long            to = 10;
+
+	while ((readhscx_0(base, hscx, HSCX_STAR) & 0x04) && to) {
+		udelay(5);
+		to--;
+	}
+	if (!to)
+		printk(KERN_WARNING "waitforCEC timeout\n");
+}
+
+static inline void
+waitforCEC_3(int iobase, byte hscx)
+{
+	long            to = 10;
+
+	while ((readhscx_3(iobase, hscx, HSCX_STAR) & 0x04) && to) {
+		udelay(5);
+		to--;
+	}
+	if (!to)
+		printk(KERN_WARNING "waitforCEC timeout\n");
+}
+
+static inline void
+waitforXFW_0(byte * base, byte hscx)
+{
+	long            to = 10;
+
+	waitforCEC_0(base, hscx);
+
+	while ((!(readhscx_0(base, hscx, HSCX_STAR) & 0x40)) && to) {
+		udelay(5);
+		to--;
+	}
+	if (!to)
+		printk(KERN_WARNING "waitforXFW timeout\n");
+}
+
+static inline void
+waitforXFW_3(int iobase, byte hscx)
+{
+	long            to = 10;
+
+	waitforCEC_3(iobase, hscx);
+
+	while ((!(readhscx_3(iobase, hscx, HSCX_STAR) & 0x40)) && to) {
+		udelay(5);
+		to--;
+	}
+	if (!to)
+		printk(KERN_WARNING "waitforXFW timeout\n");
+}
+
+static inline void
+writehscxCMDR_0(byte * base, byte hscx, byte data)
+{
+	long            flags;
+
+	save_flags(flags);
+	cli();
+	waitforCEC_0(base, hscx);
+	writehscx_0(base, hscx, HSCX_CMDR, data);
+	restore_flags(flags);
+}
+
+static inline void
+writehscxCMDR_3(int iobase, byte hscx, byte data)
+{
+	long            flags;
+
+	save_flags(flags);
+	cli();
+	waitforCEC_3(iobase, hscx);
+	writehscx_3(iobase, hscx, HSCX_CMDR, data);
+	restore_flags(flags);
+}
+
+#define WRITEHSCX_CMDR(mbase,ibase,hscx,data) \
+        ((mbase)?writehscxCMDR_0(mbase,hscx,data):writehscxCMDR_3(ibase,hscx,data))
+
+/*
+ * fast interrupt here
+ */
+
+#define ISAC_RCVBUFREADY 0
+#define ISAC_XMTBUFREADY 1
+#define ISAC_PHCHANGE    2
+
+#define HSCX_RCVBUFREADY 0
+#define HSCX_XMTBUFREADY 1
+
+void
+teles_hscxreport(struct IsdnCardState *sp, int hscx)
+{
+        printk(KERN_DEBUG "HSCX %d\n", hscx);
+        if (sp->membase) {
+                printk(KERN_DEBUG "  ISTA %x\n", readhscx_0(sp->membase,
+                                                          hscx, HSCX_ISTA));
+                printk(KERN_DEBUG "  STAR %x\n", readhscx_0(sp->membase,
+                                                          hscx, HSCX_STAR));
+                printk(KERN_DEBUG "  EXIR %x\n", readhscx_0(sp->membase,
+                                                          hscx, HSCX_EXIR));
+        } else {
+                printk(KERN_DEBUG "  ISTA %x\n", readhscx_3(sp->iobase,
+                                                          hscx, HSCX_ISTA));
+                printk(KERN_DEBUG "  STAR %x\n", readhscx_3(sp->iobase,
+                                                          hscx, HSCX_STAR));
+                printk(KERN_DEBUG "  EXIR %x\n", readhscx_3(sp->iobase,
+                                                          hscx, HSCX_EXIR));
+        }
+}
+
+void
+teles_report(struct IsdnCardState *sp)
+{
+	printk(KERN_DEBUG "ISAC\n");
+        if (sp->membase) {
+               printk(KERN_DEBUG "  ISTA %x\n", readisac_0(sp->membase,
+                                                            ISAC_ISTA));
+               printk(KERN_DEBUG "  STAR %x\n", readisac_0(sp->membase,
+                                                           ISAC_STAR));
+               printk(KERN_DEBUG "  EXIR %x\n", readisac_0(sp->membase,
+                                                           ISAC_EXIR));
+        } else {
+                printk(KERN_DEBUG "  ISTA %x\n", readisac_3(sp->iobase,
+                                                          ISAC_ISTA));
+                printk(KERN_DEBUG "  STAR %x\n", readisac_3(sp->iobase,
+                                                          ISAC_STAR));
+                printk(KERN_DEBUG "  EXIR %x\n", readisac_3(sp->iobase,
+                                                          ISAC_EXIR));
+        }
+	teles_hscxreport(sp, 0);
+	teles_hscxreport(sp, 1);
+}
+
+/*
+ * HSCX stuff goes here
+ */
+
+static void
+hscx_sched_event(struct HscxState *hsp, int event)
+{
+	hsp->event |= 1 << event;
+	queue_task_irq_off(&hsp->tqueue, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+}
+
+static void
+hscx_empty_fifo(struct HscxState *hsp, int count)
+{
+	byte             *ptr;
+	struct BufHeader *ibh = hsp->rcvibh;
+
+	if (hsp->sp->debug)
+		printk(KERN_DEBUG "hscx_empty_fifo\n");
+
+	if (hsp->rcvptr + count > BUFFER_SIZE(HSCX_RBUF_ORDER,
+					      HSCX_RBUF_BPPS)) {
+		printk(KERN_WARNING
+                       "hscx_empty_fifo: incoming packet too large\n");
+		WRITEHSCX_CMDR(hsp->membase, hsp->iobase, hsp->hscx, 0x80);
+		return;
+	}
+	ptr = DATAPTR(ibh);
+	ptr += hsp->rcvptr;
+
+	hsp->rcvptr += count;
+        if (hsp->membase) {
+                while (count--)
+                        *ptr++ = readhscx_0(hsp->membase, hsp->hscx, 0x0);
+                writehscxCMDR_0(hsp->membase, hsp->hscx, 0x80);
+        } else {
+                readhscx_s(hsp->iobase, hsp->hscx, 0x3e, ptr, count);
+                writehscxCMDR_3(hsp->iobase, hsp->hscx, 0x80);
+        }
+#ifdef BCHAN_VERBOSE
+        {
+                int i;
+                printk(KERN_DEBUG "hscx_empty_fifo");
+                for (i = 0; i < count; i++)
+                        printk(" %2x", ptr[i]);
+                printk("\n");
+        }
+#endif				/* BCHAN_VERBOSE */
+}
+
+static void
+hscx_fill_fifo(struct HscxState *hsp)
+{
+	struct BufHeader *ibh;
+	int              more, count;
+	byte             *ptr;
+
+	if (hsp->sp->debug)
+		printk(KERN_DEBUG "hscx_fill_fifo\n");
+
+	ibh = hsp->xmtibh;
+	if (!ibh)
+		return;
+
+	count = ibh->datasize - hsp->sendptr;
+	if (count <= 0)
+		return;
+
+#if 0
+	if (!hsp->sendptr) {
+		ptr = DATAPTR(ibh);
+		printk(KERN_DEBUG "snd bytes %2x %2x %2x %2x %2x\n", ptr[0], ptr[1], ptr[2],
+		       ptr[3], ptr[4]);
+	}
+#endif
+
+	more = 0;
+	if (count > 32) {
+		more = !0;
+		count = 32;
+	}
+	ptr = DATAPTR(ibh);
+	ptr += hsp->sendptr;
+	hsp->sendptr += count;
+
+#ifdef BCHAN_VERBOSE
+        {
+                int i;
+                printk(KERN_DEBUG "hscx_fill_fifo ");
+                for (i = 0; i < count; i++)
+                        printk(" %2x", ptr[i]);
+                printk("\n");
+        }
+#endif
+        if (hsp->membase) {
+                waitforXFW_0(hsp->membase, hsp->hscx);
+                while (count--)
+                        writehscx_0(hsp->membase, hsp->hscx, 0x0, *ptr++);
+                writehscxCMDR_0(hsp->membase, hsp->hscx, more ? 0x8 : 0xa);
+        } else {
+                waitforXFW_3(hsp->iobase, hsp->hscx);
+                writehscx_s(hsp->iobase, hsp->hscx, 0x3e, ptr, count);
+                writehscxCMDR_3(hsp->iobase, hsp->hscx, more ? 0x8 : 0xa);
+        }
+}
+
+static inline void
+hscx_interrupt(struct IsdnCardState *sp, byte val, byte hscx)
+{
+	byte             r;
+	struct HscxState *hsp = sp->hs + hscx;
+	int              count;
+
+	if (!hsp->init)
+		return;
+
+	if (val & 0x80) {	/* RME */
+
+		r = READHSCX(hsp->membase, sp->iobase, hsp->hscx, HSCX_RSTA);
+		if ((r & 0xf0) != 0xa0) {
+			if (!r & 0x80)
+				printk(KERN_WARNING
+                                       "Teles: HSCX invalid frame\n");
+			if (r & 0x40)
+				printk(KERN_WARNING "Teles: HSCX RDO\n");
+			if (!r & 0x20)
+				printk(KERN_WARNING "Teles: HSCX CRC error\n");
+			if (hsp->rcvibh)
+				BufPoolRelease(hsp->rcvibh);
+			hsp->rcvibh = NULL;
+			WRITEHSCX_CMDR(hsp->membase, hsp->iobase, hsp->hscx,
+                                       0x80);
+			goto afterRME;
+		}
+		if (!hsp->rcvibh)
+			if (BufPoolGet(&hsp->rcvibh, &hsp->rbufpool,
+                                       GFP_ATOMIC, (void *) 1, 1)) {
+				printk(KERN_WARNING
+                                       "HSCX RME out of buffers at %ld\n",
+                                       jiffies);
+				WRITEHSCX_CMDR(hsp->membase, hsp->iobase,
+                                               hsp->hscx, 0x80);
+				goto afterRME;
+			} else
+				hsp->rcvptr = 0;
+
+		count = READHSCX(hsp->membase, sp->iobase, hsp->hscx,
+                                 HSCX_RBCL) & 0x1f;
+		if (count == 0)
+			count = 32;
+		hscx_empty_fifo(hsp, count);
+		hsp->rcvibh->datasize = hsp->rcvptr - 1;
+		BufQueueLink(&hsp->rq, hsp->rcvibh);
+		hsp->rcvibh = NULL;
+		hscx_sched_event(hsp, HSCX_RCVBUFREADY);
+	}
+      afterRME:
+	if (val & 0x40) {	/* RPF */
+		if (!hsp->rcvibh)
+			if (BufPoolGet(&hsp->rcvibh, &hsp->rbufpool,
+				       GFP_ATOMIC, (void *) 1, 2)) {
+				printk(KERN_WARNING
+                                       "HSCX RME out of buffers at %ld\n",
+                                       jiffies);
+				WRITEHSCX_CMDR(hsp->membase, hsp->iobase,
+                                               hsp->hscx, 0x80);
+				goto afterRPF;
+			} else
+				hsp->rcvptr = 0;
+
+		hscx_empty_fifo(hsp, 32);
+#ifdef VOICE
+		hsp->rcvibh->datasize = hsp->rcvptr - 1;
+		BufQueueLink(&hsp->rq, hsp->rcvibh);
+		hsp->rcvibh = NULL;
+		hscx_sched_event(hsp, HSCX_RCVBUFREADY);
+#endif
+	}
+      afterRPF:
+	if (val & 0x10) {	/* XPR */
+		if (hsp->xmtibh)
+			if (hsp->xmtibh->datasize > hsp->sendptr) {
+				hscx_fill_fifo(hsp);
+				goto afterXPR;
+			} else {
+				if (hsp->releasebuf)
+					BufPoolRelease(hsp->xmtibh);
+				hsp->xmtibh = NULL;
+				hsp->sendptr = 0;
+				if (hsp->st->l4.l1writewakeup)
+					hsp->st->l4.l1writewakeup(hsp->st);
+			}
+		if (!BufQueueUnlink(&hsp->xmtibh, &hsp->sq)) {
+			hsp->releasebuf = !0;
+			hscx_fill_fifo(hsp);
+		} else
+			hscx_sched_event(hsp, HSCX_XMTBUFREADY);
+	}
+      afterXPR:
+}
+
+/*
+ * ISAC stuff goes here
+ */
+
+static void
+isac_sched_event(struct IsdnCardState *sp, int event)
+{
+	sp->event |= 1 << event;
+	queue_task_irq_off(&sp->tqueue, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+}
+
+static void
+empty_fifo(struct IsdnCardState *sp, int count)
+{
+	byte             *ptr;
+	struct BufHeader *ibh = sp->rcvibh;
+
+	if (sp->debug)
+		printk(KERN_DEBUG "empty_fifo\n");
+
+	if (sp->rcvptr >= 3072) {
+		printk(KERN_WARNING "empty_fifo rcvptr %d\n", sp->rcvptr);
+		return;
+	}
+	ptr = DATAPTR(ibh);
+	ptr += sp->rcvptr;
+	sp->rcvptr += count;
+
+        if (sp->membase) {
+#ifdef DCHAN_VERBOSE
+                printk(KERN_DEBUG "empty_fifo ");
+                while (count--) {
+                        *ptr = readisac_0(sp->membase, 0x0);
+                        printk("%2x ", *ptr);
+                        ptr++;
+                }
+                printk("\n");
+#else
+                while (count--)
+                        *ptr++ = readisac_0(sp->membase, 0x0);
+#endif
+                writeisac_0(sp->membase, ISAC_CMDR, 0x80);
+        } else {
+#ifdef DCHAN_VERBOSE
+                int i;
+                printk(KERN_DEBUG "empty_fifo ");
+                readisac_s(sp->iobase, 0x3e, ptr, count);
+                for (i = 0; i < count; i++)
+                        printk("%2x ", ptr[i]);
+                printk("\n");
+#else
+                readisac_s(sp->iobase, 0x3e, ptr, count);
+#endif
+                writeisac_3(sp->iobase, ISAC_CMDR, 0x80);
+        }
+}
+
+static void
+fill_fifo(struct IsdnCardState *sp)
+{
+	struct BufHeader *ibh;
+	int              count, more;
+	byte             *ptr;
+
+	if (sp->debug)
+		printk(KERN_DEBUG "fill_fifo\n");
+
+	ibh = sp->xmtibh;
+	if (!ibh)
+		return;
+
+	count = ibh->datasize - sp->sendptr;
+	if (count <= 0)
+		return;
+	if (count >= 3072)
+		return;
+
+	more = 0;
+	if (count > 32) {
+		more = !0;
+		count = 32;
+	}
+	ptr = DATAPTR(ibh);
+	ptr += sp->sendptr;
+	sp->sendptr += count;
+
+        if (sp->membase) {
+#ifdef DCHAN_VERBOSE
+                printk(KERN_DEBUG "fill_fifo ");
+                while (count--) {
+                        writeisac_0(sp->membase, 0x0, *ptr);
+                        printk("%2x ", *ptr);
+                        ptr++;
+                }
+                printk("\n");
+#else
+                while (count--)
+                        writeisac_0(sp->membase, 0x0, *ptr++);
+#endif
+                writeisac_0(sp->membase, ISAC_CMDR, more ? 0x8 : 0xa);
+        } else {
+#ifdef DCHAN_VERBOSE
+                int i;
+                writeisac_s(sp->iobase, 0x3e, ptr, count);
+                printk(KERN_DEBUG "fill_fifo ");
+                for (i = 0; i < count; i++)
+                        printk("%2x ", ptr[i]);
+                printk("\n");
+#else
+                writeisac_s(sp->iobase, 0x3e, ptr, count);
+#endif
+                writeisac_3(sp->iobase, ISAC_CMDR, more ? 0x8 : 0xa);
+        }
+}
+
+static int
+act_wanted(struct IsdnCardState *sp)
+{
+	struct PStack  *st;
+
+	st = sp->stlist;
+	while (st)
+		if (st->l1.act_state)
+			return (!0);
+		else
+			st = st->next;
+	return (0);
+}
+
+static void
+ph_command(struct IsdnCardState *sp, unsigned int command)
+{
+	printk(KERN_DEBUG "ph_command %d\n", command);
+	WRITEISAC(sp->membase, sp->iobase, ISAC_CIX0, (command << 2) | 3);
+}
+
+static void
+isac_new_ph(struct IsdnCardState *sp)
+{
+	int             enq;
+
+	enq = act_wanted(sp);
+
+	switch (sp->ph_state) {
+	  case (0):
+	  case (6):
+		  if (enq)
+			  ph_command(sp, 0);
+		  else
+			  ph_command(sp, 15);
+		  break;
+	  case (7):
+		  if (enq)
+			  ph_command(sp, 9);
+		  break;
+	  case (12):
+	  case (13):
+		  sp->ph_active = 5;
+		  isac_sched_event(sp, ISAC_PHCHANGE);
+		  if (!sp->xmtibh)
+			  if (!BufQueueUnlink(&sp->xmtibh, &sp->sq))
+				  sp->sendptr = 0;
+		  if (sp->xmtibh)
+			  fill_fifo(sp);
+		  break;
+	  case (4):
+	  case (8):
+		  break;
+	  default:
+		  sp->ph_active = 0;
+		  break;
+	}
+}
+
+static void
+teles_interrupt(int intno, struct pt_regs *regs)
+{
+	byte                 val, val2, r;
+	struct IsdnCardState *sp;
+	unsigned int         count;
+	struct HscxState     *hsp;
+
+	sp = (struct IsdnCardState *) irq2dev_map[intno];
+
+	if (!sp) {
+		printk(KERN_WARNING "Teles: Spurious interrupt!\n");
+		return;
+	}
+	val = READHSCX(sp->membase, sp->iobase, 1, HSCX_ISTA);
+
+	if (val & 0x01) {
+		hsp = sp->hs + 1;
+		printk(KERN_WARNING "HSCX B EXIR %x xmitbh %lx rcvibh %lx\n",
+                       READHSCX(sp->membase, sp->iobase, 1, HSCX_EXIR),
+                       (long) hsp->xmtibh,
+		       (long) hsp->rcvibh);
+	}
+	if (val & 0xf8) {
+		if (sp->debug)
+			printk(KERN_DEBUG "HSCX B interrupt %x\n", val);
+		hscx_interrupt(sp, val, 1);
+	}
+	if (val & 0x02) {
+		printk(KERN_WARNING "HSCX A EXIR %x\n",
+                       READHSCX(sp->membase, sp->iobase, 0, HSCX_EXIR));
+	}
+
+/* ??? Why do that vvvvvvvvvvvvvvvvvvvvv different on Teles 16-3 ??? */
+        if (sp->membase) {
+                if (val & 0x04) {
+                        val = readhscx_0(sp->membase, 0, HSCX_ISTA);
+                        if (sp->debug)
+                                printk(KERN_DEBUG "HSCX A interrupt %x\n",
+                                       val);
+                        hscx_interrupt(sp, val, 0);
+                }
+        } else {
+                val2 = readhscx_3(sp->iobase, 0, HSCX_ISTA);
+                if (sp->debug)
+                        printk(KERN_DEBUG "HSCX A ISTA %x\n", val2);
+                if (val & 0x04) {
+                        if (sp->debug)
+                                printk(KERN_DEBUG "HSCX A interrupt %x\n",
+                                       val2);
+                        hscx_interrupt(sp, val2, 0);
+                }
+        }
+/* ??? Why do that ^^^^^^^^^^^^^^^^^^^^^ different on Teles 16-3 ??? */
+
+	val = READISAC(sp->membase, sp->iobase, ISAC_ISTA);
+
+	if (sp->debug)
+		printk(KERN_DEBUG "ISAC interrupt %x\n", val);
+
+	if (val & 0x80) {	/* RME */
+
+		r = READISAC(sp->membase, sp->iobase, ISAC_RSTA);
+		if ((r & 0x70) != 0x20) {
+			if (r & 0x40)
+				printk(KERN_WARNING "Teles: ISAC RDO\n");
+			if (!r & 0x20)
+				printk(KERN_WARNING "Teles: ISAC CRC error\n");
+			if (sp->rcvibh)
+				BufPoolRelease(sp->rcvibh);
+			sp->rcvibh = NULL;
+			WRITEISAC(sp->membase, sp->iobase, ISAC_CMDR, 0x80);
+			goto afterRME;
+		}
+		if (!sp->rcvibh)
+			if (BufPoolGet(&(sp->rcvibh), &(sp->rbufpool),
+                                       GFP_ATOMIC,
+				       (void *) 1, 3)) {
+				printk(KERN_WARNING
+                                       "ISAC RME out of buffers!\n");
+				WRITEISAC(sp->membase, sp->iobase, 
+                                          ISAC_CMDR, 0x80);
+				goto afterRME;
+			} else
+				sp->rcvptr = 0;
+
+		count = READISAC(sp->membase, sp->iobase, ISAC_RBCL) & 0x1f;
+		if (count == 0)
+			count = 32;
+		empty_fifo(sp, count);
+		sp->rcvibh->datasize = sp->rcvptr;
+		BufQueueLink(&(sp->rq), sp->rcvibh);
+		sp->rcvibh = NULL;
+		isac_sched_event(sp, ISAC_RCVBUFREADY);
+	}
+      afterRME:
+	if (val & 0x40) {	/* RPF */
+		if (!sp->rcvibh)
+			if (BufPoolGet(&(sp->rcvibh), &(sp->rbufpool),
+                                       GFP_ATOMIC,
+				       (void *) 1, 4)) {
+				printk(KERN_WARNING
+                                       "ISAC RME out of buffers!\n");
+				WRITEISAC(sp->membase, sp->iobase,
+                                          ISAC_CMDR, 0x80);
+				goto afterRPF;
+			} else
+				sp->rcvptr = 0;
+		empty_fifo(sp, 32);
+	}
+      afterRPF:
+	if (val & 0x20) {
+	}
+	if (val & 0x10) {	/* XPR */
+		if (sp->xmtibh)
+			if (sp->xmtibh->datasize > sp->sendptr) {
+				fill_fifo(sp);
+				goto afterXPR;
+			} else {
+				if (sp->releasebuf)
+					BufPoolRelease(sp->xmtibh);
+				sp->xmtibh = NULL;
+				sp->sendptr = 0;
+			}
+		if (!BufQueueUnlink(&sp->xmtibh, &sp->sq)) {
+			sp->releasebuf = !0;
+			fill_fifo(sp);
+		} else
+			isac_sched_event(sp, ISAC_XMTBUFREADY);
+	}
+      afterXPR:
+	if (val & 0x04) {	/* CISQ */
+		sp->ph_state = (READISAC(sp->membase, sp->iobase, ISAC_CIX0)
+                                >> 2) & 0xf;
+		printk(KERN_DEBUG "l1state %d\n", sp->ph_state);
+		isac_new_ph(sp);
+	}
+        if (sp->membase) {
+                writeisac_0(sp->membase, ISAC_MASK, 0xFF);
+                writehscx_0(sp->membase, 0, HSCX_MASK, 0xFF);
+                writehscx_0(sp->membase, 1, HSCX_MASK, 0xFF);
+                writeisac_0(sp->membase, ISAC_MASK, 0x0);
+                writehscx_0(sp->membase, 0, HSCX_MASK, 0x0);
+                writehscx_0(sp->membase, 1, HSCX_MASK, 0x0);
+        } else {
+                writeisac_3(sp->iobase, ISAC_MASK, 0xFF);
+                writehscx_3(sp->iobase, 0, HSCX_MASK, 0xFF);
+                writehscx_3(sp->iobase, 1, HSCX_MASK, 0xFF);
+                writeisac_3(sp->iobase, ISAC_MASK, 0x0);
+                writehscx_3(sp->iobase, 0, HSCX_MASK, 0x0);
+                writehscx_3(sp->iobase, 1, HSCX_MASK, 0x0);
+        }
+}
+
+/*
+ * soft interrupt
+ */
+
+static void
+act_ivated(struct IsdnCardState *sp)
+{
+	struct PStack  *st;
+
+	st = sp->stlist;
+	while (st) {
+		if (st->l1.act_state == 1) {
+			st->l1.act_state = 2;
+			st->l1.l1man(st, PH_ACTIVATE, NULL);
+		}
+		st = st->next;
+	}
+}
+
+static void
+process_new_ph(struct IsdnCardState *sp)
+{
+	if (sp->ph_active == 5)
+		act_ivated(sp);
+}
+
+static void
+process_xmt(struct IsdnCardState *sp)
+{
+	struct PStack  *stptr;
+
+	if (sp->xmtibh)
+		return;
+
+	stptr = sp->stlist;
+	while (stptr != NULL)
+		if (stptr->l1.requestpull) {
+			stptr->l1.requestpull = 0;
+			stptr->l1.l1l2(stptr, PH_PULL_ACK, NULL);
+			break;
+		} else
+			stptr = stptr->next;
+}
+
+static void
+process_rcv(struct IsdnCardState *sp)
+{
+	struct BufHeader *ibh, *cibh;
+	struct PStack    *stptr;
+	byte             *ptr;
+	int              found, broadc;
+	char             tmp[64];
+
+	while (!BufQueueUnlink(&ibh, &sp->rq)) {
+		stptr = sp->stlist;
+		ptr = DATAPTR(ibh);
+		broadc = (ptr[1] >> 1) == 127;
+
+		if (broadc && sp->dlogflag && (!(ptr[0] >> 2)))
+			dlogframe(sp, ptr + 3, ibh->datasize - 3,
+				  "Q.931 frame network->user broadcast");
+
+		if (broadc) {
+			while (stptr != NULL) {
+				if ((ptr[0] >> 2) == stptr->l2.sap)
+					if (!BufPoolGet(&cibh, &sp->rbufpool, GFP_ATOMIC,
+							(void *) 1, 5)) {
+						memcpy(DATAPTR(cibh), DATAPTR(ibh), ibh->datasize);
+						cibh->datasize = ibh->datasize;
+						stptr->l1.l1l2(stptr, PH_DATA, cibh);
+					} else
+						printk(KERN_WARNING "isdn broadcast buffer shortage\n");
+				stptr = stptr->next;
+			}
+			BufPoolRelease(ibh);
+		} else {
+			found = 0;
+			while (stptr != NULL)
+				if (((ptr[0] >> 2) == stptr->l2.sap) &&
+				    ((ptr[1] >> 1) == stptr->l2.tei)) {
+					stptr->l1.l1l2(stptr, PH_DATA, ibh);
+					found = !0;
+					break;
+				} else
+					stptr = stptr->next;
+			if (!found) {
+				/* BD 10.10.95
+				 * Print out D-Channel msg not processed
+				 * by isdn4linux
+                                 */
+
+				if ((!(ptr[0] >> 2)) && (!(ptr[2] & 0x01))) {
+					sprintf(tmp, "Q.931 frame network->user with tei %d (not for us)", ptr[1] >> 1);
+					dlogframe(sp, ptr + 4, ibh->datasize - 4, tmp);
+				}
+				BufPoolRelease(ibh);
+			}
+		}
+
+	}
+
+}
+
+static void
+isac_bh(struct IsdnCardState *sp)
+{
+	if (!sp)
+		return;
+
+	if (clear_bit(ISAC_PHCHANGE, &sp->event))
+		process_new_ph(sp);
+	if (clear_bit(ISAC_RCVBUFREADY, &sp->event))
+		process_rcv(sp);
+	if (clear_bit(ISAC_XMTBUFREADY, &sp->event))
+		process_xmt(sp);
+}
+
+
+static void
+hscx_process_xmt(struct HscxState *hsp)
+{
+	struct PStack  *st = hsp->st;
+
+	if (hsp->xmtibh)
+		return;
+
+	if (st->l1.requestpull) {
+		st->l1.requestpull = 0;
+		st->l1.l1l2(st, PH_PULL_ACK, NULL);
+	}
+	if (!hsp->active)
+		if ((!hsp->xmtibh) && (!hsp->sq.head))
+			modehscx(hsp, 0, 0);
+}
+
+static void
+hscx_process_rcv(struct HscxState *hsp)
+{
+	struct BufHeader *ibh;
+
+#ifdef DEBUG_MAGIC
+	if (hsp->magic != 301270) {
+		printk(KERN_DEBUG "hscx_process_rcv magic not 301270\n");
+		return;
+	}
+#endif
+	while (!BufQueueUnlink(&ibh, &hsp->rq)) {
+		hsp->st->l1.l1l2(hsp->st, PH_DATA, ibh);
+	}
+}
+
+static void
+hscx_bh(struct HscxState *hsp)
+{
+
+	if (!hsp)
+		return;
+
+	if (clear_bit(HSCX_RCVBUFREADY, &hsp->event))
+		hscx_process_rcv(hsp);
+	if (clear_bit(HSCX_XMTBUFREADY, &hsp->event))
+		hscx_process_xmt(hsp);
+
+}
+
+/*
+ * interrupt stuff ends here
+ */
+
+static void
+restart_ph(struct IsdnCardState *sp)
+{
+	switch (sp->ph_active) {
+	  case (0):
+		  if (sp->ph_state == 6)
+			  ph_command(sp, 0);
+		  else
+			  ph_command(sp, 1);
+		  sp->ph_active = 1;
+		  break;
+	}
+}
+
+static void
+initisac(byte * cardmem, int iobase)
+{
+        if (cardmem) {
+                writeisac_0(cardmem, ISAC_MASK, 0xff);
+                writeisac_0(cardmem, ISAC_ADF2, 0x0);
+                writeisac_0(cardmem, ISAC_SPCR, 0xa);
+                writeisac_0(cardmem, ISAC_ADF1, 0x2);
+                writeisac_0(cardmem, ISAC_STCR, 0x70);
+                writeisac_0(cardmem, ISAC_MODE, 0xc9);
+                writeisac_0(cardmem, ISAC_CMDR, 0x41);
+                writeisac_0(cardmem, ISAC_CIX0, (1 << 2) | 3);
+        } else {
+                writeisac_3(iobase, ISAC_MASK, 0xff);
+                writeisac_3(iobase, ISAC_ADF2, 0x80);
+                writeisac_3(iobase, ISAC_SQXR, 0x2f);
+                writeisac_3(iobase, ISAC_SPCR, 0x00);
+                writeisac_3(iobase, ISAC_ADF1, 0x02);
+                writeisac_3(iobase, ISAC_STCR, 0x70);
+                writeisac_3(iobase, ISAC_MODE, 0xc9);
+                writeisac_3(iobase, ISAC_TIMR, 0x00);
+                writeisac_3(iobase, ISAC_ADF1, 0x00);
+                writeisac_3(iobase, ISAC_CMDR, 0x41);
+                writeisac_3(iobase, ISAC_CIX0, (1 << 2) | 3);
+        }
+}
+
+static int
+checkcard(int cardnr)
+{
+	int             timout;
+	byte            cfval, val;
+	struct IsdnCard *card = cards + cardnr;
+
+        if (!card->iobase) {
+                if (card->membase) {
+                        printk(KERN_NOTICE
+                               "Teles 8 assumed, mem: %lx irq: %d proto: %s\n",
+                               (long) card->membase, card->interrupt,
+                               (card->protocol == ISDN_PTYPE_1TR6) ?
+                               "1TR6" : "EDSS1");
+                        printk(KERN_INFO "HSCX version A: %x B:%x\n",
+                               readhscx_0(card->membase, 0, HSCX_VSTR) & 0xf,
+                               readhscx_0(card->membase, 1, HSCX_VSTR) & 0xf);
+                }
+        } else {
+                if (check_region(card->iobase, 8)) {
+                        printk(KERN_WARNING
+                               "teles: ports %x-%x already in use\n",
+                               card->iobase,
+                               card->iobase + 8 );
+                        return -1;
+                }
+                switch (card->interrupt) {
+                case 2:
+                        cfval = 0x00;
+                        break;
+                case 3:
+                        cfval = 0x02;
+                        break;
+                case 4:
+                        cfval = 0x04;
+                        break;
+                case 5:
+                        cfval = 0x06;
+                        break;
+                case 10:
+                        cfval = 0x08;
+                        break;
+                case 11:
+                        cfval = 0x0A;
+                        break;
+                case 12:
+                        cfval = 0x0C;
+                        break;
+                case 15:
+                        cfval = 0x0E;
+                        break;
+                default:
+                        cfval = 0x00;
+                        break;
+                }
+                if (card->membase) {
+                        cfval |= (((unsigned int) card->membase >> 9) & 0xF0);
+                        
+                        if (bytein(card->iobase + 0) != 0x51) {
+                                printk(KERN_INFO "XXX Byte at %x is %x\n",
+                                       card->iobase + 0,
+                                       bytein(card->iobase + 0));
+                                return -2;
+                        }
+                        if (bytein(card->iobase + 1) != 0x93) {
+                                printk(KERN_INFO "XXX Byte at %x is %x\n",
+                                       card->iobase + 1,
+                                       bytein(card->iobase + 1));
+                                return -2;
+                        }
+                        val = bytein(card->iobase + 2);	/* 0x1e=without AB
+                                                         * 0x1f=with AB
+                                                         */
+                        if (val != 0x1e && val != 0x1f) {
+                                printk(KERN_INFO "XXX Byte at %x is %x\n",
+                                       card->iobase + 2,
+                                       bytein(card->iobase + 2));
+                                return -2;
+                        }
+                }
+                request_region(card->iobase, 8, "teles");
+                cli();
+                timout = jiffies + (HZ / 10) + 1;
+                byteout(card->iobase + 4, cfval);
+                sti();
+                while (jiffies <= timout);
+                
+                cli();
+                timout = jiffies + (HZ / 10) + 1;
+                byteout(card->iobase + 4, cfval | 1);
+                sti();
+                while (jiffies <= timout);
+                
+                if (card->membase)
+                        printk(KERN_NOTICE
+                               "Teles 16.0 found, io: %x mem: %lx irq: %d proto: %s\n",
+                               card->iobase, (long) card->membase,
+                               card->interrupt,
+                               (card->protocol == ISDN_PTYPE_1TR6) ?
+                               "1TR6" : "EDSS1");
+                else
+                        printk(KERN_NOTICE
+                               "Teles 16.3 found, io: %x irq: %d proto: %s\n",
+                               card->iobase, card->interrupt,
+                               (card->protocol == ISDN_PTYPE_1TR6) ?
+                               "1TR6" : "EDSS1");
+                printk(KERN_INFO "HSCX version A:%x B:%x\n",
+                       READHSCX(card->membase, card->iobase, 0,
+                                HSCX_VSTR) & 0xf,
+                       READHSCX(card->membase, card->iobase, 1,
+                                HSCX_VSTR) & 0xf);
+                
+        }
+        if (card->membase) {
+                cli();
+                timout = jiffies + (HZ / 5) + 1;
+                *(byte *) (card->membase + 0x80) = 0;
+                sti();
+                while (jiffies <= timout);
+                
+                cli();
+                *(byte *) (card->membase + 0x80) = 1;
+                timout = jiffies + (HZ / 5) + 1;
+                sti();
+                while (jiffies <= timout);
+        }
+	return (0);
+}
+
+void
+modehscx(struct HscxState *hs, int mode,
+	 int ichan)
+{
+	struct IsdnCardState *sp = hs->sp;
+	int             hscx = hs->hscx;
+
+	printk(KERN_DEBUG "modehscx hscx %d mode %d ichan %d\n",
+	       hscx, mode, ichan);
+
+	if (hscx == 0)
+		ichan = 1 - ichan;	/* raar maar waar... */
+
+        if (sp->membase) {
+                writehscx_0(sp->membase, hscx, HSCX_CCR1, 0x85);
+                writehscx_0(sp->membase, hscx, HSCX_XAD1, 0xFF);
+                writehscx_0(sp->membase, hscx, HSCX_XAD2, 0xFF);
+                writehscx_0(sp->membase, hscx, HSCX_RAH2, 0xFF);
+                writehscx_0(sp->membase, hscx, HSCX_XBCH, 0x0);
+
+                switch (mode) {
+                case (0):
+                        writehscx_0(sp->membase, hscx, HSCX_CCR2, 0x30);
+                        writehscx_0(sp->membase, hscx, HSCX_TSAX, 0xff);
+                        writehscx_0(sp->membase, hscx, HSCX_TSAR, 0xff);
+                        writehscx_0(sp->membase, hscx, HSCX_XCCR, 7);
+                        writehscx_0(sp->membase, hscx, HSCX_RCCR, 7);
+                        writehscx_0(sp->membase, hscx, HSCX_MODE, 0x84);
+                        break;
+                case (1):
+                        if (ichan == 0) {
+                                writehscx_0(sp->membase, hscx, HSCX_CCR2, 0x30);
+                                writehscx_0(sp->membase, hscx, HSCX_TSAX, 0x7);
+                                writehscx_0(sp->membase, hscx, HSCX_TSAR, 0x7);
+                                writehscx_0(sp->membase, hscx, HSCX_XCCR, 7);
+                                writehscx_0(sp->membase, hscx, HSCX_RCCR, 7);
+                        } else {
+                                writehscx_0(sp->membase, hscx, HSCX_CCR2, 0x30);
+                                writehscx_0(sp->membase, hscx, HSCX_TSAX, 0x3);
+                                writehscx_0(sp->membase, hscx, HSCX_TSAR, 0x3);
+                                writehscx_0(sp->membase, hscx, HSCX_XCCR, 7);
+                                writehscx_0(sp->membase, hscx, HSCX_RCCR, 7);
+                        }
+                        writehscx_0(sp->membase, hscx, HSCX_MODE, 0xe4);
+                        writehscx_0(sp->membase, hscx, HSCX_CMDR, 0x41);
+                        break;
+                case (2):
+                        if (ichan == 0) {
+                                writehscx_0(sp->membase, hscx, HSCX_CCR2, 0x30);
+                                writehscx_0(sp->membase, hscx, HSCX_TSAX, 0x7);
+                                writehscx_0(sp->membase, hscx, HSCX_TSAR, 0x7);
+                                writehscx_0(sp->membase, hscx, HSCX_XCCR, 7);
+                                writehscx_0(sp->membase, hscx, HSCX_RCCR, 7);
+                        } else {
+                                writehscx_0(sp->membase, hscx, HSCX_CCR2, 0x30);
+                                writehscx_0(sp->membase, hscx, HSCX_TSAX, 0x3);
+                                writehscx_0(sp->membase, hscx, HSCX_TSAR, 0x3);
+                                writehscx_0(sp->membase, hscx, HSCX_XCCR, 7);
+                                writehscx_0(sp->membase, hscx, HSCX_RCCR, 7);
+                        }
+                        writehscx_0(sp->membase, hscx, HSCX_MODE, 0x8c);
+                        writehscx_0(sp->membase, hscx, HSCX_CMDR, 0x41);
+                        break;
+                }
+                writehscx_0(sp->membase, hscx, HSCX_ISTA, 0x00);
+        } else {
+                writehscx_3(sp->iobase, hscx, HSCX_CCR1, 0x85);
+                writehscx_3(sp->iobase, hscx, HSCX_XAD1, 0xFF);
+                writehscx_3(sp->iobase, hscx, HSCX_XAD2, 0xFF);
+                writehscx_3(sp->iobase, hscx, HSCX_RAH2, 0xFF);
+                writehscx_3(sp->iobase, hscx, HSCX_XBCH, 0x00);
+                writehscx_3(sp->iobase, hscx, HSCX_RLCR, 0x00);
+                
+                switch (mode) {
+                case (0):
+                        writehscx_3(sp->iobase, hscx, HSCX_CCR2, 0x30);
+                        writehscx_3(sp->iobase, hscx, HSCX_TSAX, 0xff);
+                        writehscx_3(sp->iobase, hscx, HSCX_TSAR, 0xff);
+                        writehscx_3(sp->iobase, hscx, HSCX_XCCR, 7);
+                        writehscx_3(sp->iobase, hscx, HSCX_RCCR, 7);
+                        writehscx_3(sp->iobase, hscx, HSCX_MODE, 0x84);
+                        break;
+                case (1):
+                        if (ichan == 0) {
+                                writehscx_3(sp->iobase, hscx, HSCX_CCR2, 0x30);
+                                writehscx_3(sp->iobase, hscx, HSCX_TSAX, 0x2f);
+                                writehscx_3(sp->iobase, hscx, HSCX_TSAR, 0x2f);
+                                writehscx_3(sp->iobase, hscx, HSCX_XCCR, 7);
+                                writehscx_3(sp->iobase, hscx, HSCX_RCCR, 7);
+                        } else {
+                                writehscx_3(sp->iobase, hscx, HSCX_CCR2, 0x30);
+                                writehscx_3(sp->iobase, hscx, HSCX_TSAX, 0x3);
+                                writehscx_3(sp->iobase, hscx, HSCX_TSAR, 0x3);
+                                writehscx_3(sp->iobase, hscx, HSCX_XCCR, 7);
+                                writehscx_3(sp->iobase, hscx, HSCX_RCCR, 7);
+                        }
+                        writehscx_3(sp->iobase, hscx, HSCX_MODE, 0xe4);
+                        writehscx_3(sp->iobase, hscx, HSCX_CMDR, 0x41);
+                        break;
+                case (2):
+                        if (ichan == 0) {
+                                writehscx_3(sp->iobase, hscx, HSCX_CCR2, 0x30);
+                                writehscx_3(sp->iobase, hscx, HSCX_TSAX, 0x2f);
+                                writehscx_3(sp->iobase, hscx, HSCX_TSAR, 0x2f);
+                                writehscx_3(sp->iobase, hscx, HSCX_XCCR, 7);
+                                writehscx_3(sp->iobase, hscx, HSCX_RCCR, 7);
+                        } else {
+                                writehscx_3(sp->iobase, hscx, HSCX_CCR2, 0x30);
+                                writehscx_3(sp->iobase, hscx, HSCX_TSAX, 0x3);
+                                writehscx_3(sp->iobase, hscx, HSCX_TSAR, 0x3);
+                                writehscx_3(sp->iobase, hscx, HSCX_XCCR, 7);
+                                writehscx_3(sp->iobase, hscx, HSCX_RCCR, 7);
+                        }
+                        writehscx_3(sp->iobase, hscx, HSCX_MODE, 0x8c);
+                        writehscx_3(sp->iobase, hscx, HSCX_CMDR, 0x41);
+                        break;
+                }
+                writehscx_3(sp->iobase, hscx, HSCX_ISTA, 0x00);
+        }
+}
+
+void
+teles_addlist(struct IsdnCardState *sp,
+	      struct PStack *st)
+{
+	st->next = sp->stlist;
+	sp->stlist = st;
+}
+
+void
+teles_rmlist(struct IsdnCardState *sp,
+	     struct PStack *st)
+{
+	struct PStack  *p;
+
+	if (sp->stlist == st)
+		sp->stlist = st->next;
+	else {
+		p = sp->stlist;
+		while (p)
+			if (p->next == st) {
+				p->next = st->next;
+				return;
+			} else
+				p = p->next;
+	}
+}
+
+
+static void
+teles_l2l1(struct PStack *st, int pr,
+	   struct BufHeader *ibh)
+{
+	struct IsdnCardState *sp = (struct IsdnCardState *)
+	st->l1.hardware;
+
+
+	switch (pr) {
+	  case (PH_DATA):
+		  if (sp->xmtibh)
+			  BufQueueLink(&sp->sq, ibh);
+		  else {
+			  sp->xmtibh = ibh;
+			  sp->sendptr = 0;
+			  sp->releasebuf = !0;
+			  fill_fifo(sp);
+		  }
+		  break;
+	  case (PH_DATA_PULLED):
+		  if (sp->xmtibh) {
+			  printk(KERN_DEBUG "teles_l2l1: this shouldn't happen\n");
+			  break;
+		  }
+		  sp->xmtibh = ibh;
+		  sp->sendptr = 0;
+		  sp->releasebuf = 0;
+		  fill_fifo(sp);
+		  break;
+	  case (PH_REQUEST_PULL):
+		  if (!sp->xmtibh) {
+			  st->l1.requestpull = 0;
+			  st->l1.l1l2(st, PH_PULL_ACK, NULL);
+		  } else
+			  st->l1.requestpull = !0;
+		  break;
+	}
+}
+
+static void
+check_ph_act(struct IsdnCardState *sp)
+{
+	struct PStack  *st = sp->stlist;
+
+	while (st) {
+		if (st->l1.act_state)
+			return;
+		st = st->next;
+	}
+	sp->ph_active = 0;
+}
+
+static void
+teles_manl1(struct PStack *st, int pr,
+	    void *arg)
+{
+	struct IsdnCardState *sp = (struct IsdnCardState *)
+	st->l1.hardware;
+	long            flags;
+
+	switch (pr) {
+	  case (PH_ACTIVATE):
+		  save_flags(flags);
+		  cli();
+		  if (sp->ph_active == 5) {
+			  st->l1.act_state = 2;
+			  restore_flags(flags);
+			  st->l1.l1man(st, PH_ACTIVATE, NULL);
+		  } else {
+			  st->l1.act_state = 1;
+			  if (sp->ph_active == 0)
+				  restart_ph(sp);
+			  restore_flags(flags);
+		  }
+		  break;
+	  case (PH_DEACTIVATE):
+		  st->l1.act_state = 0;
+		  check_ph_act(sp);
+		  break;
+	}
+}
+
+static void
+teles_l2l1discardq(struct PStack *st, int pr,
+		   void *heldby, int releasetoo)
+{
+	struct IsdnCardState *sp = (struct IsdnCardState *) st->l1.hardware;
+
+#ifdef DEBUG_MAGIC
+	if (sp->magic != 301271) {
+		printk(KERN_DEBUG "isac_discardq magic not 301271\n");
+		return;
+	}
+#endif
+
+	BufQueueDiscard(&sp->sq, pr, heldby, releasetoo);
+}
+
+void
+setstack_teles(struct PStack *st, struct IsdnCardState *sp)
+{
+	st->l1.hardware = sp;
+	st->l1.sbufpool = &(sp->sbufpool);
+	st->l1.rbufpool = &(sp->rbufpool);
+	st->l1.smallpool = &(sp->smallpool);
+	st->protocol = sp->teistack->protocol;
+
+	setstack_tei(st);
+
+	st->l1.stlistp = &(sp->stlist);
+	st->l1.act_state = 0;
+	st->l2.l2l1 = teles_l2l1;
+	st->l2.l2l1discardq = teles_l2l1discardq;
+	st->ma.manl1 = teles_manl1;
+	st->l1.requestpull = 0;
+}
+
+void
+init_hscxstate(struct IsdnCardState *sp,
+	       int hscx)
+{
+	struct HscxState *hsp = sp->hs + hscx;
+
+	hsp->sp = sp;
+	hsp->hscx = hscx;
+	hsp->membase = sp->membase;
+	hsp->iobase = sp->iobase;
+
+	hsp->tqueue.next = 0;
+	hsp->tqueue.sync = 0;
+	hsp->tqueue.routine = (void *) (void *) hscx_bh;
+	hsp->tqueue.data = hsp;
+
+	hsp->inuse = 0;
+	hsp->init = 0;
+	hsp->active = 0;
+
+#ifdef DEBUG_MAGIC
+	hsp->magic = 301270;
+#endif
+}
+
+void
+initcard(int cardnr)
+{
+	struct IsdnCardState *sp;
+	struct IsdnCard *card = cards + cardnr;
+
+	sp = (struct IsdnCardState *)
+	    Smalloc(sizeof(struct IsdnCardState), GFP_KERNEL,
+		    "struct IsdnCardState");
+
+	sp->membase = card->membase;
+	sp->iobase = card->iobase;
+	sp->cardnr = cardnr;
+
+	BufPoolInit(&sp->sbufpool, ISAC_SBUF_ORDER, ISAC_SBUF_BPPS,
+		    ISAC_SBUF_MAXPAGES);
+	BufPoolInit(&sp->rbufpool, ISAC_RBUF_ORDER, ISAC_RBUF_BPPS,
+		    ISAC_RBUF_MAXPAGES);
+	BufPoolInit(&sp->smallpool, ISAC_SMALLBUF_ORDER, ISAC_SMALLBUF_BPPS,
+		    ISAC_SMALLBUF_MAXPAGES);
+
+	sp->dlogspace = Smalloc(4096, GFP_KERNEL, "dlogspace");
+
+	initisac(card->membase, card->iobase);
+
+	sp->rcvibh = NULL;
+	sp->rcvptr = 0;
+	sp->xmtibh = NULL;
+	sp->sendptr = 0;
+	sp->event = 0;
+	sp->tqueue.next = 0;
+	sp->tqueue.sync = 0;
+	sp->tqueue.routine = (void *) (void *) isac_bh;
+	sp->tqueue.data = sp;
+
+	BufQueueInit(&sp->rq);
+	BufQueueInit(&sp->sq);
+
+	sp->stlist = NULL;
+
+	sp->ph_active = 0;
+
+	sp->dlogflag = 0;
+	sp->debug = 0;
+
+	sp->releasebuf = 0;
+#ifdef DEBUG_MAGIC
+	sp->magic = 301271;
+#endif
+
+	cards[sp->cardnr].sp = sp;
+
+	init_hscxstate(sp, 0);
+	init_hscxstate(sp, 1);
+
+	modehscx(sp->hs, 0, 0);
+	modehscx(sp->hs + 1, 0, 0);
+
+	WRITEISAC(sp->membase, sp->iobase, ISAC_MASK, 0x0);
+}
+
+static int
+get_irq(int cardnr)
+{
+	struct IsdnCard *card = cards + cardnr;
+	long            flags;
+
+	save_flags(flags);
+	cli();
+	if (request_irq(card->interrupt, &teles_interrupt,
+			SA_INTERRUPT, "teles")) {
+		printk(KERN_WARNING "Teles couldn't get interrupt %d\n",
+                       card->interrupt);
+		restore_flags(flags);
+		return (!0);
+	}
+	irq2dev_map[card->interrupt] = (void *) card->sp;
+	restore_flags(flags);
+	return (0);
+}
+
+static void
+release_irq(int cardnr)
+{
+	struct IsdnCard *card = cards + cardnr;
+
+	irq2dev_map[card->interrupt] = NULL;
+	free_irq(card->interrupt);
+}
+
+void
+close_hscxstate(struct HscxState *hs)
+{
+	modehscx(hs, 0, 0);
+	hs->inuse = 0;
+
+	if (hs->init) {
+		BufPoolFree(&hs->smallpool);
+		BufPoolFree(&hs->rbufpool);
+		BufPoolFree(&hs->sbufpool);
+	}
+	hs->init = 0;
+}
+
+void
+closecard(int cardnr)
+{
+	struct IsdnCardState *sp = cards[cardnr].sp;
+
+	cards[cardnr].sp = NULL;
+
+	Sfree(sp->dlogspace);
+
+	BufPoolFree(&sp->smallpool);
+	BufPoolFree(&sp->rbufpool);
+	BufPoolFree(&sp->sbufpool);
+
+	close_hscxstate(sp->hs + 1);
+	close_hscxstate(sp->hs);
+
+	if (cards[cardnr].iobase)
+		release_region(cards[cardnr].iobase, 8);
+
+	Sfree((void *) sp);
+}
+
+void
+teles_shiftcards(int idx)
+{
+        int i;
+
+        for (i = idx; i < 15; i++)
+                memcpy(&cards[i],&cards[i+1],sizeof(cards[i]));
+}
+
+int
+teles_inithardware(void)
+{
+        int             foundcards = 0;
+	int             i = 0;
+
+	while (i < nrcards) {
+                if (!cards[i].protocol)
+                        break;
+		switch (checkcard(i)) {
+		  case (0):
+			  initcard(i);
+			  if (get_irq(i)) {
+				  closecard(i);
+                                  teles_shiftcards(i);
+                          } else {
+                                  foundcards++;
+                                  i++;
+                          }
+			  break;
+		  case (-1):
+                          teles_shiftcards(i);
+			  break;
+		  case (-2):
+			  release_region(cards[i].iobase, 8);
+                          teles_shiftcards(i);
+			  printk(KERN_WARNING "NO Teles card found at 0x%x!\n", cards[i].iobase);
+			  break;
+		}
+        }
+        return foundcards;
+}
+
+void
+teles_closehardware(void)
+{
+	int             i;
+
+	for (i = 0; i < nrcards; i++)
+		if (cards[i].sp) {
+			release_irq(i);
+			closecard(i);
+		}
+}
+
+static void
+hscx_l2l1(struct PStack *st, int pr,
+	  struct BufHeader *ibh)
+{
+	struct IsdnCardState *sp = (struct IsdnCardState *)
+	st->l1.hardware;
+	struct HscxState *hsp = sp->hs + st->l1.hscx;
+
+	switch (pr) {
+	  case (PH_DATA):
+		  if (hsp->xmtibh)
+			  BufQueueLink(&hsp->sq, ibh);
+		  else {
+			  hsp->xmtibh = ibh;
+			  hsp->sendptr = 0;
+			  hsp->releasebuf = !0;
+			  hscx_fill_fifo(hsp);
+		  }
+		  break;
+	  case (PH_DATA_PULLED):
+		  if (hsp->xmtibh) {
+			  printk(KERN_DEBUG "hscx_l2l1: this shouldn't happen\n");
+			  break;
+		  }
+		  hsp->xmtibh = ibh;
+		  hsp->sendptr = 0;
+		  hsp->releasebuf = 0;
+		  hscx_fill_fifo(hsp);
+		  break;
+	  case (PH_REQUEST_PULL):
+		  if (!hsp->xmtibh) {
+			  st->l1.requestpull = 0;
+			  st->l1.l1l2(st, PH_PULL_ACK, NULL);
+		  } else
+			  st->l1.requestpull = !0;
+		  break;
+	}
+
+}
+
+extern struct IsdnBuffers *tracebuf;
+
+static void
+hscx_l2l1discardq(struct PStack *st, int pr, void *heldby,
+		  int releasetoo)
+{
+	struct IsdnCardState *sp = (struct IsdnCardState *)
+	st->l1.hardware;
+	struct HscxState *hsp = sp->hs + st->l1.hscx;
+
+#ifdef DEBUG_MAGIC
+	if (hsp->magic != 301270) {
+		printk(KERN_DEBUG "hscx_discardq magic not 301270\n");
+		return;
+	}
+#endif
+
+	BufQueueDiscard(&hsp->sq, pr, heldby, releasetoo);
+}
+
+static int
+open_hscxstate(struct IsdnCardState *sp,
+	       int hscx)
+{
+	struct HscxState *hsp = sp->hs + hscx;
+
+	if (!hsp->init) {
+		BufPoolInit(&hsp->sbufpool, HSCX_SBUF_ORDER, HSCX_SBUF_BPPS,
+			    HSCX_SBUF_MAXPAGES);
+		BufPoolInit(&hsp->rbufpool, HSCX_RBUF_ORDER, HSCX_RBUF_BPPS,
+			    HSCX_RBUF_MAXPAGES);
+		BufPoolInit(&hsp->smallpool, HSCX_SMALLBUF_ORDER, HSCX_SMALLBUF_BPPS,
+			    HSCX_SMALLBUF_MAXPAGES);
+	}
+	hsp->init = !0;
+
+	BufQueueInit(&hsp->rq);
+	BufQueueInit(&hsp->sq);
+
+	hsp->releasebuf = 0;
+	hsp->rcvibh = NULL;
+	hsp->xmtibh = NULL;
+	hsp->rcvptr = 0;
+	hsp->sendptr = 0;
+	hsp->event = 0;
+	return (0);
+}
+
+static void
+hscx_manl1(struct PStack *st, int pr,
+	   void *arg)
+{
+	struct IsdnCardState *sp = (struct IsdnCardState *)
+	st->l1.hardware;
+	struct HscxState *hsp = sp->hs + st->l1.hscx;
+
+	switch (pr) {
+	  case (PH_ACTIVATE):
+		  hsp->active = !0;
+		  modehscx(hsp, st->l1.hscxmode, st->l1.hscxchannel);
+		  st->l1.l1man(st, PH_ACTIVATE, NULL);
+		  break;
+	  case (PH_DEACTIVATE):
+		  if (!hsp->xmtibh)
+			  modehscx(hsp, 0, 0);
+
+		  hsp->active = 0;
+		  break;
+	}
+}
+
+int
+setstack_hscx(struct PStack *st, struct HscxState *hs)
+{
+	if (open_hscxstate(st->l1.hardware, hs->hscx))
+		return (-1);
+
+	st->l1.hscx = hs->hscx;
+	st->l2.l2l1 = hscx_l2l1;
+	st->ma.manl1 = hscx_manl1;
+	st->l2.l2l1discardq = hscx_l2l1discardq;
+
+	st->l1.sbufpool = &hs->sbufpool;
+	st->l1.rbufpool = &hs->rbufpool;
+	st->l1.smallpool = &hs->smallpool;
+	st->l1.act_state = 0;
+	st->l1.requestpull = 0;
+
+	hs->st = st;
+	return (0);
+}
+
+void
+teles_reportcard(int cardnr)
+{
+
+	printk(KERN_DEBUG "teles_reportcard\n");
+
+}
diff -u --recursive --new-file v1.3.68/linux/drivers/isdn/teles/config.c linux/drivers/isdn/teles/config.c
--- v1.3.68/linux/drivers/isdn/teles/config.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/isdn/teles/config.c	Mon Feb 26 11:58:05 1996
@@ -0,0 +1,40 @@
+#define __NO_VERSION__
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/timer.h>
+#include "teles.h"
+
+/*
+ * This structure array contains one entry per card. An entry looks
+ * like this:
+ * 
+ * { membase,irq,portbase,protocol,NULL }
+ *
+ * protocol can be either ISDN_PTYPE_EURO or ISDN_PTYPE_1TR6
+ *
+ * Cards which don't have an io port (Teles 8 bit cards for
+ * example) can be entered with io port 0x0
+ *
+ * For the Teles 16.3, membase has to be set to 0.
+ *
+ */
+
+struct IsdnCard cards[] =
+{
+	{(byte *) 0xd0000, 15, 0xd80, ISDN_PTYPE_EURO, NULL},	/* example */
+	{NULL, 0, 0, 0, NULL},
+	{NULL, 0, 0, 0, NULL},
+	{NULL, 0, 0, 0, NULL},
+	{NULL, 0, 0, 0, NULL},
+	{NULL, 0, 0, 0, NULL},
+	{NULL, 0, 0, 0, NULL},
+	{NULL, 0, 0, 0, NULL},
+	{NULL, 0, 0, 0, NULL},
+	{NULL, 0, 0, 0, NULL},
+	{NULL, 0, 0, 0, NULL},
+	{NULL, 0, 0, 0, NULL},
+	{NULL, 0, 0, 0, NULL},
+	{NULL, 0, 0, 0, NULL},
+	{NULL, 0, 0, 0, NULL},
+	{NULL, 0, 0, 0, NULL},
+};
diff -u --recursive --new-file v1.3.68/linux/drivers/isdn/teles/fsm.c linux/drivers/isdn/teles/fsm.c
--- v1.3.68/linux/drivers/isdn/teles/fsm.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/isdn/teles/fsm.c	Mon Feb 26 11:58:05 1996
@@ -0,0 +1,148 @@
+#define __NO_VERSION__
+#include "teles.h"
+
+void
+FsmNew(struct Fsm *fsm,
+       struct FsmNode *fnlist, int fncount)
+{
+	int             i;
+
+	fsm->jumpmatrix = (int *) Smalloc(4L * fsm->state_count * fsm->event_count,
+					  GFP_KERNEL, "Fsm jumpmatrix");
+	memset(fsm->jumpmatrix, 0, 4L * fsm->state_count * fsm->event_count);
+
+	for (i = 0; i < fncount; i++)
+		fsm->jumpmatrix[fsm->state_count * fnlist[i].event +
+			      fnlist[i].state] = (int) fnlist[i].routine;
+}
+
+void
+FsmFree(struct Fsm *fsm)
+{
+	Sfree((void *) fsm->jumpmatrix);
+}
+
+int
+FsmEvent(struct FsmInst *fi, int event, void *arg)
+{
+	void            (*r) (struct FsmInst *, int, void *);
+	char            str[80];
+
+	r = (void (*)) fi->fsm->jumpmatrix[fi->fsm->state_count * event + fi->state];
+	if (r) {
+		if (fi->debug) {
+			sprintf(str, "State %s Event %s",
+				fi->fsm->strState[fi->state],
+				fi->fsm->strEvent[event]);
+			fi->printdebug(fi, str);
+		}
+		r(fi, event, arg);
+		return (0);
+	} else {
+		if (fi->debug) {
+			sprintf(str, "State %s Event %s no routine",
+				fi->fsm->strState[fi->state],
+				fi->fsm->strEvent[event]);
+			fi->printdebug(fi, str);
+		}
+		return (!0);
+	}
+}
+
+void
+FsmChangeState(struct FsmInst *fi, int newstate)
+{
+	char            str[80];
+
+	fi->state = newstate;
+	if (fi->debug) {
+		sprintf(str, "ChangeState %s",
+			fi->fsm->strState[newstate]);
+		fi->printdebug(fi, str);
+	}
+}
+
+static void
+FsmExpireTimer(struct FsmTimer *ft)
+{
+	FsmEvent(ft->fi, ft->event, ft->arg);
+}
+
+void
+FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft)
+{
+	ft->fi = fi;
+	ft->tl.function = (void *) FsmExpireTimer;
+	ft->tl.data = (long) ft;
+	init_timer(&ft->tl);
+}
+
+void
+FsmDelTimer(struct FsmTimer *ft, int where)
+{
+	long            flags;
+
+#if 0
+	if (ft->fi->debug) {
+		sprintf(str, "FsmDelTimer %lx %d", ft, where);
+		ft->fi->printdebug(ft->fi, str);
+	}
+#endif
+
+	save_flags(flags);
+	cli();
+	if (ft->tl.next)
+		del_timer(&ft->tl);
+	restore_flags(flags);
+}
+
+int
+FsmAddTimer(struct FsmTimer *ft,
+	    int millisec, int event, void *arg, int where)
+{
+
+#if 0
+	if (ft->fi->debug) {
+		sprintf(str, "FsmAddTimer %lx %d %d", ft, millisec, where);
+		ft->fi->printdebug(ft->fi, str);
+	}
+#endif
+
+	if (ft->tl.next) {
+		printk(KERN_WARNING "FsmAddTimer: timer already active!\n");
+		return -1;
+	}
+	init_timer(&ft->tl);
+	ft->event = event;
+	ft->arg = arg;
+	ft->tl.expires = jiffies + (millisec * HZ) / 1000;
+	add_timer(&ft->tl);
+	return 0;
+}
+
+int
+FsmTimerRunning(struct FsmTimer *ft)
+{
+	return (ft->tl.next != NULL);
+}
+
+void
+jiftime(char *s, long mark)
+{
+	s += 8;
+
+	*s-- = '\0';
+	*s-- = mark % 10 + '0';
+	mark /= 10;
+	*s-- = mark % 10 + '0';
+	mark /= 10;
+	*s-- = '.';
+	*s-- = mark % 10 + '0';
+	mark /= 10;
+	*s-- = mark % 6 + '0';
+	mark /= 6;
+	*s-- = ':';
+	*s-- = mark % 10 + '0';
+	mark /= 10;
+	*s-- = mark % 10 + '0';
+}
diff -u --recursive --new-file v1.3.68/linux/drivers/isdn/teles/isdnl2.c linux/drivers/isdn/teles/isdnl2.c
--- v1.3.68/linux/drivers/isdn/teles/isdnl2.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/isdn/teles/isdnl2.c	Mon Feb 26 11:58:06 1996
@@ -0,0 +1,1451 @@
+#define __NO_VERSION__
+#include "teles.h"
+
+#define TIMER_1 2000
+
+static void     l2m_debug(struct FsmInst *fi, char *s);
+
+struct Fsm      l2fsm =
+{NULL, 0, 0};
+
+#if 0
+
+
+enum {
+	ST_PH_NULL,
+	ST_PH_ACTIVATED,
+	ST_PH_ACTIVE,
+};
+
+#define PH_STATE_COUNT (ST_PH_ACTIVE+1)
+
+static char    *strPhState[] =
+{
+	"ST_PH_NULL",
+	"ST_PH_ACTIVATED",
+	"ST_PH_ACTIVE",
+};
+
+enum {
+	EV_PH_ACTIVATE_REQ,
+	EV_PH_ACTIVATE,
+	EV_PH_DEACTIVATE_REQ,
+	EV_PH_DEACTIVATE,
+};
+
+#define PH_EVENT_COUNT (EV_PH_DEACTIVATE+1)
+
+static char    *strPhEvent[] =
+{
+	"EV_PH_ACTIVATE_REQ",
+	"EV_PH_ACTIVATE",
+	"EV_PH_DEACTIVATE_REQ",
+	"EV_PH_DEACTIVATE",
+};
+
+#endif
+
+enum {
+	ST_L2_1,
+	ST_L2_3,
+	ST_L2_4,
+	ST_L2_5,
+	ST_L2_6,
+	ST_L2_7,
+	ST_L2_8,
+};
+
+#define L2_STATE_COUNT (ST_L2_8+1)
+
+static char    *strL2State[] =
+{
+	"ST_L2_1",
+	"ST_L2_3",
+	"ST_L2_4",
+	"ST_L2_5",
+	"ST_L2_6",
+	"ST_L2_7",
+	"ST_L2_8",
+};
+
+enum {
+	EV_L2_UI,
+	EV_L2_SABMX,
+	EV_L2_UA,
+	EV_L2_DISC,
+	EV_L2_I,
+	EV_L2_RR,
+	EV_L2_REJ,
+	EV_L2_FRMR,
+	EV_L2_DL_DATA,
+	EV_L2_DL_ESTABLISH,
+	EV_L2_MDL_ASSIGN,
+	EV_L2_DL_UNIT_DATA,
+	EV_L2_DL_RELEASE,
+	EV_L2_MDL_NOTEIPROC,
+	EV_L2_T200,
+	EV_L2_ACK_PULL,
+	EV_L2_T203,
+	EV_L2_RNR,
+};
+
+#define L2_EVENT_COUNT (EV_L2_RNR+1)
+
+static char    *strL2Event[] =
+{
+	"EV_L2_UI",
+	"EV_L2_SABMX",
+	"EV_L2_UA",
+	"EV_L2_DISC",
+	"EV_L2_I",
+	"EV_L2_RR",
+	"EV_L2_REJ",
+	"EV_L2_FRMR",
+	"EV_L2_DL_DATA",
+	"EV_L2_DL_ESTABLISH",
+	"EV_L2_MDL_ASSIGN",
+	"EV_L2_DL_UNIT_DATA",
+	"EV_L2_DL_RELEASE",
+	"EV_L2_MDL_NOTEIPROC",
+	"EV_L2_T200",
+	"EV_L2_ACK_PULL",
+	"EV_L2_T203",
+	"EV_L2_RNR",
+};
+
+#if 0
+static void
+ph_r1(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack  *st = fi->userdata;
+
+	FsmChangeState(fi, ST_PH_ACTIVATED);
+	st->l1.service_down(st, PH_ACTIVATE, NULL);
+}
+
+static void
+ph_r2(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack  *st = fi->userdata;
+
+	FsmChangeState(fi, ST_PH_ACTIVE);
+	st->l3.service_up(st, DL_ACTIVATE_CNF, NULL);
+}
+
+static void
+ph_r3(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack  *st = fi->userdata;
+
+	FsmChangeState(fi, ST_PH_NULL);
+	st->l1.service_down(st, PH_DEACTIVATE, NULL);
+}
+
+static void
+ph_r4(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack  *st = fi->userdata;
+
+	FsmChangeState(fi, ST_PH_NULL);
+	st->l3.service_up(st, DL_DEACTIVATE_IND, NULL);
+}
+
+static struct FsmNode PhFnList[] =
+{
+	{ST_PH_NULL, EV_PH_ACTIVATE_REQ, ph_r1},
+	{ST_PH_ACTIVATED, EV_PH_ACTIVATE, ph_r2},
+	{ST_PH_ACTIVATED, EV_PH_DEACTIVATE, ph_r4},
+	{ST_PH_ACTIVE, EV_PH_DEACTIVATE_REQ, ph_r3},
+};
+
+#define PH_FN_COUNT (sizeof(PhFnList)/sizeof(struct FsmNode))
+#endif
+
+int             errcount = 0;
+
+static int      l2addrsize(struct Layer2 *tsp);
+
+static int
+cansend(struct PStack *st)
+{
+	int             p1;
+
+	p1 = (st->l2.va + st->l2.window) % (st->l2.extended ? 128 : 8);
+	return (st->l2.vs != p1);
+}
+
+static void
+discard_i_queue(struct PStack *st)
+{
+	struct BufHeader *ibh;
+
+	while (!BufQueueUnlink(&ibh, &st->l2.i_queue))
+		BufPoolRelease(ibh);
+}
+
+#ifdef DEFINED_BUT_NOT_USED
+static void
+discard_window(struct PStack *st)
+{
+	struct BufHeader *ibh;
+	struct Layer2  *l2;
+	int             i, p1, p2;
+
+	l2 = &st->l2;
+	p1 = l2->vs - l2->va;
+	if (p1 < 0)
+		p1 += l2->extended ? 128 : 8;
+
+	for (i = 0; i < p1; i++) {
+		p2 = (i + l2->sow) % l2->window;
+		ibh = l2->windowar[p2];
+		BufPoolRelease(ibh);
+	}
+}
+#endif
+
+int
+l2headersize(struct Layer2 *tsp, int UI)
+{
+	return ((tsp->extended && (!UI) ? 2 : 1) + (tsp->laptype == LAPD ? 2 : 1));
+}
+
+int
+l2addrsize(struct Layer2 *tsp)
+{
+	return (tsp->laptype == LAPD ? 2 : 1);
+}
+
+static int
+sethdraddr(struct Layer2 *tsp,
+	   struct BufHeader *ibh, int rsp)
+{
+	byte           *ptr = DATAPTR(ibh);
+	int             crbit;
+
+	if (tsp->laptype == LAPD) {
+		crbit = rsp;
+		if (!tsp->orig)
+			crbit = !crbit;
+		*ptr++ = (tsp->sap << 2) | (crbit ? 2 : 0);
+		*ptr++ = (tsp->tei << 1) | 1;
+		return (2);
+	} else {
+		crbit = rsp;
+		if (tsp->orig)
+			crbit = !crbit;
+		if (crbit)
+			*ptr++ = 1;
+		else
+			*ptr++ = 3;
+		return (1);
+	}
+}
+
+static void
+enqueue_ui(struct PStack *st,
+	   struct BufHeader *ibh)
+{
+#ifdef FRITZDEBUG
+	static char     tmp[100];
+
+	sprintf(tmp, "enqueue_ui: %d bytes\n", ibh->datasize);
+	teles_putstatus(tmp);
+#endif
+	st->l2.l2l1(st, PH_DATA, ibh);
+}
+
+static void
+enqueue_super(struct PStack *st,
+	      struct BufHeader *ibh)
+{
+#ifdef FRITZDEBUG
+	static char     tmp[100];
+
+	sprintf(tmp, "enqueue_super: %d bytes\n", ibh->datasize);
+	teles_putstatus(tmp);
+#endif
+	st->l2.l2l1(st, PH_DATA, ibh);
+}
+
+static int
+legalnr(struct PStack *st, int nr)
+{
+	struct Layer2  *l2 = &st->l2;
+	int             lnr, lvs;
+
+	lvs = (l2->vs >= l2->va) ? l2->vs : (l2->vs + l2->extended ? 128 : 8);
+	lnr = (nr >= l2->va) ? nr : (nr + l2->extended ? 128 : 8);
+	return (lnr <= lvs);
+}
+
+static void
+setva(struct PStack *st, int nr)
+{
+	struct Layer2  *l2 = &st->l2;
+
+	if (l2->va != nr) {
+                while (l2->va != nr) {
+                        l2->va = (l2->va + 1) % (l2->extended ? 128 : 8);
+                        BufPoolRelease(l2->windowar[l2->sow]);
+                        l2->sow = (l2->sow + 1) % l2->window;
+                }
+                if (st->l4.l2writewakeup)
+                        st->l4.l2writewakeup(st);
+        }
+}
+
+static void
+l2s1(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack  *st = fi->userdata;
+
+	st->l2.l2tei(st, MDL_ASSIGN, (void *)st->l2.ces);
+	FsmChangeState(fi, ST_L2_3);
+}
+
+static void
+l2s2(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack  *st = fi->userdata;
+	struct BufHeader *ibh = arg;
+
+	byte           *ptr;
+	int             i;
+
+	i = sethdraddr(&(st->l2), ibh, 0);
+	ptr = DATAPTR(ibh);
+	ptr += i;
+	*ptr = 0x3;
+
+	enqueue_ui(st, ibh);
+}
+
+static void
+l2s3(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack  *st = fi->userdata;
+	struct BufHeader *ibh = arg;
+
+	st->l2.l2l3(st, DL_UNIT_DATA, ibh);
+}
+
+static void
+establishlink(struct FsmInst *fi)
+{
+	struct PStack  *st = fi->userdata;
+	struct BufHeader *ibh;
+	int             i;
+	byte           *ptr;
+
+	FsmChangeState(fi, ST_L2_5);
+	st->l2.rc = 0;
+
+	if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 1))
+		if (st->l2.l2m.debug)
+			l2m_debug(&st->l2.l2m, "FAT 1");
+
+
+	if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 15))
+		return;
+	i = sethdraddr(&st->l2, ibh, 0);
+	ptr = DATAPTR(ibh);
+	ptr += i;
+	if (st->l2.extended)
+		*ptr = 0x7f;
+	else
+		*ptr = 0x3f;
+	ibh->datasize = i + 1;
+
+	enqueue_super(st, ibh);
+}
+
+static void
+l2s11(struct FsmInst *fi, int event, void *arg)
+{
+	establishlink(fi);
+}
+
+static void
+l2s13(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack  *st = fi->userdata;
+	struct Channel *chanp = st->l4.userdata;
+	byte           *ptr;
+	struct BufHeader *ibh;
+	int             i;
+
+	FsmChangeState(fi, ST_L2_6);
+
+	FsmDelTimer(&st->l2.t203_timer, 1);
+	if (st->l2.t200_running) {
+		FsmDelTimer(&st->l2.t200_timer, 2);
+		st->l2.t200_running = 0;
+	}
+	st->l2.rc = 0;
+	if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 2))
+		if (st->l2.l2m.debug)
+			l2m_debug(&st->l2.l2m, "FAT 2");
+
+
+	if ((chanp->impair == 2) && (st->l2.laptype == LAPB))
+		goto nodisc;
+
+	if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 9))
+		return;
+	i = sethdraddr(&(st->l2), ibh, 0);
+	ptr = DATAPTR(ibh);
+	ptr += i;
+	*ptr = 0x53;
+	ibh->datasize = i + 1;
+	enqueue_super(st, ibh);
+
+      nodisc:
+	discard_i_queue(st);
+}
+
+static void
+l2s12(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack  *st = fi->userdata;
+	struct BufHeader *ibh = arg;
+	byte           *ptr;
+	int             i;
+
+	BufPoolRelease(ibh);
+	st->l2.vs = 0;
+	st->l2.va = 0;
+	st->l2.vr = 0;
+	st->l2.sow = 0;
+	FsmChangeState(fi, ST_L2_7);
+	if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 3))
+		if (st->l2.l2m.debug)
+			l2m_debug(&st->l2.l2m, "FAT 3");
+
+	st->l2.l2man(st, DL_ESTABLISH, NULL);
+
+	if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 10))
+		return;
+	i = sethdraddr(&(st->l2), ibh, 0);
+	ptr = DATAPTR(ibh);
+	ptr += i;
+	*ptr = 0x73;
+	ibh->datasize = i + 1;
+	enqueue_super(st, ibh);
+
+}
+
+static void
+l2s14(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack  *st = fi->userdata;
+	struct BufHeader *ibh = arg;
+	struct Channel *chanp = st->l4.userdata;
+	byte           *ptr;
+	int             i, p;
+
+	ptr = DATAPTR(ibh);
+	ptr += l2addrsize(&(st->l2));
+	p = (*ptr) & 0x10;
+	BufPoolRelease(ibh);
+
+	FsmChangeState(fi, ST_L2_4);
+
+	FsmDelTimer(&st->l2.t203_timer, 3);
+	if (st->l2.t200_running) {
+		FsmDelTimer(&st->l2.t200_timer, 4);
+		st->l2.t200_running = 0;
+	}
+	if ((chanp->impair == 1) && (st->l2.laptype == LAPB))
+		goto noresponse;
+
+	if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 11))
+		return;
+	i = sethdraddr(&(st->l2), ibh, 0);
+	ptr = DATAPTR(ibh);
+	ptr += i;
+	*ptr = 0x63 | (p ? 0x10 : 0x0);
+	ibh->datasize = i + 1;
+	enqueue_super(st, ibh);
+
+      noresponse:
+	st->l2.l2man(st, DL_RELEASE, NULL);
+
+}
+
+static void
+l2s5(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack  *st = fi->userdata;
+	struct BufHeader *ibh = arg;
+	int             f;
+	byte           *data;
+
+	data = DATAPTR(ibh);
+	data += l2addrsize(&(st->l2));
+
+	f = *data & 0x10;
+	BufPoolRelease(ibh);
+
+	if (f) {
+		st->l2.vs = 0;
+		st->l2.va = 0;
+		st->l2.vr = 0;
+		st->l2.sow = 0;
+		FsmChangeState(fi, ST_L2_7);
+
+		FsmDelTimer(&st->l2.t200_timer, 5);
+		if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 4))
+			if (st->l2.l2m.debug)
+				l2m_debug(&st->l2.l2m, "FAT 4");
+
+
+		st->l2.l2man(st, DL_ESTABLISH, NULL);
+	}
+}
+
+static void
+l2s15(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack  *st = fi->userdata;
+	struct BufHeader *ibh = arg;
+	int             f;
+	byte           *data;
+
+	data = DATAPTR(ibh);
+	data += l2addrsize(&st->l2);
+
+	f = *data & 0x10;
+	BufPoolRelease(ibh);
+
+	if (f) {
+		FsmDelTimer(&st->l2.t200_timer, 6);
+		FsmChangeState(fi, ST_L2_4);
+		st->l2.l2man(st, DL_RELEASE, NULL);
+	}
+}
+
+static void
+l2s6(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack  *st = fi->userdata;
+	struct Channel *chanp = st->l4.userdata;
+	struct BufHeader *ibh = arg;
+	int             p, i, seq, rsp;
+	byte           *ptr;
+	struct Layer2  *l2;
+
+	l2 = &st->l2;
+	ptr = DATAPTR(ibh);
+
+	if (l2->laptype == LAPD) {
+		rsp = ptr[0] & 0x2;
+		if (l2->orig)
+			rsp = !rsp;
+	} else {
+		rsp = ptr[0] == 0x3;
+		if (l2->orig)
+			rsp = !rsp;
+	}
+
+	ptr += l2addrsize(l2);
+
+	if (l2->extended) {
+		p = (ptr[1] & 0x1) == 0x1;
+		seq = ptr[1] >> 1;
+	} else {
+		p = (ptr[0] & 0x10);
+		seq = (ptr[0] >> 5) & 0x7;
+	}
+	BufPoolRelease(ibh);
+
+	if ((chanp->impair == 4) && (st->l2.laptype == LAPB))
+		goto noresp;
+
+	if ((!rsp) && p) {
+		if (!BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 12)) {
+			i = sethdraddr(l2, ibh, !0);
+			ptr = DATAPTR(ibh);
+			ptr += i;
+
+			if (l2->extended) {
+				*ptr++ = 0x1;
+				*ptr++ = (l2->vr << 1) | (p ? 1 : 0);
+				i += 2;
+			} else {
+				*ptr++ = (l2->vr << 5) | 0x1 | (p ? 0x10 : 0x0);
+				i += 1;
+			}
+			ibh->datasize = i;
+			enqueue_super(st, ibh);
+		}
+	}
+      noresp:
+	if (legalnr(st, seq))
+		if (seq == st->l2.vs) {
+			setva(st, seq);
+			FsmDelTimer(&st->l2.t200_timer, 7);
+			st->l2.t200_running = 0;
+			FsmDelTimer(&st->l2.t203_timer, 8);
+			if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 5))
+				if (st->l2.l2m.debug)
+					l2m_debug(&st->l2.l2m, "FAT 5");
+
+			if (st->l2.i_queue.head)
+				st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+		} else if (st->l2.va != seq) {
+			setva(st, seq);
+			FsmDelTimer(&st->l2.t200_timer, 9);
+			if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 6))
+				if (st->l2.l2m.debug)
+					l2m_debug(&st->l2.l2m, "FAT 6");
+
+			if (st->l2.i_queue.head)
+				st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+		}
+}
+
+static void
+l2s7(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack  *st = fi->userdata;
+	struct BufHeader *ibh = arg;
+	int             i;
+	byte           *ptr;
+	struct IsdnCardState *sp = st->l1.hardware;
+	char            str[64];
+
+	i = sethdraddr(&st->l2, ibh, 0);
+	ptr = DATAPTR(ibh);
+
+	if (st->l2.laptype == LAPD)
+		if (sp->dlogflag) {
+			sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei);
+			dlogframe(sp, ptr + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
+				  str);
+		}
+	BufQueueLink(&st->l2.i_queue, ibh);
+
+	st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+}
+
+static void
+l2s8(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack  *st = fi->userdata;
+	struct Channel *chanp = st->l4.userdata;
+	struct BufHeader *ibh = arg;
+	byte           *ptr;
+	struct BufHeader *ibh2;
+	struct IsdnCardState *sp = st->l1.hardware;
+	struct Layer2  *l2 = &(st->l2);
+	int             i, p, seq, nr, wasok;
+	char            str[64];
+
+	ptr = DATAPTR(ibh);
+	ptr += l2addrsize(l2);
+	if (l2->extended) {
+		p = (ptr[1] & 0x1) == 0x1;
+		seq = ptr[0] >> 1;
+		nr = (ptr[1] >> 1) & 0x7f;
+	} else {
+		p = (ptr[0] & 0x10);
+		seq = (ptr[0] >> 1) & 0x7;
+		nr = (ptr[0] >> 5) & 0x7;
+	}
+
+	if (l2->vr == seq) {
+		wasok = !0;
+
+		l2->vr = (l2->vr + 1) % (l2->extended ? 128 : 8);
+		l2->rejexp = 0;
+
+		ptr = DATAPTR(ibh);
+		if (st->l2.laptype == LAPD)
+			if (sp->dlogflag) {
+				sprintf(str, "Q.931 frame network->user tei %d", st->l2.tei);
+				dlogframe(st->l1.hardware, ptr + l2->ihsize,
+					ibh->datasize - l2->ihsize, str);
+			}
+	      label8_1:
+		if ((chanp->impair == 3) && (st->l2.laptype == LAPB))
+			goto noRR;
+
+		if (!BufPoolGet(&ibh2, st->l1.smallpool, GFP_ATOMIC, (void *) st, 13)) {
+			i = sethdraddr(&(st->l2), ibh2, p);
+			ptr = DATAPTR(ibh2);
+			ptr += i;
+
+			if (l2->extended) {
+				*ptr++ = 0x1;
+				*ptr++ = (l2->vr << 1) | (p ? 1 : 0);
+				i += 2;
+			} else {
+				*ptr++ = (l2->vr << 5) | 0x1 | (p ? 0x10 : 0x0);
+				i += 1;
+			}
+			ibh2->datasize = i;
+			enqueue_super(st, ibh2);
+		      noRR:
+		}
+	} else {
+	        /* n(s)!=v(r) */
+		wasok = 0;
+		BufPoolRelease(ibh);
+		if (st->l2.rejexp) {
+			if (p)
+				goto label8_1;
+		} else {
+			st->l2.rejexp = !0;
+			if (!BufPoolGet(&ibh2, st->l1.smallpool, GFP_ATOMIC, (void *) st, 14)) {
+				i = sethdraddr(&(st->l2), ibh2, p);
+				ptr = DATAPTR(ibh2);
+				ptr += i;
+
+				if (l2->extended) {
+					*ptr++ = 0x9;
+					*ptr++ = (l2->vr << 1) | (p ? 1 : 0);
+					i += 2;
+				} else {
+					*ptr++ = (l2->vr << 5) | 0x9 | (p ? 0x10 : 0x0);
+					i += 1;
+				}
+				ibh2->datasize = i;
+				enqueue_super(st, ibh2);
+			}
+		}
+	}
+
+	if (legalnr(st, nr))
+		if (nr == st->l2.vs) {
+			setva(st, nr);
+			FsmDelTimer(&st->l2.t200_timer, 10);
+			st->l2.t200_running = 0;
+			FsmDelTimer(&st->l2.t203_timer, 11);
+			if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 7))
+				if (st->l2.l2m.debug)
+					l2m_debug(&st->l2.l2m, "FAT 5");
+
+			if (st->l2.i_queue.head)
+				st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+		} else if (nr != st->l2.va) {
+			setva(st, nr);
+			FsmDelTimer(&st->l2.t200_timer, 12);
+			if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 8))
+				if (st->l2.l2m.debug)
+					l2m_debug(&st->l2.l2m, "FAT 6");
+
+			if (st->l2.i_queue.head)
+				st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+		}
+	if (wasok)
+		st->l2.l2l3(st, DL_DATA, ibh);
+
+}
+
+static void
+l2s17(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack  *st = fi->userdata;
+
+	st->l2.tei = (int) arg;
+	establishlink(fi);
+}
+
+static void
+enquiry_response(struct PStack *st)
+{
+	struct BufHeader *ibh2;
+	int             i;
+	byte           *ptr;
+	struct Layer2  *l2;
+
+	l2 = &st->l2;
+	if (!BufPoolGet(&ibh2, st->l1.smallpool, GFP_ATOMIC, (void *) st, 16)) {
+		i = sethdraddr(&(st->l2), ibh2, !0);
+		ptr = DATAPTR(ibh2);
+		ptr += i;
+
+		if (l2->extended) {
+			*ptr++ = 0x1;
+			*ptr++ = (l2->vr << 1) | 0x1;
+			i += 2;
+		} else {
+			*ptr++ = (l2->vr << 5) | 0x1 | 0x10;
+			i += 1;
+		}
+		ibh2->datasize = i;
+		enqueue_super(st, ibh2);
+	}
+}
+
+static void
+invoke_retransmission(struct PStack *st, int nr)
+{
+	struct Layer2  *l2 = &st->l2;
+	int             p1;
+
+	if (l2->vs != nr) {
+		while (l2->vs != nr) {
+
+			l2->vs = l2->vs - 1;
+			if (l2->vs < 0)
+				l2->vs += l2->extended ? 128 : 8;
+
+			p1 = l2->vs - l2->va;
+			if (p1 < 0)
+				p1 += l2->extended ? 128 : 8;
+			p1 = (p1 + l2->sow) % l2->window;
+
+			BufQueueLinkFront(&l2->i_queue, l2->windowar[p1]);
+		}
+		st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+	}
+}
+
+static void
+l2s16(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack  *st = fi->userdata;
+	struct BufHeader *ibh = arg;
+	int             p, seq, rsp;
+	byte           *ptr;
+	struct Layer2  *l2;
+
+	l2 = &(st->l2);
+	ptr = DATAPTR(ibh);
+
+	if (l2->laptype == LAPD) {
+		rsp = ptr[0] & 0x2;
+		if (l2->orig)
+			rsp = !rsp;
+	} else {
+		rsp = ptr[0] == 0x3;
+		if (l2->orig)
+			rsp = !rsp;
+	}
+
+
+	ptr += l2addrsize(l2);
+
+	if (l2->extended) {
+		p = (ptr[1] & 0x1) == 0x1;
+		seq = ptr[1] >> 1;
+	} else {
+		p = (ptr[0] & 0x10);
+		seq = (ptr[0] >> 5) & 0x7;
+	}
+	BufPoolRelease(ibh);
+
+	if ((!rsp) && p)
+		enquiry_response(st);
+
+	if (!legalnr(st, seq))
+		return;
+
+	setva(st, seq);
+	invoke_retransmission(st, seq);
+
+}
+
+static void
+l2s19(struct FsmInst *fi, int event, void *arg)
+{
+	FsmChangeState(fi, ST_L2_4);
+}
+
+static void
+l2s20(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack  *st = fi->userdata;
+	int             i;
+	struct BufHeader *ibh;
+	byte           *ptr;
+
+	if (st->l2.rc == st->l2.n200) {
+		FsmChangeState(fi, ST_L2_4);
+		st->l2.l2man(st, DL_RELEASE, NULL);
+	} else {
+		st->l2.rc++;
+
+		if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 9))
+			if (st->l2.l2m.debug)
+				l2m_debug(&st->l2.l2m, "FAT 7");
+
+		if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 15))
+			return;
+
+		i = sethdraddr(&st->l2, ibh, 0);
+		ptr = DATAPTR(ibh);
+		ptr += i;
+		if (st->l2.extended)
+			*ptr = 0x7f;
+		else
+			*ptr = 0x3f;
+		ibh->datasize = i + 1;
+		enqueue_super(st, ibh);
+	}
+}
+
+static void
+l2s21(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack  *st = fi->userdata;
+	struct Channel *chanp = st->l4.userdata;
+	int             i;
+	struct BufHeader *ibh;
+	byte           *ptr;
+
+	if (st->l2.rc == st->l2.n200) {
+		FsmChangeState(fi, ST_L2_4);
+		st->l2.l2man(st, DL_RELEASE, NULL);
+	} else {
+		st->l2.rc++;
+
+		if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 10))
+			if (st->l2.l2m.debug)
+				l2m_debug(&st->l2.l2m, "FAT 8");
+
+
+		if ((chanp->impair == 2) && (st->l2.laptype == LAPB))
+			goto nodisc;
+
+		if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 15))
+			return;
+
+		i = sethdraddr(&st->l2, ibh, 0);
+		ptr = DATAPTR(ibh);
+		ptr += i;
+		*ptr = 0x53;
+		ibh->datasize = i + 1;
+		enqueue_super(st, ibh);
+	      nodisc:
+
+	}
+}
+
+static void
+l2s22(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack  *st = fi->userdata;
+	struct BufHeader *ibh;
+	struct Layer2  *l2 = &st->l2;
+	byte           *ptr;
+	int             p1;
+
+	if (!cansend(st))
+		return;
+
+	if (BufQueueUnlink(&ibh, &l2->i_queue))
+		return;
+
+
+	p1 = l2->vs - l2->va;
+	if (p1 < 0)
+		p1 += l2->extended ? 128 : 8;
+	p1 = (p1 + l2->sow) % l2->window;
+	l2->windowar[p1] = ibh;
+
+	ptr = DATAPTR(ibh);
+	ptr += l2addrsize(l2);
+
+	if (l2->extended) {
+		*ptr++ = l2->vs << 1;
+		*ptr++ = (l2->vr << 1) | 0x1;
+		l2->vs = (l2->vs + 1) % 128;
+	} else {
+		*ptr++ = (l2->vr << 5) | (l2->vs << 1) | 0x10;
+		l2->vs = (l2->vs + 1) % 8;
+	}
+
+	st->l2.l2l1(st, PH_DATA_PULLED, ibh);
+
+	if (!st->l2.t200_running) {
+		FsmDelTimer(&st->l2.t203_timer, 13);
+		if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 11))
+			if (st->l2.l2m.debug)
+				l2m_debug(&st->l2.l2m, "FAT 9");
+
+		st->l2.t200_running = !0;
+	}
+	if (l2->i_queue.head && cansend(st))
+		st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+
+}
+
+static void
+transmit_enquiry(struct PStack *st)
+{
+	struct BufHeader *ibh;
+	byte           *ptr;
+
+	if (!BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 12)) {
+		ptr = DATAPTR(ibh);
+		ptr += sethdraddr(&st->l2, ibh, 0);
+
+		if (st->l2.extended) {
+			*ptr++ = 0x1;
+			*ptr++ = (st->l2.vr << 1) | 1;
+		} else {
+			*ptr++ = (st->l2.vr << 5) | 0x11;
+		}
+		ibh->datasize = ptr - DATAPTR(ibh);
+		enqueue_super(st, ibh);
+		if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 12))
+			if (st->l2.l2m.debug)
+				l2m_debug(&st->l2.l2m, "FAT 10");
+
+		st->l2.t200_running = !0;
+	}
+}
+
+static void
+l2s23(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack  *st = fi->userdata;
+
+	st->l2.t200_running = 0;
+
+	st->l2.rc = 1;
+	FsmChangeState(fi, ST_L2_8);
+	transmit_enquiry(st);
+}
+
+static void
+l2s24(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack  *st = fi->userdata;
+	struct BufHeader *ibh = arg;
+	int             p, seq, rsp;
+	byte           *ptr;
+	struct Layer2  *l2;
+
+	l2 = &st->l2;
+	ptr = DATAPTR(ibh);
+
+	if (l2->laptype == LAPD) {
+		rsp = ptr[0] & 0x2;
+		if (l2->orig)
+			rsp = !rsp;
+	} else {
+		rsp = ptr[0] == 0x3;
+		if (l2->orig)
+			rsp = !rsp;
+	}
+
+
+	ptr += l2addrsize(l2);
+
+	if (l2->extended) {
+		p = (ptr[1] & 0x1) == 0x1;
+		seq = ptr[1] >> 1;
+	} else {
+		p = (ptr[0] & 0x10);
+		seq = (ptr[0] >> 5) & 0x7;
+	}
+	BufPoolRelease(ibh);
+
+	if (rsp && p) {
+		if (legalnr(st, seq)) {
+			FsmChangeState(fi, ST_L2_7);
+			setva(st, seq);
+			if (st->l2.t200_running) {
+				FsmDelTimer(&st->l2.t200_timer, 14);
+				st->l2.t200_running = 0;
+			}
+			if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 13))
+				if (st->l2.l2m.debug)
+					l2m_debug(&st->l2.l2m, "FAT 11");
+
+			invoke_retransmission(st, seq);
+		}
+	} else {
+		if (!rsp && p)
+			enquiry_response(st);
+		if (legalnr(st, seq)) {
+			setva(st, seq);
+		}
+	}
+}
+
+static void
+l2s25(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack  *st = fi->userdata;
+
+	st->l2.rc = 0;
+	FsmChangeState(fi, ST_L2_8);
+	transmit_enquiry(st);
+}
+
+static void
+l2s26(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack  *st = fi->userdata;
+
+	if (st->l2.rc == st->l2.n200) {
+		l2s13(fi, event, NULL);
+	} else {
+		st->l2.rc++;
+		transmit_enquiry(st);
+	}
+}
+
+static void
+l2s27(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack  *st = fi->userdata;
+	struct BufHeader *ibh = arg;
+	byte           *ptr;
+	int             i, p, est;
+
+	ptr = DATAPTR(ibh);
+	ptr += l2addrsize(&st->l2);
+
+	if (st->l2.extended)
+		p = ptr[1] & 0x1;
+	else
+		p = ptr[0] & 0x10;
+
+	BufPoolRelease(ibh);
+
+	if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 10))
+		return;
+	i = sethdraddr(&st->l2, ibh, 0);
+	ptr = DATAPTR(ibh);
+	ptr += i;
+	*ptr = 0x63 | p;
+	ibh->datasize = i + 1;
+	enqueue_super(st, ibh);
+
+	if (st->l2.vs != st->l2.va) {
+		discard_i_queue(st);
+		est = !0;
+	} else
+		est = 0;
+
+	FsmDelTimer(&st->l2.t200_timer, 15);
+	st->l2.t200_running = 0;
+
+	if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 3))
+		if (st->l2.l2m.debug)
+			l2m_debug(&st->l2.l2m, "FAT 12");
+
+	st->l2.vs = 0;
+	st->l2.va = 0;
+	st->l2.vr = 0;
+	st->l2.sow = 0;
+
+
+	if (est)
+		st->l2.l2man(st, DL_ESTABLISH, NULL);
+
+}
+
+static void
+l2s28(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack  *st = fi->userdata;
+	struct BufHeader *ibh = arg;
+	byte           *ptr;
+	char            tmp[64];
+
+	ptr = DATAPTR(ibh);
+	ptr += l2addrsize(&st->l2);
+	ptr++;
+
+	if (st->l2.l2m.debug) {
+		if (st->l2.extended)
+			sprintf(tmp, "FRMR information %2x %2x %2x %2x %2x",
+				ptr[0], ptr[1], ptr[2], ptr[3], ptr[4]);
+		else
+			sprintf(tmp, "FRMR information %2x %2x %2x",
+				ptr[0], ptr[1], ptr[2]);
+
+		l2m_debug(&st->l2.l2m, tmp);
+	}
+	BufPoolRelease(ibh);
+}
+
+static int
+IsUI(byte * data, int ext)
+{
+	return ((data[0] & 0xef) == 0x3);
+}
+
+static int
+IsUA(byte * data, int ext)
+{
+	return ((data[0] & 0xef) == 0x63);
+}
+
+static int
+IsDISC(byte * data, int ext)
+{
+	return ((data[0] & 0xef) == 0x43);
+}
+
+static int
+IsRR(byte * data, int ext)
+{
+	if (ext)
+		return (data[0] == 0x1);
+	else
+		return ((data[0] & 0xf) == 1);
+}
+
+static int
+IsI(byte * data, int ext)
+{
+	return ((data[0] & 0x1) == 0x0);
+}
+
+static int
+IsSABMX(byte * data, int ext)
+{
+	return (ext ? data[0] == 0x7f : data[0] == 0x3f);
+}
+
+static int
+IsREJ(byte * data, int ext)
+{
+	return (ext ? data[0] == 0x9 : (data[0] & 0xf) == 0x9);
+}
+
+static int
+IsFRMR(byte * data, int ext)
+{
+	return ((data[0] & 0xef) == 0x87);
+}
+
+static int
+IsRNR(byte * data, int ext)
+{
+	if (ext)
+		return (data[0] == 0x5);
+	else
+		return ((data[0] & 0xf) == 5);
+}
+
+static struct FsmNode L2FnList[] =
+{
+	{ST_L2_1, EV_L2_DL_ESTABLISH, l2s1},
+	{ST_L2_1, EV_L2_MDL_NOTEIPROC, l2s19},
+	{ST_L2_3, EV_L2_MDL_ASSIGN, l2s17},
+	{ST_L2_4, EV_L2_DL_UNIT_DATA, l2s2},
+	{ST_L2_4, EV_L2_DL_ESTABLISH, l2s11},
+	{ST_L2_7, EV_L2_DL_UNIT_DATA, l2s2},
+	{ST_L2_7, EV_L2_DL_DATA, l2s7},
+	{ST_L2_7, EV_L2_DL_RELEASE, l2s13},
+	{ST_L2_7, EV_L2_ACK_PULL, l2s22},
+	{ST_L2_8, EV_L2_DL_RELEASE, l2s13},
+
+	{ST_L2_1, EV_L2_UI, l2s3},
+	{ST_L2_4, EV_L2_UI, l2s3},
+	{ST_L2_4, EV_L2_SABMX, l2s12},
+	{ST_L2_5, EV_L2_UA, l2s5},
+	{ST_L2_6, EV_L2_UA, l2s15},
+	{ST_L2_7, EV_L2_UI, l2s3},
+	{ST_L2_7, EV_L2_DISC, l2s14},
+	{ST_L2_7, EV_L2_I, l2s8},
+	{ST_L2_7, EV_L2_RR, l2s6},
+	{ST_L2_7, EV_L2_REJ, l2s16},
+	{ST_L2_7, EV_L2_SABMX, l2s27},
+	{ST_L2_7, EV_L2_FRMR, l2s28},
+	{ST_L2_8, EV_L2_RR, l2s24},
+	{ST_L2_8, EV_L2_DISC, l2s14},
+	{ST_L2_8, EV_L2_FRMR, l2s28},
+
+	{ST_L2_5, EV_L2_T200, l2s20},
+	{ST_L2_6, EV_L2_T200, l2s21},
+	{ST_L2_7, EV_L2_T200, l2s23},
+	{ST_L2_7, EV_L2_T203, l2s25},
+	{ST_L2_8, EV_L2_T200, l2s26},
+};
+
+#define L2_FN_COUNT (sizeof(L2FnList)/sizeof(struct FsmNode))
+
+static void
+isdnl2_l1l2(struct PStack *st, int pr, struct BufHeader *arg)
+{
+	struct BufHeader *ibh;
+	byte           *datap;
+	int             ret = !0;
+
+	switch (pr) {
+	  case (PH_DATA):
+
+		  ibh = arg;
+		  datap = DATAPTR(ibh);
+		  datap += l2addrsize(&st->l2);
+
+		  if (IsI(datap, st->l2.extended))
+			  ret = FsmEvent(&st->l2.l2m, EV_L2_I, ibh);
+		  else if (IsRR(datap, st->l2.extended))
+			  ret = FsmEvent(&st->l2.l2m, EV_L2_RR, ibh);
+		  else if (IsUI(datap, st->l2.extended))
+			  ret = FsmEvent(&st->l2.l2m, EV_L2_UI, ibh);
+		  else if (IsSABMX(datap, st->l2.extended))
+			  ret = FsmEvent(&st->l2.l2m, EV_L2_SABMX, ibh);
+		  else if (IsUA(datap, st->l2.extended))
+			  ret = FsmEvent(&st->l2.l2m, EV_L2_UA, ibh);
+		  else if (IsDISC(datap, st->l2.extended))
+			  ret = FsmEvent(&st->l2.l2m, EV_L2_DISC, ibh);
+		  else if (IsREJ(datap, st->l2.extended))
+			  ret = FsmEvent(&st->l2.l2m, EV_L2_REJ, ibh);
+		  else if (IsFRMR(datap, st->l2.extended))
+			  ret = FsmEvent(&st->l2.l2m, EV_L2_FRMR, ibh);
+		  else if (IsRNR(datap, st->l2.extended))
+			  ret = FsmEvent(&st->l2.l2m, EV_L2_RNR, ibh);
+
+		  if (ret)
+			  BufPoolRelease(ibh);
+
+		  break;
+	  case (PH_PULL_ACK):
+		  FsmEvent(&st->l2.l2m, EV_L2_ACK_PULL, arg);
+		  break;
+	}
+}
+
+static void
+isdnl2_l3l2(struct PStack *st, int pr,
+	    void *arg)
+{
+	switch (pr) {
+	  case (DL_DATA):
+		  if (FsmEvent(&st->l2.l2m, EV_L2_DL_DATA, arg))
+			  BufPoolRelease((struct BufHeader *) arg);
+		  break;
+	  case (DL_UNIT_DATA):
+		  if (FsmEvent(&st->l2.l2m, EV_L2_DL_UNIT_DATA, arg))
+			  BufPoolRelease((struct BufHeader *) arg);
+		  break;
+	}
+}
+
+static void
+isdnl2_manl2(struct PStack *st, int pr,
+	     void *arg)
+{
+	switch (pr) {
+	  case (DL_ESTABLISH):
+		  FsmEvent(&st->l2.l2m, EV_L2_DL_ESTABLISH, arg);
+		  break;
+	  case (DL_RELEASE):
+		  FsmEvent(&st->l2.l2m, EV_L2_DL_RELEASE, arg);
+		  break;
+	  case (MDL_NOTEIPROC):
+		  FsmEvent(&st->l2.l2m, EV_L2_MDL_NOTEIPROC, NULL);
+		  break;
+	}
+}
+
+static void
+isdnl2_teil2(struct PStack *st, int pr,
+	     void *arg)
+{
+	switch (pr) {
+	  case (MDL_ASSIGN):
+		  FsmEvent(&st->l2.l2m, EV_L2_MDL_ASSIGN, arg);
+		  break;
+	}
+}
+
+void
+releasestack_isdnl2(struct PStack *st)
+{
+	FsmDelTimer(&st->l2.t200_timer, 15);
+	FsmDelTimer(&st->l2.t203_timer, 16);
+}
+
+static void
+l2m_debug(struct FsmInst *fi, char *s)
+{
+	struct PStack  *st = fi->userdata;
+	char            tm[32], str[256];
+
+	jiftime(tm, jiffies);
+	sprintf(str, "%s %s %s\n", tm, st->l2.debug_id, s);
+	teles_putstatus(str);
+}
+
+
+void
+setstack_isdnl2(struct PStack *st, char *debug_id)
+{
+	st->l1.l1l2 = isdnl2_l1l2;
+	st->l3.l3l2 = isdnl2_l3l2;
+	st->ma.manl2 = isdnl2_manl2;
+	st->ma.teil2 = isdnl2_teil2;
+
+	st->l2.uihsize = l2headersize(&st->l2, !0);
+	st->l2.ihsize = l2headersize(&st->l2, 0);
+	BufQueueInit(&(st->l2.i_queue));
+	st->l2.rejexp = 0;
+	st->l2.debug = 1;
+
+	st->l2.l2m.fsm = &l2fsm;
+	st->l2.l2m.state = ST_L2_1;
+	st->l2.l2m.debug = 0;
+	st->l2.l2m.userdata = st;
+	st->l2.l2m.printdebug = l2m_debug;
+	strcpy(st->l2.debug_id, debug_id);
+
+	FsmInitTimer(&st->l2.l2m, &st->l2.t200_timer);
+	FsmInitTimer(&st->l2.l2m, &st->l2.t203_timer);
+	st->l2.t200_running = 0;
+}
+
+#ifdef DEFINED_BUT_NOT_USED
+static void
+trans_acceptph(struct PStack *st, struct BufHeader *ibh)
+{
+#if 0
+	st->l3.service_up(st, DL_DATA, ibh);
+#endif
+}
+
+
+static void
+transdown(struct PStack *st, int pr,
+	  struct BufHeader *ibh)
+{
+	if (pr == DL_DATA) {
+		ibh->primitive = !0;
+		st->l2.l2l1(st, PH_DATA, ibh);
+	}
+}
+#endif
+
+void
+setstack_transl2(struct PStack *st)
+{
+#if 0
+	st->l2.phdata_up = trans_acceptph;
+	st->l2.service_down = (void *) transdown;
+	st->l2.ihsize = 0;
+	st->l2.debug = 0;
+#endif
+}
+
+void
+releasestack_transl2(struct PStack *st)
+{
+}
+
+void
+Isdnl2New(void)
+{
+	l2fsm.state_count = L2_STATE_COUNT;
+	l2fsm.event_count = L2_EVENT_COUNT;
+	l2fsm.strEvent = strL2Event;
+	l2fsm.strState = strL2State;
+	FsmNew(&l2fsm, L2FnList, L2_FN_COUNT);
+}
+
+void
+Isdnl2Free(void)
+{
+	FsmFree(&l2fsm);
+}
diff -u --recursive --new-file v1.3.68/linux/drivers/isdn/teles/isdnl3.c linux/drivers/isdn/teles/isdnl3.c
--- v1.3.68/linux/drivers/isdn/teles/isdnl3.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/isdn/teles/isdnl3.c	Mon Feb 26 11:58:07 1996
@@ -0,0 +1,537 @@
+#define __NO_VERSION__
+#define P_1TR6
+#include "teles.h"
+#include "l3_1TR6.h"
+#define DEBUG_1TR6 0
+
+static void
+i_down(struct PStack *st,
+       struct BufHeader *ibh)
+{
+	st->l3.l3l2(st, DL_DATA, ibh);
+}
+
+static void
+newl3state(struct PStack *st, int state)
+{
+	st->l3.state = state;
+	if (DEBUG_1TR6 > 4)
+		printk(KERN_INFO "isdnl3: bc:%d cr:%x new state %d\n",
+		       st->pa->bchannel, st->pa->callref, state);
+
+}
+
+static void
+l3_message(struct PStack *st, int mt)
+{
+	struct BufHeader *dibh;
+	byte           *p;
+	int             size;
+
+	BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 18);
+	p = DATAPTR(dibh);
+	p += st->l2.ihsize;
+	size = st->l2.ihsize;
+
+	*p++ = 0x8;
+	*p++ = 0x1;
+	*p++ = st->l3.callref;
+	*p++ = mt;
+	size += 4;
+
+	dibh->datasize = size;
+	i_down(st, dibh);
+}
+
+static void
+l3s3(struct PStack *st, byte pr, void *arg)
+{
+	l3_message(st, MT_RELEASE);
+	newl3state(st, 19);
+}
+
+static void
+l3s4(struct PStack *st, byte pr, void *arg)
+{
+	struct BufHeader *ibh = arg;
+
+	BufPoolRelease(ibh);
+	newl3state(st, 0);
+	st->l3.l3l4(st, CC_RELEASE_CNF, NULL);
+}
+
+static void
+l3s5(struct PStack *st, byte pr,
+     void *arg)
+{
+	struct BufHeader *dibh;
+	byte           *p;
+	char           *teln;
+
+	st->l3.callref = st->pa->callref;
+	BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 19);
+	p = DATAPTR(dibh);
+	p += st->l2.ihsize;
+
+	*p++ = 0x8;
+	*p++ = 0x1;
+	*p++ = st->l3.callref;
+	*p++ = MT_SETUP;
+	*p++ = 0xa1;
+
+	/*
+         * Set Bearer Capability, Map info from 1TR6-convention to EDSS1
+         */
+	switch (st->pa->info) {
+	  case 1:		/* Telephony                               */
+		  *p++ = 0x4;	/* BC-IE-code                              */
+		  *p++ = 0x3;	/* Length                                  */
+		  *p++ = 0x90;	/* Coding Std. national, 3.1 kHz audio     */
+		  *p++ = 0x90;	/* Circuit-Mode 64kbps                     */
+		  *p++ = 0xa3;	/* A-Law Audio                             */
+		  break;
+	  case 5:		/* Datatransmission 64k, BTX               */
+	  case 7:		/* Datatransmission 64k                    */
+	  default:
+		  *p++ = 0x4;	/* BC-IE-code                              */
+		  *p++ = 0x2;	/* Length                                  */
+		  *p++ = 0x88;	/* Coding Std. nat., unrestr. dig. Inform. */
+		  *p++ = 0x90;	/* Packet-Mode 64kbps                      */
+		  break;
+	}
+/*
+ * What about info2? Mapping to High-Layer-Compatibility?
+ */
+
+#if 0				/* user-user not allowed in The Netherlands! */
+	*p++ = 0x7f;
+	*p++ = 0x2;
+	*p++ = 0x0;
+	*p++ = 66;
+#endif
+
+	if (st->pa->calling[0] != '\0') {
+		*p++ = 0x6c;
+		*p++ = strlen(st->pa->calling) + 1;
+		/* Classify as AnyPref. */
+		*p++ = 0x81;	/* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
+		teln = st->pa->calling;
+		while (*teln)
+			*p++ = *teln++ & 0x7f;
+	}
+	*p++ = 0x70;
+	*p++ = strlen(st->pa->called) + 1;
+	/* Classify as AnyPref. */
+	*p++ = 0x81;		/* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
+
+	teln = st->pa->called;
+	while (*teln)
+		*p++ = *teln++ & 0x7f;
+
+
+	dibh->datasize = p - DATAPTR(dibh);
+
+	newl3state(st, 1);
+	i_down(st, dibh);
+
+}
+
+static void
+l3s6(struct PStack *st, byte pr, void *arg)
+{
+	byte           *p;
+	struct BufHeader *ibh = arg;
+
+	p = DATAPTR(ibh);
+	if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
+			0x18, 0))) {
+		st->pa->bchannel = p[2] & 0x3;
+	} else
+		printk(KERN_WARNING "octect 3 not found\n");
+
+	BufPoolRelease(ibh);
+	newl3state(st, 3);
+	st->l3.l3l4(st, CC_PROCEEDING_IND, NULL);
+}
+
+static void
+l3s7(struct PStack *st, byte pr, void *arg)
+{
+	struct BufHeader *ibh = arg;
+
+	BufPoolRelease(ibh);
+	newl3state(st, 12);
+	st->l3.l3l4(st, CC_DISCONNECT_IND, NULL);
+}
+
+static void
+l3s8(struct PStack *st, byte pr, void *arg)
+{
+	struct BufHeader *ibh = arg;
+
+	BufPoolRelease(ibh);
+	st->l3.l3l4(st, CC_SETUP_CNF, NULL);
+	newl3state(st, 10);
+}
+
+static void
+l3s11(struct PStack *st, byte pr, void *arg)
+{
+	struct BufHeader *ibh = arg;
+
+	BufPoolRelease(ibh);
+	newl3state(st, 4);
+	st->l3.l3l4(st, CC_ALERTING_IND, NULL);
+}
+
+static void
+l3s12(struct PStack *st, byte pr, void *arg)
+{
+	byte           *p;
+	struct BufHeader *ibh = arg;
+
+	p = DATAPTR(ibh);
+	p += st->l2.uihsize;
+	st->pa->callref = getcallref(p);
+	st->l3.callref = 0x80 + st->pa->callref;
+
+	/*
+         * Channel Identification
+         */
+	p = DATAPTR(ibh);
+	if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize,
+			0x18, 0))) {
+		st->pa->bchannel = p[2] & 0x3;
+	} else
+		printk(KERN_WARNING "l3s12: Channel ident not found\n");
+
+	p = DATAPTR(ibh);
+	if (st->protocol == ISDN_PTYPE_1TR6) {
+		if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize, 0x01, 6))) {
+			st->pa->info = p[2];
+			st->pa->info2 = p[3];
+		} else
+			printk(KERN_WARNING "l3s12(1TR6): ServiceIndicator not found\n");
+	} else {
+		/*
+                 * Bearer Capabilities
+                 */
+		if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize, 0x04, 0))) {
+			switch (p[2] & 0x1f) {
+			  case 0x00:
+                                  /* Speech */
+			  case 0x10:
+                                  /* 3.1 Khz audio */
+				  st->pa->info = 1;
+				  break;
+			  case 0x08:
+                                  /* Unrestricted digital information */
+				  st->pa->info = 7;
+				  break;
+			  case 0x09:
+                                  /* Restricted digital information */
+				  st->pa->info = 2;
+				  break;
+			  case 0x11:
+                                  /* Unrestr. digital information  with tones/announcements */
+				  st->pa->info = 3;
+				  break;
+			  case 0x18:
+                                  /* Video */
+				  st->pa->info = 4;
+				  break;
+			  default:
+				  st->pa->info = 0;
+			}
+		} else
+			printk(KERN_WARNING "l3s12: Bearer capabilities not found\n");
+	}
+
+	p = DATAPTR(ibh);
+	if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize,
+			0x70, 0)))
+		iecpy(st->pa->called, p, 1);
+	else
+		strcpy(st->pa->called, "");
+
+	p = DATAPTR(ibh);
+	if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize,
+			0x6c, 0))) {
+		if (st->protocol == ISDN_PTYPE_1TR6)
+			iecpy(st->pa->calling, p, 1);
+		else
+			iecpy(st->pa->calling, p, 2);
+	} else
+		strcpy(st->pa->calling, "");
+	BufPoolRelease(ibh);
+
+	if (st->pa->info == 7) {
+		newl3state(st, 6);
+		st->l3.l3l4(st, CC_SETUP_IND, NULL);
+	} else {
+		printk(KERN_WARNING "non-digital call: %s -> %s\n",
+		       st->pa->calling,
+		       st->pa->called);
+	}
+}
+
+static void
+l3s13(struct PStack *st, byte pr, void *arg)
+{
+	newl3state(st, 0);
+}
+
+#ifdef DEFINED_BUT_NOT_USED
+static void
+l3s15(struct PStack *st, byte pr, void *arg)
+{
+	newl3state(st, 0);
+	st->l3.l3l4(st, CC_REJECT, NULL);
+}
+#endif
+
+static void
+l3s16(struct PStack *st, byte pr,
+      void *arg)
+{
+	st->l3.callref = 0x80 + st->pa->callref;
+	l3_message(st, MT_CONNECT);
+	newl3state(st, 8);
+}
+
+static void
+l3s17(struct PStack *st, byte pr, void *arg)
+{
+	struct BufHeader *ibh = arg;
+
+	BufPoolRelease(ibh);
+	st->l3.l3l4(st, CC_SETUP_COMPLETE_IND, NULL);
+	newl3state(st, 10);
+}
+
+static void
+l3s18(struct PStack *st, byte pr, void *arg)
+{
+	struct BufHeader *dibh;
+	byte           *p;
+	int             size;
+
+	BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 20);
+	p = DATAPTR(dibh);
+	p += st->l2.ihsize;
+	size = st->l2.ihsize;
+
+	*p++ = 0x8;
+	*p++ = 0x1;
+	*p++ = st->l3.callref;
+	*p++ = MT_DISCONNECT;
+	size += 4;
+
+	*p++ = IE_CAUSE;
+	*p++ = 0x2;
+	*p++ = 0x80;
+	*p++ = 0x90;
+	size += 4;
+
+	dibh->datasize = size;
+	i_down(st, dibh);
+
+	newl3state(st, 11);
+}
+
+static void
+l3s19(struct PStack *st, byte pr, void *arg)
+{
+	struct BufHeader *ibh = arg;
+
+	BufPoolRelease(ibh);
+	newl3state(st, 0);
+	l3_message(st, MT_RELEASE_COMPLETE);
+	st->l3.l3l4(st, CC_RELEASE_IND, NULL);
+}
+
+static void
+l3s20(struct PStack *st, byte pr,
+      void *arg)
+{
+	l3_message(st, MT_ALERTING);
+	newl3state(st, 7);
+}
+
+struct stateentry {
+	int             state;
+	byte            primitive;
+	void            (*rout) (struct PStack *, byte, void *);
+};
+
+static struct stateentry downstatelist[] =
+{
+	{0, CC_SETUP_REQ, l3s5},
+	{6, CC_REJECT_REQ, l3s13},
+	{6, CC_SETUP_RSP, l3s16},
+	{6, CC_ALERTING_REQ, l3s20},
+	{7, CC_SETUP_RSP, l3s16},
+	{10, CC_DISCONNECT_REQ, l3s18},
+	{12, CC_RELEASE_REQ, l3s3},
+};
+
+static int      downsllen = sizeof(downstatelist) /
+sizeof(struct stateentry);
+
+static struct stateentry datastatelist[] =
+{
+	{0, MT_SETUP, l3s12},
+	{1, MT_CALL_PROCEEDING, l3s6},
+	{1, MT_RELEASE_COMPLETE, l3s7},
+	{3, MT_DISCONNECT, l3s7},
+	{3, MT_CONNECT, l3s8},
+	{3, MT_ALERTING, l3s11},
+	{4, MT_CONNECT, l3s8},
+	{4, MT_DISCONNECT, l3s7},
+	{4, MT_RELEASE, l3s19},
+	{7, MT_RELEASE, l3s19},
+	{8, MT_CONNECT_ACKNOWLEDGE, l3s17},
+	{10, MT_DISCONNECT, l3s7},
+	{11, MT_RELEASE, l3s19},
+	{19, MT_RELEASE_COMPLETE, l3s4},
+};
+
+static int      datasllen = sizeof(datastatelist) /
+sizeof(struct stateentry);
+
+#ifdef P_1TR6
+#include "l3_1TR6.c"
+#endif
+
+static void
+l3up(struct PStack *st,
+     int pr, void *arg)
+{
+	int             i, mt, size;
+	byte           *ptr;
+	struct BufHeader *ibh = arg;
+
+	if (pr == DL_DATA) {
+		ptr = DATAPTR(ibh);
+		ptr += st->l2.ihsize;
+		size = ibh->datasize - st->l2.ihsize;
+		if (DEBUG_1TR6 > 6) {
+			printk(KERN_INFO "isdnl3/l3up DL_DATA size=%d\n", size);
+			for (i = 0; i < size; i++)
+				printk(KERN_INFO "l3up data %x\n", ptr[i]);
+		}
+		mt = ptr[3];
+		switch (ptr[0]) {
+#ifdef P_1TR6
+		  case PROTO_DIS_N0:
+			  BufPoolRelease(ibh);
+			  break;
+		  case PROTO_DIS_N1:
+			  for (i = 0; i < datasl_1tr6t_len; i++)
+				  if ((st->l3.state == datastatelist_1tr6t[i].state) &&
+				      (mt == datastatelist_1tr6t[i].primitive))
+					  break;
+			  if (i == datasl_1tr6t_len) {
+				  BufPoolRelease(ibh);
+				  if (DEBUG_1TR6 > 0)
+					  printk(KERN_INFO "isdnl3up unhandled 1tr6 state %d MT %s\n",
+						 st->l3.state, mt_trans(PROTO_DIS_N1, mt));
+			  } else
+				  datastatelist_1tr6t[i].rout(st, pr, ibh);
+			  break;
+#endif
+		  default:	/* E-DSS1 */
+			  for (i = 0; i < datasllen; i++)
+				  if ((st->l3.state == datastatelist[i].state) &&
+				      (mt == datastatelist[i].primitive))
+					  break;
+			  if (i == datasllen) {
+				  BufPoolRelease(ibh);
+			  } else
+				  datastatelist[i].rout(st, pr, ibh);
+		}
+	} else if (pr == DL_UNIT_DATA) {
+		ptr = DATAPTR(ibh);
+		ptr += st->l2.uihsize;
+		size = ibh->datasize - st->l2.uihsize;
+		if (DEBUG_1TR6 > 6) {
+			printk(KERN_INFO "isdnl3/l3up DL_UNIT_DATA size=%d\n", size);
+			for (i = 0; i < size; i++)
+				printk(KERN_INFO "l3up data %x\n", ptr[i]);
+		}
+		mt = ptr[3];
+		switch (ptr[0]) {
+#ifdef P_1TR6
+		  case PROTO_DIS_N0:
+			  BufPoolRelease(ibh);
+			  break;
+		  case PROTO_DIS_N1:
+			  for (i = 0; i < datasl_1tr6t_len; i++)
+				  if ((st->l3.state == datastatelist_1tr6t[i].state) &&
+				      (mt == datastatelist_1tr6t[i].primitive))
+					  break;
+			  if (i == datasl_1tr6t_len) {
+				  if (DEBUG_1TR6 > 0) {
+					  printk(KERN_INFO "isdnl3up unhandled 1tr6 state %d MT %s\n"
+						 ,st->l3.state, mt_trans(PROTO_DIS_N1, mt));
+				  }
+				  BufPoolRelease(ibh);
+			  } else
+				  datastatelist_1tr6t[i].rout(st, pr, ibh);
+			  break;
+#endif
+		  default:	/* E-DSS1 */
+			  for (i = 0; i < datasllen; i++)
+				  if ((st->l3.state == datastatelist[i].state) &&
+				      (mt == datastatelist[i].primitive))
+					  break;
+			  if (i == datasllen) {
+				  BufPoolRelease(ibh);
+			  } else
+				  datastatelist[i].rout(st, pr, ibh);
+		}
+	}
+}
+
+static void
+l3down(struct PStack *st,
+       int pr, void *arg)
+{
+	int             i;
+	struct BufHeader *ibh = arg;
+
+	switch (st->protocol) {
+#ifdef P_1TR6
+	  case ISDN_PTYPE_1TR6:
+		  for (i = 0; i < downsl_1tr6t_len; i++)
+			  if ((st->l3.state == downstatelist_1tr6t[i].state) &&
+			      (pr == downstatelist_1tr6t[i].primitive))
+				  break;
+		  if (i == downsl_1tr6t_len) {
+			  if (DEBUG_1TR6 > 0) {
+				  printk(KERN_INFO "isdnl3down unhandled 1tr6 state %d primitiv %x\n", st->l3.state, pr);
+			  }
+		  } else
+			  downstatelist_1tr6t[i].rout(st, pr, ibh);
+		  break;
+#endif
+	  default:
+		  for (i = 0; i < downsllen; i++)
+			  if ((st->l3.state == downstatelist[i].state) &&
+			      (pr == downstatelist[i].primitive))
+				  break;
+		  if (i == downsllen) {
+		  } else
+			  downstatelist[i].rout(st, pr, ibh);
+	}
+}
+
+void
+setstack_isdnl3(struct PStack *st)
+{
+	st->l4.l4l3 = l3down;
+	st->l2.l2l3 = l3up;
+	st->l3.state = 0;
+	st->l3.callref = 0;
+	st->l3.debug = 0;
+}
diff -u --recursive --new-file v1.3.68/linux/drivers/isdn/teles/l3_1TR6.c linux/drivers/isdn/teles/l3_1TR6.c
--- v1.3.68/linux/drivers/isdn/teles/l3_1TR6.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/isdn/teles/l3_1TR6.c	Mon Feb 26 11:58:07 1996
@@ -0,0 +1,502 @@
+#define DEBUG_1TR6 0
+
+char           *
+mt_trans(int pd, int mt)
+{
+	int             i;
+
+	if (pd == PROTO_DIS_N0) {
+		for (i = 0; i < (sizeof(mtdesc_n0) / sizeof(struct MTypeDesc)); i++) {
+			if (mt == mtdesc_n0[i].mt)
+				return (mtdesc_n0[i].descr);
+		}
+		return ("unkown Message Type PD=N0");
+	} else if (pd == PROTO_DIS_N1) {
+		for (i = 0; i < (sizeof(mtdesc_n1) / sizeof(struct MTypeDesc)); i++) {
+			if (mt == mtdesc_n1[i].mt)
+				return (mtdesc_n1[i].descr);
+		}
+		return ("unkown Message Type PD=N1");
+	}
+	return ("unkown Protokolldiscriminator");
+}
+
+static void
+l3_1TR6_message(struct PStack *st, int mt, int pd)
+{
+	struct BufHeader *dibh;
+	byte           *p;
+
+	BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 18);
+	p = DATAPTR(dibh);
+	p += st->l2.ihsize;
+
+	*p++ = pd;
+	*p++ = 0x1;
+	*p++ = st->l3.callref;
+	*p++ = mt;
+
+	dibh->datasize = p - DATAPTR(dibh);
+	i_down(st, dibh);
+}
+
+static void
+l3_1tr6_setup(struct PStack *st, byte pr, void *arg)
+{
+	struct BufHeader *dibh;
+	byte           *p;
+	char           *teln;
+
+#if DEBUG_1TR6
+	printk(KERN_INFO "1tr6: sent SETUP\n");
+#endif
+
+	st->l3.callref = st->pa->callref;
+	BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 19);
+	p = DATAPTR(dibh);
+	p += st->l2.ihsize;
+
+	*p++ = PROTO_DIS_N1;
+	*p++ = 0x1;
+	*p++ = st->l3.callref;
+	*p++ = MT_N1_SETUP;
+
+
+	if (st->pa->calling[0] != '\0') {
+		*p++ = WE0_origAddr;
+		*p++ = strlen(st->pa->calling) + 1;
+		/* Classify as AnyPref. */
+		*p++ = 0x81;	/* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
+		teln = st->pa->calling;
+		while (*teln)
+			*p++ = *teln++ & 0x7f;
+	}
+	*p++ = WE0_destAddr;
+	*p++ = strlen(st->pa->called) + 1;
+	/* Classify as AnyPref. */
+	*p++ = 0x81;		/* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
+
+	teln = st->pa->called;
+	while (*teln)
+		*p++ = *teln++ & 0x7f;
+
+	*p++ = WE_Shift_F6;
+	/* Codesatz 6 fuer Service */
+	*p++ = WE6_serviceInd;
+	*p++ = 2;		/* len=2 info,info2 */
+	*p++ = st->pa->info;
+	*p++ = st->pa->info2;
+
+	dibh->datasize = p - DATAPTR(dibh);
+
+	newl3state(st, 1);
+	i_down(st, dibh);
+
+}
+
+
+static void
+l3_1tr6_tu_setup(struct PStack *st, byte pr, void *arg)
+{
+	byte           *p;
+	struct BufHeader *ibh = arg;
+
+#if DEBUG_1TR6
+	printk(KERN_INFO "1tr6: TU_SETUP\n");
+#endif
+
+	p = DATAPTR(ibh);
+	p += st->l2.uihsize;
+	st->pa->callref = getcallref(p);
+	st->l3.callref = 0x80 + st->pa->callref;
+
+	/*
+         * Channel Identification
+         */
+	p = DATAPTR(ibh);
+	if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize,
+			WE0_chanID, 0))) {
+		st->pa->bchannel = p[2] & 0x3;
+	} else
+		printk(KERN_INFO "l3tu_setup: Channel ident not found\n");
+
+	p = DATAPTR(ibh);
+
+	if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize, WE6_serviceInd, 6))) {
+		st->pa->info = p[2];
+		st->pa->info2 = p[3];
+	} else
+		printk(KERN_INFO "l3s12(1TR6): ServiceIndicator not found\n");
+
+	p = DATAPTR(ibh);
+	if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize,
+			WE0_destAddr, 0)))
+		iecpy(st->pa->called, p, 1);
+	else
+		strcpy(st->pa->called, "");
+
+	p = DATAPTR(ibh);
+	if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize,
+			WE0_origAddr, 0))) {
+		iecpy(st->pa->calling, p, 1);
+	} else
+		strcpy(st->pa->calling, "");
+
+	BufPoolRelease(ibh);
+
+	if (st->pa->info == 7) {
+		newl3state(st, 6);
+		st->l3.l3l4(st, CC_SETUP_IND, NULL);
+	}
+}
+
+static void
+l3_1tr6_tu_setup_ack(struct PStack *st, byte pr, void *arg)
+{
+	byte           *p;
+	struct BufHeader *ibh = arg;
+
+#if DEBUG_1TR6
+	printk(KERN_INFO "1tr6: SETUP_ACK\n");
+#endif
+
+	p = DATAPTR(ibh);
+	if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
+			WE0_chanID, 0))) {
+		st->pa->bchannel = p[2] & 0x3;
+	} else
+		printk(KERN_INFO "octect 3 not found\n");
+
+	BufPoolRelease(ibh);
+	newl3state(st, 2);
+}
+
+static void
+l3_1tr6_tu_call_sent(struct PStack *st, byte pr, void *arg)
+{
+	byte           *p;
+	struct BufHeader *ibh = arg;
+
+#if DEBUG_1TR6
+	printk(KERN_INFO "1tr6: CALL_SENT\n");
+#endif
+
+	p = DATAPTR(ibh);
+	if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
+			WE0_chanID, 0))) {
+		st->pa->bchannel = p[2] & 0x3;
+	} else
+		printk(KERN_INFO "octect 3 not found\n");
+
+	BufPoolRelease(ibh);
+	newl3state(st, 3);
+	st->l3.l3l4(st, CC_PROCEEDING_IND, NULL);
+}
+
+static void
+l3_1tr6_tu_alert(struct PStack *st, byte pr, void *arg)
+{
+	byte           *p;
+	struct BufHeader *ibh = arg;
+
+#if DEBUG_1TR6
+	printk(KERN_INFO "1tr6: TU_ALERT\n");
+#endif
+
+	p = DATAPTR(ibh);
+	if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
+			WE6_statusCalled, 6))) {
+		if (DEBUG_1TR6 > 2)
+			printk(KERN_INFO "status called %x\n", p[2]);
+	} else if (DEBUG_1TR6 > 0)
+		printk(KERN_INFO "statusCalled not found\n");
+
+	BufPoolRelease(ibh);
+	newl3state(st, 4);
+	st->l3.l3l4(st, CC_ALERTING_IND, NULL);
+}
+
+static void
+l3_1tr6_tu_info(struct PStack *st, byte pr, void *arg)
+{
+	byte           *p;
+	int             i;
+	char            a_charge[8];
+	struct BufHeader *ibh = arg;
+
+#if DEBUG_1TR6
+	printk(KERN_INFO "1tr6: TU_INFO\n");
+#endif
+
+	p = DATAPTR(ibh);
+	if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
+			WE6_chargingInfo, 6))) {
+		iecpy(a_charge, p, 1);
+		st->pa->chargeinfo = 0;
+		for (i = 0; i < strlen(a_charge); i++) {
+			st->pa->chargeinfo *= 10;
+			st->pa->chargeinfo += a_charge[i] & 0xf;
+			st->l3.l3l4(st, CC_INFO_CHARGE, NULL);
+		}
+		if (DEBUG_1TR6 > 2)
+			printk(KERN_INFO "chargingInfo %d\n", st->pa->chargeinfo);
+	} else if (DEBUG_1TR6 > 2)
+		printk(KERN_INFO "chargingInfo not found\n");
+
+	BufPoolRelease(ibh);
+}
+
+static void
+l3_1tr6_tu_info_s2(struct PStack *st, byte pr, void *arg)
+{
+	byte           *p;
+	int             i;
+	struct BufHeader *ibh = arg;
+
+#if DEBUG_1TR6
+	printk(KERN_INFO "1tr6: TU_INFO 2\n");
+#endif
+
+	if (DEBUG_1TR6 > 4) {
+		p = DATAPTR(ibh);
+		for (i = 0; i < ibh->datasize; i++) {
+			printk(KERN_INFO "Info DATA %x\n", p[i]);
+		}
+	}
+	BufPoolRelease(ibh);
+}
+
+static void
+l3_1tr6_tu_connect(struct PStack *st, byte pr, void *arg)
+{
+	struct BufHeader *ibh = arg;
+
+#if DEBUG_1TR6
+	printk(KERN_INFO "1tr6: CONNECT\n");
+#endif
+
+	BufPoolRelease(ibh);
+	st->l3.l3l4(st, CC_SETUP_CNF, NULL);
+	newl3state(st, 10);
+}
+
+static void
+l3_1tr6_tu_rel(struct PStack *st, byte pr, void *arg)
+{
+	struct BufHeader *ibh = arg;
+
+#if DEBUG_1TR6
+	printk(KERN_INFO "1tr6: REL\n");
+#endif
+
+
+	BufPoolRelease(ibh);
+	l3_1TR6_message(st, MT_N1_REL_ACK, PROTO_DIS_N1);
+	st->l3.l3l4(st, CC_RELEASE_IND, NULL);
+	newl3state(st, 0);
+}
+
+static void
+l3_1tr6_tu_rel_ack(struct PStack *st, byte pr, void *arg)
+{
+	struct BufHeader *ibh = arg;
+
+#if DEBUG_1TR6
+	printk(KERN_INFO "1tr6: REL_ACK\n");
+#endif
+
+
+	BufPoolRelease(ibh);
+	newl3state(st, 0);
+	st->l3.l3l4(st, CC_RELEASE_CNF, NULL);
+}
+
+static void
+l3_1tr6_tu_disc(struct PStack *st, byte pr, void *arg)
+{
+	struct BufHeader *ibh = arg;
+	byte           *p;
+	int             i;
+	char            a_charge[8];
+
+
+#if DEBUG_1TR6
+	printk(KERN_INFO "1tr6: TU_DISC\n");
+#endif
+
+
+	p = DATAPTR(ibh);
+	if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
+			WE6_chargingInfo, 6))) {
+		iecpy(a_charge, p, 1);
+		st->pa->chargeinfo = 0;
+		for (i = 0; i < strlen(a_charge); i++) {
+			st->pa->chargeinfo *= 10;
+			st->pa->chargeinfo += a_charge[i] & 0xf;
+			st->l3.l3l4(st, CC_INFO_CHARGE, NULL);
+		}
+		if (DEBUG_1TR6 > 2)
+			printk(KERN_INFO "chargingInfo %d\n", st->pa->chargeinfo);
+	} else if (DEBUG_1TR6 > 2)
+		printk(KERN_INFO "chargingInfo not found\n");
+
+	p = DATAPTR(ibh);
+	if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
+			WE0_cause, 0))) {
+		if (p[1] > 0) {
+			st->pa->cause = p[2];
+		} else {
+			st->pa->cause = 0;
+		}
+		if (DEBUG_1TR6 > 1)
+			printk(KERN_INFO "Cause %x\n", st->pa->cause);
+	} else if (DEBUG_1TR6 > 0)
+		printk(KERN_INFO "Cause not found\n");
+
+	BufPoolRelease(ibh);
+	newl3state(st, 12);
+	st->l3.l3l4(st, CC_DISCONNECT_IND, NULL);
+}
+
+
+static void
+l3_1tr6_tu_connect_ack(struct PStack *st, byte pr, void *arg)
+{
+	struct BufHeader *ibh = arg;
+
+#if DEBUG_1TR6
+	printk(KERN_INFO "1tr6: CONN_ACK\n");
+#endif
+
+
+	BufPoolRelease(ibh);
+	st->l3.l3l4(st, CC_SETUP_COMPLETE_IND, NULL);
+	newl3state(st, 10);
+}
+
+static void
+l3_1tr6_alert(struct PStack *st, byte pr,
+	      void *arg)
+{
+#if DEBUG_1TR6
+	printk(KERN_INFO "1tr6: send ALERT\n");
+#endif
+
+	l3_1TR6_message(st, MT_N1_ALERT, PROTO_DIS_N1);
+	newl3state(st, 7);
+}
+
+static void
+l3_1tr6_conn(struct PStack *st, byte pr,
+	     void *arg)
+{
+#if DEBUG_1TR6
+	printk(KERN_INFO "1tr6: send CONNECT\n");
+#endif
+
+	st->l3.callref = 0x80 + st->pa->callref;
+	l3_1TR6_message(st, MT_N1_CONN, PROTO_DIS_N1);
+	newl3state(st, 8);
+}
+
+static void
+l3_1tr6_ignore(struct PStack *st, byte pr, void *arg)
+{
+#if DEBUG_1TR6
+	printk(KERN_INFO "1tr6: IGNORE\n");
+#endif
+
+	newl3state(st, 0);
+}
+
+static void
+l3_1tr6_disconn_req(struct PStack *st, byte pr, void *arg)
+{
+	struct BufHeader *dibh;
+	byte           *p;
+
+#if DEBUG_1TR6
+	printk(KERN_INFO "1tr6: send DISCON\n");
+#endif
+
+	BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 20);
+	p = DATAPTR(dibh);
+	p += st->l2.ihsize;
+
+	*p++ = PROTO_DIS_N1;
+	*p++ = 0x1;
+	*p++ = st->l3.callref;
+	*p++ = MT_N1_DISC;
+
+	*p++ = WE0_cause;
+	*p++ = 0x0;		/* Laenge = 0 normales Ausloesen */
+
+	dibh->datasize = p - DATAPTR(dibh);
+
+	i_down(st, dibh);
+
+	newl3state(st, 11);
+}
+
+static void
+l3_1tr6_rel_req(struct PStack *st, byte pr, void *arg)
+{
+	l3_1TR6_message(st, MT_N1_REL, PROTO_DIS_N1);
+	newl3state(st, 19);
+}
+
+
+
+static struct stateentry downstatelist_1tr6t[] =
+{
+	{0, CC_SETUP_REQ, l3_1tr6_setup},
+	{6, CC_REJECT_REQ, l3_1tr6_ignore},
+	{6, CC_SETUP_RSP, l3_1tr6_conn},
+	{6, CC_ALERTING_REQ, l3_1tr6_alert},
+	{7, CC_SETUP_RSP, l3_1tr6_conn},
+	{7, CC_DISCONNECT_REQ, l3_1tr6_disconn_req},
+	{8, CC_DISCONNECT_REQ, l3_1tr6_disconn_req},
+	{10, CC_DISCONNECT_REQ, l3_1tr6_disconn_req},
+	{12, CC_RELEASE_REQ, l3_1tr6_rel_req}
+};
+
+static int      downsl_1tr6t_len = sizeof(downstatelist_1tr6t) /
+sizeof(struct stateentry);
+
+static struct stateentry datastatelist_1tr6t[] =
+{
+	{0, MT_N1_SETUP, l3_1tr6_tu_setup},
+	{0, MT_N1_REL, l3_1tr6_tu_rel},
+	{1, MT_N1_SETUP_ACK, l3_1tr6_tu_setup_ack},
+	{1, MT_N1_CALL_SENT, l3_1tr6_tu_call_sent},
+	{1, MT_N1_REL, l3_1tr6_tu_rel},
+	{1, MT_N1_DISC, l3_1tr6_tu_disc},
+	{2, MT_N1_CALL_SENT, l3_1tr6_tu_call_sent},
+	{2, MT_N1_ALERT, l3_1tr6_tu_alert},
+	{2, MT_N1_CONN, l3_1tr6_tu_connect},
+	{2, MT_N1_REL, l3_1tr6_tu_rel},
+	{2, MT_N1_DISC, l3_1tr6_tu_disc},
+	{2, MT_N1_INFO, l3_1tr6_tu_info_s2},
+	{3, MT_N1_ALERT, l3_1tr6_tu_alert},
+	{3, MT_N1_CONN, l3_1tr6_tu_connect},
+	{3, MT_N1_REL, l3_1tr6_tu_rel},
+	{3, MT_N1_DISC, l3_1tr6_tu_disc},
+	{4, MT_N1_ALERT, l3_1tr6_tu_alert},
+	{4, MT_N1_CONN, l3_1tr6_tu_connect},
+	{4, MT_N1_REL, l3_1tr6_tu_rel},
+	{4, MT_N1_DISC, l3_1tr6_tu_disc},
+	{7, MT_N1_REL, l3_1tr6_tu_rel},
+	{7, MT_N1_DISC, l3_1tr6_tu_disc},
+	{8, MT_N1_REL, l3_1tr6_tu_rel},
+	{8, MT_N1_DISC, l3_1tr6_tu_disc},
+	{8, MT_N1_CONN_ACK, l3_1tr6_tu_connect_ack},
+	{10, MT_N1_REL, l3_1tr6_tu_rel},
+	{10, MT_N1_DISC, l3_1tr6_tu_disc},
+	{10, MT_N1_INFO, l3_1tr6_tu_info},
+	{11, MT_N1_REL, l3_1tr6_tu_rel},
+	{12, MT_N1_REL, l3_1tr6_tu_rel},
+	{19, MT_N1_REL_ACK, l3_1tr6_tu_rel_ack}
+};
+
+static int      datasl_1tr6t_len = sizeof(datastatelist_1tr6t) /
+sizeof(struct stateentry);
diff -u --recursive --new-file v1.3.68/linux/drivers/isdn/teles/l3_1TR6.h linux/drivers/isdn/teles/l3_1TR6.h
--- v1.3.68/linux/drivers/isdn/teles/l3_1TR6.h	Thu Jan  1 02:00:00 1970
+++ linux/drivers/isdn/teles/l3_1TR6.h	Mon Feb 26 11:58:07 1996
@@ -0,0 +1,202 @@
+#ifndef l3_1TR6
+#define l3_1TR6
+
+#define PROTO_DIS_N0 0x40
+#define PROTO_DIS_N1 0x41
+
+/*
+ * MsgType N0
+ */
+#define MT_N0_REG_IND 61
+#define MT_N0_CANC_IND 62
+#define MT_N0_FAC_STA 63
+#define MT_N0_STA_ACK 64
+#define MT_N0_STA_REJ 65
+#define MT_N0_FAC_INF 66
+#define MT_N0_INF_ACK 67
+#define MT_N0_INF_REJ 68
+#define MT_N0_CLOSE 75
+#define MT_N0_CLO_ACK 77
+
+
+/*
+ * MsgType N1
+ */
+
+#define MT_N1_ESC 0x00
+#define MT_N1_ALERT 0x01
+#define MT_N1_CALL_SENT 0x02
+#define MT_N1_CONN 0x07
+#define MT_N1_CONN_ACK 0x0F
+#define MT_N1_SETUP 0x05
+#define MT_N1_SETUP_ACK 0x0D
+#define MT_N1_RES 0x26
+#define MT_N1_RES_ACK 0x2E
+#define MT_N1_RES_REJ 0x22
+#define MT_N1_SUSP 0x25
+#define MT_N1_SUSP_ACK 0x2D
+#define MT_N1_SUSP_REJ 0x21
+#define MT_N1_USER_INFO 0x20
+#define MT_N1_DET 0x40
+#define MT_N1_DISC 0x45
+#define MT_N1_REL 0x4D
+#define MT_N1_REL_ACK 0x5A
+#define MT_N1_CANC_ACK 0x6E
+#define MT_N1_CANC_REJ 0x67
+#define MT_N1_CON_CON 0x69
+#define MT_N1_FAC 0x60
+#define MT_N1_FAC_ACK 0x68
+#define MT_N1_FAC_CAN 0x66
+#define MT_N1_FAC_REG 0x64
+#define MT_N1_FAC_REJ 0x65
+#define MT_N1_INFO 0x6D
+#define MT_N1_REG_ACK 0x6C
+#define MT_N1_REG_REJ 0x6F
+#define MT_N1_STAT 0x63
+
+
+struct MTypeDesc {
+	byte            mt;
+	char           *descr;
+};
+
+static struct MTypeDesc mtdesc_n0[] =
+{
+	{MT_N0_REG_IND, "MT_N0_REG_IND"},
+	{MT_N0_CANC_IND, "MT_N0_CANC_IND"},
+	{MT_N0_FAC_STA, "MT_N0_FAC_STA"},
+	{MT_N0_STA_ACK, "MT_N0_STA_ACK"},
+	{MT_N0_STA_REJ, "MT_N0_STA_REJ"},
+	{MT_N0_FAC_INF, "MT_N0_FAC_INF"},
+	{MT_N0_INF_ACK, "MT_N0_INF_ACK"},
+	{MT_N0_INF_REJ, "MT_N0_INF_REJ"},
+	{MT_N0_CLOSE, "MT_N0_CLOSE"},
+	{MT_N0_CLO_ACK, "MT_N0_CLO_ACK"}
+};
+
+static struct MTypeDesc mtdesc_n1[] =
+{
+	{MT_N1_ESC, "MT_N1_ESC"},
+	{MT_N1_ALERT, "MT_N1_ALERT"},
+	{MT_N1_CALL_SENT, "MT_N1_CALL_SENT"},
+	{MT_N1_CONN, "MT_N1_CONN"},
+	{MT_N1_CONN_ACK, "MT_N1_CONN_ACK"},
+	{MT_N1_SETUP, "MT_N1_SETUP"},
+	{MT_N1_SETUP_ACK, "MT_N1_SETUP_ACK"},
+	{MT_N1_RES, "MT_N1_RES"},
+	{MT_N1_RES_ACK, "MT_N1_RES_ACK"},
+	{MT_N1_RES_REJ, "MT_N1_RES_REJ"},
+	{MT_N1_SUSP, "MT_N1_SUSP"},
+	{MT_N1_SUSP_ACK, "MT_N1_SUSP_ACK"},
+	{MT_N1_SUSP_REJ, "MT_N1_SUSP_REJ"},
+	{MT_N1_USER_INFO, "MT_N1_USER_INFO"},
+	{MT_N1_DET, "MT_N1_DET"},
+	{MT_N1_DISC, "MT_N1_DISC"},
+	{MT_N1_REL, "MT_N1_REL"},
+	{MT_N1_REL_ACK, "MT_N1_REL_ACK"},
+	{MT_N1_CANC_ACK, "MT_N1_CANC_ACK"},
+	{MT_N1_CANC_REJ, "MT_N1_CANC_REJ"},
+	{MT_N1_CON_CON, "MT_N1_CON_CON"},
+	{MT_N1_FAC, "MT_N1_FAC"},
+	{MT_N1_FAC_ACK, "MT_N1_FAC_ACK"},
+	{MT_N1_FAC_CAN, "MT_N1_FAC_CAN"},
+	{MT_N1_FAC_REG, "MT_N1_FAC_REG"},
+	{MT_N1_FAC_REJ, "MT_N1_FAC_REJ"},
+	{MT_N1_INFO, "MT_N1_INFO"},
+	{MT_N1_REG_ACK, "MT_N1_REG_ACK"},
+	{MT_N1_REG_REJ, "MT_N1_REG_REJ"},
+	{MT_N1_STAT, "MT_N1_STAT"}
+};
+
+
+/*
+ * W Elemente
+ */
+
+#define WE_Shift_F0 0x90
+#define WE_Shift_F6 0x96
+#define WE_Shift_OF0 0x98
+#define WE_Shift_OF6 0x9E
+
+#define WE0_cause 0x08
+#define WE0_connAddr 0x0C
+#define WE0_callID 0x10
+#define WE0_chanID 0x18
+#define WE0_netSpecFac 0x20
+#define WE0_display 0x28
+#define WE0_keypad 0x2C
+#define WE0_origAddr 0x6C
+#define WE0_destAddr 0x70
+#define WE0_userInfo 0x7E
+
+#define WE0_moreData 0xA0
+#define WE0_congestLevel 0xB0
+
+#define WE6_serviceInd 0x01
+#define WE6_chargingInfo 0x02
+#define WE6_date 0x03
+#define WE6_facSelect 0x05
+#define WE6_facStatus 0x06
+#define WE6_statusCalled 0x07
+#define WE6_addTransAttr 0x08
+
+/*
+ * FacCodes
+ */
+#define FAC_Sperre 0x01
+#define FAC_Sperre_All 0x02
+#define FAC_Sperre_Fern 0x03
+#define FAC_Sperre_Intl 0x04
+#define FAC_Sperre_Interk 0x05
+
+#define FAC_Forward1 0x02
+#define FAC_Forward2 0x03
+#define FAC_Konferenz 0x06
+#define FAC_GrabBchan 0x0F
+#define FAC_Reactivate 0x10
+#define FAC_Konferenz3 0x11
+#define FAC_Dienstwechsel1 0x12
+#define FAC_Dienstwechsel2 0x13
+#define FAC_NummernIdent 0x14
+#define FAC_GBG 0x15
+#define FAC_DisplayUebergeben 0x17
+#define FAC_DisplayUmgeleitet 0x1A
+#define FAC_Unterdruecke 0x1B
+#define FAC_Deactivate 0x1E
+#define FAC_Activate 0x1D
+#define FAC_SVC 0x1F
+#define FAC_Rueckwechsel 0x23
+#define FAC_Umleitung 0x24
+
+/*
+ * Cause codes
+ */
+#define CAUSE_InvCRef 0x01
+#define CAUSE_BearerNotImpl 0x03
+#define CAUSE_CIDunknown 0x07
+#define CAUSE_CIDinUse 0x08
+#define CAUSE_NoChans 0x0A
+#define CAUSE_FacNotImpl 0x10
+#define CAUSE_FacNotSubscr 0x11
+#define CAUSE_OutgoingBarred 0x20
+#define CAUSE_UserAssessBusy 0x21
+#define CAUSE_NegativeGBG 0x22
+#define CAUSE_UnknownGBG 0x23
+#define CAUSE_NoSPVknown 0x25
+#define CAUSE_DestNotObtain 0x35
+#define CAUSE_NumberChanged 0x38
+#define CAUSE_OutOfOrder 0x39
+#define CAUSE_NoUserResponse 0x3A
+#define CAUSE_UserBusy 0x3B
+#define CAUSE_IncomingBarred 0x3D
+#define CAUSE_CallRejected 0x3E
+#define CAUSE_NetworkCongestion 0x59
+#define CAUSE_RemoteUser 0x5A
+#define CAUSE_LocalProcErr 0x70
+#define CAUSE_RemoteProcErr 0x71
+#define CAUSE_RemoteUserSuspend 0x72
+#define CAUSE_RemoteUserResumed 0x73
+#define CAUSE_UserInfoDiscarded 0x7F
+
+
+#endif
diff -u --recursive --new-file v1.3.68/linux/drivers/isdn/teles/llglue.c linux/drivers/isdn/teles/llglue.c
--- v1.3.68/linux/drivers/isdn/teles/llglue.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/isdn/teles/llglue.c	Mon Feb 26 11:58:07 1996
@@ -0,0 +1,124 @@
+#define __NO_VERSION__
+#include "teles.h"
+#include <linux/malloc.h>
+#include <linux/timer.h>
+
+
+extern struct Channel *chanlist;
+int             drid;
+char            *teles_id = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
+
+isdn_if         iif;
+
+#define TELES_STATUS_BUFSIZE 4096
+static byte    *teles_status_buf = NULL;
+static byte    *teles_status_read = NULL;
+static byte    *teles_status_write = NULL;
+static byte    *teles_status_end = NULL;
+
+int
+teles_readstatus(byte * buf, int len, int user)
+{
+	int             count;
+	byte           *p;
+
+	for (p = buf, count = 0; count < len; p++, count++) {
+		if (user)
+			put_fs_byte(*teles_status_read++, p);
+		else
+			*p++ = *teles_status_read++;
+		if (teles_status_read > teles_status_end)
+			teles_status_read = teles_status_buf;
+	}
+	return count;
+}
+
+void
+teles_putstatus(char *buf)
+{
+	long            flags;
+	int             len, count, i;
+	byte           *p;
+	isdn_ctrl       ic;
+
+	save_flags(flags);
+	cli();
+	count = 0;
+	len = strlen(buf);
+	for (p = buf, i = len; i > 0; i--, p++) {
+		*teles_status_write++ = *p;
+		if (teles_status_write > teles_status_end)
+			teles_status_write = teles_status_buf;
+		count++;
+	}
+	restore_flags(flags);
+	if (count) {
+		ic.command = ISDN_STAT_STAVAIL;
+		ic.driver = drid;
+		ic.arg = count;
+		iif.statcallb(&ic);
+	}
+}
+
+
+int
+ll_init(void)
+{
+	isdn_ctrl       ic;
+
+	teles_status_buf = Smalloc(TELES_STATUS_BUFSIZE,
+				   GFP_KERNEL, "teles_status_buf");
+	if (!teles_status_buf) {
+		printk(KERN_ERR "teles: Could not allocate status-buffer\n");
+		return (-EIO);
+	} else {
+		teles_status_read = teles_status_buf;
+		teles_status_write = teles_status_buf;
+		teles_status_end = teles_status_buf + TELES_STATUS_BUFSIZE - 1;
+	}
+
+	iif.channels = CallcNewChan();
+	iif.maxbufsize = BUFFER_SIZE(HSCX_SBUF_ORDER, HSCX_SBUF_BPPS);
+	iif.features =
+	    ISDN_FEATURE_L2_X75I |
+	    ISDN_FEATURE_L2_HDLC |
+	    ISDN_FEATURE_L3_TRANS |
+	    ISDN_FEATURE_P_1TR6 |
+	    ISDN_FEATURE_P_EURO;
+
+	iif.command = teles_command;
+	iif.writebuf = teles_writebuf;
+	iif.writecmd = NULL;
+	iif.readstat = teles_readstatus;
+	strncpy(iif.id, teles_id, sizeof(iif.id) - 1);
+
+	register_isdn(&iif);
+	drid = iif.channels;
+
+	ic.driver = drid;
+	ic.command = ISDN_STAT_RUN;
+	iif.statcallb(&ic);
+	return 0;
+}
+
+void
+ll_stop(void)
+{
+	isdn_ctrl       ic;
+
+	ic.command = ISDN_STAT_STOP;
+	ic.driver = drid;
+	iif.statcallb(&ic);
+
+	CallcFreeChan();
+}
+
+void
+ll_unload(void)
+{
+	isdn_ctrl       ic;
+
+	ic.command = ISDN_STAT_UNLOAD;
+	ic.driver = drid;
+	iif.statcallb(&ic);
+}
diff -u --recursive --new-file v1.3.68/linux/drivers/isdn/teles/mod.c linux/drivers/isdn/teles/mod.c
--- v1.3.68/linux/drivers/isdn/teles/mod.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/isdn/teles/mod.c	Mon Feb 26 11:58:07 1996
@@ -0,0 +1,137 @@
+#include "teles.h"
+
+
+
+extern struct IsdnCard cards[];
+extern char   *teles_id;
+
+int             nrcards;
+
+typedef struct {
+	byte           *membase;
+	int             interrupt;
+	unsigned int    iobase;
+	unsigned int    protocol;
+} io_type;
+
+io_type         io[] =
+{
+	{0, 0, 0, 0},
+	{0, 0, 0, 0},
+	{0, 0, 0, 0},
+	{0, 0, 0, 0},
+	{0, 0, 0, 0},
+	{0, 0, 0, 0},
+	{0, 0, 0, 0},
+	{0, 0, 0, 0},
+	{0, 0, 0, 0},
+	{0, 0, 0, 0},
+	{0, 0, 0, 0},
+	{0, 0, 0, 0},
+	{0, 0, 0, 0},
+	{0, 0, 0, 0},
+	{0, 0, 0, 0},
+	{0, 0, 0, 0},
+};
+
+void
+teles_mod_dec_use_count(void)
+{
+	MOD_DEC_USE_COUNT;
+}
+
+void
+teles_mod_inc_use_count(void)
+{
+	MOD_INC_USE_COUNT;
+}
+
+#ifdef MODULE
+#define teles_init init_module
+#else
+void teles_setup(char *str, int *ints)
+{
+        int  i, j, argc;
+	static char sid[20];
+
+        argc = ints[0];
+        i = 0;
+        j = 1;
+        while (argc && (i<16)) {
+                if (argc) {
+                        io[i].iobase    = ints[j];
+                        j++; argc--;
+                }
+                if (argc) {
+                        io[i].interrupt = ints[j];
+                        j++; argc--;
+                }
+                if (argc) {
+                        io[i].membase   = (byte *)ints[j];
+                        j++; argc--;
+                }
+                if (argc) {
+                        io[i].protocol  = ints[j];
+                        j++; argc--;
+                }
+                i++;
+        }
+	if (strlen(str)) {
+		strcpy(sid,str);
+		teles_id = sid;
+	}
+}
+#endif
+
+int
+teles_init(void)
+{
+	int             i;
+
+	nrcards = 0;
+	for (i = 0; i < 16; i++) {
+		if (io[i].protocol) {
+			cards[i].membase   = io[i].membase;
+			cards[i].interrupt = io[i].interrupt;
+			cards[i].iobase    = io[i].iobase;
+			cards[i].protocol  = io[i].protocol;
+		}
+	}
+	for (i = 0; i < 16; i++)
+                if (cards[i].protocol)
+                        nrcards++;
+	printk(KERN_DEBUG "teles: Total %d card%s defined\n",
+               nrcards, (nrcards > 1) ? "s" : "");
+	if (teles_inithardware()) {
+                /* Install only, if at least one card found */
+                Isdnl2New();
+                TeiNew();
+                CallcNew();
+                ll_init();
+
+		/* No symbols to export, hide all symbols */
+		register_symtab(NULL);
+
+#ifdef MODULE
+                printk(KERN_NOTICE "Teles module installed\n");
+#endif
+                return (0);
+        } else
+                return -EIO;
+}
+
+#ifdef MODULE
+void
+cleanup_module(void)
+{
+
+	ll_stop();
+	TeiFree();
+	Isdnl2Free();
+	CallcFree();
+	teles_closehardware();
+	ll_unload();
+	printk(KERN_NOTICE "Teles module removed\n");
+
+}
+#endif
diff -u --recursive --new-file v1.3.68/linux/drivers/isdn/teles/q931.c linux/drivers/isdn/teles/q931.c
--- v1.3.68/linux/drivers/isdn/teles/q931.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/isdn/teles/q931.c	Mon Feb 26 11:58:07 1996
@@ -0,0 +1,869 @@
+/*
+ * q931.c               code to decode ITU Q.931 call control messages
+ * 
+ * Author               Jan den Ouden
+ * 
+ * Changelog
+ * 
+ * Pauline Middelink    general improvements
+ * 
+ * Beat Doebeli         cause texts, display information element
+ * 
+ */
+
+
+#define __NO_VERSION__
+#include "teles.h"
+
+byte           *
+findie(byte * p, int size, byte ie, int wanted_set)
+{
+	int             l, codeset, maincodeset;
+	byte           *pend = p + size;
+
+	/* skip protocol discriminator, callref and message type */
+	p++;
+	l = (*p++) & 0xf;
+	p += l;
+	p++;
+	codeset = 0;
+	maincodeset = 0;
+	/* while there are bytes left... */
+	while (p < pend) {
+		if ((*p & 0xf0) == 0x90) {
+			codeset = *p & 0x07;
+			if (!(*p & 0x08))
+				maincodeset = codeset;
+		}
+		if (*p & 0x80)
+			p++;
+		else {
+			if (codeset == wanted_set) {
+				if (*p == ie)
+					return (p);
+				if (*p > ie)
+					return (NULL);
+			}
+			p++;
+			l = *p++;
+			p += l;
+			codeset = maincodeset;
+		}
+	}
+	return (NULL);
+}
+
+void
+iecpy(byte * dest, byte * iestart, int ieoffset)
+{
+	byte           *p;
+	int             l;
+
+	p = iestart + ieoffset + 2;
+	l = iestart[1] - ieoffset;
+	while (l--)
+		*dest++ = *p++;
+	*dest++ = '\0';
+}
+
+int
+getcallref(byte * p)
+{
+	p++;			/* prot discr */
+	p++;			/* callref length */
+	return (*p);		/* assuming one-byte callref */
+}
+
+/*
+ * According to Table 4-2/Q.931
+ */
+static
+struct MessageType {
+	byte            nr;
+	char           *descr;
+} mtlist[] = {
+
+	{
+		0x1, "ALERTING"
+	},
+	{
+		0x2, "CALL PROCEEDING"
+	},
+	{
+		0x7, "CONNECT"
+	},
+	{
+		0xf, "CONNECT ACKNOWLEDGE"
+	},
+	{
+		0x3, "PROGRESS"
+	},
+	{
+		0x5, "SETUP"
+	},
+	{
+		0xd, "SETUP ACKNOWLEDGE"
+	},
+	{
+		0x26, "RESUME"
+	},
+	{
+		0x2e, "RESUME ACKNOWLEDGE"
+	},
+	{
+		0x22, "RESUME REJECT"
+	},
+	{
+		0x25, "SUSPEND"
+	},
+	{
+		0x2d, "SUSPEND ACKNOWLEDGE"
+	},
+	{
+		0x21, "SUSPEND REJECT"
+	},
+	{
+		0x20, "USER INFORMATION"
+	},
+	{
+		0x45, "DISCONNECT"
+	},
+	{
+		0x4d, "RELEASE"
+	},
+	{
+		0x5a, "RELEASE COMPLETE"
+	},
+	{
+		0x46, "RESTART"
+	},
+	{
+		0x4e, "RESTART ACKNOWLEDGE"
+	},
+	{
+		0x60, "SEGMENT"
+	},
+	{
+		0x79, "CONGESTION CONTROL"
+	},
+	{
+		0x7b, "INFORMATION"
+	},
+	{
+		0x62, "FACILITY"
+	},
+	{
+		0x6e, "NOTIFY"
+	},
+	{
+		0x7d, "STATUS"
+	},
+	{
+		0x75, "STATUS ENQUIRY"
+	}
+};
+
+#define MTSIZE sizeof(mtlist)/sizeof(struct MessageType)
+
+
+static
+int
+prbits(char *dest, byte b, int start, int len)
+{
+	char           *dp = dest;
+
+	b = b << (8 - start);
+	while (len--) {
+		if (b & 0x80)
+			*dp++ = '1';
+		else
+			*dp++ = '0';
+		b = b << 1;
+	}
+	return (dp - dest);
+}
+
+static
+byte           *
+skipext(byte * p)
+{
+	while (!(*p++ & 0x80));
+	return (p);
+}
+
+/*
+ * Cause Values According to Q.850
+ * edescr: English description
+ * ddescr: German description used by Swissnet II (Swiss Telecom
+ *         not yet written...
+ */
+
+static
+struct CauseValue {
+	byte            nr;
+	char           *edescr;
+	char           *ddescr;
+} cvlist[] = {
+
+	{
+		0x01, "Unallocated (unassigned) number", "Nummer nicht zugeteilt"
+	},
+	{
+		0x02, "No route to specified transit network", ""
+	},
+	{
+		0x03, "No route to destination", ""
+	},
+	{
+		0x04, "Send special information tone", ""
+	},
+	{
+		0x05, "Misdialled trunk prefix", ""
+	},
+	{
+		0x06, "Channel unacceptable", "Kanal nicht akzeptierbar"
+	},
+	{
+		0x07, "Channel awarded and being delivered in an established channel", ""
+	},
+	{
+		0x08, "Preemption", ""
+	},
+	{
+		0x09, "Preemption - circuit reserved for reuse", ""
+	},
+	{
+		0x10, "Normal call clearing", "Normale Ausloesung"
+	},
+	{
+		0x11, "User busy", "TNB besetzt"
+	},
+	{
+		0x12, "No user responding", ""
+	},
+	{
+		0x13, "No answer from user (user alerted)", ""
+	},
+	{
+		0x14, "Subscriber absent", ""
+	},
+	{
+		0x15, "Call rejected", ""
+	},
+	{
+		0x16, "Number changed", ""
+	},
+	{
+		0x1a, "non-selected user clearing", ""
+	},
+	{
+		0x1b, "Destination out of order", ""
+	},
+	{
+		0x1c, "Invalid number format (address incomplete)", ""
+	},
+	{
+		0x1d, "Facility rejected", ""
+	},
+	{
+		0x1e, "Response to Status enuiry", ""
+	},
+	{
+		0x1f, "Normal, unspecified", ""
+	},
+	{
+		0x22, "No circuit/channel available", ""
+	},
+	{
+		0x26, "Network out of order", ""
+	},
+	{
+		0x27, "Permanent frame mode connection out-of-service", ""
+	},
+	{
+		0x28, "Permanent frame mode connection operational", ""
+	},
+	{
+		0x29, "Temporary failure", ""
+	},
+	{
+		0x2a, "Switching equipment congestion", ""
+	},
+	{
+		0x2b, "Access information discarded", ""
+	},
+	{
+		0x2c, "Requested circuit/channel not available", ""
+	},
+	{
+		0x2e, "Precedence call blocked", ""
+	},
+	{
+		0x2f, "Resource unavailable, unspecified", ""
+	},
+	{
+		0x31, "Quality of service unavailable", ""
+	},
+	{
+		0x32, "Requested facility not subscribed", ""
+	},
+	{
+		0x35, "Outgoing calls barred within CUG", ""
+	},
+	{
+		0x37, "Incoming calls barred within CUG", ""
+	},
+	{
+		0x39, "Bearer capability not auhorized", ""
+	},
+	{
+		0x3a, "Bearer capability not presently available", ""
+	},
+	{
+		0x3e, "Inconsistency in designated outgoing access information and subscriber class ", " "
+	},
+	{
+		0x3f, "Service or option not available, unspecified", ""
+	},
+	{
+		0x41, "Bearer capability not implemented", ""
+	},
+	{
+		0x42, "Channel type not implemented", ""
+	},
+	{
+		0x43, "Requested facility not implemented", ""
+	},
+	{
+		0x44, "Only restricted digital information bearer capability is available", ""
+	},
+	{
+		0x4f, "Service or option not implemented", ""
+	},
+	{
+		0x51, "Invalid call reference value", ""
+	},
+	{
+		0x52, "Identified channel does not exist", ""
+	},
+	{
+		0x53, "A suspended call exists, but this call identity does not", ""
+	},
+	{
+		0x54, "Call identity in use", ""
+	},
+	{
+		0x55, "No call suspended", ""
+	},
+	{
+		0x56, "Call having the requested call identity has been cleared", ""
+	},
+	{
+		0x57, "User not member of CUG", ""
+	},
+	{
+		0x58, "Incompatible destination", ""
+	},
+	{
+		0x5a, "Non-existent CUG", ""
+	},
+	{
+		0x5b, "Invalid transit network selection", ""
+	},
+	{
+		0x5f, "Invalid message, unspecified", ""
+	},
+	{
+		0x60, "Mandatory information element is missing", ""
+	},
+	{
+		0x61, "Message type non-existent or not implemented", ""
+	},
+	{
+		0x62, "Message not compatible with call state or message type non-existent or not implemented ", " "
+	},
+	{
+		0x63, "Information element/parameter non-existent or not implemented", ""
+	},
+	{
+		0x64, "Invalid information element contents", ""
+	},
+	{
+		0x65, "Message not compatible with call state", ""
+	},
+	{
+		0x66, "Recovery on timer expiry", ""
+	},
+	{
+		0x67, "Parameter non-existent or not implemented - passed on", ""
+	},
+	{
+		0x6e, "Message with unrecognized parameter discarded", ""
+	},
+	{
+		0x6f, "Protocol error, unspecified", ""
+	},
+	{
+		0x7f, "Interworking, unspecified", ""
+	},
+};
+
+#define CVSIZE sizeof(cvlist)/sizeof(struct CauseValue)
+
+static
+int
+prcause(char *dest, byte * p)
+{
+	byte           *end;
+	char           *dp = dest;
+	int             i, cause;
+
+	end = p + p[1] + 1;
+	p += 2;
+	dp += sprintf(dp, "    coding ");
+	dp += prbits(dp, *p, 7, 2);
+	dp += sprintf(dp, " location ");
+	dp += prbits(dp, *p, 4, 4);
+	*dp++ = '\n';
+	p = skipext(p);
+
+	cause = 0x7f & *p++;
+
+	/* locate cause value */
+	for (i = 0; i < CVSIZE; i++)
+		if (cvlist[i].nr == cause)
+			break;
+
+	/* display cause value if it exists */
+	if (i == CVSIZE)
+		dp += sprintf(dp, "Unknown cause type %x!\n", cause);
+	else
+		dp += sprintf(dp, "  cause value %x : %s \n", cause, cvlist[i].edescr);
+
+
+#if 0
+	dp += sprintf(dp,"    cause value ");
+        dp += prbits(dp,*p++,7,7);
+        *dp++ = '\n';
+#endif
+	while (!0) {
+		if (p > end)
+			break;
+		dp += sprintf(dp, "    diag attribute %d ", *p++ & 0x7f);
+		dp += sprintf(dp, " rej %d ", *p & 0x7f);
+		if (*p & 0x80) {
+			*dp++ = '\n';
+			break;
+		} else
+			dp += sprintf(dp, " av %d\n", (*++p) & 0x7f);
+	}
+	return (dp - dest);
+
+}
+
+static
+int
+prchident(char *dest, byte * p)
+{
+	char           *dp = dest;
+
+	p += 2;
+	dp += sprintf(dp, "    octet 3 ");
+	dp += prbits(dp, *p, 8, 8);
+	*dp++ = '\n';
+	return (dp - dest);
+}
+static
+int
+prcalled(char *dest, byte * p)
+{
+	int             l;
+	char           *dp = dest;
+
+	p++;
+	l = *p++ - 1;
+	dp += sprintf(dp, "    octet 3 ");
+	dp += prbits(dp, *p++, 8, 8);
+	*dp++ = '\n';
+	dp += sprintf(dp, "    number digits ");
+	while (l--)
+		*dp++ = *p++;
+	*dp++ = '\n';
+	return (dp - dest);
+}
+static
+int
+prcalling(char *dest, byte * p)
+{
+	int             l;
+	char           *dp = dest;
+
+	p++;
+	l = *p++ - 1;
+	dp += sprintf(dp, "    octet 3 ");
+	dp += prbits(dp, *p, 8, 8);
+	*dp++ = '\n';
+	if (!(*p & 0x80)) {
+		dp += sprintf(dp, "    octet 3a ");
+		dp += prbits(dp, *++p, 8, 8);
+		*dp++ = '\n';
+		l--;
+	};
+
+	p++;
+
+	dp += sprintf(dp, "    number digits ");
+	while (l--)
+		*dp++ = *p++;
+	*dp++ = '\n';
+	return (dp - dest);
+}
+
+static
+int
+prbearer(char *dest, byte * p)
+{
+	char           *dp = dest, ch;
+
+	p += 2;
+	dp += sprintf(dp, "    octet 3  ");
+	dp += prbits(dp, *p++, 8, 8);
+	*dp++ = '\n';
+	dp += sprintf(dp, "    octet 4  ");
+	dp += prbits(dp, *p, 8, 8);
+	*dp++ = '\n';
+	if ((*p++ & 0x1f) == 0x18) {
+		dp += sprintf(dp, "    octet 4.1 ");
+		dp += prbits(dp, *p++, 8, 8);
+		*dp++ = '\n';
+	}
+	/* check for user information layer 1 */
+	if ((*p & 0x60) == 0x20) {
+		ch = ' ';
+		do {
+			dp += sprintf(dp, "    octet 5%c ", ch);
+			dp += prbits(dp, *p, 8, 8);
+			*dp++ = '\n';
+			if (ch == ' ')
+				ch = 'a';
+			else
+				ch++;
+		}
+		while (!(*p++ & 0x80));
+	}
+	/* check for user information layer 2 */
+	if ((*p & 0x60) == 0x40) {
+		dp += sprintf(dp, "    octet 6  ");
+		dp += prbits(dp, *p++, 8, 8);
+		*dp++ = '\n';
+	}
+	/* check for user information layer 3 */
+	if ((*p & 0x60) == 0x60) {
+		dp += sprintf(dp, "    octet 7  ");
+		dp += prbits(dp, *p++, 8, 8);
+		*dp++ = '\n';
+	}
+	return (dp - dest);
+}
+
+static
+int
+general(char *dest, byte * p)
+{
+	char           *dp = dest;
+	char            ch = ' ';
+	int             l, octet = 3;
+
+	p++;
+	l = *p++;
+	/* Iterate over all octets in the information element */
+	while (l--) {
+		dp += sprintf(dp, "    octet %d%c ", octet, ch);
+		dp += prbits(dp, *p++, 8, 8);
+		*dp++ = '\n';
+
+		/* last octet in group? */
+		if (*p & 0x80) {
+			octet++;
+			ch = ' ';
+		} else if (ch == ' ')
+			ch = 'a';
+
+		else
+			ch++;
+	}
+	return (dp - dest);
+}
+
+static
+int
+display(char *dest, byte * p)
+{
+	char           *dp = dest;
+	char            ch = ' ';
+	int             l, octet = 3;
+
+	p++;
+	l = *p++;
+	/* Iterate over all octets in the * display-information element */
+	dp += sprintf(dp, "   \"");
+	while (l--) {
+		dp += sprintf(dp, "%c", *p++);
+
+		/* last octet in group? */
+		if (*p & 0x80) {
+			octet++;
+			ch = ' ';
+		} else if (ch == ' ')
+			ch = 'a';
+
+		else
+			ch++;
+	}
+	*dp++ = '\"';
+	*dp++ = '\n';
+	return (dp - dest);
+}
+
+int
+prfacility(char *dest, byte * p)
+{
+	char           *dp = dest;
+	int             l, l2;
+
+	p++;
+	l = *p++;
+	dp += sprintf(dp, "    octet 3 ");
+	dp += prbits(dp, *p++, 8, 8);
+	dp += sprintf(dp, "\n");
+	l -= 1;
+
+	while (l > 0) {
+		dp += sprintf(dp, "   octet 4 ");
+		dp += prbits(dp, *p++, 8, 8);
+		dp += sprintf(dp, "\n");
+		dp += sprintf(dp, "   octet 5 %d\n", l2 = *p++ & 0x7f);
+		l -= 2;
+		dp += sprintf(dp, "   contents ");
+		while (l2--) {
+			dp += sprintf(dp, "%2x ", *p++);
+			l--;
+		}
+		dp += sprintf(dp, "\n");
+	}
+
+	return (dp - dest);
+}
+
+static
+struct InformationElement {
+	byte            nr;
+	char           *descr;
+	int             (*f) (char *, byte *);
+} ielist[] = {
+
+	{
+		0x00, "Segmented message", general
+	},
+	{
+		0x04, "Bearer capability", prbearer
+	},
+	{
+		0x08, "Cause", prcause
+	},
+	{
+		0x10, "Call identity", general
+	},
+	{
+		0x14, "Call state", general
+	},
+	{
+		0x18, "Channel identification", prchident
+	},
+	{
+		0x1c, "Facility", prfacility
+	},
+	{
+		0x1e, "Progress indicator", general
+	},
+	{
+		0x20, "Network-specific facilities", general
+	},
+	{
+		0x27, "Notification indicator", general
+	},
+	{
+		0x28, "Display", display
+	},
+	{
+		0x29, "Date/Time", general
+	},
+	{
+		0x2c, "Keypad facility", general
+	},
+	{
+		0x34, "Signal", general
+	},
+	{
+		0x40, "Information rate", general
+	},
+	{
+		0x42, "End-to-end delay", general
+	},
+	{
+		0x43, "Transit delay selection and indication", general
+	},
+	{
+		0x44, "Packet layer binary parameters", general
+	},
+	{
+		0x45, "Packet layer window size", general
+	},
+	{
+		0x46, "Packet size", general
+	},
+	{
+		0x47, "Closed user group", general
+	},
+	{
+		0x4a, "Reverse charge indication", general
+	},
+	{
+		0x6c, "Calling party number", prcalling
+	},
+	{
+		0x6d, "Calling party subaddress", general
+	},
+	{
+		0x70, "Called party number", prcalled
+	},
+	{
+		0x71, "Called party subaddress", general
+	},
+	{
+		0x74, "Redirecting number", general
+	},
+	{
+		0x78, "Transit network selection", general
+	},
+	{
+		0x79, "Restart indicator", general
+	},
+	{
+		0x7c, "Low layer compatibility", general
+	},
+	{
+		0x7d, "High layer compatibility", general
+	},
+	{
+		0x7e, "User-user", general
+	},
+	{
+		0x7f, "Escape for extension", general
+	},
+};
+
+#define IESIZE sizeof(ielist)/sizeof(struct InformationElement)
+
+#ifdef FRITZDEBUG
+void
+hexdump(byte * buf, int len, char *comment)
+{
+	static char     dbuf[1024];
+	char           *p = dbuf;
+
+	p += sprintf(p, "%s: ", comment);
+	while (len) {
+		p += sprintf(p, "%02x ", *p++);
+		len--;
+	}
+	p += sprintf(p, "\n");
+
+	teles_putstatus(dbuf);
+}
+#endif
+
+void
+dlogframe(struct IsdnCardState *sp, byte * buf, int size, char *comment)
+{
+	byte           *bend = buf + size;
+	char           *dp;
+	int             i;
+
+	/* display header */
+	dp = sp->dlogspace;
+	dp += sprintf(dp, "%s\n", comment);
+
+	{
+		byte           *p = buf;
+
+		dp += sprintf(dp, "hex: ");
+		while (p < bend)
+			dp += sprintf(dp, "%02x ", *p++);
+		dp += sprintf(dp, "\n");
+		teles_putstatus(sp->dlogspace);
+		dp = sp->dlogspace;
+	}
+	/* locate message type */
+	for (i = 0; i < MTSIZE; i++)
+		if (mtlist[i].nr == buf[3])
+			break;
+
+	/* display message type iff it exists */
+	if (i == MTSIZE)
+		dp += sprintf(dp, "Unknown message type %x!\n", buf[3]);
+	else
+		dp += sprintf(dp, "call reference %d size %d message type %s\n",
+			      buf[2], size, mtlist[i].descr);
+
+	/* display each information element */
+	buf += 4;
+	while (buf < bend) {
+		/* Is it a single octet information element? */
+		if (*buf & 0x80) {
+			switch ((*buf >> 4) & 7) {
+			  case 1:
+				  dp += sprintf(dp, "  Shift %x\n", *buf & 0xf);
+				  break;
+			  case 3:
+				  dp += sprintf(dp, "  Congestion level %x\n", *buf & 0xf);
+				  break;
+			  case 5:
+				  dp += sprintf(dp, "  Repeat indicator %x\n", *buf & 0xf);
+				  break;
+			  case 2:
+				  if (*buf == 0xa0) {
+					  dp += sprintf(dp, "  More data\n");
+					  break;
+				  }
+				  if (*buf == 0xa1) {
+					  dp += sprintf(dp, "  Sending complete\n");
+				  }
+				  break;
+				  /* fall through */
+			  default:
+				  dp += sprintf(dp, "  Reserved %x\n", *buf);
+				  break;
+			}
+			buf++;
+			continue;
+		}
+		/* No, locate it in the table */
+		for (i = 0; i < IESIZE; i++)
+			if (*buf == ielist[i].nr)
+				break;
+
+		/* When not found, give apropriate msg */
+		if (i != IESIZE) {
+			dp += sprintf(dp, "  %s\n", ielist[i].descr);
+			dp += ielist[i].f(dp, buf);
+		} else
+			dp += sprintf(dp, "  attribute %x attribute size %d\n", *buf, buf[1]);
+
+		/* Skip to next element */
+		buf += buf[1] + 2;
+	}
+
+	dp += sprintf(dp, "\n");
+	teles_putstatus(sp->dlogspace);
+}
diff -u --recursive --new-file v1.3.68/linux/drivers/isdn/teles/tei.c linux/drivers/isdn/teles/tei.c
--- v1.3.68/linux/drivers/isdn/teles/tei.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/isdn/teles/tei.c	Mon Feb 26 11:58:07 1996
@@ -0,0 +1,240 @@
+#define __NO_VERSION__
+#include "teles.h"
+
+extern struct IsdnCard cards[];
+extern int      nrcards;
+
+static struct PStack *
+findces(struct PStack *st, int ces)
+{
+	struct PStack  *ptr = *(st->l1.stlistp);
+
+	while (ptr)
+		if (ptr->l2.ces == ces)
+			return (ptr);
+		else
+			ptr = ptr->next;
+	return (NULL);
+}
+
+static struct PStack *
+findtei(struct PStack *st, int tei)
+{
+	struct PStack  *ptr = *(st->l1.stlistp);
+
+	if (tei == 127)
+		return (NULL);
+
+	while (ptr)
+		if (ptr->l2.tei == tei)
+			return (ptr);
+		else
+			ptr = ptr->next;
+	return (NULL);
+}
+
+void 
+tei_handler(struct PStack *st,
+	    byte pr, struct BufHeader *ibh)
+{
+	byte           *bp;
+	unsigned int    tces;
+	struct PStack  *otsp, *ptr;
+	unsigned int    data;
+
+	if (st->l2.debug)
+		printk(KERN_DEBUG "teihandler %d\n", pr);
+
+	switch (pr) {
+	  case (MDL_ASSIGN):
+		  data = (unsigned int) ibh;
+		  BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 6);
+		  if (!ibh)
+			  return;
+		  bp = DATAPTR(ibh);
+		  bp += st->l2.uihsize;
+		  bp[0] = 0xf;
+		  bp[1] = data >> 8;
+		  bp[2] = data & 0xff;
+		  bp[3] = 0x1;
+		  bp[4] = 0xff;
+		  ibh->datasize = 8;
+		  st->l3.l3l2(st, DL_UNIT_DATA, ibh);
+		  break;
+	  case (DL_UNIT_DATA):
+		  bp = DATAPTR(ibh);
+		  bp += 3;
+		  if (bp[0] != 0xf)
+			  break;
+		  switch (bp[3]) {
+		    case (2):
+			    tces = (bp[1] << 8) | bp[2];
+			    BufPoolRelease(ibh);
+			    if (st->l3.debug)
+				    printk(KERN_DEBUG "tei identity assigned for %d=%d\n", tces,
+					   bp[4] >> 1);
+			    if ((otsp = findces(st, tces)))
+				    otsp->ma.teil2(otsp, MDL_ASSIGN,
+						   (void *)(bp[4] >> 1));
+			    break;
+		    case (4):
+			    if (st->l3.debug)
+				    printk(KERN_DEBUG "checking identity for %d\n", bp[4] >> 1);
+			    if (bp[4] >> 1 == 0x7f) {
+				    BufPoolRelease(ibh);
+				    ptr = *(st->l1.stlistp);
+				    while (ptr) {
+					    if ((ptr->l2.tei & 0x7f) != 0x7f) {
+						    if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 7))
+							    break;
+						    bp = DATAPTR(ibh);
+						    bp += 3;
+						    bp[0] = 0xf;
+						    bp[1] = ptr->l2.ces >> 8;
+						    bp[2] = ptr->l2.ces & 0xff;
+						    bp[3] = 0x5;
+						    bp[4] = (ptr->l2.tei << 1) | 1;
+						    ibh->datasize = 8;
+						    st->l3.l3l2(st, DL_UNIT_DATA, ibh);
+					    }
+					    ptr = ptr->next;
+				    }
+			    } else {
+				    otsp = findtei(st, bp[4] >> 1);
+				    BufPoolRelease(ibh);
+				    if (!otsp)
+					    break;
+				    if (st->l3.debug)
+					    printk(KERN_DEBUG "ces is %d\n", otsp->l2.ces);
+				    if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 7))
+					    break;
+				    bp = DATAPTR(ibh);
+				    bp += 3;
+				    bp[0] = 0xf;
+				    bp[1] = otsp->l2.ces >> 8;
+				    bp[2] = otsp->l2.ces & 0xff;
+				    bp[3] = 0x5;
+				    bp[4] = (otsp->l2.tei << 1) | 1;
+				    ibh->datasize = 8;
+				    st->l3.l3l2(st, DL_UNIT_DATA, ibh);
+			    }
+			    break;
+		    default:
+			    BufPoolRelease(ibh);
+			    if (st->l3.debug)
+				    printk(KERN_DEBUG "tei message unknown %d ai %d\n", bp[3], bp[4] >> 1);
+		  }
+		  break;
+	  default:
+		  printk(KERN_WARNING "tei handler unknown primitive %d\n", pr);
+		  break;
+	}
+}
+
+unsigned int 
+randomces(void)
+{
+	int             x = jiffies & 0xffff;
+
+	return (x);
+}
+
+static void 
+tei_man(struct PStack *sp, int i, void *v)
+{
+	printk(KERN_DEBUG "tei_man\n");
+}
+
+static void 
+tei_l2tei(struct PStack *st, int pr, void *arg)
+{
+	struct IsdnCardState *sp = st->l1.hardware;
+
+	tei_handler(sp->teistack, pr, arg);
+}
+
+void 
+setstack_tei(struct PStack *st)
+{
+	st->l2.l2tei = tei_l2tei;
+}
+
+static void 
+init_tei(struct IsdnCardState *sp, int protocol)
+{
+	struct PStack  *st;
+	char            tmp[128];
+
+#define DIRTY_HACK_AGAINST_SIGSEGV
+
+	st = (struct PStack *) Smalloc(sizeof(struct PStack), GFP_KERNEL,
+				       "struct PStack");
+
+#ifdef DIRTY_HACK_AGAINST_SIGSEGV
+	sp->teistack = st;	                /* struct is not initialized yet */
+	sp->teistack->protocol = protocol;	/* struct is not initialized yet */
+#endif	                                        /* DIRTY_HACK_AGAINST_SIGSEGV    */
+
+
+	setstack_teles(st, sp);
+
+	st->l2.extended = !0;
+	st->l2.laptype = LAPD;
+	st->l2.window = 1;
+	st->l2.orig = !0;
+	st->protocol = protocol;
+
+/*
+ * the following is not necessary for tei mng. (broadcast only)
+ */
+
+	st->l2.t200 = 500;	/* 500 milliseconds */
+	st->l2.n200 = 4;	/* try 4 times */
+
+	st->l2.sap = 63;
+	st->l2.tei = 127;
+
+	sprintf(tmp, "Card %d tei ", sp->cardnr);
+	setstack_isdnl2(st, tmp);
+	st->l2.debug = 0;
+	st->l3.debug = 0;
+
+	st->ma.manl2(st, MDL_NOTEIPROC, NULL);
+
+	st->l2.l2l3 = (void *) tei_handler;
+	st->l1.l1man = tei_man;
+	st->l2.l2man = tei_man;
+	st->l4.l2writewakeup = NULL;
+	
+	teles_addlist(sp, st);
+	sp->teistack = st;
+}
+
+static void 
+release_tei(struct IsdnCardState *sp)
+{
+	struct PStack  *st = sp->teistack;
+
+	teles_rmlist(sp, st);
+	Sfree((void *) st);
+}
+
+void 
+TeiNew(void)
+{
+	int             i;
+
+	for (i = 0; i < nrcards; i++)
+		if (cards[i].sp)
+			init_tei(cards[i].sp, cards[i].protocol);
+}
+
+void 
+TeiFree(void)
+{
+	int             i;
+
+	for (i = 0; i < nrcards; i++)
+		if (cards[i].sp)
+			release_tei(cards[i].sp);
+}
diff -u --recursive --new-file v1.3.68/linux/drivers/isdn/teles/teles.h linux/drivers/isdn/teles/teles.h
--- v1.3.68/linux/drivers/isdn/teles/teles.h	Thu Jan  1 02:00:00 1970
+++ linux/drivers/isdn/teles/teles.h	Mon Feb 26 12:29:43 1996
@@ -0,0 +1,475 @@
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/major.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/ioport.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/isdnif.h>
+#include <linux/tty.h>
+
+#define PH_ACTIVATE   1
+#define PH_DATA       2
+#define PH_DEACTIVATE 3
+
+#define MDL_ASSIGN       4
+#define DL_UNIT_DATA     5
+#define SC_STARTUP       6
+#define CC_ESTABLISH     7
+#define DL_ESTABLISH     8
+#define DL_DATA          9
+#define CC_S_STATUS_ENQ  10
+
+#define CC_CONNECT       15
+#define CC_CONNECT_ACKNOWLEDGE 16
+#define CO_EOF                 17
+#define SC_DISCONNECT          18
+#define CO_DTMF                19
+#define DL_RELEASE             20
+
+#define CO_ALARM               22
+#define CC_REJECT              23
+
+#define CC_SETUP_REQ           24
+#define CC_SETUP_CNF           25
+#define CC_SETUP_IND           26
+#define CC_SETUP_RSP           27
+#define CC_SETUP_COMPLETE_IND  28
+
+#define CC_DISCONNECT_REQ      29
+#define CC_DISCONNECT_IND      30
+
+#define CC_RELEASE_CNF         31
+#define CC_RELEASE_IND         32
+#define CC_RELEASE_REQ         33
+
+#define CC_REJECT_REQ          34
+
+#define CC_PROCEEDING_IND      35
+
+#define CC_DLRL                36
+#define CC_DLEST               37
+
+#define CC_ALERTING_REQ        38
+#define CC_ALERTING_IND        39
+
+#define DL_STOP                40
+#define DL_START               41
+
+#define MDL_NOTEIPROC          46
+
+#define LC_ESTABLISH           47
+#define LC_RELEASE             48
+
+#define PH_REQUEST_PULL        49
+#define PH_PULL_ACK            50
+#define	PH_DATA_PULLED	       51
+#define CC_INFO_CHARGE         52
+
+/*
+ * Message-Types 
+ */
+
+#define MT_ALERTING            0x01
+#define MT_CALL_PROCEEDING     0x02
+#define MT_CONNECT             0x07
+#define MT_CONNECT_ACKNOWLEDGE 0x0f
+#define MT_PROGRESS            0x03
+#define MT_SETUP               0x05
+#define MT_SETUP_ACKNOWLEDGE   0x0d
+#define MT_RESUME              0x26
+#define MT_RESUME_ACKNOWLEDGE  0x2e
+#define MT_RESUME_REJECT       0x22
+#define MT_SUSPEND             0x25
+#define MT_SUSPEND_ACKNOWLEDGE 0x2d
+#define MT_SUSPEND_REJECT      0x21
+#define MT_USER_INFORMATION    0x20
+#define MT_DISCONNECT          0x45
+#define MT_RELEASE             0x4d
+#define MT_RELEASE_COMPLETE    0x5a
+#define MT_RESTART             0x46
+#define MT_RESTART_ACKNOWLEDGE 0x4e
+#define MT_SEGMENT             0x60
+#define MT_CONGESTION_CONTROL  0x79
+#define MT_INFORMATION         0x7b
+#define MT_FACILITY            0x62
+#define MT_NOTIFY              0x6e
+#define MT_STATUS              0x7d
+#define MT_STATUS_ENQUIRY      0x75
+
+#define IE_CAUSE               0x08
+
+struct HscxIoctlArg {
+	int             channel;
+	int             mode;
+	int             transbufsize;
+};
+
+#ifdef __KERNEL__
+
+#undef DEBUG_MAGIC
+
+#define HSCX_SBUF_ORDER     1
+#define HSCX_SBUF_BPPS      2
+#define HSCX_SBUF_MAXPAGES  3
+
+#define HSCX_RBUF_ORDER     1
+#define HSCX_RBUF_BPPS      2
+#define HSCX_RBUF_MAXPAGES  3
+
+#define HSCX_SMALLBUF_ORDER     0
+#define HSCX_SMALLBUF_BPPS      40
+#define HSCX_SMALLBUF_MAXPAGES  1
+
+#define ISAC_SBUF_ORDER     0
+#define ISAC_SBUF_BPPS      16
+#define ISAC_SBUF_MAXPAGES  1
+
+#define ISAC_RBUF_ORDER     0
+#define ISAC_RBUF_BPPS      16
+#define ISAC_RBUF_MAXPAGES  1
+
+#define ISAC_SMALLBUF_ORDER     0
+#define ISAC_SMALLBUF_BPPS      40
+#define ISAC_SMALLBUF_MAXPAGES  1
+
+#define byte unsigned char
+
+#define MAX_WINDOW 8
+
+byte           *Smalloc(int size, int pr, char *why);
+void            Sfree(byte * ptr);
+
+/*
+ * Statemachine 
+ */
+struct Fsm {
+	int            *jumpmatrix;
+	int             state_count, event_count;
+	char          **strEvent, **strState;
+};
+
+struct FsmInst {
+	struct Fsm     *fsm;
+	int             state;
+	int             debug;
+	void           *userdata;
+	int             userint;
+	void            (*printdebug) (struct FsmInst *, char *);
+};
+
+struct FsmNode {
+	int             state, event;
+	void            (*routine) (struct FsmInst *, int, void *);
+};
+
+struct FsmTimer {
+	struct FsmInst *fi;
+	struct timer_list tl;
+	int             event;
+	void           *arg;
+};
+
+struct BufHeader {
+#ifdef DEBUG_MAGIC
+	int             magic;
+#endif
+	struct BufHeader *next;
+	struct BufPool *bp;
+	int             datasize;
+	byte            primitive, where;
+	void           *heldby;
+};
+
+struct Pages {
+	struct Pages   *next;
+};
+
+struct BufPool {
+#ifdef DEBUG_MAGIC
+	int             magic;
+#endif
+	struct BufHeader *freelist;
+	struct Pages   *pageslist;
+	int             pageorder;
+	int             pagescount;
+	int             bpps;
+	int             bufsize;
+	int             maxpages;
+};
+
+struct BufQueue {
+#ifdef DEBUG_MAGIC
+	int             magic;
+#endif
+	struct BufHeader *head, *tail;
+};
+
+struct Layer1 {
+	void           *hardware;
+	int             hscx;
+	struct BufPool *sbufpool, *rbufpool, *smallpool;
+	struct PStack **stlistp;
+	int             act_state;
+	void            (*l1l2) (struct PStack *, int, struct BufHeader *);
+        void            (*l1man) (struct PStack *, int, void *);
+	int             hscxmode, hscxchannel, requestpull;
+};
+
+struct Layer2 {
+	int             sap, tei, ces;
+	int             extended, laptype;
+	int             uihsize, ihsize;
+	int             vs, va, vr;
+	struct BufQueue i_queue;
+	int             window, orig;
+	int             rejexp;
+	int             debug;
+	struct BufHeader *windowar[MAX_WINDOW];
+	int             sow;
+	struct FsmInst  l2m;
+	void            (*l2l1) (struct PStack *, int, struct BufHeader *);
+        void            (*l2l1discardq) (struct PStack *, int, void *, int);
+        void            (*l2man) (struct PStack *, int, void *);
+	void            (*l2l3) (struct PStack *, int, void *);
+	void            (*l2tei) (struct PStack *, int, void *);
+	struct FsmTimer t200_timer, t203_timer;
+	int             t200, n200, t203;
+	int             rc, t200_running;
+	char            debug_id[32];
+};
+
+struct Layer3 {
+	void            (*l3l4) (struct PStack *, int, struct BufHeader *);
+        void            (*l3l2) (struct PStack *, int, void *);
+	int             state, callref;
+	int             debug;
+};
+
+struct Layer4 {
+	void            (*l4l3) (struct PStack *, int, void *);
+	void           *userdata;
+	void            (*l1writewakeup) (struct PStack *);
+	void		(*l2writewakeup) (struct PStack *);
+};
+
+struct Management {
+	void            (*manl1) (struct PStack *, int, void *);
+        void            (*manl2) (struct PStack *, int, void *);
+	void            (*teil2) (struct PStack *, int, void *);
+};
+
+struct Param {
+	int             cause;
+	int             bchannel;
+	int             callref;     /* TEI-Number                      */
+	int             itc;
+	int             info;	     /* Service-Indicator               */
+	int             info2;	     /* Service-Indicator, second octet */
+	char            calling[40]; /* Called Id                       */
+	char            called[40];  /* Caller Id                       */
+	int             chargeinfo;  /* Charge Info - only for 1tr6 in
+				      * the moment 
+				      */
+};
+
+struct PStack {
+	struct PStack  *next;
+	struct Layer1   l1;
+	struct Layer2   l2;
+	struct Layer3   l3;
+	struct Layer4   l4;
+	struct Management ma;
+	struct Param   *pa;
+	int             protocol;     /* EDSS1 or 1TR6 */
+};
+
+struct HscxState {
+	byte           *membase;
+	int             iobase;
+	int             inuse, init, active;
+	struct BufPool  sbufpool, rbufpool, smallpool;
+	struct IsdnCardState *sp;
+	int             hscx, mode;
+	int             transbufsize, receive;
+	struct BufHeader *rcvibh, *xmtibh;
+	int             rcvptr, sendptr;
+	struct PStack  *st;
+	struct tq_struct tqueue;
+	int             event;
+	struct BufQueue rq, sq;
+	int             releasebuf;
+#ifdef DEBUG_MAGIC
+	int             magic;	/* 301270 */
+#endif
+};
+
+struct IsdnCardState {
+#ifdef DEBUG_MAGIC
+	int             magic;
+#endif
+	byte           *membase;
+	int             iobase;
+	struct BufPool  sbufpool, rbufpool, smallpool;
+	struct PStack  *stlist;
+	struct BufHeader *xmtibh, *rcvibh;
+	int             rcvptr, sendptr;
+	int             event;
+	struct tq_struct tqueue;
+	int             ph_active;
+	struct BufQueue rq, sq;
+
+	int             cardnr, ph_state;
+	struct PStack  *teistack;
+	struct HscxState hs[2];
+
+	int             dlogflag;
+	char           *dlogspace;
+	int             debug;
+	int             releasebuf;
+};
+
+struct IsdnCard {
+	byte           *membase;
+	int             interrupt;
+	unsigned int    iobase;
+	int             protocol;	/* EDSS1 or 1TR6 */
+	struct IsdnCardState *sp;
+};
+
+#define DATAPTR(x) ((byte *)x+sizeof(struct BufHeader))
+
+#define LAPD 0
+#define LAPB 1
+
+void            BufPoolInit(struct BufPool *bp, int order, int bpps,
+			    int maxpages);
+int             BufPoolAdd(struct BufPool *bp, int priority);
+void            BufPoolFree(struct BufPool *bp);
+int             BufPoolGet(struct BufHeader **bh,
+	      struct BufPool *bp, int priority, void *heldby, int where);
+void            BufPoolRelease(struct BufHeader *bh);
+void            BufQueueLink(struct BufQueue *bq,
+			     struct BufHeader *bh);
+int             BufQueueUnlink(struct BufHeader **bh, struct BufQueue *bq);
+void            BufQueueInit(struct BufQueue *bq);
+void            BufQueueRelease(struct BufQueue *bq);
+void            BufQueueDiscard(struct BufQueue *q, int pr, void *heldby,
+				int releasetoo);
+int             BufQueueLength(struct BufQueue *bq);
+void            BufQueueLinkFront(struct BufQueue *bq,
+				  struct BufHeader *bh);
+
+void            l2down(struct PStack *st,
+		       byte pr, struct BufHeader *ibh);
+void            l2up(struct PStack *st,
+		     byte pr, struct BufHeader *ibh);
+void            acceptph(struct PStack *st,
+			 struct BufHeader *ibh);
+void            setstack_isdnl2(struct PStack *st, char *debug_id);
+int             teles_inithardware(void);
+void            teles_closehardware(void);
+
+void            setstack_teles(struct PStack *st, struct IsdnCardState *sp);
+unsigned int    randomces(void);
+void            setstack_isdnl3(struct PStack *st);
+void            teles_addlist(struct IsdnCardState *sp,
+			      struct PStack *st);
+void            releasestack_isdnl2(struct PStack *st);
+void            teles_rmlist(struct IsdnCardState *sp,
+			     struct PStack *st);
+void            newcallref(struct PStack *st);
+
+int             ll_init(void);
+void            ll_stop(void), ll_unload(void);
+int             setstack_hscx(struct PStack *st, struct HscxState *hs);
+void            modehscx(struct HscxState *hs, int mode, int ichan);
+byte           *findie(byte * p, int size, byte ie, int wanted_set);
+int             getcallref(byte * p);
+
+void            FsmNew(struct Fsm *fsm,
+		       struct FsmNode *fnlist, int fncount);
+void            FsmFree(struct Fsm *fsm);
+int             FsmEvent(struct FsmInst *fi,
+			 int event, void *arg);
+void            FsmChangeState(struct FsmInst *fi,
+			       int newstate);
+void            FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft);
+int             FsmAddTimer(struct FsmTimer *ft,
+			  int millisec, int event, void *arg, int where);
+void            FsmDelTimer(struct FsmTimer *ft, int where);
+int             FsmTimerRunning(struct FsmTimer *ft);
+void            jiftime(char *s, long mark);
+
+void            CallcNew(void);
+void            CallcFree(void);
+int             CallcNewChan(void);
+void            CallcFreeChan(void);
+int             teles_command(isdn_ctrl * ic);
+int             teles_writebuf(int id, int chan, const u_char * buf, int count, int user);
+void            teles_putstatus(char *buf);
+void            teles_reportcard(int cardnr);
+int             ListLength(struct BufHeader *ibh);
+void            dlogframe(struct IsdnCardState *sp, byte * p, int size, char *comment);
+void            iecpy(byte * dest, byte * iestart, int ieoffset);
+void            setstack_transl2(struct PStack *st);
+void            releasestack_transl2(struct PStack *st);
+void            close_hscxstate(struct HscxState *);
+void            setstack_tei(struct PStack *st);
+
+struct LcFsm {
+	struct FsmInst  lcfi;
+	int             type;
+	struct Channel *ch;
+	void            (*lccall) (struct LcFsm *, int, void *);
+	struct PStack  *st;
+	int             l2_establish;
+	int             l2_start;
+	struct FsmTimer act_timer;
+	char            debug_id[32];
+};
+
+struct Channel {
+	struct PStack   ds, is;
+	struct IsdnCardState *sp;
+	int             hscx;
+	int             chan;
+	int             incoming;
+	struct FsmInst  fi;
+	struct LcFsm    lc_d, lc_b;
+	struct Param    para;
+	int             debug;
+#ifdef DEBUG_MAGIC
+	int             magic;	/* 301272 */
+#endif
+	int             l2_protocol, l2_active_protocol;
+	int             l2_primitive, l2_headersize;
+	int             data_open;
+	int             outcallref;
+	int             impair;
+};
+
+#define PART_SIZE(order,bpps) (( (PAGE_SIZE<<order) -\
+  sizeof(void *))/bpps)
+#define BUFFER_SIZE(order,bpps) (PART_SIZE(order,bpps)-\
+  sizeof(struct BufHeader))
+
+#endif
+
+void            Isdnl2New(void);
+void            Isdnl2Free(void);
+void            TeiNew(void);
+void            TeiFree(void);
+
+
+
+
diff -u --recursive --new-file v1.3.68/linux/drivers/net/3c509.c linux/drivers/net/3c509.c
--- v1.3.68/linux/drivers/net/3c509.c	Wed Feb  7 15:11:24 1996
+++ linux/drivers/net/3c509.c	Mon Feb 26 11:58:08 1996
@@ -710,7 +710,7 @@
 {
 	dev_3c509.base_addr = io;
 	dev_3c509.irq       = irq;
-	if (!EISA_bus) {
+	if (!EISA_bus && !io) {
 		printk("3c509: WARNING! Module load-time probing works reliably only for EISA-bus!\n");
 	}
 	if (register_netdev(&dev_3c509) != 0)
diff -u --recursive --new-file v1.3.68/linux/drivers/net/Config.in linux/drivers/net/Config.in
--- v1.3.68/linux/drivers/net/Config.in	Fri Feb  9 17:53:01 1996
+++ linux/drivers/net/Config.in	Mon Feb 26 11:58:08 1996
@@ -42,7 +42,7 @@
     tristate 'AT1700 support' CONFIG_AT1700
     tristate 'EtherExpressPro support' CONFIG_EEXPRESS_PRO
     tristate 'EtherExpress support' CONFIG_EEXPRESS
-    bool 'NI5210 support' CONFIG_NI52
+    tristate 'NI5210 support' CONFIG_NI52
     bool 'NI6510 support' CONFIG_NI65
     tristate 'WaveLAN support' CONFIG_WAVELAN
     tristate 'ICL EtherTeam 16i/32 support' CONFIG_ETH16I
diff -u --recursive --new-file v1.3.68/linux/drivers/net/Makefile linux/drivers/net/Makefile
--- v1.3.68/linux/drivers/net/Makefile	Fri Feb 23 13:54:34 1996
+++ linux/drivers/net/Makefile	Mon Feb 26 11:58:08 1996
@@ -19,6 +19,18 @@
 CONFIG_SLHC_BUILTIN :=
 CONFIG_SLHC_MODULE  :=
 
+ifeq ($(CONFIG_ISDN),y)
+  ifeq ($(CONFIG_ISDN_PPP),y)
+    CONFIG_SLHC_BUILTIN = y
+  endif
+else
+  ifeq ($(CONFIG_ISDN),m)
+    ifeq ($(CONFIG_ISDN_PPP),y)
+      CONFIG_SLHC_MODULE = y
+    endif
+  endif
+endif
+
 ifeq ($(CONFIG_SEEQ8005),y)
 L_OBJS += seeq8005.o
 endif
@@ -292,6 +304,10 @@
 
 ifeq ($(CONFIG_NI52),y)
 L_OBJS += ni52.o
+else
+  ifeq ($(CONFIG_NI52),m)
+  M_OBJS += ni52.o
+  endif
 endif
 
 ifeq ($(CONFIG_NI65),y)
diff -u --recursive --new-file v1.3.68/linux/drivers/net/Space.c linux/drivers/net/Space.c
--- v1.3.68/linux/drivers/net/Space.c	Fri Jan 26 01:37:06 1996
+++ linux/drivers/net/Space.c	Mon Feb 26 11:58:08 1996
@@ -174,9 +174,6 @@
 #ifdef CONFIG_NI52
 	&& ni52_probe(dev)
 #endif
-#ifdef CONFIG_NI65
-	&& ni65_probe(dev)
-#endif
 	&& 1 ) {
 	return 1;	/* -ENODEV or -EAGAIN would be more accurate. */
     }
diff -u --recursive --new-file v1.3.68/linux/drivers/net/new_tunnel.c linux/drivers/net/new_tunnel.c
--- v1.3.68/linux/drivers/net/new_tunnel.c	Fri Feb 23 13:54:36 1996
+++ linux/drivers/net/new_tunnel.c	Mon Feb 26 11:58:14 1996
@@ -357,7 +357,8 @@
 	dev->mtu		= 1500-tunnel_hlen; 	/* eth_mtu */
 	dev->addr_len		= 0;		/* Is this only for ARP? */
 	dev->tx_queue_len	= 2;		/* Small queue */
-											it should all run through */
+
+	/* it should all run through */
 	memset(dev->broadcast,0xFF, ETH_ALEN);
 
 	/* New-style flags. */
diff -u --recursive --new-file v1.3.68/linux/drivers/net/ni52.c linux/drivers/net/ni52.c
--- v1.3.68/linux/drivers/net/ni52.c	Sat Nov 25 19:04:46 1995
+++ linux/drivers/net/ni52.c	Mon Feb 26 11:58:14 1996
@@ -4,10 +4,14 @@
  * This is an extension to the Linux operating system, and is covered by the
  * same Gnu Public License that covers that work.
  * 
- * Alphacode 0.62 (95/01/19) for Linux 1.1.82 (or later)
- * Copyrights (c) 1994,1995 by M.Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ * Alphacode 0.80 (96/02/19) for Linux 1.3.66 (or later)
+ * Copyrights (c) 1994,1995,1996 by M.Hipp (Michael.Hipp@student.uni-tuebingen.de)
  *    [feel free to mail ....]
  *
+ * when using as module: (no autoprobing!)
+ *   compile with: gcc -D__KERNEL__ -DMODULE -O2 -c ni52.c
+ *   run with e.g: insmod ni52.o io=0x360 irq=9 memstart=0xd0000 memend=0xd4000
+ *
  * CAN YOU PLEASE REPORT ME YOUR PERFORMANCE EXPERIENCES !!.
  * 
  * If you find a bug, please report me:
@@ -18,7 +22,8 @@
  *   maybe the ni5210-card revision and the i82586 version
  *
  * autoprobe for: base_addr: 0x300,0x280,0x360,0x320,0x340
- *                mem_start: 0xc8000,0xd0000,0xd4000,0xd8000 (8K and 16K)
+ *                mem_start: 0xd0000,0xd2000,0xc8000,0xca000,0xd4000,0xd6000,
+ *                           0xd8000,0xcc000,0xce000,0xda000,0xdc000
  *
  * sources:
  *   skeleton.c from Donald Becker
@@ -33,13 +38,15 @@
  *   The internal sysbus seems to be slow. So we often lose packets because of
  *   overruns while receiving from a fast remote host. 
  *   This can slow down TCP connections. Maybe the newer ni5210 cards are better.
+ *   my experinece is, that if a machine sends with more then about 500-600K/s
+ *   the fifo/sysbus overflows.
  * 
  * IMPORTANT NOTE:
  *   On fast networks, it's a (very) good idea to have 16K shared memory. With
  *   8K, we can store only 4 receive frames, so it can (easily) happen that a remote 
  *   machine 'overruns' our system.
  *
- * Known i82586 bugs (I'm sure, there are many more!):
+ * Known i82586/card problems (I'm sure, there are many more!):
  *   Running the NOP-mode, the i82586 sometimes seems to forget to report
  *   every xmit-interrupt until we restart the CU.
  *   Another MAJOR bug is, that the RU sometimes seems to ignore the EL-Bit 
@@ -48,12 +55,22 @@
  *   usually used) RBD and begins to fill it. (Maybe, this happens only if 
  *   the last buffer from the previous RFD fits exact into the queue and
  *   the next RFD can't fetch an initial RBD. Anyone knows more? )
+ *
+ * results from ftp performance tests with Linux 1.2.5 
+ *   send and receive about 350-400 KByte/s (peak up to 460 kbytes/s)
+ *   sending in NOP-mode: peak performance up to 530K/s (but better don't run this mode)
  */
 
 /*
+ * 19.Feb.96: more Mcast changes, module support (MH)
+ *
  * 18.Nov.95: Mcast changes (AC).
  *
- * 19.Jan.95: verified (MH)
+ * 23.April.95: fixed(?) receiving problems by configuring a RFD more
+ *              than the number of RBD's. Can maybe cause other problems. 
+ * 18.April.95: Added MODULE support (MH)
+ * 17.April.95: MC related changes in init586() and set_multicast_list().
+ *              removed use of 'jiffies' in init586() (MH)
  *
  * 19.Sep.94: Added Multicast support (not tested yet) (MH)
  * 
@@ -78,9 +95,16 @@
  *
  * < 30.Sep.93: first versions 
  */
- 
+
+static int debuglevel = 0; /* debug-printk 0: off 1: a few 2: more */
+static int automatic_resume = 0; /* experimental .. better should be zero */
+static int rfdadd = 0; /* rfdadd=1 may be better for 8K MEM cards */
+static int fifo=0x8;	/* don't change */
+
+/* #define REALLY_SLOW_IO */
+
+#include <linux/module.h>
 #include <linux/kernel.h>
-#include <linux/sched.h>
 #include <linux/string.h>
 #include <linux/errno.h>
 #include <linux/ioport.h>
@@ -101,6 +125,8 @@
 
 #define ni_attn586()  {outb(0,dev->base_addr+NI52_ATTENTION);}
 #define ni_reset586() {outb(0,dev->base_addr+NI52_RESET);}
+#define ni_disint()   {outb(0,dev->base_addr+NI52_INTDIS);}
+#define ni_enaint()   {outb(0,dev->base_addr+NI52_INTENA);}
 
 #define make32(ptr16) (p->memtop + (short) (ptr16) )
 #define make24(ptr32) ((char *) (ptr32) - p->base)
@@ -118,7 +144,7 @@
 sizeof(tbd) = 8; sizeof(transmit_cmd) = 16;
 sizeof(nop_cmd) = 8; 
 
-  * if you don't know the driver, better do not change this values: */
+  * if you don't know the driver, better do not change these values: */
 
 #define RECV_BUFF_SIZE 1524 /* slightly oversized */
 #define XMIT_BUFF_SIZE 1524 /* slightly oversized */
@@ -129,36 +155,38 @@
 
 /**************************************************************************/
 
-#define DELAY(x) {int i=jiffies; \
-                  if(loops_per_sec == 1) \
-                     while(i+(x)>jiffies); \
-                  else \
-                     __delay((loops_per_sec>>5)*x); \
-                 }
-
-/* a much shorter delay: */
+/* different DELAYs */
+#define DELAY(x) __delay((loops_per_sec>>5)*(x)); 
 #define DELAY_16(); { __delay( (loops_per_sec>>16)+1 ); }
+#define DELAY_18(); { __delay( (loops_per_sec>>18)+1 ); }
 
 /* wait for command with timeout: */
 #define WAIT_4_SCB_CMD() { int i; \
-  for(i=0;i<1024;i++) { \
-    if(!p->scb->cmd) break; \
-    DELAY_16(); \
-    if(i == 1023) { \
-      printk("%s: scb_cmd timed out .. resetting i82586\n",dev->name); \
-      ni_reset586(); } } }
-
+  for(i=0;i<16384;i++) { \
+    if(!p->scb->cmd_cuc) break; \
+    DELAY_18(); \
+    if(i == 16383) { \
+      printk("%s: scb_cmd timed out: %04x,%04x .. disabeling i82586!!\n",dev->name,p->scb->cmd_cuc,p->scb->cus); \
+       if(!p->reseted) { p->reseted = 1; ni_reset586(); } } } }
+
+#define WAIT_4_SCB_CMD_RUC() { int i; \
+  for(i=0;i<16384;i++) { \
+    if(!p->scb->cmd_ruc) break; \
+    DELAY_18(); \
+    if(i == 16383) { \
+      printk("%s: scb_cmd (ruc) timed out: %04x,%04x .. disabeling i82586!!\n",dev->name,p->scb->cmd_ruc,p->scb->rus); \
+       if(!p->reseted) { p->reseted = 1; ni_reset586(); } } } }
+
+#define WAIT_4_STAT_COMPL(addr) { int i; \
+   for(i=0;i<32767;i++) { \
+     if((addr)->cmd_status & STAT_COMPL) break; \
+     DELAY_16(); DELAY_16(); } }
 
 #define NI52_TOTAL_SIZE 16
 #define NI52_ADDR0 0x02
 #define NI52_ADDR1 0x07
 #define NI52_ADDR2 0x01
 
-#ifndef HAVE_PORTRESERVE
-#define check_region(ioaddr, size)              0
-#define request_region(ioaddr, size,name)    do ; while (0)
-#endif
-
 static int     ni52_probe1(struct device *dev,int ioaddr);
 static void    ni52_interrupt(int irq,struct pt_regs *reg_ptr);
 static int     ni52_open(struct device *dev);
@@ -166,6 +194,9 @@
 static int     ni52_send_packet(struct sk_buff *,struct device *);
 static struct  enet_statistics *ni52_get_stats(struct device *dev);
 static void    set_multicast_list(struct device *dev);
+#if 0
+static void    ni52_dump(struct device *,void *);
+#endif
 
 /* helper-functions */
 static int     init586(struct device *dev);
@@ -182,6 +213,7 @@
   struct enet_statistics stats;
   unsigned long base;
   char *memtop;
+  int lock,reseted;
   volatile struct rfd_struct  *rfd_last,*rfd_top,*rfd_first;
   volatile struct scp_struct  *scp;  /* volatile is important */
   volatile struct iscp_struct *iscp; /* volatile is important */
@@ -198,36 +230,37 @@
   volatile int    xmit_count,xmit_last;
 };
 
-
 /**********************************************
  * close device 
  */
-
 static int ni52_close(struct device *dev)
 {
   free_irq(dev->irq);
-  irq2dev_map[dev->irq] = 0;
+  irq2dev_map[dev->irq] = NULL;
 
   ni_reset586(); /* the hard way to stop the receiver */
 
   dev->start = 0;
   dev->tbusy = 0;
 
+  MOD_DEC_USE_COUNT;
+
   return 0;
 }
 
 /**********************************************
  * open device 
  */
-
 static int ni52_open(struct device *dev)
 {
+  ni_disint();
   alloc586(dev);
   init586(dev);  
   startrecv586(dev);
+  ni_enaint();
 
-  if(request_irq(dev->irq, &ni52_interrupt,0,"ni52")) 
-  {    
+  if(request_irq(dev->irq, &ni52_interrupt,0,"ni5210")) 
+  {
     ni_reset586();
     return -EAGAIN;
   }  
@@ -237,16 +270,18 @@
   dev->tbusy = 0;
   dev->start = 1;
 
+  MOD_INC_USE_COUNT;
+
   return 0; /* most done by init */
 }
 
 /**********************************************
  * Check to see if there's an 82586 out there. 
  */
-
 static int check586(struct device *dev,char *where,unsigned size)
 {
-  struct priv *p = (struct priv *) dev->priv;
+  struct priv pb;
+  struct priv *p = /* (struct priv *) dev->priv*/ &pb;
   char *iscp_addrs[2];
   int i;
 
@@ -254,7 +289,12 @@
   p->memtop = where + size;
   p->scp = (struct scp_struct *)(p->base + SCP_DEFAULT_ADDRESS);
   memset((char *)p->scp,0, sizeof(struct scp_struct));
+  for(i=0;i<sizeof(struct scp_struct);i++) /* memory was writeable? */
+    if(((char *)p->scp)[i])
+      return 0;
   p->scp->sysbus = SYSBUSVAL;        /* 1 = 8Bit-Bus, 0 = 16 Bit */
+  if(p->scp->sysbus != SYSBUSVAL)
+    return 0;
   
   iscp_addrs[0] = where;
   iscp_addrs[1]= (char *) p->scp - sizeof(struct iscp_struct);
@@ -269,7 +309,7 @@
 
     ni_reset586();
     ni_attn586();
-    DELAY(2);	/* wait a while... */
+    DELAY(1);	/* wait a while... */
 
     if(p->iscp->busy) /* i82586 clears 'busy' after successful init */
       return 0;
@@ -280,13 +320,12 @@
 /******************************************************************
  * set iscp at the right place, called by ni52_probe1 and open586. 
  */
-
 void alloc586(struct device *dev)
 {
   struct priv *p =  (struct priv *) dev->priv; 
 
   ni_reset586();
-  DELAY(2);
+  DELAY(1);
 
   p->scp  = (struct scp_struct *)  (p->base + SCP_DEFAULT_ADDRESS);
   p->scb  = (struct scb_struct *)  (dev->mem_start);
@@ -303,21 +342,25 @@
   ni_reset586();
   ni_attn586();
 
-  DELAY(2); 
+  DELAY(1); 
 
   if(p->iscp->busy)
     printk("%s: Init-Problems (alloc).\n",dev->name);
 
+  p->reseted = 0;
+
   memset((char *)p->scb,0,sizeof(struct scb_struct));
 }
 
 /**********************************************
  * probe the ni5210-card
  */
-
 int ni52_probe(struct device *dev)
 {
-  int *port, ports[] = {0x300, 0x280, 0x360 , 0x320 , 0x340, 0};
+#ifndef MODULE
+  int *port;
+  static int ports[] = {0x300, 0x280, 0x360 , 0x320 , 0x340, 0};
+#endif
   int base_addr = dev->base_addr;
 
   if (base_addr > 0x1ff)		/* Check a single specified location. */
@@ -327,6 +370,9 @@
   else if (base_addr > 0)		/* Don't probe at all. */
     return ENXIO;
 
+#ifdef MODULE
+  printk("%s: no autoprobing allowed for modules.\n",dev->name);
+#else
   for (port = ports; *port; port++) {
     int ioaddr = *port;
     if (check_region(ioaddr, NI52_TOTAL_SIZE))
@@ -340,35 +386,57 @@
       return 0;
   }
 
+#ifdef FULL_IO_PROBE
+  for(dev->base_addr=0x200;dev->base_addr<0x400;dev->base_addr+=8)
+  {
+    int ioaddr = dev->base_addr;
+    if (check_region(ioaddr, NI52_TOTAL_SIZE))
+      continue;
+    if( !(inb(ioaddr+NI52_MAGIC1) == NI52_MAGICVAL1) || 
+        !(inb(ioaddr+NI52_MAGIC2) == NI52_MAGICVAL2))
+      continue;
+    if (ni52_probe1(dev, ioaddr) == 0)
+      return 0;    
+  }
+#endif
+
+#endif
+
   dev->base_addr = base_addr;
   return ENODEV;
 }
 
 static int ni52_probe1(struct device *dev,int ioaddr)
 {
-  long memaddrs[] = { 0xd0000,0xd2000,0xc8000,0xca000,0xd4000,0xd6000,0xd8000, 0 };
   int i,size;
 
   for(i=0;i<ETH_ALEN;i++)
     dev->dev_addr[i] = inb(dev->base_addr+i);
 
   if(dev->dev_addr[0] != NI52_ADDR0 || dev->dev_addr[1] != NI52_ADDR1
-                                    || dev->dev_addr[2] != NI52_ADDR2)
+     || dev->dev_addr[2] != NI52_ADDR2)
     return ENODEV;
 
-  printk("%s: Ni52 found at %#3lx, ",dev->name,dev->base_addr);
-
-  request_region(ioaddr,NI52_TOTAL_SIZE,"ni52");
+  printk("%s: NI5210 found at %#3lx, ",dev->name,dev->base_addr);
 
-  dev->priv = (void *) kmalloc(sizeof(struct priv),GFP_KERNEL); 
-                                  /* warning: we don't free it on errors */
-  if (dev->priv == NULL)
-     return -ENOMEM;
-  memset((char *) dev->priv,0,sizeof(struct priv));
+  request_region(ioaddr,NI52_TOTAL_SIZE,"ni5210");
 
   /* 
    * check (or search) IO-Memory, 8K and 16K
    */
+#ifdef MODULE
+  size = dev->mem_end - dev->mem_start;
+  if(size != 0x2000 && size != 0x4000)
+  {
+    printk("\n%s: Illegal memory size %d. Allowed is 0x2000 or 0x4000 bytes.\n",dev->name,size);
+    return ENODEV;
+  }
+  if(!check586(dev,(char *) dev->mem_start,size))
+  {
+    printk("?memcheck, Can't find memory at 0x%lx with size %d!\n",dev->mem_start,size);
+    return ENODEV;
+  }
+#else
   if(dev->mem_start != 0) /* no auto-mem-probe */
   {
     size = 0x4000; /* check for 16K mem */
@@ -382,7 +450,9 @@
   }
   else  
   {
-    for(i=0;;i++)
+   static long memaddrs[] = { 0xc8000,0xca000,0xcc000,0xce000,0xd0000,0xd2000,
+                              0xd4000,0xd6000,0xd8000,0xda000,0xdc000, 0 };
+   for(i=0;;i++)
     {
       if(!memaddrs[i]) {
         printk("?memprobe, Can't find io-memory!\n");
@@ -398,7 +468,18 @@
     }
   }
   dev->mem_end = dev->mem_start + size; /* set mem_end showed by 'ifconfig' */
+#endif
+
+  dev->priv = (void *) kmalloc(sizeof(struct priv),GFP_KERNEL); 
+  if(dev->priv == NULL)
+  {
+    printk("%s: Ooops .. can't allocate private driver memory.\n",dev->name);
+    return -ENOMEM;
+  }
+                                  /* warning: we don't free it on errors */
+  memset((char *) dev->priv,0,sizeof(struct priv));
   
+  ((struct priv *) (dev->priv))->memtop = (char *) dev->mem_start + size;
   ((struct priv *) (dev->priv))->base =  dev->mem_start + size - 0x01000000;
   alloc586(dev);
 
@@ -420,11 +501,13 @@
       printk("?autoirq, Failed to detect IRQ line!\n"); 
       return 1;
     }
+    printk("IRQ %d (autodetected).\n",dev->irq);
+  }
+  else  {
+    if(dev->irq == 2)
+      dev->irq = 9;
+    printk("IRQ %d (assigned and not checked!).\n",dev->irq);
   }
-  else if(dev->irq == 2) 
-    dev->irq = 9;
-
-  printk("IRQ %d.\n",dev->irq);
 
   dev->open            = &ni52_open;
   dev->stop            = &ni52_close;
@@ -451,7 +534,6 @@
 static int init586(struct device *dev)
 {
   void *ptr;
-  unsigned long s;
   int i,result=0;
   struct priv *p = (struct priv *) dev->priv;
   volatile struct configure_cmd_struct  *cfg_cmd;
@@ -469,7 +551,7 @@
   cfg_cmd->cmd_link   = 0xffff;
 
   cfg_cmd->byte_cnt   = 0x0a; /* number of cfg bytes */
-  cfg_cmd->fifo       = 0x08; /* fifo-limit (8=tx:32/rx:64) */
+  cfg_cmd->fifo       = fifo; /* fifo-limit (8=tx:32/rx:64) */
   cfg_cmd->sav_bf     = 0x40; /* hold or discard bad recv frames (bit 7) */
   cfg_cmd->adr_len    = 0x2e; /* addr_len |!src_insert |pre-len |loopback */
   cfg_cmd->priority   = 0x00;
@@ -477,25 +559,31 @@
   cfg_cmd->time_low   = 0x00;
   cfg_cmd->time_high  = 0xf2;
   cfg_cmd->promisc    = 0;
-  if(dev->flags&(IFF_ALLMULTI|IFF_PROMISC))
+  if(dev->flags & IFF_ALLMULTI) {
+    int len = ((char *) p->iscp - (char *) ptr - 8) / 6;
+    if(num_addrs > len)  {
+      printk("%s: switching to promisc. mode\n",dev->name);
+      dev->flags|=IFF_PROMISC;
+    }
+  }
+  if(dev->flags&IFF_PROMISC)
   {
-	cfg_cmd->promisc=1;
-	dev->flags|=IFF_PROMISC;
+       cfg_cmd->promisc=1;
+       dev->flags|=IFF_PROMISC;
   }
   cfg_cmd->carr_coll  = 0x00;
  
   p->scb->cbl_offset = make16(cfg_cmd);
+  p->scb->cmd_ruc = 0;
 
-  p->scb->cmd = CUC_START; /* cmd.-unit start */
+  p->scb->cmd_cuc = CUC_START; /* cmd.-unit start */
   ni_attn586();
- 
-  s = jiffies; /* warning: only active with interrupts on !! */
-  while(!(cfg_cmd->cmd_status & STAT_COMPL)) 
-    if(jiffies-s > 30) break;
+
+  WAIT_4_STAT_COMPL(cfg_cmd);
 
   if((cfg_cmd->cmd_status & (STAT_OK|STAT_COMPL)) != (STAT_COMPL|STAT_OK))
   {
-    printk("%s (ni52): configure command failed: %x\n",dev->name,cfg_cmd->cmd_status);
+    printk("%s: configure command failed: %x\n",dev->name,cfg_cmd->cmd_status);
     return 1; 
   }
 
@@ -512,12 +600,10 @@
 
   p->scb->cbl_offset = make16(ias_cmd);
 
-  p->scb->cmd = CUC_START; /* cmd.-unit start */
+  p->scb->cmd_cuc = CUC_START; /* cmd.-unit start */
   ni_attn586();
 
-  s = jiffies;
-  while(!(ias_cmd->cmd_status & STAT_COMPL)) 
-    if(jiffies-s > 30) break;
+  WAIT_4_STAT_COMPL(ias_cmd);
 
   if((ias_cmd->cmd_status & (STAT_OK|STAT_COMPL)) != (STAT_OK|STAT_COMPL)) {
     printk("%s (ni52): individual address setup command failed: %04x\n",dev->name,ias_cmd->cmd_status);
@@ -535,28 +621,27 @@
   tdr_cmd->status      = 0;
 
   p->scb->cbl_offset = make16(tdr_cmd);
-
-  p->scb->cmd = CUC_START; /* cmd.-unit start */
+  p->scb->cmd_cuc = CUC_START; /* cmd.-unit start */
   ni_attn586();
 
-  s = jiffies; 
-  while(!(tdr_cmd->cmd_status & STAT_COMPL))
-    if(jiffies - s > 30) {
-      printk("%s: Problems while running the TDR.\n",dev->name);
-      result = 1;
-    }
+  WAIT_4_STAT_COMPL(tdr_cmd);
 
-  if(!result)
+  if(!(tdr_cmd->cmd_status & STAT_COMPL))
   {
-    DELAY(2); /* wait for result */
+    printk("%s: Problems while running the TDR.\n",dev->name);
+  }
+  else
+  {
+    DELAY_16(); /* wait for result */
     result = tdr_cmd->status;
 
-    p->scb->cmd = p->scb->status & STAT_MASK;
+    p->scb->cmd_cuc = p->scb->cus & STAT_MASK;
     ni_attn586(); /* ack the interrupts */
 
-    if(result & TDR_LNK_OK) ;
+    if(result & TDR_LNK_OK) 
+      ;
     else if(result & TDR_XCVR_PRB)
-      printk("%s: TDR: Transceiver problem!\n",dev->name);
+      printk("%s: TDR: Transceiver problem. Check the cable(s)!\n",dev->name);
     else if(result & TDR_ET_OPN)
       printk("%s: TDR: No correct termination %d clocks away.\n",dev->name,result & TDR_TIMEMASK);
     else if(result & TDR_ET_SRT) 
@@ -567,12 +652,30 @@
     else
       printk("%s: TDR: Unknown status %04x\n",dev->name,result);
   }
- 
-   /* 
-    * ack interrupts 
-    */
-  p->scb->cmd = p->scb->status & STAT_MASK;
-  ni_attn586();
+
+  /*
+   * Multicast setup
+   */
+  if(num_addrs && !(dev->flags & IFF_PROMISC) )
+  {
+    mc_cmd = (struct mcsetup_cmd_struct *) ptr;
+    mc_cmd->cmd_status = 0;
+    mc_cmd->cmd_cmd = CMD_MCSETUP | CMD_LAST;
+    mc_cmd->cmd_link = 0xffff;
+    mc_cmd->mc_cnt = num_addrs * 6;
+
+    for(i=0;i<num_addrs;i++,dmi=dmi->next)
+      memcpy((char *) mc_cmd->mc_list[i], dmi->dmi_addr,6);
+
+    p->scb->cbl_offset = make16(mc_cmd);
+    p->scb->cmd_cuc = CUC_START;
+    ni_attn586();
+
+    WAIT_4_STAT_COMPL(mc_cmd);
+
+    if( (mc_cmd->cmd_status & (STAT_COMPL|STAT_OK)) != (STAT_COMPL|STAT_OK) )
+      printk("%s: Can't apply multicast-address-list.\n",dev->name);
+  }
 
    /*
     * alloc nop/xmit-cmds
@@ -586,8 +689,6 @@
     p->nop_cmds[i]->cmd_link   = make16((p->nop_cmds[i]));
     ptr = (char *) ptr + sizeof(struct nop_cmd_struct);
   }
-  p->xmit_cmds[0] = (struct transmit_cmd_struct *)ptr; /* transmit cmd/buff 0 */
-  ptr = (char *) ptr + sizeof(struct transmit_cmd_struct);
 #else
   for(i=0;i<NUM_XMIT_BUFFS;i++)
   {
@@ -596,59 +697,18 @@
     p->nop_cmds[i]->cmd_status = 0;
     p->nop_cmds[i]->cmd_link   = make16((p->nop_cmds[i]));
     ptr = (char *) ptr + sizeof(struct nop_cmd_struct);
-    p->xmit_cmds[i] = (struct transmit_cmd_struct *)ptr; /*transmit cmd/buff 0*/
-    ptr = (char *) ptr + sizeof(struct transmit_cmd_struct);
   }
 #endif
 
   ptr = alloc_rfa(dev,(void *)ptr); /* init receive-frame-area */ 
 
-  /* 
-   * Multicast setup
-   */
-  
-  if(dev->mc_count)
-  { /* I don't understand this: do we really need memory after the init? */
-    int len = ((char *) p->iscp - (char *) ptr - 8) / 6;
-    if(len <= 0)
-    {
-      printk("%s: Ooooops, no memory for MC-Setup!\n",dev->name);
-    }
-    else
-    {
-      if(len < num_addrs)
-      {
-      	/* BUG - should go ALLMULTI in this case */
-        num_addrs = len;
-        printk("%s: Sorry, can only apply %d MC-Address(es).\n",dev->name,num_addrs);
-      }
-      mc_cmd = (struct mcsetup_cmd_struct *) ptr;
-      mc_cmd->cmd_status = 0;
-      mc_cmd->cmd_cmd = CMD_MCSETUP | CMD_LAST;
-      mc_cmd->cmd_link = 0xffff;
-      mc_cmd->mc_cnt = num_addrs * 6;
-      for(i=0;i<num_addrs;i++)
-      {
-		memcpy((char *) mc_cmd->mc_list[i], dmi->dmi_addr,6);
-		dmi=dmi->next;
-      }
-      p->scb->cbl_offset = make16(mc_cmd);
-      p->scb->cmd = CUC_START;
-      ni_attn586();
-      s = jiffies;
-      while(!(mc_cmd->cmd_status & STAT_COMPL))
-        if(jiffies - s > 30)
-          break;
-      if(!(mc_cmd->cmd_status & STAT_COMPL))
-        printk("%s: Can't apply multicast-address-list.\n",dev->name);
-    }
-  }
-
   /*
    * alloc xmit-buffs / init xmit_cmds
    */
   for(i=0;i<NUM_XMIT_BUFFS;i++)
   {
+    p->xmit_cmds[i] = (struct transmit_cmd_struct *)ptr; /*transmit cmd/buff 0*/
+    ptr = (char *) ptr + sizeof(struct transmit_cmd_struct);
     p->xmit_cbuffs[i] = (char *)ptr; /* char-buffs */
     ptr = (char *) ptr + XMIT_BUFF_SIZE;
     p->xmit_buffs[i] = (struct tbd_struct *)ptr; /* TBD */
@@ -660,6 +720,7 @@
     }   
     memset((char *)(p->xmit_cmds[i]) ,0, sizeof(struct transmit_cmd_struct));
     memset((char *)(p->xmit_buffs[i]),0, sizeof(struct tbd_struct));
+    p->xmit_cmds[i]->cmd_link = make16(p->nop_cmds[(i+1)%NUM_XMIT_BUFFS]);
     p->xmit_cmds[i]->cmd_status = STAT_COMPL;
     p->xmit_cmds[i]->cmd_cmd = CMD_XMIT | CMD_INT;
     p->xmit_cmds[i]->tbd_offset = make16((p->xmit_buffs[i]));
@@ -674,18 +735,27 @@
 #endif
 
    /*
-    * 'start transmitter' (nop-loop)
+    * 'start transmitter'
     */
 #ifndef NO_NOPCOMMANDS
   p->scb->cbl_offset = make16(p->nop_cmds[0]);
-  p->scb->cmd = CUC_START;
+  p->scb->cmd_cuc = CUC_START;
   ni_attn586();
   WAIT_4_SCB_CMD();
 #else
-  p->xmit_cmds[0]->cmd_link = 0xffff;
-  p->xmit_cmds[0]->cmd_cmd  = CMD_XMIT | CMD_LAST | CMD_INT;
+  p->xmit_cmds[0]->cmd_link = make16(p->xmit_cmds[0]);
+  p->xmit_cmds[0]->cmd_cmd  = CMD_XMIT | CMD_SUSPEND | CMD_INT;
 #endif
 
+  /*
+   * ack. interrupts
+   */
+  p->scb->cmd_cuc = p->scb->cus & STAT_MASK;
+  ni_attn586();
+  DELAY_16();
+
+  ni_enaint();
+
   return 0;
 }
 
@@ -701,20 +771,22 @@
   int i;
   struct priv *p = (struct priv *) dev->priv;
 
-  memset((char *) rfd,0,sizeof(struct rfd_struct)*p->num_recv_buffs);
+  memset((char *) rfd,0,sizeof(struct rfd_struct)*(p->num_recv_buffs+rfdadd));
   p->rfd_first = rfd;
 
-  for(i = 0; i < p->num_recv_buffs; i++)
-    rfd[i].next = make16(rfd + (i+1) % p->num_recv_buffs);
-  rfd[p->num_recv_buffs-1].last = RFD_SUSP;   /* RU suspend */
+  for(i = 0; i < (p->num_recv_buffs+rfdadd); i++) {
+    rfd[i].next = make16(rfd + (i+1) % (p->num_recv_buffs+rfdadd) );
+    rfd[i].rbd_offset = 0xffff;
+  }
+  rfd[p->num_recv_buffs-1+rfdadd].last = RFD_SUSP;   /* RU suspend */
 
-  ptr = (void *) (rfd + p->num_recv_buffs);
+  ptr = (void *) (rfd + (p->num_recv_buffs + rfdadd) );
 
   rbd = (struct rbd_struct *) ptr;
   ptr = (void *) (rbd + p->num_recv_buffs);
 
    /* clr descriptors */
-  memset((char *) rbd,0,sizeof(struct rbd_struct)*p->num_recv_buffs);
+  memset((char *) rbd,0,sizeof(struct rbd_struct)*(p->num_recv_buffs));
 
   for(i=0;i<p->num_recv_buffs;i++)
   {
@@ -725,7 +797,7 @@
   }
 
   p->rfd_top  = p->rfd_first;
-  p->rfd_last = p->rfd_first + p->num_recv_buffs - 1;
+  p->rfd_last = p->rfd_first + (p->num_recv_buffs - 1 + rfdadd);
 
   p->scb->rfa_offset		= make16(p->rfd_first);
   p->rfd_first->rbd_offset	= make16(rbd);
@@ -742,54 +814,73 @@
 {
   struct device *dev = (struct device *) irq2dev_map[irq];
   unsigned short stat;
+  int cnt=0;
   struct priv *p;
 
-  if (dev == NULL) {
-    printk ("ni52-interrupt: irq %d for unknown device.\n",(int) -(((struct pt_regs *)reg_ptr)->orig_eax+2));
+  if (!dev) {
+    printk ("ni5210-interrupt: irq %d for unknown device.\n",irq);
     return;
   }
   p = (struct priv *) dev->priv;
 
+  if(debuglevel > 1)
+    printk("I");
+
   dev->interrupt = 1;
 
-  while((stat=p->scb->status & STAT_MASK))
-  {
-    p->scb->cmd = stat;
-    ni_attn586(); /* ack inter. */
+  WAIT_4_SCB_CMD(); /* wait for last command  */
 
-   if(stat & STAT_CX)    /* command with I-bit set complete */
-      ni52_xmt_int(dev);
+  while((stat=p->scb->cus & STAT_MASK))
+  {
+    p->scb->cmd_cuc = stat;
+    ni_attn586();
 
     if(stat & STAT_FR)   /* received a frame */
       ni52_rcv_int(dev);
 
-#ifndef NO_NOPCOMMANDS
-    if(stat & STAT_CNA)  /* CU went 'not ready' */
-    {
-      if(dev->start)
-        printk("%s: oops! CU has left active state. stat: %04x/%04x.\n",dev->name,(int) stat,(int) p->scb->status);
-    }
-#endif
-
     if(stat & STAT_RNR) /* RU went 'not ready' */
     {
-      if(p->scb->status & RU_SUSPEND) /* special case: RU_SUSPEND */
+      printk("(R)");
+      if(p->scb->rus & RU_SUSPEND) /* special case: RU_SUSPEND */
       {
         WAIT_4_SCB_CMD();
-        p->scb->cmd = RUC_RESUME;
+        p->scb->cmd_ruc = RUC_RESUME;
         ni_attn586();
+        WAIT_4_SCB_CMD_RUC();
       }
       else
       {
-        printk("%s: Receiver-Unit went 'NOT READY': %04x/%04x.\n",dev->name,(int) stat,(int) p->scb->status);
+        printk("%s: Receiver-Unit went 'NOT READY': %04x/%02x.\n",dev->name,(int) stat,(int) p->scb->rus);
         ni52_rnr_int(dev); 
       }
     }
+
+    if(stat & STAT_CX)    /* command with I-bit set complete */
+       ni52_xmt_int(dev);
+
+#ifndef NO_NOPCOMMANDS
+    if(stat & STAT_CNA)  /* CU went 'not ready' */
+    {
+      if(dev->start)
+        printk("%s: oops! CU has left active state. stat: %04x/%02x.\n",dev->name,(int) stat,(int) p->scb->cus);
+    }
+#endif
+
+    if(debuglevel > 1)
+      printk("%d",cnt++);
+
     WAIT_4_SCB_CMD(); /* wait for ack. (ni52_xmt_int can be faster than ack!!) */
-    if(p->scb->cmd)   /* timed out? */
+    if(p->scb->cmd_cuc)   /* timed out? */
+    {
+      printk("%s: Acknowledge timed out.\n",dev->name);
+      ni_disint();
       break;
+    }
   }
 
+  if(debuglevel > 1)
+    printk("i");
+
   dev->interrupt = 0;
 }
 
@@ -799,17 +890,20 @@
 
 static void ni52_rcv_int(struct device *dev)
 {
-  int status;
+  int status,cnt=0;
   unsigned short totlen;
   struct sk_buff *skb;
   struct rbd_struct *rbd;
   struct priv *p = (struct priv *) dev->priv;
 
-  for(;(status = p->rfd_top->status) & STAT_COMPL;)
+  if(debuglevel > 0)
+    printk("R");
+
+  for(;(status = p->rfd_top->stat_high) & RFD_COMPL;)
   {
       rbd = (struct rbd_struct *) make32(p->rfd_top->rbd_offset);
 
-      if(status & STAT_OK) /* frame received without error? */
+      if(status & RFD_OK) /* frame received without error? */
       {
         if( (totlen = rbd->status) & RBD_LAST) /* the first and the last buffer? */
         {
@@ -819,8 +913,9 @@
           if(skb != NULL)
           {
             skb->dev = dev;
-            skb_reserve(skb,2);		/* 16 byte alignment */
-            memcpy(skb_put(skb,totlen),(char *) p->base+(unsigned long) rbd->buffer, totlen);
+            skb_reserve(skb,2);
+            skb_put(skb,totlen);
+            eth_copy_and_sum(skb,(char *) p->base+(unsigned long) rbd->buffer,totlen,0);
             skb->protocol=eth_type_trans(skb,dev);
             netif_rx(skb);
             p->stats.rx_packets++;
@@ -830,25 +925,90 @@
         }
         else
         {
-          printk("%s: received oversized frame.\n",dev->name);
+          int rstat;
+             /* free all RBD's until RBD_LAST is set */
+          totlen = 0;
+          while(!((rstat=rbd->status) & RBD_LAST))
+          {
+            totlen += rstat & RBD_MASK;
+            if(!rstat)
+            {
+              printk("%s: Whoops .. no end mark in RBD list\n",dev->name);
+              break;
+            }
+            rbd->status = 0;
+            rbd = (struct rbd_struct *) make32(rbd->next);
+          }
+          totlen += rstat & RBD_MASK;
+          rbd->status = 0;
+          printk("%s: received oversized frame! length: %d\n",dev->name,totlen);
           p->stats.rx_dropped++;
-        }
+       }
       }
       else /* frame !(ok), only with 'save-bad-frames' */
       {
         printk("%s: oops! rfd-error-status: %04x\n",dev->name,status);
         p->stats.rx_errors++;
       }
-      p->rfd_top->status = 0;
-      p->rfd_top->last = RFD_SUSP;
-      p->rfd_last->last = 0;        /* delete RU_SUSP  */
+      p->rfd_top->stat_high = 0;
+      p->rfd_top->last = RFD_SUSP; /* maybe exchange by RFD_LAST */
+      p->rfd_top->rbd_offset = 0xffff;
+      p->rfd_last->last = 0;        /* delete RFD_SUSP  */
       p->rfd_last = p->rfd_top;
       p->rfd_top = (struct rfd_struct *) make32(p->rfd_top->next); /* step to next RFD */
+      p->scb->rfa_offset = make16(p->rfd_top);
+
+      if(debuglevel > 0)
+        printk("%d",cnt++);
+  }
+
+  if(automatic_resume)
+  {
+    WAIT_4_SCB_CMD();
+    p->scb->cmd_ruc = RUC_RESUME;
+    ni_attn586();
+    WAIT_4_SCB_CMD_RUC();
+  }
+
+#ifdef WAIT_4_BUSY
+  {
+    int i;
+    for(i=0;i<1024;i++)
+    {
+      if(p->rfd_top->status)
+        break;
+      DELAY_16();
+      if(i == 1023)
+        printk("%s: RU hasn't fetched next RFD (not busy/complete)\n",dev->name);
+    }
   }
+#endif
+
+#ifdef 0
+  if(!at_least_one)
+  { 
+    int i;
+    volatile struct rfd_struct *rfds=p->rfd_top;
+    volatile struct rbd_struct *rbds;
+    printk("%s: received a FC intr. without having a frame: %04x %d\n",dev->name,status,old_at_least);
+    for(i=0;i< (p->num_recv_buffs+4);i++)
+    {
+      rbds = (struct rbd_struct *) make32(rfds->rbd_offset);
+      printk("%04x:%04x ",rfds->status,rbds->status);
+      rfds = (struct rfd_struct *) make32(rfds->next);
+    }
+    printk("\nerrs: %04x %04x stat: %04x\n",(int)p->scb->rsc_errs,(int)p->scb->ovrn_errs,(int)p->scb->status);
+    printk("\nerrs: %04x %04x rus: %02x, cus: %02x\n",(int)p->scb->rsc_errs,(int)p->scb->ovrn_errs,(int)p->scb->rus,(int)p->scb->cus);  
+  }
+  old_at_least = at_least_one;
+#endif
+
+  if(debuglevel > 0)
+    printk("r");
 }
 
 /**********************************************************
- * handle 'Receiver went not ready'. 
+ * handle 'Receiver went not ready'.
  */
 
 static void ni52_rnr_int(struct device *dev)
@@ -857,15 +1017,16 @@
 
   p->stats.rx_errors++;
 
-  WAIT_4_SCB_CMD();    /* wait for the last cmd */
-  p->scb->cmd = RUC_ABORT; /* usually the RU is in the 'no resource'-state .. abort it now. */
+  WAIT_4_SCB_CMD();    /* wait for the last cmd, WAIT_4_FULLSTAT?? */
+  p->scb->cmd_ruc = RUC_ABORT; /* usually the RU is in the 'no resource'-state .. abort it now. */
   ni_attn586(); 
-  WAIT_4_SCB_CMD();    /* wait for accept cmd. */
+  WAIT_4_SCB_CMD_RUC();    /* wait for accept cmd. */
 
   alloc_rfa(dev,(char *)p->rfd_first);
+/* maybe add a check here, before restarting the RU */
   startrecv586(dev); /* restart RU */
 
-  printk("%s: Receive-Unit restarted. Status: %04x\n",dev->name,p->scb->status);
+  printk("%s: Receive-Unit restarted. Status: %04x\n",dev->name,p->scb->rus);
 
 }
 
@@ -878,6 +1039,9 @@
   int status;
   struct priv *p = (struct priv *) dev->priv;
 
+  if(debuglevel > 0)
+    printk("X");
+
   status = p->xmit_cmds[p->xmit_last]->cmd_status;
   if(!(status & STAT_COMPL))
     printk("%s: strange .. xmit-int without a 'COMPLETE'\n",dev->name);
@@ -910,7 +1074,7 @@
     } 
   }
 
-#if (NUM_XMIT_BUFFS != 1)
+#if (NUM_XMIT_BUFFS > 1)
   if( (++p->xmit_last) == NUM_XMIT_BUFFS) 
     p->xmit_last = 0;
 #endif
@@ -927,10 +1091,12 @@
 {
   struct priv *p = (struct priv *) dev->priv;
 
+  WAIT_4_SCB_CMD();
+  WAIT_4_SCB_CMD_RUC();
   p->scb->rfa_offset = make16(p->rfd_first);
-  p->scb->cmd = RUC_START;
+  p->scb->cmd_ruc = RUC_START;
   ni_attn586();		/* start cmd. */
-  WAIT_4_SCB_CMD();	/* wait for accept cmd. (no timeout!!) */
+  WAIT_4_SCB_CMD_RUC();	/* wait for accept cmd. (no timeout!!) */
 }
 
 /******************************************************
@@ -951,28 +1117,31 @@
     if (tickssofar < 5)
       return 1;
 
-    if(p->scb->status & CU_ACTIVE) /* COMMAND-UNIT active? */
+#ifndef NO_NOPCOMMANDS
+    if(p->scb->cus & CU_ACTIVE) /* COMMAND-UNIT active? */
     {
       dev->tbusy = 0;
 #ifdef DEBUG
       printk("%s: strange ... timeout with CU active?!?\n",dev->name);
       printk("%s: X0: %04x N0: %04x N1: %04x %d\n",dev->name,(int)p->xmit_cmds[0]->cmd_status,(int)p->nop_cmds[0]->cmd_status,(int)p->nop_cmds[1]->cmd_status,(int)p->nop_point);
 #endif
-      p->scb->cmd = CUC_ABORT;
+      p->scb->cmd_cuc = CUC_ABORT;
       ni_attn586();
       WAIT_4_SCB_CMD();
       p->scb->cbl_offset = make16(p->nop_cmds[p->nop_point]);
-      p->scb->cmd = CUC_START;
+      p->scb->cmd_cuc = CUC_START;
       ni_attn586();
       WAIT_4_SCB_CMD();
       dev->trans_start = jiffies;
       return 0;
     }
     else
+#endif
     {
 #ifdef DEBUG
-      printk("%s: xmitter timed out, try to restart! stat: %04x\n",dev->name,p->scb->status);
+      printk("%s: xmitter timed out, try to restart! stat: %02x\n",dev->name,p->scb->cus);
       printk("%s: command-stats: %04x %04x\n",dev->name,p->xmit_cmds[0]->cmd_status,p->xmit_cmds[1]->cmd_status);
+      printk("%s: check, whether you set the right interrupt number!\n",dev->name);
 #endif
       ni52_close(dev);
       ni52_open(dev);
@@ -995,8 +1164,16 @@
     return 0;
   }
 
-  if (set_bit(0, (void*)&dev->tbusy) != 0)
-     printk("%s: Transmitter access conflict.\n", dev->name);
+  if (set_bit(0, (void*)&dev->tbusy)) {
+    printk("%s: Transmitter access conflict.\n", dev->name);
+    return 1;
+  }
+#if(NUM_XMIT_BUFFS > 1)
+  else if(set_bit(0,(void *) &p->lock)) {
+    printk("%s: Queue was locked\n",dev->name);
+    return 1;
+  }
+#endif
   else
   {
     memcpy((char *)p->xmit_cbuffs[p->xmit_count],(char *)(skb->data),skb->len);
@@ -1004,19 +1181,34 @@
 
 #if (NUM_XMIT_BUFFS == 1)
 #  ifdef NO_NOPCOMMANDS
+
+#ifdef DEBUG
+    if(p->scb->cus & CU_ACTIVE)
+    {
+      printk("%s: Hmmm .. CU is still running and we wanna send a new packet.\n",dev->name);
+      printk("%s: stat: %04x %04x\n",dev->name,p->scb->cus,p->xmit_cmds[0]->cmd_status);
+    }
+#endif
+
     p->xmit_buffs[0]->size = TBD_LAST | len;
     for(i=0;i<16;i++)
     {
-      p->scb->cbl_offset = make16(p->xmit_cmds[0]);
-      p->scb->cmd = CUC_START;
       p->xmit_cmds[0]->cmd_status = 0;
+      WAIT_4_SCB_CMD();
+      if( (p->scb->cus & CU_STATUS) == CU_SUSPEND)
+        p->scb->cmd_cuc = CUC_RESUME;
+      else
+      {
+        p->scb->cbl_offset = make16(p->xmit_cmds[0]);
+        p->scb->cmd_cuc = CUC_START;
+      }
 
       ni_attn586();
       dev->trans_start = jiffies;
       if(!i)
         dev_kfree_skb(skb,FREE_WRITE);
       WAIT_4_SCB_CMD();
-      if( (p->scb->status & CU_ACTIVE)) /* test it, because CU sometimes doesn't start immediately */
+      if( (p->scb->cus & CU_ACTIVE)) /* test it, because CU sometimes doesn't start immediately */
         break;
       if(p->xmit_cmds[0]->cmd_status)
         break;
@@ -1042,18 +1234,23 @@
       next_nop = 0;
 
     p->xmit_cmds[p->xmit_count]->cmd_status  = 0;
-    p->xmit_cmds[p->xmit_count]->cmd_link = p->nop_cmds[next_nop]->cmd_link 
-                                          = make16((p->nop_cmds[next_nop]));
+	/* linkpointer of xmit-command allready points to next nop cmd */
+    p->nop_cmds[next_nop]->cmd_link = make16((p->nop_cmds[next_nop]));
     p->nop_cmds[next_nop]->cmd_status = 0;
 
     p->nop_cmds[p->xmit_count]->cmd_link = make16((p->xmit_cmds[p->xmit_count]));
     dev->trans_start = jiffies;
     p->xmit_count = next_nop;
-  
-    cli();
-    if(p->xmit_count != p->xmit_last)
-      dev->tbusy = 0;
-    sti();
+ 
+    {
+      long flags; 
+      save_flags(flags);
+      cli();
+      if(p->xmit_count != p->xmit_last)
+        dev->tbusy = 0;
+      p->lock = 0;
+      restore_flags(flags);
+    }
     dev_kfree_skb(skb,FREE_WRITE);
 #endif
   }
@@ -1070,13 +1267,13 @@
   unsigned short crc,aln,rsc,ovrn;
 
   crc = p->scb->crc_errs; /* get error-statistic from the ni82586 */
-  p->scb->crc_errs -= crc;
+  p->scb->crc_errs = 0;
   aln = p->scb->aln_errs;
-  p->scb->aln_errs -= aln;
+  p->scb->aln_errs = 0;
   rsc = p->scb->rsc_errs;
-  p->scb->rsc_errs -= rsc;
+  p->scb->rsc_errs = 0;
   ovrn = p->scb->ovrn_errs;
-  p->scb->ovrn_errs -= ovrn;
+  p->scb->ovrn_errs = 0;
 
   p->stats.rx_crc_errors += crc;
   p->stats.rx_fifo_errors += ovrn;
@@ -1089,7 +1286,6 @@
 /********************************************************
  * Set MC list ..  
  */
-
 static void set_multicast_list(struct device *dev)
 {
   if(!dev->start)
@@ -1099,12 +1295,92 @@
   }
 
   dev->start = 0;
+
+  ni_disint();
   alloc586(dev);
   init586(dev);  
   startrecv586(dev);
+  ni_enaint();
+
   dev->start = 1;
 }
 
+#ifdef MODULE
+static struct device dev_ni52 = {
+  "        ",  /* "ni5210": device name inserted by net_init.c */
+  0, 0, 0, 0,
+  0x300, 9,   /* I/O address, IRQ */
+  0, 0, 0, NULL, ni52_probe };
+
+/* set: io,irq,memstart,memend or set it when calling insmod */
+int irq=9;
+int io=0x300;
+long memstart=0; /* e.g 0xd0000 */
+long memend=0;   /* e.g 0xd4000 */
+
+int init_module(void)
+{
+  if(io <= 0x0 || !memend || !memstart || irq < 2) {
+    printk("ni52: Autoprobing not allowed for modules.\nni52: Set symbols 'io' 'irq' 'memstart' and 'memend'\n");
+    return -ENODEV;
+  }
+  dev_ni52.irq = irq;
+  dev_ni52.base_addr = io;
+  dev_ni52.mem_end = memend;
+  dev_ni52.mem_start = memstart;
+  if (register_netdev(&dev_ni52) != 0)
+    return -EIO;
+  return 0;
+}
+
+void cleanup_module(void)
+{
+  release_region(dev_ni52.base_addr, NI52_TOTAL_SIZE);
+  kfree(dev_ni52.priv);
+  dev_ni52.priv = NULL;
+  unregister_netdev(&dev_ni52);
+}
+#endif /* MODULE */
+
+#if 0
+/*
+ * DUMP .. we expect a not running CMD unit and enough space
+ */
+void ni52_dump(struct device *dev,void *ptr)
+{
+  struct priv *p = (struct priv *) dev->priv;
+  struct dump_cmd_struct *dump_cmd = (struct dump_cmd_struct *) ptr;
+  int i;
+
+  p->scb->cmd_cuc = CUC_ABORT;
+  ni_attn586();
+  WAIT_4_SCB_CMD();
+  WAIT_4_SCB_CMD_RUC();
+
+  dump_cmd->cmd_status = 0;
+  dump_cmd->cmd_cmd = CMD_DUMP | CMD_LAST;
+  dump_cmd->dump_offset = make16((dump_cmd + 1));
+  dump_cmd->cmd_link = 0xffff;
+
+  p->scb->cbl_offset = make16(dump_cmd);
+  p->scb->cmd_cuc = CUC_START;
+  ni_attn586();
+  WAIT_4_STAT_COMPL(dump_cmd);
+
+  if( (dump_cmd->cmd_status & (STAT_COMPL|STAT_OK)) != (STAT_COMPL|STAT_OK) )
+        printk("%s: Can't get dump information.\n",dev->name);
+
+  for(i=0;i<170;i++) {
+    printk("%02x ",(int) ((unsigned char *) (dump_cmd + 1))[i]);
+    if(i % 24 == 23)
+      printk("\n");
+  }
+  printk("\n");
+}
+#endif
+
 /*
  * END: linux/drivers/net/ni52.c 
  */
+
+
diff -u --recursive --new-file v1.3.68/linux/drivers/net/ni52.h linux/drivers/net/ni52.h
--- v1.3.68/linux/drivers/net/ni52.h	Fri Feb  3 15:19:28 1995
+++ linux/drivers/net/ni52.h	Mon Feb 26 11:58:15 1996
@@ -61,8 +61,10 @@
  */
 struct scb_struct
 {
-  unsigned short status;        /* status word */
-  unsigned short cmd;           /* command word */
+  unsigned char rus;
+  unsigned char cus;
+  unsigned char cmd_ruc;           /* command word: RU part */
+  unsigned char cmd_cuc;           /* command word: CU part & ACK */
   unsigned short cbl_offset;    /* pointeroffset, command block list */
   unsigned short rfa_offset;    /* pointeroffset, receive frame area */
   unsigned short crc_errs;      /* CRC-Error counter */
@@ -81,31 +83,31 @@
 #define RUC_SUSPEND	0x0030	/* suspend RU */
 #define RUC_ABORT	0x0040	/* abort receiver operation immediately */
 
-#define CUC_MASK	0x0700	/* mask for CU command */
-#define CUC_NOP		0x0000	/* NOP-command */
-#define CUC_START	0x0100	/* start execution of 1. cmd on the CBL */
-#define CUC_RESUME	0x0200	/* resume after suspend */
-#define CUC_SUSPEND	0x0300	/* Suspend CU */
-#define CUC_ABORT	0x0400	/* abort command operation immediately */
-
-#define ACK_MASK	0xf000	/* mask for ACK command */
-#define ACK_CX		0x8000	/* acknowledges STAT_CX */
-#define ACK_FR		0x4000	/* ack. STAT_FR */
-#define ACK_CNA		0x2000	/* ack. STAT_CNA */
-#define ACK_RNR		0x1000	/* ack. STAT_RNR */
+#define CUC_MASK        0x07  /* mask for CU command */
+#define CUC_NOP         0x00  /* NOP-command */
+#define CUC_START       0x01  /* start execution of 1. cmd on the CBL */
+#define CUC_RESUME      0x02  /* resume after suspend */
+#define CUC_SUSPEND     0x03  /* Suspend CU */
+#define CUC_ABORT       0x04  /* abort command operation immediately */
+
+#define ACK_MASK        0xf0  /* mask for ACK command */
+#define ACK_CX          0x80  /* acknowledges STAT_CX */
+#define ACK_FR          0x40  /* ack. STAT_FR */
+#define ACK_CNA         0x20  /* ack. STAT_CNA */
+#define ACK_RNR         0x10  /* ack. STAT_RNR */
 
 /*
  * possible status values for the status word
  */
-#define STAT_MASK	0xf000	/* mask for cause of interrupt */
-#define STAT_CX		0x8000	/* CU finished cmd with its I bit set */
-#define STAT_FR		0x4000	/* RU finished receiving a frame */
-#define STAT_CNA	0x2000	/* CU left active state */
-#define STAT_RNR	0x1000	/* RU left ready state */
-
-#define CU_STATUS	0x700	/* CU status, 0=idle */
-#define CU_SUSPEND	0x100	/* CU is suspended */
-#define CU_ACTIVE	0x200	/* CU is active */
+#define STAT_MASK       0xf0  /* mask for cause of interrupt */
+#define STAT_CX         0x80  /* CU finished cmd with its I bit set */
+#define STAT_FR         0x40  /* RU finished receiving a frame */
+#define STAT_CNA        0x20  /* CU left active state */
+#define STAT_RNR        0x10  /* RU left ready state */
+
+#define CU_STATUS       0x7   /* CU status, 0=idle */
+#define CU_SUSPEND      0x1   /* CU is suspended */
+#define CU_ACTIVE       0x2   /* CU is active */
 
 #define RU_STATUS	0x70	/* RU status, 0=idle */
 #define RU_SUSPEND	0x10	/* RU suspended */
@@ -117,8 +119,10 @@
  */
 struct rfd_struct
 {
-  unsigned short status;	/* status word */
-  unsigned short last;		/* Bit15,Last Frame on List / Bit14,suspend */
+  unsigned char  stat_low;	/* status word */
+  unsigned char  stat_high;	/* status word */
+  unsigned char  rfd_sf;	/* 82596 mode only */
+  unsigned char  last;		/* Bit15,Last Frame on List / Bit14,suspend */
   unsigned short next;		/* linkoffset to next RFD */
   unsigned short rbd_offset;	/* pointeroffset to RBD-buffer */
   unsigned char  dest[6];	/* ethernet-address, destination */
@@ -127,11 +131,22 @@
   unsigned short zero_dummy;	/* dummy */
 };
 
-#define RFD_LAST     0x8000	/* last: last rfd in the list */
-#define RFD_SUSP     0x4000	/* last: suspend RU after  */
-#define RFD_ERRMASK  0x0fe1     /* status: errormask */
-#define RFD_MATCHADD 0x0002     /* status: Destinationaddress !matches IA */
-#define RFD_RNR      0x0200	/* status: receiver out of resources */
+#define RFD_LAST     0x80	/* last: last rfd in the list */
+#define RFD_SUSP     0x40	/* last: suspend RU after  */
+#define RFD_COMPL    0x80
+#define RFD_OK       0x20
+#define RFD_BUSY     0x40
+#define RFD_ERR_LEN  0x10     /* Length error (if enabled length-checking */
+#define RFD_ERR_CRC  0x08     /* CRC error */
+#define RFD_ERR_ALGN 0x04     /* Alignment error */
+#define RFD_ERR_RNR  0x02     /* status: receiver out of resources */
+#define RFD_ERR_OVR  0x01     /* DMA Overrun! */
+
+#define RFD_ERR_FTS  0x0080	/* Frame to short */
+#define RFD_ERR_NEOP 0x0040	/* No EOP flag (for bitstuffing only) */
+#define RFD_ERR_TRUN 0x0020	/* (82596 only/SF mode) indicates truncated frame */
+#define RFD_MATCHADD 0x0002     /* status: Destinationaddress !matches IA (only 82596) */
+#define RFD_COLLDET  0x0001	/* Detected collision during reception */
 
 /*
  * Receive Buffer Descriptor (RBD)
@@ -228,6 +243,17 @@
   unsigned short cmd_link;
   unsigned short mc_cnt;		/* number of bytes in the MC-List */
   unsigned char  mc_list[0][6];  	/* pointer to 6 bytes entries */
+};
+
+/*
+ * DUMP command
+ */
+struct dump_cmd_struct
+{
+  unsigned short cmd_status;
+  unsigned short cmd_cmd;
+  unsigned short cmd_link;
+  unsigned short dump_offset;    /* pointeroffset to DUMP space */
 };
 
 /*
diff -u --recursive --new-file v1.3.68/linux/drivers/net/ni65.c linux/drivers/net/ni65.c
--- v1.3.68/linux/drivers/net/ni65.c	Sat Nov 25 19:04:46 1995
+++ linux/drivers/net/ni65.c	Mon Feb 26 11:58:15 1996
@@ -1,35 +1,42 @@
 /*
  * ni6510 (am7990 'lance' chip) driver for Linux-net-3 by MH
- * Alphacode v0.33 (94/08/22) for 1.1.47 (or later)
+ * Alphacode v0.51 (96/02/20) for 1.3.66 (or later)
  *
- * ----------------------------------------------------------
- * WARNING: DOESN'T WORK ON MACHINES WITH MORE THAN 16MB !!!!
- * ----------------------------------------------------------
- *
- * copyright (c) 1994 M.Hipp
+ * copyright (c) 1994,1995,1996 by M.Hipp
  *
  * This is an extension to the Linux operating system, and is covered by the
  * same Gnu Public License that covers the Linux-kernel.
  *
  * comments/bugs/suggestions can be sent to:
- *    Michael Hipp
- *    email: mhipp@student.uni-tuebingen.de
+ *   Michael Hipp
+ *   email: Michael.Hipp@student.uni-tuebingen.de
  *
  * sources:
- *  some things are from the 'ni6510-packet-driver for dos by Russ Nelson'
- *  and from the original drivers by D.Becker
- */
-
-/*
- * Nov.18: multicast tweaked (AC).
+ *   some things are from the 'ni6510-packet-driver for dos by Russ Nelson'
+ *   and from the original drivers by D.Becker
+ *
+ * known problems:
+ *   on some PCI boards (including my own) the card/board/ISA-bridge has
+ *   problems with bus master DMA. This results in lotsa overruns.
+ *   It may help to '#define RCV_PARANOIA_CHECK'
+ *   or just play with your BIOS options to optimize ISA-DMA access.
  *
- * Aug.22: changes in xmit_intr (ack more than one xmitted-packet), ni65_send_packet (p->lock) (MH)
+ * credits:
+ *   thanx to Jason Sullivan for sending me a ni6510 card!
  *
- * July.16: fixed bugs in recv_skb and skb-alloc stuff  (MH)
+ * simple performance test:
+ *   8.1 seconds for getting a 8MB file via FTP -> near 1MB/s
  */
 
 /*
- * known BUGS: 16MB limit
+ * 96.Feb.19: fixed a few bugs .. cleanups .. tested for 1.3.66
+ *            hopefully no more 16MB limit
+ *
+ * 95.Nov.18: multicast tweaked (AC).
+ *
+ * 94.Aug.22: changes in xmit_intr (ack more than one xmitted-packet), ni65_send_packet (p->lock) (MH)
+ *
+ * 94,July.16: fixed bugs in recv_skb and skb-alloc stuff  (MH)
  */
 
 #include <linux/kernel.h>
@@ -40,6 +47,7 @@
 #include <linux/ioport.h>
 #include <linux/malloc.h>
 #include <linux/interrupt.h>
+#include <linux/delay.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
 #include <asm/dma.h>
@@ -50,44 +58,41 @@
 
 #include "ni65.h"
 
-/************************************
- * skeleton-stuff
- */
-
-#ifndef HAVE_PORTRESERVE
-#define check_region(ioaddr, size)              0
-#define request_region(ioaddr, size,name)       do ; while (0)
-#endif
-
-#ifndef NET_DEBUG
-#define NET_DEBUG 2
-#endif
 /*
-static unsigned int net_debug = NET_DEBUG;
-*/
+ * the current setting allows max. perforamnce
+ * for 'RCV_PARANOIA_CHECK' read the 'known problems' part in 
+ * the header of this file
+ */
+#define RCV_VIA_SKB
+#undef RCV_PARANOIA_CHECK
+#define XMT_VIA_SKB
 
+/*
+ * a few card specific defines
+ */
 #define NI65_TOTAL_SIZE    16
-
-#define SA_ADDR0 0x02
-#define SA_ADDR1 0x07
-#define SA_ADDR2 0x01
-#define CARD_ID0 0x00
-#define CARD_ID1 0x55
-
-/*****************************************/
+#define NI65_ADDR0 0x02
+#define NI65_ADDR1 0x07
+#define NI65_ADDR2 0x01
+#define NI65_ID0   0x00
+#define NI65_ID1   0x55
 
 #define PORT dev->base_addr
 
+/*
+ * buffer configuration
+ */
 #define RMDNUM 8
-#define RMDNUMMASK 0x6000 /* log2(RMDNUM)<<13 */
+#define RMDNUMMASK 0x60000000 /* log2(RMDNUM)<<29 */
 #define TMDNUM 4
-#define TMDNUMMASK 0x4000 /* log2(TMDNUM)<<13 */
-
-#define R_BUF_SIZE 1518
-#define T_BUF_SIZE 1518
+#define TMDNUMMASK 0x40000000 /* log2(TMDNUM)<<29 */
 
-#define MEMSIZE 8+RMDNUM*8+TMDNUM*8
+#define R_BUF_SIZE 1536
+#define T_BUF_SIZE 1536
 
+/*
+ * lance register defines
+ */
 #define L_DATAREG 0x00
 #define L_ADDRREG 0x02
 
@@ -104,51 +109,53 @@
 #define CSR2 0x02
 #define CSR3 0x03
 
-/* if you #define NO_STATIC the driver is faster but you will have (more) problems with >16MB memory */
-#undef NO_STATIC
-
 #define writereg(val,reg) {outw(reg,PORT+L_ADDRREG);inw(PORT+L_ADDRREG); \
                            outw(val,PORT+L_DATAREG);inw(PORT+L_DATAREG);}
 #define readreg(reg) (outw(reg,PORT+L_ADDRREG),inw(PORT+L_ADDRREG),\
                        inw(PORT+L_DATAREG))
 #define writedatareg(val) {outw(val,PORT+L_DATAREG);inw(PORT+L_DATAREG);}
 
-static int   ni65_probe1(struct device *dev,int);
-static void  ni65_interrupt(int irq, struct pt_regs *regs);
-  static void recv_intr(struct device *dev);
-  static void xmit_intr(struct device *dev);
-static int   ni65_open(struct device *dev);
-   static int am7990_reinit(struct device *dev);
-static int   ni65_send_packet(struct sk_buff *skb, struct device *dev);
-static int   ni65_close(struct device *dev);
+static int  ni65_probe1(struct device **dev,int);
+static void ni65_interrupt(int irq, struct pt_regs *regs);
+static void ni65_recv_intr(struct device *dev,int);
+static void ni65_xmit_intr(struct device *dev,int);
+static int  ni65_open(struct device *dev);
+static int  ni65_am7990_reinit(struct device *dev);
+static int  ni65_send_packet(struct sk_buff *skb, struct device *dev);
+static int  ni65_close(struct device *dev);
 static struct enet_statistics *ni65_get_stats(struct device *);
-
 static void set_multicast_list(struct device *dev);
 
 struct priv 
 {
+  struct rmd rmdhead[RMDNUM];
+  struct tmd tmdhead[TMDNUM];
   struct init_block ib; 
-  void *memptr;
-  struct rmd *rmdhead;
-  struct tmd *tmdhead;
   int rmdnum;
   int tmdnum,tmdlast;
+#ifdef RCV_VIA_SKB
   struct sk_buff *recv_skb[RMDNUM];
-  void *tmdbufs[TMDNUM];
+#else
+  void *recvbounce[RMDNUM];
+#endif
+#ifdef XMT_VIA_SKB
+  struct sk_buff *tmd_skb[TMDNUM];
+#endif
+  void *tmdbounce[TMDNUM];
   int lock,xmit_queued;
   struct enet_statistics stats;
 }; 
 
-int irqtab[] = { 9,12,15,5 }; /* irq config-translate */
-int dmatab[] = { 0,3,5,6 };   /* dma config-translate */
+static int irqtab[] = { 9,12,15,5 }; /* irq config-translate */
+static int dmatab[] = { 0,3,5,6 };   /* dma config-translate */
+static int debuglevel = 0;
 
 /*
  * open (most done by init) 
  */
-
 static int ni65_open(struct device *dev)
 {
-  if(am7990_reinit(dev))
+  if(ni65_am7990_reinit(dev))
   {
     dev->tbusy     = 0;
     dev->interrupt = 0;
@@ -174,49 +181,64 @@
  * Probe The Card (not the lance-chip) 
  * and set hardaddress
  */ 
-
 int ni65_probe(struct device *dev)
 {
-  int *port, ports[] = {0x300,0x320,0x340,0x360, 0};
-  int base_addr = dev->base_addr;
+  int *port;
+  static int ports[] = {0x300,0x320,0x340,0x360, 0};
 
-  if (base_addr > 0x1ff)          /* Check a single specified location. */
-     return ni65_probe1(dev, base_addr);
-  else if (base_addr > 0)         /* Don't probe at all. */
-     return ENXIO;
+  if(dev) {
+    int base_addr = dev->base_addr;
+    if (base_addr > 0x1ff)          /* Check a single specified location. */
+       return ni65_probe1(&dev, base_addr);
+    else if (base_addr > 0)         /* Don't probe at all. */
+       return -ENXIO;
+    dev->base_addr = base_addr;
+  }
 
   for (port = ports; *port; port++) 
   {
     int ioaddr = *port;
+
     if (check_region(ioaddr, NI65_TOTAL_SIZE))
        continue;
-    if( !(inb(ioaddr+L_EBASE+6) == CARD_ID0) || 
-        !(inb(ioaddr+L_EBASE+7) == CARD_ID1) )
+    if( !(inb(ioaddr+L_EBASE+6) == NI65_ID0) || 
+        !(inb(ioaddr+L_EBASE+7) == NI65_ID1) )
        continue;
-    dev->base_addr = ioaddr;
-    if (ni65_probe1(dev, ioaddr) == 0)
+    if (ni65_probe1(&dev, ioaddr) == 0)
        return 0;
   }
 
-  dev->base_addr = base_addr;
-  return ENODEV;
+  return -ENODEV;
 }
 
+int ni65_init(void)
+{
+        ni65_probe(NULL);
+        return 0;
+}
 
-static int ni65_probe1(struct device *dev,int ioaddr)
+static int ni65_probe1(struct device **dev1,int ioaddr)
 {
   int i;
-  unsigned char station_addr[6];
+  unsigned char *ptr;
   struct priv *p; 
+  struct device *dev = *dev1;
 
-  for(i=0;i<6;i++)
-    station_addr[i] = dev->dev_addr[i] = inb(PORT+L_EBASE+i);
-
-  if(station_addr[0] != SA_ADDR0 || station_addr[1] != SA_ADDR1)
+  if(inb(ioaddr+L_EBASE+0) != NI65_ADDR0 || inb(ioaddr+L_EBASE+1) != NI65_ADDR1 
+      || inb(ioaddr+L_EBASE+2) != NI65_ADDR2)
   {
-    printk("%s: wrong Hardaddress \n",dev->name);
-    return ENODEV;
+    printk("%s: wrong Hardaddress \n",dev ? dev->name : "ni6510" );
+    return -ENODEV;
+  }
+
+  if(!dev) {
+    dev = init_etherdev(0,0);
+    *dev1 = dev;
   }
+  dev->base_addr = ioaddr;
+
+  for(i=0;i<6;i++)
+    dev->dev_addr[i] = inb(PORT+L_EBASE+i);
 
   if(dev->irq == 0) 
     dev->irq = irqtab[(inw(PORT+L_CONFIG)>>2)&3];
@@ -224,31 +246,28 @@
     dev->dma = dmatab[inw(PORT+L_CONFIG)&3];
 
   printk("%s: %s found at %#3lx, IRQ %d DMA %d.\n", dev->name,
-           "network card", dev->base_addr, dev->irq,dev->dma);
+           "ni6510", dev->base_addr, dev->irq,dev->dma);
 
   {        
-    int irqval = request_irq(dev->irq, &ni65_interrupt,0,"ni65");
+    int irqval = request_irq(dev->irq, &ni65_interrupt,0,"ni6510");
     if (irqval) {
       printk ("%s: unable to get IRQ %d (irqval=%d).\n", 
                 dev->name,dev->irq, irqval);
-      return EAGAIN;
+      return -EAGAIN;
     }
-    if(request_dma(dev->dma, "ni65") != 0)
+    if(request_dma(dev->dma, "ni6510") != 0)
     {
       printk("%s: Can't request dma-channel %d\n",dev->name,(int) dev->dma);
       free_irq(dev->irq);
-      return EAGAIN;
+      return -EAGAIN;
     }
   }
   irq2dev_map[dev->irq] = dev;
 
-  /* Grab the region so we can find another board if autoIRQ fails. */
-        request_region(ioaddr,NI65_TOTAL_SIZE,"ni65");
-
-  p = dev->priv = (void *) kmalloc(sizeof(struct priv),GFP_KERNEL);
-  if (p == NULL)
-   	return -ENOMEM;
-  memset((char *) dev->priv,0,sizeof(struct priv));
+  /* 
+   * Grab the region so we can find another board if autoIRQ fails. 
+   */
+  request_region(ioaddr,NI65_TOTAL_SIZE,"ni6510");
 
   dev->open               = ni65_open;
   dev->stop               = ni65_close;
@@ -258,47 +277,70 @@
 
   ether_setup(dev);
 
-  dev->flags 	     &= ~IFF_MULTICAST;
   dev->interrupt      = 0;
   dev->tbusy          = 0;
   dev->start          = 0;
 
-  if( (p->memptr = kmalloc(MEMSIZE,GFP_KERNEL)) == NULL) {
-    printk("%s: Can't alloc TMD/RMD-buffer.\n",dev->name);
-    return EAGAIN;
-  }
-  if( (unsigned long) (p->memptr + MEMSIZE) & 0xff000000) {
-    printk("%s: Can't alloc TMD/RMD buffer in lower 16MB!\n",dev->name);
-    return EAGAIN;
+  /* 
+   * we need 8-aligned memory ..
+   */
+  ptr = kmalloc(sizeof(struct priv)+8,GFP_KERNEL|GFP_DMA);
+  if(!ptr)
+    return -ENOMEM;
+  ptr = (unsigned char *) (((unsigned long) ptr + 7) & ~0x7);
+  if( (unsigned long) ptr + sizeof(struct priv) > 0x1000000) {
+    printk("%s: Can't alloc buffer in lower 16MB!\n",dev->name);
+    return -EAGAIN;
   }
-  p->tmdhead = (struct tmd *) ((( (unsigned long)p->memptr ) + 8) & 0xfffffff8);
-  p->rmdhead = (struct rmd *) (p->tmdhead + TMDNUM);   
+  p = dev->priv = (struct priv *) ptr;
+  memset((char *) dev->priv,0,sizeof(struct priv));
 
-#ifndef NO_STATIC
-   for(i=0;i<TMDNUM;i++)
+  for(i=0;i<TMDNUM;i++)
+  {
+    if( (ptr = kmalloc(T_BUF_SIZE,GFP_KERNEL | GFP_DMA )) == NULL) {
+      printk("%s: Can't alloc Xmit-Mem.\n",dev->name);
+      return -ENOMEM;
+    }
+    if( (unsigned long) (ptr+T_BUF_SIZE) > 0x1000000) {
+      printk("%s: Can't alloc Xmit-Mem in lower 16MB!\n",dev->name);
+      return -EAGAIN;
+    }
+    p->tmdbounce[i] = ptr;
+#ifdef XMT_VIA_SKB
+    p->tmd_skb[i] = NULL;
+#endif
+  }
+
+#ifdef RCV_VIA_SKB
+   for(i=0;i<RMDNUM;i++)
    {
-     if( (p->tmdbufs[i] = kmalloc(T_BUF_SIZE,GFP_ATOMIC)) == NULL) {
-       printk("%s: Can't alloc Xmit-Mem.\n",dev->name);
-       return EAGAIN;
+     struct sk_buff *skb;
+     if( !(skb = dev_alloc_skb(R_BUF_SIZE+2)) ) {
+       printk("%s: unable to alloc recv-mem\n",dev->name);
+       return -ENOMEM;
      }
-     if( (unsigned long) (p->tmdbufs[i]+T_BUF_SIZE) & 0xff000000) {
-       printk("%s: Can't alloc Xmit-Mem in lower 16MB!\n",dev->name);
-       return EAGAIN;
+     skb->dev = dev;
+     skb_reserve(skb,2);
+     skb_put(skb,R_BUF_SIZE);	/* grab the whole space .. (not necessary) */
+     if( (unsigned long) (skb->data + R_BUF_SIZE) > 0x1000000 ) {
+       printk("%s: unable to alloc receive-memory in lower 16MB!\n",dev->name);
+       return -EAGAIN;
      }
+     p->recv_skb[i] = skb;
    }
-#endif
-
+#else
    for(i=0;i<RMDNUM;i++)
    {
-     if( (p->recv_skb[i] = dev_alloc_skb(R_BUF_SIZE)) == NULL) {
+     if( !(p->recvbounce[i] = kmalloc(R_BUF_SIZE,GFP_KERNEL | GFP_DMA )) ) {
        printk("%s: unable to alloc recv-mem\n",dev->name);
-       return EAGAIN;
+       return -ENOMEM;
      }
-     if( (unsigned long) (p->recv_skb[i]->data + R_BUF_SIZE) & 0xff000000) {
+     if( (unsigned long) p->recvbounce[i] + R_BUF_SIZE > 0x1000000 ) {
        printk("%s: unable to alloc receive-memory in lower 16MB!\n",dev->name);
-       return EAGAIN;
+       return -EAGAIN;
      }
    }
+#endif
 
   return 0; /* we've found everything */
 }
@@ -307,11 +349,9 @@
  * init lance (write init-values .. init-buffers) (open-helper)
  */
 
-static int am7990_reinit(struct device *dev)
+static int ni65_am7990_reinit(struct device *dev)
 {
-   int i,j;
-   struct tmd *tmdp;
-   struct rmd *rmdp;
+   int i;
    struct priv *p = (struct priv *) dev->priv;
 
    p->lock = 0;
@@ -324,67 +364,79 @@
    outw(0,PORT+L_RESET); /* first: reset the card */
    if(inw(PORT+L_DATAREG) != 0x4)
    {
-     printk("%s: can't RESET ni6510 card: %04x\n",dev->name,(int) inw(PORT+L_DATAREG));
+     printk(KERN_ERR "%s: can't RESET ni6510 card: %04x\n",dev->name,(int) inw(PORT+L_DATAREG));
      disable_dma(dev->dma);
      free_dma(dev->dma);
      free_irq(dev->irq);
      return 0;
    }
 
-   /* here: memset all buffs to zero */
-
-   memset(p->memptr,0,MEMSIZE);
-
    p->tmdnum = 0; p->tmdlast = 0;
    for(i=0;i<TMDNUM;i++)
    {
-     tmdp = p->tmdhead + i;
-#ifndef NO_STATIC
-     tmdp->u.buffer = (unsigned long) p->tmdbufs[i];     
+     struct tmd *tmdp = p->tmdhead + i;
+#ifdef XMT_VIA_SKB
+     if(p->tmd_skb[i]) {
+       dev_kfree_skb(p->tmd_skb[i],FREE_WRITE);
+       p->tmd_skb[i] = NULL;
+     }
 #endif
+     tmdp->u.buffer = 0x0;
      tmdp->u.s.status = XMIT_START | XMIT_END;
+     tmdp->blen = tmdp->status2 = 0;
    }
 
    p->rmdnum = 0;
    for(i=0;i<RMDNUM;i++)
    {
-     rmdp = p->rmdhead + i;
+     struct rmd *rmdp = p->rmdhead + i;
+#ifdef RCV_VIA_SKB
      rmdp->u.buffer = (unsigned long) p->recv_skb[i]->data;
-     rmdp->u.s.status = RCV_OWN;
-     rmdp->blen = -R_BUF_SIZE;
+#else
+     rmdp->u.buffer = (unsigned long) p->recvbounce[i];
+#endif
+     rmdp->blen = -(R_BUF_SIZE-8);
      rmdp->mlen = 0;
+     rmdp->u.s.status = RCV_OWN;
    }
    
    for(i=0;i<6;i++)
-   {
      p->ib.eaddr[i] = dev->dev_addr[i];
+
+   for(i=0;i<8;i++)
+     p->ib.filter[i] = 0x0;
+   p->ib.mode = 0x0;
+
+   if(dev->flags & IFF_PROMISC) {
+     p->ib.mode = M_PROM;
+   }
+   else if(dev->mc_count || dev->flags & IFF_ALLMULTI) {
+     for(i=0;i<8;i++)
+       p->ib.filter[i] = 0xff;
    }
-   p->ib.mode = 0;
-   for(i=0;i<8;i++) 
-     p->ib.filter[i] = 0;
-   p->ib.trplow = (unsigned short) (( (unsigned long) p->tmdhead ) & 0xffff);
-   p->ib.trphigh = (unsigned short) ((( (unsigned long) p->tmdhead )>>16) & 0x00ff) | TMDNUMMASK; 
-   p->ib.rrplow = (unsigned short) (( (unsigned long) p->rmdhead ) & 0xffff);
-   p->ib.rrphigh = (unsigned short) ((( (unsigned long) p->rmdhead )>>16) & 0x00ff) | RMDNUMMASK;
+
+   p->ib.trp = (unsigned long) p->tmdhead | TMDNUMMASK;
+   p->ib.rrp = (unsigned long) p->rmdhead | RMDNUMMASK;
 
    writereg(0,CSR3);  /* busmaster/no word-swap */
    writereg((unsigned short) (((unsigned long) &(p->ib)) & 0xffff),CSR1);
    writereg((unsigned short) (((unsigned long) &(p->ib))>>16),CSR2);
-   
+
    writereg(CSR0_INIT,CSR0); /* this changes L_ADDRREG to CSR0 */
 
   /*
-   * NOW, WE NEVER WILL CHANGE THE L_ADDRREG, CSR0 IS ALWAYS SELECTED 
+   * NOW, WE WILL NEVER CHANGE THE L_ADDRREG, CSR0 IS ALWAYS SELECTED 
    */
 
-    for(i=0;i<5;i++)
+    for(i=0;i<32;i++)
     {
-      for(j=0;j<2000000;j++); /* wait a while */
-      if(inw(PORT+L_DATAREG) & CSR0_IDON) break; /* init ok ? */
+      __delay((loops_per_sec>>8)); /* wait a while */
+      if(inw(PORT+L_DATAREG) & CSR0_IDON) 
+        break; /* init ok ? */
     }
-    if(i == 5) 
+    if(i == 32) 
     {
-      printk("%s: can't init am7990, status: %04x\n",dev->name,(int) inw(PORT+L_DATAREG));
+      printk(KERN_ERR "%s: can't init am7990/lance, status: %04x\n",dev->name,(int) inw(PORT+L_DATAREG));
       disable_dma(dev->dma);
       free_dma(dev->dma);
       free_irq(dev->irq);
@@ -399,42 +451,73 @@
 /* 
  * interrupt handler  
  */
-
 static void ni65_interrupt(int irq, struct pt_regs * regs)
 {
   int csr0;
   struct device *dev = (struct device *) irq2dev_map[irq];
+  int bcnt = 32;
 
   if (dev == NULL) {
-    printk ("net_interrupt(): irq %d for unknown device.\n", irq);
+    printk (KERN_ERR "ni65_interrupt(): irq %d for unknown device.\n", irq);
     return;
   }
 
-  csr0 = inw(PORT+L_DATAREG);
-  writedatareg(csr0 & CSR0_CLRALL); /* ack interrupts, disable int. */
-
   dev->interrupt = 1;
 
-  if(csr0 & CSR0_ERR)
-  {
-     struct priv *p = (struct priv *) dev->priv;
+  while(--bcnt) {
+
+    csr0 = inw(PORT+L_DATAREG);
+    writedatareg(csr0 & CSR0_CLRALL); /* ack interrupts, disable int. */
 
-     if(csr0 & CSR0_BABL)
-       p->stats.tx_errors++;
-     if(csr0 & CSR0_MISS)
-       p->stats.rx_errors++;
+    if(!(csr0 & (CSR0_ERR | CSR0_RINT | CSR0_TINT)))
+      break;
+
+    if(csr0 & CSR0_ERR)
+    {
+      struct priv *p = (struct priv *) dev->priv;
+
+      if(csr0 & CSR0_BABL)
+        p->stats.tx_errors++;
+      if(csr0 & CSR0_MISS)
+        p->stats.rx_errors++;
+      if(csr0 & CSR0_MERR) {
+        writedatareg(CSR0_STOP);
+        writedatareg(CSR0_STRT);
+      }
+    }
+    if(csr0 & CSR0_RINT) /* RECV-int? */
+      ni65_recv_intr(dev,csr0);
+    if(csr0 & CSR0_TINT) /* XMIT-int? */
+      ni65_xmit_intr(dev,csr0);
   }
 
-  if(csr0 & CSR0_RINT) /* RECV-int? */
-  { 
-    recv_intr(dev);
+#ifdef RCV_PARANOIA_CHECK
+{
+  struct priv *p = (struct priv *) dev->priv;
+  int i,f=0;
+  for(i=0;i<RMDNUM;i++) {
+     struct rmd *rmdp = p->rmdhead + ((p->rmdnum - i - 1) & (RMDNUM-1));
+     if(! (rmdp->u.s.status & RCV_OWN) )
+        f = 1;
+     else if(f)
+        break;
   }
-  if(csr0 & CSR0_TINT) /* XMIT-int? */
-  {  
-    xmit_intr(dev);
+
+  if(i < RMDNUM) {
+    p->rmdnum = (p->rmdnum + 8 - i) & (RMDNUM - 1);
+    printk(KERN_ERR "%s: Ooops, receive ring currupted\n",dev->name);
+
+    ni65_recv_intr(dev,csr0);
   }
+}
+#endif
 
-  writedatareg(CSR0_INEA);  /* reenable inter. */
+  if(csr0 & (CSR0_RXON | CSR0_TXON) != (CSR0_RXON | CSR0_TXON) ) {
+    writedatareg(CSR0_STOP);
+    writedatareg(CSR0_STRT | CSR0_INEA);
+  }
+  else
+    writedatareg(CSR0_INEA);
   dev->interrupt = 0;
 
   return;
@@ -444,39 +527,45 @@
  * We have received an Xmit-Interrupt ..
  * send a new packet if necessary
  */
-
-static void xmit_intr(struct device *dev)
+static void ni65_xmit_intr(struct device *dev,int csr0)
 {
-  int tmdstat;
-  struct tmd *tmdp;
   struct priv *p = (struct priv *) dev->priv;
 
-#ifdef NO_STATIC
-  struct sk_buff *skb;
-#endif
-
   while(p->xmit_queued)
   {
-    tmdp = p->tmdhead + p->tmdlast;
-    tmdstat = tmdp->u.s.status;
+    struct tmd *tmdp = p->tmdhead + p->tmdlast;
+    int tmdstat = tmdp->u.s.status;
+
     if(tmdstat & XMIT_OWN)
       break;
-#ifdef NO_STATIC
-    skb = (struct sk_buff *) p->tmdbufs[p->tmdlast];
-    dev_kfree_skb(skb,FREE_WRITE); 
+
+#ifdef XMT_VIA_SKB
+    if(p->tmd_skb[p->tmdlast]) {
+       dev_kfree_skb(p->tmd_skb[p->tmdlast],FREE_WRITE);
+       p->tmd_skb[p->tmdlast] = NULL;
+    }
 #endif
 
     if(tmdstat & XMIT_ERR)
     {
-      printk("%s: xmit-error: %04x %04x\n",dev->name,(int) tmdstat,(int) tmdp->status2);
-      if(tmdp->status2 & XMIT_TDRMASK) 
-        printk("%s: tdr-problems (e.g. no resistor)\n",dev->name);
-
+#if 0
+      if(tmdp->status2 & XMIT_TDRMASK && debuglevel > 3)
+        printk(KERN_ERR "%s: tdr-problems (e.g. no resistor)\n",dev->name);
+#endif
      /* checking some errors */
-      if(tmdp->status2 & XMIT_RTRY) 
+      if(tmdp->status2 & XMIT_RTRY)
         p->stats.tx_aborted_errors++;
-      if(tmdp->status2 & XMIT_LCAR) 
+      if(tmdp->status2 & XMIT_LCAR)
         p->stats.tx_carrier_errors++;
+      if(tmdp->status2 & (XMIT_BUFF | XMIT_UFLO )) {
+        p->stats.tx_fifo_errors++;
+        writedatareg(CSR0_STOP);
+        writedatareg(CSR0_STRT);
+        if(debuglevel > 1)
+          printk(KERN_ERR "%s: Xmit FIFO/BUFF error\n",dev->name);
+      }
+      if(debuglevel > 2)
+        printk(KERN_ERR "%s: xmit-error: %04x %02x-%04x\n",dev->name,csr0,(int) tmdstat,(int) tmdp->status2);
       p->stats.tx_errors++;
       tmdp->status2 = 0;
     }
@@ -487,7 +576,6 @@
     if(p->tmdlast == p->tmdnum)
       p->xmit_queued = 0;
   }
-
   dev->tbusy = 0;
   mark_bh(NET_BH);
 }
@@ -496,66 +584,88 @@
  * We have received a packet
  */
 
-static void recv_intr(struct device *dev)
+static void ni65_recv_intr(struct device *dev,int csr0)
 {
   struct rmd *rmdp; 
   int rmdstat,len;
-  struct sk_buff *skb,*skb1;
   struct priv *p = (struct priv *) dev->priv;
 
   rmdp = p->rmdhead + p->rmdnum;
   while(!( (rmdstat = rmdp->u.s.status) & RCV_OWN))
   {
-    if( (rmdstat & (RCV_START | RCV_END)) != (RCV_START | RCV_END) ) /* is packet start & end? */ 
+    if( (rmdstat & (RCV_START | RCV_END | RCV_ERR)) != (RCV_START | RCV_END) ) /* error or oversized? */ 
     {
-      if(rmdstat & RCV_START)
-      {
-        p->stats.rx_errors++;
-        p->stats.rx_length_errors++;
-        printk("%s: packet too long\n",dev->name);
+      if(!(rmdstat & RCV_ERR)) {
+        if(rmdstat & RCV_START)
+        {
+          p->stats.rx_length_errors++;
+          printk(KERN_ERR "%s: recv, packet too long: %d\n",dev->name,rmdp->mlen & 0x0fff);
+        }
+      }
+      else {
+        printk(KERN_ERR "%s: receive-error: %04x, lance-status: %04x/%04x\n",
+                dev->name,(int) rmdstat,csr0,(int) inw(PORT+L_DATAREG) );
+        if(rmdstat & RCV_FRAM)
+          p->stats.rx_frame_errors++;
+        if(rmdstat & RCV_OFLO)
+          p->stats.rx_over_errors++;
+        if(rmdstat & (RCV_OFLO | RCV_BUF_ERR) ) { 
+          writedatareg(CSR0_STOP);
+          writedatareg(CSR0_STRT);
+          if(debuglevel > 1)
+            printk(KERN_ERR "%s: Rcv FIFO/BUFF error.\n",dev->name);
+        }
+        if(rmdstat & RCV_CRC)  p->stats.rx_crc_errors++;
       }
       rmdp->u.s.status = RCV_OWN; /* change owner */
-    }
-    else if(rmdstat & RCV_ERR)
-    {
-      printk("%s: receive-error: %04x\n",dev->name,(int) rmdstat );
       p->stats.rx_errors++;
-      if(rmdstat & RCV_FRAM) p->stats.rx_frame_errors++;
-      if(rmdstat & RCV_OFLO) p->stats.rx_over_errors++;
-      if(rmdstat & RCV_CRC)  p->stats.rx_crc_errors++;
-      rmdp->u.s.status = RCV_OWN;
-      printk("%s: lance-status: %04x\n",dev->name,(int) inw(PORT+L_DATAREG));
     }
-    else
+    else if( (len = (rmdp->mlen & 0x0fff) - 4) >= 60)
     {
-      len = (rmdp->mlen & 0x0fff) - 4; /* -4: ignore FCS */
-      skb = dev_alloc_skb(R_BUF_SIZE);
-      if(skb != NULL)
+#ifdef RCV_VIA_SKB
+      struct sk_buff *skb = dev_alloc_skb(R_BUF_SIZE+2);
+#else
+      struct sk_buff *skb = dev_alloc_skb(len+2);
+#endif
+      if(skb)
       {
-        if( (unsigned long) (skb->data + R_BUF_SIZE) & 0xff000000) {
-          memcpy(skb_put(skb,len),p->recv_skb[p->rmdnum]->data,len);
-	  skb1 = skb;
+        skb_reserve(skb,2);
+	skb->dev = dev;
+#ifdef RCV_VIA_SKB
+        if( (unsigned long) (skb->data + R_BUF_SIZE) > 0x1000000) {
+          skb_put(skb,len);
+          eth_copy_and_sum(skb, (unsigned char *)(p->recv_skb[p->rmdnum]->data),len,0);
         }
         else {
-          skb1 = p->recv_skb[p->rmdnum];
+          struct sk_buff *skb1 = p->recv_skb[p->rmdnum];
+          skb_put(skb,R_BUF_SIZE);
           p->recv_skb[p->rmdnum] = skb;
-          rmdp->u.buffer = (unsigned long) skb_put(skb1,len);
+          rmdp->u.buffer = (unsigned long) skb->data;
+          skb = skb1;
+          skb_trim(skb,len);
         }
+#else
+        skb_put(skb,len);
+        eth_copy_and_sum(skb, (unsigned char *) p->recvbounce[p->rmdnum],len,0);
+#endif
         rmdp->u.s.status = RCV_OWN;
-        rmdp->mlen = 0;   /* not necc ???? */
-        skb1->dev = dev;
         p->stats.rx_packets++;
-        skb1->protocol=eth_type_trans(skb1,dev);
-        netif_rx(skb1);
+        skb->protocol=eth_type_trans(skb,dev);
+        netif_rx(skb);
       }
       else
       {
         rmdp->u.s.status = RCV_OWN;
-        printk("%s: can't alloc new sk_buff\n",dev->name);
+        printk(KERN_ERR "%s: can't alloc new sk_buff\n",dev->name);
         p->stats.rx_dropped++;
       }
     }
-    p->rmdnum++; p->rmdnum &= RMDNUM-1;
+    else {
+      rmdp->u.s.status = RCV_OWN;
+      printk(KERN_INFO "%s: received runt packet\n",dev->name);
+      p->stats.rx_errors++;
+    }
+    p->rmdnum = (p->rmdnum + 1) & (RMDNUM-1);
     rmdp = p->rmdhead + p->rmdnum;
   }
 }
@@ -563,26 +673,23 @@
 /*
  * kick xmitter ..
  */
-
 static int ni65_send_packet(struct sk_buff *skb, struct device *dev)
 {
   struct priv *p = (struct priv *) dev->priv;
-  struct tmd *tmdp;
 
   if(dev->tbusy)
   {
     int tickssofar = jiffies - dev->trans_start;
-    if (tickssofar < 25)
+    if (tickssofar < 5)
       return 1;
 
-    printk("%s: xmitter timed out, try to restart!\n",dev->name);
-    am7990_reinit(dev);
+    printk(KERN_ERR "%s: xmitter timed out, try to restart!\n",dev->name);
+    ni65_am7990_reinit(dev);
     dev->tbusy=0;
     dev->trans_start = jiffies;
   }
 
-  if(skb == NULL)
-  {
+  if(skb == NULL) {
     dev_tint(dev);
     return 0;
   }
@@ -590,44 +697,50 @@
   if (skb->len <= 0)
     return 0;
 
-  if (set_bit(0, (void*)&dev->tbusy) != 0)
-  {
-     printk("%s: Transmitter access conflict.\n", dev->name);
+  if (set_bit(0, (void*)&dev->tbusy) != 0) {
+     printk(KERN_ERR "%s: Transmitter access conflict.\n", dev->name);
      return 1;
   }
-  if(set_bit(0,(void*) &p->lock) != 0)
-  {
-    printk("%s: Queue was locked!\n",dev->name);
-    return 1;
+  if (set_bit(0, (void*)&p->lock)) {
+	printk(KERN_ERR "%s: Queue was locked.\n", dev->name);
+	return 1;
   }
 
   {
     short len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+    struct tmd *tmdp = p->tmdhead + p->tmdnum;
+    long flags;
 
-    tmdp = p->tmdhead + p->tmdnum;
-
-#ifdef NO_STATIC
-    tmdp->u.buffer = (unsigned long) (skb->data);
-    p->tmdbufs[p->tmdnum] = skb;
-#else
-    memcpy((char *) (tmdp->u.buffer & 0x00ffffff),(char *)skb->data,skb->len);
-    dev_kfree_skb (skb, FREE_WRITE);
+#ifdef XMT_VIA_SKB
+    if( (unsigned long) (skb->data + skb->len) > 0x1000000) {
+#endif
+      tmdp->u.buffer = (unsigned long ) p->tmdbounce[p->tmdnum]; 
+      memcpy((char *) tmdp->u.buffer,(char *)skb->data,
+               (skb->len > T_BUF_SIZE) ? T_BUF_SIZE : skb->len);
+      dev_kfree_skb (skb, FREE_WRITE);
+#ifdef XMT_VIA_SKB
+    }
+    else {
+      tmdp->u.buffer = (unsigned long) skb->data;
+      p->tmd_skb[p->tmdnum] = skb;
+    }
 #endif
     tmdp->blen = -len;
-    tmdp->u.s.status = XMIT_OWN | XMIT_START | XMIT_END;
 
+    save_flags(flags);
     cli();
-    p->xmit_queued = 1;
+
+    tmdp->u.s.status = XMIT_OWN | XMIT_START | XMIT_END;
     writedatareg(CSR0_TDMD | CSR0_INEA); /* enable xmit & interrupt */
-    p->tmdnum++; p->tmdnum &= TMDNUM-1;
- 
-    if( !((p->tmdhead + p->tmdnum)->u.s.status & XMIT_OWN) ) 
-      dev->tbusy = 0;
-    p->lock = 0;
-    sti();
 
+    p->xmit_queued = 1;
+    p->tmdnum = (p->tmdnum + 1) & (TMDNUM-1);
+
+    dev->tbusy = (p->tmdnum == p->tmdlast) ? 1 : 0;
+    p->lock = 0;
     dev->trans_start = jiffies;
 
+    restore_flags(flags);
   }
 
   return 0;
@@ -640,6 +753,9 @@
 
 static void set_multicast_list(struct device *dev)
 {
+	if(!ni65_am7990_reinit(dev))
+		printk(KERN_ERR "%s: Can't switch card into MC mode!\n",dev->name);
+	dev->tbusy = 0;
 }
 
 /*
diff -u --recursive --new-file v1.3.68/linux/drivers/net/ni65.h linux/drivers/net/ni65.h
--- v1.3.68/linux/drivers/net/ni65.h	Fri Aug 26 09:37:38 1994
+++ linux/drivers/net/ni65.h	Mon Feb 26 11:58:15 1996
@@ -78,23 +78,23 @@
  * transmit status (2) (valid if XMIT_ERR == 1)
  */
 
-#define XMIT_RTRY 	0x0200  /* Failed after 16 retransmissions  */
-#define XMIT_LCAR 	0x0400  /* Loss of Carrier */
+#define XMIT_TDRMASK    0x03ff  /* time-domain-reflectometer-value */
+#define XMIT_RTRY 	0x0400  /* Failed after 16 retransmissions  */
+#define XMIT_LCAR 	0x0800  /* Loss of Carrier */
 #define XMIT_LCOL 	0x1000  /* Late collision */
 #define XMIT_RESERV 	0x2000  /* Reserved */
 #define XMIT_UFLO 	0x4000  /* Underflow (late memory) */
 #define XMIT_BUFF 	0x8000  /* Buffering error (no ENP) */
-#define XMIT_TDRMASK    0x003f  /* time-domain-reflectometer-value */
 
 struct init_block 
 {
   unsigned short mode;
   unsigned char eaddr[6];
   unsigned char filter[8];
-  unsigned short rrplow;   /* receive ring pointer (align 8) */
-  unsigned short rrphigh;  /* bit 13-15: number of rmd's (power of 2) */
-  unsigned short trplow;   /* transmit ring pointer (align 8) */
-  unsigned short trphigh;  /* bit 13-15: number of tmd's (power of 2) */
+  /* bit 29-31: number of rmd's (power of 2) */
+  unsigned long rrp;   /* receive ring pointer (align 8) */
+  /* bit 29-31: number of tmd's (power of 2) */
+  unsigned long trp;   /* transmit ring pointer (align 8) */
 };
 
 struct rmd /* Receive Message Descriptor */
diff -u --recursive --new-file v1.3.68/linux/drivers/net/slip.c linux/drivers/net/slip.c
--- v1.3.68/linux/drivers/net/slip.c	Wed Feb 14 14:37:10 1996
+++ linux/drivers/net/slip.c	Mon Feb 26 11:58:15 1996
@@ -471,8 +471,7 @@
 		 * transmission of another packet */
 		sl->tx_packets++;
 		tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
-		if (test_bit(0, (void *) &sl->dev->tbusy)) /* add by VSV */
-			sl_unlock(sl);
+		sl_unlock(sl);
 		mark_bh(NET_BH);
 		return;
 	}
@@ -803,6 +802,13 @@
 	
 	tty->disc_data = 0;
 	sl->tty = NULL;
+	/* VSV = very important to remove timers */
+#ifdef CONFIG_SLIP_SMART
+	if (sl->keepalive)
+		(void)del_timer (&sl->keepalive_timer);
+	if (sl->outfill)
+		(void)del_timer (&sl->outfill_timer);
+#endif
 	sl_free(sl);
 	unregister_netdev(sl->dev);
 	MOD_DEC_USE_COUNT;
@@ -1116,7 +1122,7 @@
 	/* VSV changes start here */
         case SIOCSKEEPALIVE:
                 if (sl->keepalive)
-                        del_timer (&sl->keepalive_timer);
+                        (void)del_timer (&sl->keepalive_timer);
 		err = verify_area(VERIFY_READ, arg, sizeof(int));
 		if (err)  {
 			return -err;
@@ -1329,7 +1335,9 @@
 	{
 		for (i = 0; i < slip_maxdev; i++)  
 		{
-			if (slip_ctrls[i] != NULL) 
+			if (slip_ctrls[i]->dev.start)
+			/* VSV = if dev->start==0, then device
+			unregistred while close proc. */ 
 			{
 				unregister_netdev(&(slip_ctrls[i]->dev));
 				kfree(slip_ctrls[i]);
@@ -1355,7 +1363,7 @@
 {
 	struct slip *sl=(struct slip *)sls;
 
-	if(sls==NULL)
+	if(sls==0L)
 		return;
 
 	if(sl->outfill)
@@ -1372,7 +1380,6 @@
 			if (!test_bit(0, (void *) &sl->dev->tbusy))
 			{ 
 				/* if device busy no outfill */
-				sl->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
 				sl->tty->driver.write(sl->tty, 0, &s, 1);
 			}
 		}
diff -u --recursive --new-file v1.3.68/linux/drivers/scsi/advansys.c linux/drivers/scsi/advansys.c
--- v1.3.68/linux/drivers/scsi/advansys.c	Tue Jan 23 21:15:43 1996
+++ linux/drivers/scsi/advansys.c	Sat Feb 24 11:17:23 1996
@@ -1,4 +1,4 @@
-/* $Id: advansys.c,v 1.11 1996/01/16 22:39:19 bobf Exp bobf $ */
+/* $Id: advansys.c,v 1.12 1996/02/23 20:48:27 bobf Exp bobf $ */
 /*
  * advansys.c - Linux Host Driver for AdvanSys SCSI Adapters
  * 
@@ -14,16 +14,16 @@
  * bobf@advansys.com (Bob Frey)
  */
 
-/* The driver has been tested with Linux 1.2.1 and 1.3.57 kernels. */
-#define ASC_VERSION "1.2"	/* AdvanSys Driver Version */
+/* The driver has been tested with Linux v1.2.1 and v1.3.57 kernels. */
+#define ASC_VERSION "1.3"	/* AdvanSys Driver Version */
 
 /*
 
   Documentation for the AdvanSys Driver
 
   A. Adapters Supported by this Driver
-  B. Linux 1.2.X - Directions for Adding the AdvanSys Driver
-  C. Linux 1.3.X - Directions for Adding the AdvanSys Driver
+  B. Linux v1.2.X - Directions for Adding the AdvanSys Driver
+  C. Linux v1.3.X - Directions for Adding the AdvanSys Driver
   D. Source Comments
   E. Driver Compile Time Options and Debugging
   F. Driver LILO Option
@@ -67,7 +67,11 @@
        no BIOS so it cannot control a boot device, but it can control
        any secondary devices.
  
-  B. Linux 1.2.X - Directions for Adding the AdvanSys Driver
+  B. Linux v1.2.X - Directions for Adding the AdvanSys Driver
+
+     These directions apply to v1.2.1. For versions that follow v1.2.1
+     but precede v1.3.57 some of the changes for Linux v1.3.X listed
+     below may need to be modified or included.
  
      There are two source files: advansys.h and advansys.c. Copy
      both of these files to the directory /usr/src/linux/drivers/scsi.
@@ -114,9 +118,9 @@
              { "advansys=", advansys_setup },
           #endif
 
-     5. If you have the HP 4020i CD-R driver and Linux 1.2.X you should
+     5. If you have the HP 4020i CD-R driver and Linux v1.2.X you should
         add a fix to the CD-ROM target driver. This fix will allow
-        you to mount CDs with the iso9660 file system. Linux 1.3.X
+        you to mount CDs with the iso9660 file system. Linux v1.3.X
         already has this fix. In the file /usr/src/linux/drivers/scsi/sr.c
         and function get_sectorsize() after the line:
 
@@ -133,7 +137,11 @@
         'make modules_install'. Use 'insmod' and 'rmmod' to install
         and remove advansys.o.
  
-  C. Linux 1.3.X - Directions for Adding the AdvanSys Driver
+  C. Linux v1.3.X - Directions for Adding the AdvanSys Driver
+
+     These directions apply to v1.3.57. For versions that precede v1.3.57
+     some of these changes may need to be modified or eliminated. Beginning
+     with v1.3.58 this driver is included with the Linux distribution.
 
      There are two source files: advansys.h and advansys.c. Copy
      both of these files to the directory /usr/src/linux/drivers/scsi.
@@ -253,7 +261,7 @@
         The following command line will look for an adapter at 0x330
         and set the debug level to 2.
 
-           linux advansys=0x330,0x0,0x0,0x0,0xdeb2
+           linux advansys=0x330,0,0,0,0xdeb2
 
         If the driver is built as a loadable module this variable can be
         defined when the driver is loaded. The following insmod command
@@ -281,14 +289,14 @@
  
      2. ADVANSYS_STATS - enable statistics and tracing
  
-        For Linux 1.2.X if ADVANSYS_STATS_1_2_PRINT is defined every
+        For Linux v1.2.X if ADVANSYS_STATS_1_2_PRINT is defined every
         10,000 I/O operations the driver will print statistics to the
         console. This value can be changed by modifying the constant
         used in advansys_queuecommand(). ADVANSYS_STATS_1_2_PRINT is
         off by default.
 
-        For Linux 1.3.X statistics can be accessed by reading the
-        /proc/scsi/advansys/[0-9] files.
+        For Linux v1.3.X statistics can be accessed by reading the
+        /proc/scsi/advansys/[0-(ASC_NUM_BOARD_SUPPORTED-1)] files.
 
         Note: these statistics are currently maintained on a global driver
         basis and not per board.
@@ -340,7 +348,17 @@
             recognized on some PCI motherboards.
          3. Add support for the ABP-5140 PnP ISA card.
          4. Fix check condition return status.
-         5. Add conditionally compiled code for Linux 1.3.X.
+         5. Add conditionally compiled code for Linux v1.3.X.
+
+     2/23/96 1.3:
+         1. Fix problem in advansys_biosparam() that resulted in the
+            wrong drive geometry being returned for drives > 1GB with
+            extended translation enabled.
+         2. Add additional tracing during device initialization.
+         3. Change code that only applies to ISA PnP adapter.
+         4. Eliminate 'make dep' warning.
+         5. Try to fix problem with handling resets by increasing their
+            timeout value.
         
   H. Known Problems or Issues
 
@@ -357,14 +375,21 @@
         the timeout value. This gives the driver more time to perform
         its own initialization for the board and each device. The timeout
         value is only changed on the first scsi command for each device
-        and never thereafter.
+        and never thereafter. The same change is made for reset commands.
+
+     3. The driver occasionally enters a loop handling reset requests. It
+        isn't clear yet whether this is a bug in the upper or mid-level
+        scsi modules or in this driver.
 
   I. Credits
 
-     Nathan Hartwell (mage@cdc3.cdc.net) provided the directions and
-     and basis for the Linux 1.3.X changes which were included in the
+     Nathan Hartwell <mage@cdc3.cdc.net> provided the directions and
+     and basis for the Linux v1.3.X changes which were included in the
      1.2 release.
 
+     Thomas E Zerucha <zerucha@shell.portal.com> pointed out the bug
+     in advansys_biosparam() which was fixed the 1.3 release.
+
   J. AdvanSys Contact Information
  
      Mail:                   Advanced System Products, Inc.
@@ -389,7 +414,7 @@
  */
 
 /*
- * The driver can be used in Linux 1.2.X or 1.3.X.
+ * The driver can be used in Linux v1.2.X or v1.3.X.
  */
 #if !defined(LINUX_1_2) && !defined(LINUX_1_3)
 #ifndef LINUX_VERSION_CODE
@@ -407,12 +432,11 @@
  * --- Linux Include Files 
  */
 
-#ifdef MODULE
 #ifdef LINUX_1_3
-#include <linux/autoconf.h>
-#endif /* LINUX_1_3 */ 
+#ifdef MODULE
 #include <linux/module.h>
 #endif /* MODULE */
+#endif /* LINUX_1_3 */
 #include <linux/string.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
@@ -421,7 +445,6 @@
 #include <linux/ioport.h>
 #include <linux/delay.h>
 #include <linux/malloc.h>
-#include <linux/config.h>
 #ifdef LINUX_1_3
 #include <linux/proc_fs.h>
 #endif /* LINUX_1_3 */ 
@@ -2569,7 +2592,7 @@
 
 #ifdef LINUX_1_3
 /*
- * advansys_proc_info() - /proc/scsi/advansys/[0-ASC_NUM_BOARD_SUPPORTED]
+ * advansys_proc_info() - /proc/scsi/advansys/[0-(ASC_NUM_BOARD_SUPPORTED-1)]
  *
  * *buffer: I/O buffer
  * **start: if inout == FALSE pointer into buffer where user read should start
@@ -3192,10 +3215,10 @@
 /*
  * advansys_command()
  *
- * Polled-I/O. Apparently host driver shouldn't return until
+ * Polled-I/O. Apparently host drivers shouldn't return until
  * command is finished.
  *
- * XXX - Can host driver block here instead of spinning on command status?
+ * XXX - Can host drivers block here instead of spinning on command status?
  */
 int
 advansys_command(Scsi_Cmnd *scp)
@@ -3230,7 +3253,7 @@
 #ifdef LINUX_1_2
 	/*
 	 * For LINUX_1_3, if statistics are enabled they can be accessed
-	 * by reading /proc/scsi/advansys/[0-9].
+	 * by reading /proc/scsi/advansys/[0-(ASC_NUM_BOARD_SUPPORTED-1)].
 	 */
 #ifdef ADVANSYS_STATS_1_2_PRINT
 	/* Display statistics every 10000 commands. */
@@ -3366,18 +3389,24 @@
 				tscp->scsi_done(tscp);
 			}
 		}
+		/*
+		 * XXX - Host drivers should not modify the timeout field.
+		 * Allow the SCSI bus reset more time to complete.
+		 */
+		scp->timeout += 2000;	/* Add 5 seconds to the request timeout. */
+
 		/* Must enable interrupts for AscResetSB() */
 		sti();
 		boardp = &ASC_BOARD(scp->host)->board;
 		scp->result = HOST_BYTE(DID_RESET);
 		switch (AscResetSB(boardp)) {
 		case ASC_TRUE:
-			ASC_DBG(1, "advansys_abort: AscResetSB() TRUE\n");
+			ASC_DBG(1, "advansys_reset: AscResetSB() TRUE\n");
 			ret = SCSI_RESET_SUCCESS;
 			break;
 		case ASC_ERROR:
 		default:
-			ASC_DBG(1, "advansys_abort: AscResetSB() ERROR\n");
+			ASC_DBG(1, "advansys_reset: AscResetSB() ERROR\n");
 			ret = SCSI_RESET_ERROR;
 			break;
 		}
@@ -3410,7 +3439,7 @@
 	if ((ASC_BOARD(dp->device->host)->board.dvc_cntl & ASC_CNTL_BIOS_GT_1GB) &&
 		dp->capacity > 0x200000) {
 			ip[0] = 255;
-			ip[1] = 64;
+			ip[1] = 63;
 	} else {
 			ip[0] = 64;
 			ip[1] = 32;
@@ -7926,7 +7955,7 @@
 			(asc_dvc->cfg->pci_device_id == ASC_PCI_DEVICE_ID_REV_B)) {
 			asc_dvc->pci_fix_asyn_xfer = ASC_ALL_DEVICE_BIT_SET;
 		}
-	} else if (asc_dvc->bus_type & ASC_IS_ISAPNP) {
+	} else if ((asc_dvc->bus_type & ASC_IS_ISAPNP) == ASC_IS_ISAPNP) {
 
 		if (AscGetChipVersion(iop_base, asc_dvc->bus_type)
 			== ASC_CHIP_VER_ASYN_BUG) {
@@ -8447,11 +8476,28 @@
 			AscDispInquiry(tid_no, lun, inq);
 #endif
 
+			ASC_DBG_PRT_INQUIRY(2, inq, sizeof(ASC_SCSI_INQUIRY));
+
 			if (lun == 0) {
 
+				ASC_DBG1(2, "AscInitPollTarget: vendor_id: \"%.8s\"\n",
+					inq->vendor_id);
+				ASC_DBG1(2, "AscInitPollTarget: product_id: \"%.16s\"\n",
+					inq->product_id);
+				ASC_DBG1(2, "AscInitPollTarget: product_rev_level: \"%.4s\"\n",
+					inq->product_rev_level);
+
+				ASC_DBG1(2, "AscInitPollTarget: byte3.rsp_data_fmt %x\n",
+					inq->byte3.rsp_data_fmt);
+				ASC_DBG1(2, "AscInitPollTarget: byte3.ansi_apr_ver %x\n",
+					inq->byte2.ansi_apr_ver);
+					
 				if ((inq->byte3.rsp_data_fmt >= 2) ||
 					(inq->byte2.ansi_apr_ver >= 2)) {
 
+					ASC_DBG1(2, "AscInitPollTarget: byte7.CmdQue %x\n",
+						inq->byte7.CmdQue);
+
 					if (inq->byte7.CmdQue) {
 						asc_dvc->cfg->can_tagged_qng |= tid_bits;
 						if (asc_dvc->cfg->cmd_qng_enabled & tid_bits) {
@@ -8460,6 +8506,9 @@
 							  asc_dvc->cfg->max_tag_qng[tid_no];
 						}
 					}
+					ASC_DBG1(2, "AscInitPollTarget: byte7.Sync %x\n",
+						inq->byte7.Sync);
+
 					if (!inq->byte7.Sync) {
 
 						asc_dvc->init_sdtr &= ~tid_bits;
diff -u --recursive --new-file v1.3.68/linux/drivers/sound/.blurb linux/drivers/sound/.blurb
--- v1.3.68/linux/drivers/sound/.blurb	Tue Jan 23 21:15:44 1996
+++ linux/drivers/sound/.blurb	Fri Feb 23 14:54:44 1996
@@ -1,4 +1,7 @@
 *********************************************************
 * Readme.cards (this directory) contains some card	*
 * specific instructions.				*
+*							*
+* DON'T USE PROGRAMS FROM SND_UTIL PACKAGE EARLIER THAN *
+* snd-util-3.5 WITH THIS SOUND DRIVER VERSION.          *
 *********************************************************
diff -u --recursive --new-file v1.3.68/linux/drivers/sound/.version linux/drivers/sound/.version
--- v1.3.68/linux/drivers/sound/.version	Wed Feb 14 14:37:12 1996
+++ linux/drivers/sound/.version	Fri Feb 23 14:54:44 1996
@@ -1,2 +1,2 @@
-3.5-beta2
+3.5-beta7
 0x030505
diff -u --recursive --new-file v1.3.68/linux/drivers/sound/CHANGELOG linux/drivers/sound/CHANGELOG
--- v1.3.68/linux/drivers/sound/CHANGELOG	Wed Feb 14 14:37:12 1996
+++ linux/drivers/sound/CHANGELOG	Fri Feb 23 14:54:44 1996
@@ -1,5 +1,28 @@
-Changelog for version 3.5-beta2
+Changelog for version 3.5-beta7
 -------------------------------
+
+Since 3.5-beta6
+- Fixed bugs in mmap() support.
+- Minor changes to Maui driver.
+
+Since 3.5-beta5
+- Fixed crash after recording with ESS688. It's generally a good
+  idea to stop inbound DMA transfers before freeing the memory
+  buffer. 
+- Fixed handling of AD1845 codec (for example Shuttle Sound System).
+- Few other fixes.
+
+Since 3.5-beta4
+- Fixed bug in handling of uninitialized instruments with GUS.
+
+Since 3.5-beta3
+- Few changes which decrease popping at end/beginning of audio playback.
+
+Since 3.5-beta2
+- Removed MAD16+CS4231 hack made in previous version since it didn't
+  help.
+- Fixed the above bug in proper way and in proper place. Many thanks
+  to James Hightower.
 
 Since 3.5-beta1
 - Bugfixes.
diff -u --recursive --new-file v1.3.68/linux/drivers/sound/Makefile linux/drivers/sound/Makefile
--- v1.3.68/linux/drivers/sound/Makefile	Sat Feb 17 16:02:52 1996
+++ linux/drivers/sound/Makefile	Fri Feb 23 17:21:10 1996
@@ -25,9 +25,15 @@
 	 mad16.o mad16_sb_midi.o cs4232.o maui.o
 endif
 
+ifndef TOPDIR
+TOPDIR=/usr/src/linux
+endif
+
 
 build:
+	@echo Compiling modularized sound driver
 	@make sound.o
+	@echo Sound module compiled.
 
 install:	sound.o
 	cp sound.o $(MODULEDIR)
diff -u --recursive --new-file v1.3.68/linux/drivers/sound/Readme linux/drivers/sound/Readme
--- v1.3.68/linux/drivers/sound/Readme	Wed Feb 14 14:37:13 1996
+++ linux/drivers/sound/Readme	Fri Feb 23 14:54:42 1996
@@ -1,5 +1,5 @@
 
-Version 3.5-beta2 release notes
+Version 3.5-beta6 release notes
 -------------------------------
 
 IMPORTANT! This version of the driver is compatible only with Linux versions
@@ -38,7 +38,7 @@
 after v3.5 (I will not add aditional features before v3.5 is ready).
 
    ====================================================
--  THIS VERSION ____REQUIRES____ Linux 1.3.58 OR LATER.
+-  THIS VERSION ____REQUIRES____ Linux 1.3.64 OR LATER.
    ====================================================
 
 -  THIS VERSION MAY NOT WORK WITH Linux VERSIONS RELEASED
@@ -46,7 +46,7 @@
    your kernel version, please use the sound driver version
    included in your kernel.
 
-You will need the snd-util-3.0.tar.gz and snd-data-0.1.tar.Z
+You may need the snd-util-3.5.tar.gz and snd-data-0.1.tar.Z
 packages to use this driver. They should be in the same
 ftp site or BBS from where you got this driver. For
 example at nic.funet.fi:pub/OS/Linux/*.
@@ -139,6 +139,7 @@
 	Davor Jadrijevic MAD16 support
 	Gregor Hoffleit	Mozart support
 	Riccardo Facchetti Audio Excel DSP 16 (aedsp16) support
+	James Hightower Spotting a tiny but important bug in CS423x support.
 
 There are propably many other names missing. If you have sent me some
 patches and your name is not in the above list, please inform me.
diff -u --recursive --new-file v1.3.68/linux/drivers/sound/Readme.cards linux/drivers/sound/Readme.cards
--- v1.3.68/linux/drivers/sound/Readme.cards	Wed Feb 14 14:37:13 1996
+++ linux/drivers/sound/Readme.cards	Fri Feb 23 14:54:43 1996
@@ -4,6 +4,39 @@
 NOTE! This document may contain some error. Please inform me
       if you find any mistakes.
 
+Cards that are not (fully) supported by this driver
+---------------------------------------------------
+
+There are many soundcards which don't work with this driver
+version (v3.5). Support for some of them is expected to be
+available during/after summer 1996 (in version 3.6). Please check
+http://personal.eunet.fi/pp/voxware for latest news. Please don't
+mail me and ask about these cards. The unsupported cards are:
+
+	- All PnP soundcards (SB PnP, GUS PnP, Soundscape PnP etc.)
+		(SB PnP in first 3.6-alpha version (Apr?), GUS PnP bit later,
+		Soundscape PnP propably much later, others ???).
+	- Mwave soundcards and motherboards
+		(Version 3.6 or 3.7. Depends on how fast I get a Mwave
+		card and suitable documents for it).
+	- Emu8k (SB 32/AWE)
+		(Propably not before Nov/Dec 96. I know the unofficial
+		AWE programmers guide so don't send me more copies of it).
+	- Diamond Edge 3D
+		(ASAP. In practice this may take relatively long time).
+	- Compaq Deskpro
+		(Version 3.6???)
+	- Miro soundcards
+		(Early 3.6-alpha versions)
+	- OPTi 82C930 based soundcards
+		(Early 3.6-alpha versions?)
+	- Sound Galaxy Washington/Waverider
+		(3.6-alpha versions. Can't promise the waverider synth).
+	- TB Maui
+		(v3.6)
+	- Yamaha OPL4 (on cards having _RAM_ for samples)
+		(Late 96?. Works as OPL3 with current driver versions)
+
 Read this before trying to configure the driver
 -----------------------------------------------
 
@@ -130,7 +163,6 @@
 Audio Excell DSP16 
 	Support for this card is made by Riccardo Faccetti
 	(riccardo@cdc8g5.cdc.polimi.it). See aedsp16.c for more info.
-	(This driver is not functional in 3.5 versions of this driver).
 	
 Crystal CS4232 based cards such as AcerMagic S23 and many PC motherboards.
 	CS4232 is a PnP multimedia chip which contains a CS3231A codec,
diff -u --recursive --new-file v1.3.68/linux/drivers/sound/ad1848.c linux/drivers/sound/ad1848.c
--- v1.3.68/linux/drivers/sound/ad1848.c	Wed Feb 14 14:37:13 1996
+++ linux/drivers/sound/ad1848.c	Fri Feb 23 14:54:47 1996
@@ -55,6 +55,7 @@
     int             dual_dma;	/* 1, when two DMA channels allocated */
     unsigned char   MCE_bit;
     unsigned char   saved_regs[16];
+    int             debug_flag;
 
     int             speed;
     unsigned char   speed_bits;
@@ -562,6 +563,7 @@
 
   devc = (ad1848_info *) audio_devs[dev]->devc;
 
+
   save_flags (flags);
   cli ();
   if (devc->opened)
@@ -663,7 +665,7 @@
 	arg = 50000;
 
       devc->speed = arg;
-      devc->speed_bits = speed_table[selected].bits;
+      devc->speed_bits = speed_table[3].bits;
       return devc->speed;
     }
 
@@ -864,6 +866,7 @@
 
   if (dma_restart)
     {
+      ad_write (devc, 9, ad_read (devc, 9) & ~0x01);	/* Playback disable */
       /* ad1848_halt (dev); */
       DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE);
     }
@@ -871,7 +874,6 @@
   ad_write (devc, 15, (unsigned char) (cnt & 0xff));
   ad_write (devc, 14, (unsigned char) ((cnt >> 8) & 0xff));
 
-  /* ad_write (devc, 9, ad_read (devc, 9) | 0x01); *//* Playback enable */
   ad_unmute (devc);
 
   devc->xfer_count = cnt;
@@ -916,6 +918,7 @@
   if (dma_restart)
     {
       /* ad1848_halt (dev); */
+      ad_write (devc, 9, ad_read (devc, 9) & ~0x02);	/* Capture disable */
       DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
     }
 
@@ -931,7 +934,6 @@
       ad_write (devc, 30, (unsigned char) ((cnt >> 8) & 0xff));
     }
 
-  /*  ad_write (devc, 9, ad_read (devc, 9) | 0x02); *//* Capture enable */
   ad_unmute (devc);
 
   devc->xfer_count = cnt;
@@ -966,7 +968,9 @@
       ad_write (devc, 23, devc->speed & 0xff);	/* Speed LSB */
     }
 
-  if (fs == (old_fs = ad_read (devc, 8)))	/* No change */
+  old_fs = ad_read (devc, 8);
+
+  if (fs == old_fs)		/* No change */
     {
       restore_flags (flags);
       devc->xfer_count = 0;
@@ -974,6 +978,7 @@
     }
 
   ad_enter_MCE (devc);		/* Enables changes to the format select reg */
+
   ad_write (devc, 8, fs);
   /*
    * Write to I8 starts resyncronization. Wait until it completes.
@@ -1027,6 +1032,7 @@
   unsigned long   flags;
   int             timeout;
 
+
   save_flags (flags);
   cli ();
 
@@ -1035,16 +1041,16 @@
   ad_write (devc, 9, ad_read (devc, 9) & ~0x03);	/* Stop DMA */
   ad_write (devc, 9, ad_read (devc, 9) & ~0x03);	/* Stop DMA */
 
-  ad_write (devc, 15, 0);	/* Clear DMA counter */
+  ad_write (devc, 15, 4);	/* Clear DMA counter */
   ad_write (devc, 14, 0);	/* Clear DMA counter */
 
   if (devc->mode != MD_1848)
     {
-      ad_write (devc, 30, 0);	/* Clear DMA counter */
+      ad_write (devc, 30, 4);	/* Clear DMA counter */
       ad_write (devc, 31, 0);	/* Clear DMA counter */
     }
 
-  for (timeout = 0; timeout < 1000 && !(inb (io_Status (devc)) & 0x80);
+  for (timeout = 0; timeout < 10000 && !(inb (io_Status (devc)) & 0x80);
        timeout++);		/* Wait for interrupt */
 
   ad_write (devc, 9, ad_read (devc, 9) & ~0x03);	/* Stop DMA */
@@ -1076,6 +1082,9 @@
   ad_write (devc, 9, ad_read (devc, 9) & ~0x02);	/* Stop capture */
 
 
+  outb (0, io_Status (devc));	/* Clear interrupt status */
+  outb (0, io_Status (devc));	/* Clear interrupt status */
+
   devc->irq_mode &= ~PCM_ENABLE_INPUT;
 
   restore_flags (flags);
@@ -1100,6 +1109,9 @@
   ad_write (devc, 9, ad_read (devc, 9) & ~0x01);	/* Stop playback */
 
 
+  outb (0, io_Status (devc));	/* Clear interrupt status */
+  outb (0, io_Status (devc));	/* Clear interrupt status */
+
   devc->irq_mode &= ~PCM_ENABLE_OUTPUT;
 
   restore_flags (flags);
@@ -1160,6 +1172,7 @@
   devc->chip_name = "AD1848";
   devc->mode = MD_1848;		/* AD1848 or CS4248 */
   devc->osp = osp;
+  devc->debug_flag = 0;
 
   /*
      * Check that the I/O address is in use.
@@ -1434,11 +1447,9 @@
       for (i = 16; i < 32; i++)
 	ad_write (devc, i, init_values[i]);
 
-      if (devc->mode == MD_4231A || devc->mode == MD_4232)
-	ad_write (devc, 9, init_values[9] | 0x18);	/* Enable full calibration */
 
       if (devc->mode == MD_1845)
-	ad_write (devc, 27, init_values[27] | 0x08);	/* Alternate freq select enabled */
+	ad_write (devc, 27, ad_read (devc, 27) | 0x08);		/* Alternate freq select enabled */
     }
   else
     {
@@ -1464,7 +1475,7 @@
       if (irq > 0)
 	{
 	  audio_devs[my_dev]->devc = devc;
-	  irq2dev[irq] = my_dev;
+	  irq2dev[irq] = devc->dev_no = my_dev;
 	  if (snd_set_irq_handler (devc->irq, ad1848_interrupt,
 				   audio_devs[my_dev]->name,
 				   devc->osp) < 0)
@@ -1549,7 +1560,7 @@
   int             i, dev = 0;
   ad1848_info    *devc = NULL;
 
-  for (i = 0; devc == NULL && nr_ad1848_devs; i++)
+  for (i = 0; devc == NULL && i < nr_ad1848_devs; i++)
     if (dev_info[i].base == io_base)
       {
 	devc = &dev_info[i];
diff -u --recursive --new-file v1.3.68/linux/drivers/sound/audio.c linux/drivers/sound/audio.c
--- v1.3.68/linux/drivers/sound/audio.c	Sat Feb 17 16:02:52 1996
+++ linux/drivers/sound/audio.c	Fri Feb 23 17:31:04 1996
@@ -67,7 +67,7 @@
 	else
 	  fmt = AFMT_U8;	/* This is always supported */
 
-      audio_format[dev] = DMAbuf_ioctl (dev, SNDCTL_DSP_SETFMT, (caddr_t) (long) fmt, 1);
+      audio_format[dev] = DMAbuf_ioctl (dev, SNDCTL_DSP_SETFMT, (caddr_t) fmt, 1);
     }
 
   if (local_conversion[dev])	/* This shadows the HW format */
@@ -145,6 +145,9 @@
   dev = dev >> 4;
   mode = file->mode & O_ACCMODE;
 
+  audio_devs[dev]->dmap_out->closing = 1;
+  audio_devs[dev]->dmap_in->closing = 1;
+
   sync_output (dev);
 
   if (audio_devs[dev]->coproc)
@@ -366,7 +369,7 @@
 	break;
 
       case SNDCTL_DSP_GETFMTS:
-	return snd_ioctl_return ((int *) arg, audio_devs[dev]->format_mask);
+	return snd_ioctl_return ((int *) arg, audio_devs[dev]->format_mask | AFMT_MU_LAW);
 	break;
 
       case SNDCTL_DSP_SETFMT:
@@ -429,6 +432,8 @@
 
 	  if (audio_devs[dev]->trigger)		/* Supports SETTRIGGER */
 	    info |= DSP_CAP_TRIGGER;
+
+	  info |= DSP_CAP_MMAP;
 
 	  memcpy_tofs ((&((char *) arg)[0]), (char *) &info, sizeof (info));
 	  return 0;
diff -u --recursive --new-file v1.3.68/linux/drivers/sound/configure.c linux/drivers/sound/configure.c
--- v1.3.68/linux/drivers/sound/configure.c	Wed Feb 14 14:37:13 1996
+++ linux/drivers/sound/configure.c	Fri Feb 23 15:17:23 1996
@@ -1,8 +1,11 @@
 /*
- *	 PnP support is not included in this driver version.
+ *	 PnP soundcard support is not included in this version.
+ *
  *       AEDSP16 will not work without significant changes.
+ *
+ *       SB Pro and SB16 drivers are always enabled together with SB.
  */
-#define DISABLED_OPTIONS 	(B(OPT_PNP)|B(OPT_AEDSP16))
+#define DISABLED_OPTIONS 	(B(OPT_PNP)|B(OPT_AEDSP16)|B(OPT_SBPRO)|B(OPT_SB16))
 /*
  * sound/configure.c  - Configuration program for the Linux Sound Driver
  */
@@ -159,7 +162,7 @@
 char           *questions[] =
 {
   "ProAudioSpectrum 16 support",
-  "SoundBlaster support",
+  "SoundBlaster (SB, SBPro, SB16, clones) support",
   "Generic OPL2/OPL3 FM synthesizer support",
   "Gravis Ultrasound support",
   "MPU-401 support (NOT for SB16)",
@@ -476,6 +479,20 @@
 	  if (strcmp (tmp, "KERNEL_SOUNDCARD") == 0)
 	    continue;
 
+	  if (strcmp (tmp, "JAZZ_DMA16") == 0)	/* Rename it (hack) */
+	    {
+	      printf ("#define SB_DMA2 %s\n",
+		      &buf[18]);
+	      continue;
+	    }
+
+	  if (strcmp (tmp, "SB16_DMA") == 0)	/* Rename it (hack) */
+	    {
+	      printf ("#define SB_DMA2 %s\n",
+		      &buf[16]);
+	      continue;
+	    }
+
 	  tmp[8] = 0;		/* Truncate the string */
 	  if (strcmp (tmp, "EXCLUDE_") == 0)
 	    continue;		/* Skip excludes */
@@ -679,6 +696,13 @@
 	       "Do you have to use this switch with DOS (Y/n) ?");
       if (think_positively (0))
 	printf ("#define BROKEN_BUS_CLOCK\n");
+
+      fprintf (stderr, "PAS16 has SoundBlaster emulation. You should disable\n"
+	       "this feature if you have another SB compatible card\n"
+	       "on the machine\n"
+	       "Do you want to disable SB emulation of PAS16 (y/N) ?");
+      if (think_positively (0))
+	printf ("#define DISABLE_SB_EMULATION\n");
     }
 
 
@@ -1340,6 +1364,9 @@
 	  printf ("#define INCLUDE_TRIX_BOOT\n");
 	}
     }
+
+  if (selected_options & B (OPT_SB))
+    selected_options |= B (OPT_SBPRO) | B (OPT_SB16);
 
   if (!(selected_options & ANY_DEVS))
     {
diff -u --recursive --new-file v1.3.68/linux/drivers/sound/cs4232.c linux/drivers/sound/cs4232.c
--- v1.3.68/linux/drivers/sound/cs4232.c	Wed Feb 14 14:37:13 1996
+++ linux/drivers/sound/cs4232.c	Fri Feb 23 14:54:52 1996
@@ -84,7 +84,7 @@
 int
 probe_cs4232 (struct address_info *hw_config)
 {
-  int             i;
+  int             i, n;
   int             base = hw_config->io_base, irq = hw_config->irq;
   int             dma1 = hw_config->dma, dma2 = hw_config->dma2;
 
@@ -112,65 +112,76 @@
  */
 
 /*
+ * Repeat initialization few times since it doesn't always succeed in
+ * first time.
+ */
+
+  for (n = 0; n < 4; n++)
+    {
+/*
  * Wake up the card by sending a 32 byte Crystal key to the key port.
  */
-  for (i = 0; i < 32; i++)
-    CS_OUT (crystal_key[i]);
+      for (i = 0; i < 32; i++)
+	CS_OUT (crystal_key[i]);
 
 /*
  * Now set the CSN (Card Select Number).
  */
 
-  CS_OUT2 (0x06, CSN_NUM);
+      CS_OUT2 (0x06, CSN_NUM);
 
 
 /*
  * Then set some config bytes. First logical device 0 
  */
 
-  CS_OUT2 (0x15, 0x00);		/* Select logical device 0 (WSS/SB/FM) */
-  CS_OUT3 (0x47, (base >> 8) & 0xff, base & 0xff);	/* WSSbase */
+      CS_OUT2 (0x15, 0x00);	/* Select logical device 0 (WSS/SB/FM) */
+      CS_OUT3 (0x47, (base >> 8) & 0xff, base & 0xff);	/* WSSbase */
 
-  if (check_region (0x388, 4))	/* Not free */
-    CS_OUT3 (0x48, 0x00, 0x00)	/* FMbase off */
-      else
-    CS_OUT3 (0x48, 0x03, 0x88);	/* FMbase 0x388 */
-
-  CS_OUT3 (0x42, 0x00, 0x00);	/* SBbase off */
-  CS_OUT2 (0x22, irq);		/* SB+WSS IRQ */
-  CS_OUT2 (0x2a, dma1);		/* SB+WSS DMA */
-
-  if (dma2 != -1)
-    CS_OUT2 (0x25, dma2)	/* WSS DMA2 */
-      else
-    CS_OUT2 (0x25, 4);		/* No WSS DMA2 */
+      if (check_region (0x388, 4))	/* Not free */
+	CS_OUT3 (0x48, 0x00, 0x00)	/* FMbase off */
+	  else
+	CS_OUT3 (0x48, 0x03, 0x88);	/* FMbase 0x388 */
+
+      CS_OUT3 (0x42, 0x00, 0x00);	/* SBbase off */
+      CS_OUT2 (0x22, irq);	/* SB+WSS IRQ */
+      CS_OUT2 (0x2a, dma1);	/* SB+WSS DMA */
+
+      if (dma2 != -1)
+	CS_OUT2 (0x25, dma2)	/* WSS DMA2 */
+	  else
+	CS_OUT2 (0x25, 4);	/* No WSS DMA2 */
 
-  CS_OUT2 (0x33, 0x01);		/* Activate logical dev 0 */
+      CS_OUT2 (0x33, 0x01);	/* Activate logical dev 0 */
 
 /*
  * Initialize logical device 3 (MPU)
  */
 
 #if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI)
-  if (mpu_base != 0 && mpu_irq != 0)
-    {
-      CS_OUT2 (0x15, 0x03);	/* Select logical device 3 (MPU) */
-      CS_OUT3 (0x47, (mpu_base >> 8) & 0xff, mpu_base & 0xff);	/* MPUbase */
-      CS_OUT2 (0x22, mpu_irq);	/* MPU IRQ */
-      CS_OUT2 (0x33, 0x01);	/* Activate logical dev 3 */
-    }
+      if (mpu_base != 0 && mpu_irq != 0)
+	{
+	  CS_OUT2 (0x15, 0x03);	/* Select logical device 3 (MPU) */
+	  CS_OUT3 (0x47, (mpu_base >> 8) & 0xff, mpu_base & 0xff);	/* MPUbase */
+	  CS_OUT2 (0x22, mpu_irq);	/* MPU IRQ */
+	  CS_OUT2 (0x33, 0x01);	/* Activate logical dev 3 */
+	}
 #endif
 
 /*
  * Finally activate the chip
  */
-  CS_OUT (0x79);
+      CS_OUT (0x79);
 
 /*
  * Then try to detect the codec part of the chip
  */
 
-  return ad1848_detect (hw_config->io_base, NULL, hw_config->osp);
+      if (ad1848_detect (hw_config->io_base, NULL, hw_config->osp))
+	return 1;
+    }
+
+  return 0;
 }
 
 long
diff -u --recursive --new-file v1.3.68/linux/drivers/sound/dev_table.h linux/drivers/sound/dev_table.h
--- v1.3.68/linux/drivers/sound/dev_table.h	Wed Feb 14 14:37:13 1996
+++ linux/drivers/sound/dev_table.h	Fri Feb 23 14:54:36 1996
@@ -95,6 +95,7 @@
 #define DMA_RESTART	0x00000002
 #define DMA_ACTIVE	0x00000004
 #define DMA_STARTED	0x00000008
+#define DMA_EMPTY	0x00000010	
 #define DMA_ALLOC_DONE	0x00000020
 
 	int      open_mode;
@@ -148,6 +149,7 @@
 #define NEEDS_RESTART		1
 #define DMA_AUTOMODE		2
 #define DMA_DUPLEX		4
+#define DMA_PSEUDO_AUTOMODE	8
 	int  format_mask;	/* Bitmask for supported audio formats */
 	void *devc;		/* Driver specific info */
 	int (*open) (int dev, int mode);
@@ -342,6 +344,9 @@
 		{"TRXPRO", 0, SNDCARD_TRXPRO, "MediaTriX AudioTriX Pro",	attach_trix_wss, probe_trix_wss, unload_trix_wss},
 		{"TRXPROSB", 0, SNDCARD_TRXPRO_SB, "AudioTriX (SB mode)",	attach_trix_sb, probe_trix_sb, unload_trix_sb},
 		{"TRXPROMPU", 0, SNDCARD_TRXPRO_MPU, "AudioTriX MIDI",	attach_trix_mpu, probe_trix_mpu, unload_trix_mpu},
+#endif
+#ifdef CONFIG_PNP
+		{"AD1848", 0, 500, "SoundPort",	attach_pnp_ad1848, probe_pnp_ad1848, unload_pnp_ad1848},
 #endif
 		{NULL, 0, 0,		"*?*",			NULL, NULL, NULL}
 	};
diff -u --recursive --new-file v1.3.68/linux/drivers/sound/dmabuf.c linux/drivers/sound/dmabuf.c
--- v1.3.68/linux/drivers/sound/dmabuf.c	Wed Feb 14 14:37:14 1996
+++ linux/drivers/sound/dmabuf.c	Fri Feb 23 14:54:55 1996
@@ -44,25 +44,24 @@
 {
   {0}};
 
+#define NEUTRAL8	0x80
+#define NEUTRAL16	0x00
+
 static int      ndmaps = 0;
 
 #define MAX_DMAP (MAX_AUDIO_DEV*2)
 
 static struct dma_buffparms dmaps[MAX_DMAP] =
 {
-  {0}};				/*
+  {0}};
 
-				   * Primitive way to allocate
-				   * such a large array.
-				   * Needs dynamic run-time alloction.
-				 */
 static int      space_in_queue (int dev);
 
 static void     dma_reset_output (int dev);
 static void     dma_reset_input (int dev);
 
 static void
-reorganize_buffers (int dev, struct dma_buffparms *dmap)
+reorganize_buffers (int dev, struct dma_buffparms *dmap, int recording)
 {
   /*
    * This routine breaks the physical device buffers to logical ones.
@@ -81,9 +80,9 @@
       sz = dsp_dev->ioctl (dev, SOUND_PCM_READ_BITS, 0, 1);
 
       if (sz == 8)
-	dmap->neutral_byte = 0x80;
+	dmap->neutral_byte = NEUTRAL8;
       else
-	dmap->neutral_byte = 0x00;
+	dmap->neutral_byte = NEUTRAL16;
 
       if (sr < 1 || nc < 1 || sz < 1)
 	{
@@ -118,11 +117,13 @@
  */
 
       if (dmap->subdivision == 0)	/* Not already set */
-#ifdef V35A9_COMPATIBLE
-	dmap->subdivision = 1;	/* Init to the default value */
-#else
-	dmap->subdivision = 4;	/* Init to the default value */
+	{
+	  dmap->subdivision = 1;	/* Init to the default value */
+#ifndef V35A9_COMPATIBLE
+	  if (recording)
+	    dmap->subdivision = 4;	/* Use shorter fragments when recording */
 #endif
+	}
 
       bsz /= dmap->subdivision;
 
@@ -155,12 +156,16 @@
   dmap->nbufs = n;
   dmap->bytes_in_use = n * bsz;
 
+  memset (dmap->raw_buf,
+	  dmap->neutral_byte,
+	  dmap->bytes_in_use);
+
   for (i = 0; i < dmap->nbufs; i++)
     {
       dmap->counts[i] = 0;
     }
 
-  dmap->flags |= DMA_ALLOC_DONE;
+  dmap->flags |= DMA_ALLOC_DONE | DMA_EMPTY;
 }
 
 static void
@@ -182,7 +187,7 @@
 
   dmap->dma_mode = DMODE_NONE;
   dmap->mapping_flags = 0;
-  dmap->neutral_byte = 0x80;
+  dmap->neutral_byte = NEUTRAL8;
   dmap->cfrag = -1;
   dmap->closing = 0;
 }
@@ -320,7 +325,7 @@
   restore_flags (flags);
 
   dma_init_buffers (dev, audio_devs[dev]->dmap_out);
-  reorganize_buffers (dev, audio_devs[dev]->dmap_out);
+  reorganize_buffers (dev, audio_devs[dev]->dmap_out, 0);
 }
 
 static void
@@ -338,7 +343,7 @@
   restore_flags (flags);
 
   dma_init_buffers (dev, audio_devs[dev]->dmap_in);
-  reorganize_buffers (dev, audio_devs[dev]->dmap_in);
+  reorganize_buffers (dev, audio_devs[dev]->dmap_in, 1);
 }
 
 static int
@@ -354,8 +359,10 @@
       save_flags (flags);
       cli ();
 
+      audio_devs[dev]->dmap_out->underrun_count = 0;
       while (!current_got_fatal_signal ()
-	     && audio_devs[dev]->dmap_out->qlen)
+	     && audio_devs[dev]->dmap_out->qlen
+	     && audio_devs[dev]->dmap_out->underrun_count == 0)
 	{
 
 	  {
@@ -472,7 +479,7 @@
     }
 
   if (!(dmap->flags & DMA_ALLOC_DONE))
-    reorganize_buffers (dev, dmap);
+    reorganize_buffers (dev, dmap, 1);
 
   if (!dmap->dma_mode)
     {
@@ -517,7 +524,7 @@
     }
   else if (!dmap->qlen)
     {
-      int             timeout;
+      int             tmout;
 
       if ((err = activate_recording (dev, dmap)) < 0)
 	{
@@ -541,16 +548,16 @@
 	}
 
       if (!audio_devs[dev]->go)
-	timeout = 0;
+	tmout = 0;
       else
-	timeout = 2 * HZ;
+	tmout = 2 * HZ;
 
 
       {
 	unsigned long   tl;
 
-	if (timeout)
-	  current_set_timeout (tl = jiffies + (timeout));
+	if (tmout)
+	  current_set_timeout (tl = jiffies + (tmout));
 	else
 	  tl = (unsigned long) -1;
 	in_sleep_flag[dev].mode = WK_SLEEP;
@@ -709,6 +716,7 @@
     }
 }
 
+
 int
 DMAbuf_ioctl (int dev, unsigned int cmd, caddr_t arg, int local)
 {
@@ -730,7 +738,7 @@
 
     case SNDCTL_DSP_GETBLKSIZE:
       if (!(dmap_out->flags & DMA_ALLOC_DONE))
-	reorganize_buffers (dev, dmap_out);
+	reorganize_buffers (dev, dmap_out, 0);
 
       return snd_ioctl_return ((int *) arg, dmap_out->fragment_size);
       break;
@@ -784,7 +792,7 @@
 	    return -EINVAL;
 
 	  if (!(dmap->flags & DMA_ALLOC_DONE))
-	    reorganize_buffers (dev, dmap);
+	    reorganize_buffers (dev, dmap, (cmd == SNDCTL_DSP_GETISPACE));
 
 	  info->fragstotal = dmap->nbufs;
 
@@ -846,8 +854,16 @@
 
 	if ((changed & bits) & PCM_ENABLE_INPUT && audio_devs[dev]->go)
 	  {
+	    int             err;
+
 	    if (!(dmap_in->flags & DMA_ALLOC_DONE))
-	      reorganize_buffers (dev, dmap_in);
+	      {
+		reorganize_buffers (dev, dmap_in, 1);
+	      }
+
+	    if ((err = audio_devs[dev]->prepare_for_input (dev,
+			       dmap_in->fragment_size, dmap_in->nbufs)) < 0)
+	      return -err;
 
 	    activate_recording (dev, dmap_in);
 	  }
@@ -856,8 +872,16 @@
 	    dmap_out->mapping_flags & DMA_MAP_MAPPED &&
 	    audio_devs[dev]->go)
 	  {
+	    int             err;
+
 	    if (!(dmap_out->flags & DMA_ALLOC_DONE))
-	      reorganize_buffers (dev, dmap_out);
+	      {
+		reorganize_buffers (dev, dmap_out, 0);
+	      }
+
+	    if ((err = audio_devs[dev]->prepare_for_output (dev,
+			     dmap_out->fragment_size, dmap_out->nbufs)) < 0)
+	      return -err;
 
 	    dmap_out->counts[dmap_out->qhead] = dmap_out->fragment_size;
 	    DMAbuf_start_output (dev, 0, dmap_out->fragment_size);
@@ -1009,10 +1033,10 @@
       dma_reset_output (dev);
     }
 
-  dmap->flags &= ~DMA_RESTART;
+  dmap->flags &= ~(DMA_RESTART | DMA_EMPTY);
 
   if (!(dmap->flags & DMA_ALLOC_DONE))
-    reorganize_buffers (dev, dmap);
+    reorganize_buffers (dev, dmap, 0);
 
   if (!dmap->dma_mode)
     {
@@ -1031,7 +1055,7 @@
   while (!space_in_queue (dev) &&
 	 !abort)
     {
-      int             timeout;
+      int             tmout;
 
       if (dontblock)
 	{
@@ -1050,16 +1074,16 @@
        * Wait for free space
        */
       if (!audio_devs[dev]->go)
-	timeout = 0;
+	tmout = 0;
       else
-	timeout = 2 * HZ;
+	tmout = 2 * HZ;
 
 
       {
 	unsigned long   tl;
 
-	if (timeout)
-	  current_set_timeout (tl = jiffies + (timeout));
+	if (tmout)
+	  current_set_timeout (tl = jiffies + (tmout));
 	else
 	  tl = (unsigned long) -1;
 	out_sleep_flag[dev].mode = WK_SLEEP;
@@ -1094,11 +1118,6 @@
 
   *buf = dmap->raw_buf + dmap->qtail * dmap->fragment_size;
   *size = dmap->fragment_size;
-#if 1
-  memset (*buf,
-          dmap->neutral_byte,
-          *size);
-#endif
   dmap->counts[dmap->qtail] = 0;
 
   return dmap->qtail;
@@ -1160,6 +1179,14 @@
 		dev, dmap->qlen, dmap->nbufs);
 
       dmap->counts[dmap->qtail] = l;
+      if (l < dmap->fragment_size)
+	{
+	  int             p = dmap->fragment_size * dmap->qtail;
+
+	  memset (dmap->raw_buf + p + l,
+		  dmap->neutral_byte,
+		  dmap->fragment_size - l);
+	}
 
       if ((l != dmap->fragment_size) &&
 	  ((audio_devs[dev]->flags & DMA_AUTOMODE) &&
@@ -1266,6 +1293,41 @@
   return mem_start;
 }
 
+static void
+polish_buffers (struct dma_buffparms *dmap)
+{
+  int             i;
+
+  if (dmap->cfrag < 0)
+    {
+      memset (dmap->raw_buf,
+	      dmap->neutral_byte,
+	      dmap->bytes_in_use);
+      return;
+    }
+
+  for (i = 0; i < dmap->nbufs; i++)
+    {
+      int             p, l;
+
+      p = dmap->fragment_size * i;
+
+      if (i == dmap->cfrag)
+	{
+	  l = dmap->fragment_size - dmap->counts[i];
+	}
+      else
+	l = dmap->fragment_size;
+
+      if (l)
+	{
+	  memset (dmap->raw_buf + p,
+		  dmap->neutral_byte,
+		  l);
+	}
+    }
+}
+
 void
 DMAbuf_outputintr (int dev, int event_type)
 {
@@ -1291,12 +1353,6 @@
   if (dmap->mapping_flags & DMA_MAP_MAPPED)
     {
       /* mmapped access */
-      int             p;
-
-      p = dmap->fragment_size * dmap->qhead;
-      memset (dmap->raw_buf + p,
-	      dmap->neutral_byte,
-	      dmap->fragment_size);
 
       dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
       dmap->qlen++;		/* Yes increment it (don't decrement) */
@@ -1334,17 +1390,19 @@
 	  /* Ignore underrun. Just move the tail pointer forward and go */
 	  if (dmap->closing)
 	    {
+	      polish_buffers (dmap);
 	      audio_devs[dev]->halt_xfer (dev);
 	    }
 	  else
 	    {
 	      dmap->qlen++;
-	      dmap->cfrag = -1;
 	      dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
 
-	      memset (audio_devs[dev]->dmap_out->raw_buf,
-		      audio_devs[dev]->dmap_out->neutral_byte,
-		      audio_devs[dev]->dmap_out->bytes_in_use);
+	      if (!(dmap->flags & DMA_EMPTY))
+		polish_buffers (dmap);
+
+	      dmap->cfrag = -1;
+	      dmap->flags |= DMA_EMPTY;
 	    }
 	}
 
diff -u --recursive --new-file v1.3.68/linux/drivers/sound/gus_card.c linux/drivers/sound/gus_card.c
--- v1.3.68/linux/drivers/sound/gus_card.c	Wed Feb 14 14:37:14 1996
+++ linux/drivers/sound/gus_card.c	Fri Feb 23 14:54:56 1996
@@ -111,9 +111,19 @@
 int
 probe_gus (struct address_info *hw_config)
 {
-  int             io_addr;
+  int             io_addr, irq;
 
   gus_osp = hw_config->osp;
+
+  irq = hw_config->irq;
+
+  if (hw_config->card_subtype == 0)	/* GUS/MAX/ACE */
+    if (irq != 3 && irq != 5 && irq != 7 && irq != 9 &&
+	irq != 11 && irq != 12 && irq != 15)
+      {
+	printk ("GUS: Unsupported IRQ %d\n", irq);
+	return 0;
+      }
 
   if (!check_region (hw_config->io_base, 16))
     if (!check_region (hw_config->io_base + 0x100, 16))
diff -u --recursive --new-file v1.3.68/linux/drivers/sound/gus_wave.c linux/drivers/sound/gus_wave.c
--- v1.3.68/linux/drivers/sound/gus_wave.c	Wed Feb 14 14:37:14 1996
+++ linux/drivers/sound/gus_wave.c	Fri Feb 23 14:55:01 1996
@@ -941,7 +941,7 @@
   sample_no = patch_table[instr_no];
   patch_map[voice] = -1;
 
-  if (sample_no < 0)
+  if (sample_no == NOT_SAMPLE)
     {
       printk ("GUS: Undefined patch %d for voice %d\n", instr_no, voice);
       return -EINVAL;		/* Patch not defined */
@@ -2557,7 +2557,7 @@
 
 	  rec->data.data8[i] = 0;
 
-	  while (ptr >= 0 && ptr < free_sample)
+	  while (ptr >= 0 && ptr < free_sample && ptr != NOT_SAMPLE)
 	    {
 	      rec->data.data8[i]++;
 	      ptr = samples[ptr].key;	/* Follow link */
@@ -2572,7 +2572,7 @@
 
 	n = 0;
 
-	while (ptr >= 0 && ptr < free_sample)
+	while (ptr >= 0 && ptr < free_sample && ptr != NOT_SAMPLE)
 	  {
 	    rec->data.data32[n++] = ptr;
 	    ptr = samples[ptr].key;	/* Follow link */
diff -u --recursive --new-file v1.3.68/linux/drivers/sound/hex2hex.h linux/drivers/sound/hex2hex.h
--- v1.3.68/linux/drivers/sound/hex2hex.h	Wed Feb 14 14:37:14 1996
+++ linux/drivers/sound/hex2hex.h	Fri Feb 23 14:54:36 1996
@@ -95,7 +95,7 @@
 
 
 	fprintf(outf, "/*\n *\t Computer generated file. Do not edit.\n */\n");
-        fprintf(outf, "static int %s_len = %d\n", l);
+        fprintf(outf, "static int %s_len = %d\n", varline, l);
 	fprintf(outf, "static unsigned char %s[] = {\n", varline);
 
 	for (i=0;i<l;i++)
diff -u --recursive --new-file v1.3.68/linux/drivers/sound/mad16.c linux/drivers/sound/mad16.c
--- v1.3.68/linux/drivers/sound/mad16.c	Wed Feb 14 14:37:14 1996
+++ linux/drivers/sound/mad16.c	Fri Feb 23 14:55:02 1996
@@ -428,12 +428,6 @@
 	{
 	  dma2_bit = 0x04;	/* Enable capture DMA */
 
-	  /* Change SB DMA so that it doesn't conflict with codec's DMAs */
-
-	  if (dma == 3)
-	    mad_write (MC3_PORT, (mad_read (MC3_PORT) & ~0x30) | 0x00);		/*DMA1 */
-	  else
-	    mad_write (MC3_PORT, (mad_read (MC3_PORT) & ~0x30) | 0x20);		/*DMA3 */
 	}
       else
 	{
diff -u --recursive --new-file v1.3.68/linux/drivers/sound/maui.c linux/drivers/sound/maui.c
--- v1.3.68/linux/drivers/sound/maui.c	Wed Feb 14 14:37:14 1996
+++ linux/drivers/sound/maui.c	Fri Feb 23 14:55:03 1996
@@ -55,20 +55,67 @@
 static int      (*orig_load_patch) (int dev, int format, const char *addr,
 				 int offs, int count, int pmgr_flag) = NULL;
 
+static wait_handle *maui_sleeper = NULL;
+static volatile struct snd_wait maui_sleep_flag =
+{0};
+
 static int
-maui_read (void)
+maui_wait (int mask)
 {
-  int             timeout;
+  int             i;
 
-  for (timeout = 0; timeout < 1000000; timeout++)
+/*
+ * Perform a short initial wait without sleeping
+ */
+
+  for (i = 0; i < 100; i++)
     {
-      if (inb (HOST_STAT_PORT) & STAT_RX_AVAIL)
+      if (inb (HOST_STAT_PORT) & mask)
 	{
-	  return inb (HOST_DATA_PORT);
+	  return 1;
 	}
     }
 
-  printk ("Maui: Receive timeout\n");
+/*
+ * Wait up to 15 seconds with sleeping
+ */
+
+  for (i = 0; i < 150; i++)
+    {
+      if (inb (HOST_STAT_PORT) & mask)
+	{
+	  return 1;
+	}
+
+
+      {
+	unsigned long   tl;
+
+	if (HZ / 10)
+	  current_set_timeout (tl = jiffies + (HZ / 10));
+	else
+	  tl = (unsigned long) -1;
+	maui_sleep_flag.mode = WK_SLEEP;
+	module_interruptible_sleep_on (&maui_sleeper);
+	if (!(maui_sleep_flag.mode & WK_WAKEUP))
+	  {
+	    if (jiffies >= tl)
+	      maui_sleep_flag.mode |= WK_TIMEOUT;
+	  }
+	maui_sleep_flag.mode &= ~WK_SLEEP;
+      };
+      if (current_got_fatal_signal ())
+	return 0;
+    }
+
+  return 0;
+}
+
+static int
+maui_read (void)
+{
+  if (maui_wait (STAT_RX_AVAIL))
+    return inb (HOST_DATA_PORT);
 
   return -1;
 }
@@ -76,17 +123,11 @@
 static int
 maui_write (unsigned char data)
 {
-  int             timeout;
-
-  for (timeout = 0; timeout < 10000000; timeout++)
+  if (maui_wait (STAT_TX_AVAIL))
     {
-      if (inb (HOST_STAT_PORT) & STAT_TX_AVAIL)
-	{
-	  outb (data, HOST_DATA_PORT);
-	  return 1;
-	}
+      outb (data, HOST_DATA_PORT);
+      return 1;
     }
-
   printk ("Maui: Write timeout\n");
 
   return 0;
@@ -169,7 +210,7 @@
 probe_maui (struct address_info *hw_config)
 {
   int             i;
-  int             tmp1, tmp2;
+  int             tmp1, tmp2, ret;
 
   if (check_region (hw_config->io_base, 8))
     return 0;
@@ -180,15 +221,24 @@
   if (snd_set_irq_handler (hw_config->irq, mauiintr, "Maui", maui_osp) < 0)
     return 0;
 
+  maui_sleep_flag.mode = WK_NONE;
 
   if (!maui_write (0xCF))	/* Report hardware version */
     {
+      printk ("No WaveFront firmware detected (card uninitialized?)\n");
       snd_release_irq (hw_config->irq);
       return 0;
     }
 
   if ((tmp1 = maui_read ()) == -1 || (tmp2 = maui_read ()) == -1)
     {
+      printk ("No WaveFront firmware detected (card uninitialized?)\n");
+      snd_release_irq (hw_config->irq);
+      return 0;
+    }
+
+  if (tmp1 == 0xff || tmp2 == 0xff)
+    {
       snd_release_irq (hw_config->irq);
       return 0;
     }
@@ -214,13 +264,16 @@
   if (trace_init)
     printk ("Available DRAM %dk\n", tmp1 / 1024);
 
-  request_region (hw_config->io_base + 2, 6, "Maui");
-
   for (i = 0; i < 1000; i++)
     if (probe_mpu401 (hw_config))
       break;
 
-  return probe_mpu401 (hw_config);
+  ret = probe_mpu401 (hw_config);
+
+  if (ret)
+    request_region (hw_config->io_base + 2, 6, "Maui");
+
+  return ret;
 }
 
 long
diff -u --recursive --new-file v1.3.68/linux/drivers/sound/mpu401.c linux/drivers/sound/mpu401.c
--- v1.3.68/linux/drivers/sound/mpu401.c	Wed Feb 14 14:37:14 1996
+++ linux/drivers/sound/mpu401.c	Fri Feb 23 14:55:07 1996
@@ -1089,7 +1089,10 @@
     {
       /* Verify the hardware again */
       if (!reset_mpu401 (devc))
-	return mem_start;
+	{
+	  printk ("MPU401: Device didn't respond\n");
+	  return mem_start;
+	}
 
       if (!devc->shared_irq)
 	if (snd_set_irq_handler (devc->irq, mpuintr, "mpu401", devc->osp) < 0)
diff -u --recursive --new-file v1.3.68/linux/drivers/sound/opl3.c linux/drivers/sound/opl3.c
--- v1.3.68/linux/drivers/sound/opl3.c	Wed Feb 14 14:37:14 1996
+++ linux/drivers/sound/opl3.c	Fri Feb 23 14:55:09 1996
@@ -82,6 +82,7 @@
 
 static struct opl_devinfo *devc = NULL;
 
+static int      force_opl3_mode = 0;
 
 static int      detected_model;
 
@@ -93,7 +94,7 @@
 void
 enable_opl3_mode (int left, int right, int both)
 {
-  /* NOP */
+  force_opl3_mode = 1;
 }
 
 static void
@@ -254,7 +255,7 @@
    * There is a FM chicp in this address. Detect the type (OPL2 to OPL4)
    */
 
-  if (signature == 0x06)	/* OPL2 */
+  if (signature == 0x06 && !force_opl3_mode)	/* OPL2 */
     {
       detected_model = 2;
     }
diff -u --recursive --new-file v1.3.68/linux/drivers/sound/pas2_card.c linux/drivers/sound/pas2_card.c
--- v1.3.68/linux/drivers/sound/pas2_card.c	Wed Feb 14 14:37:15 1996
+++ linux/drivers/sound/pas2_card.c	Fri Feb 23 14:55:10 1996
@@ -261,7 +261,7 @@
   mix_write (P_M_MV508_ADDRESS | 5, PARALLEL_MIXER);
   mix_write (5, PARALLEL_MIXER);
 
-#if defined(CONFIG_SB_EMULATION) && defined(CONFIG_SB)
+#if !defined(DISABLE_SB_EMULATION) && defined(CONFIG_SB)
 
   {
     struct address_info *sb_config;
@@ -386,7 +386,7 @@
 	  mem_start = pas_pcm_init (mem_start, hw_config);
 #endif
 
-#if defined(CONFIG_SB_EMULATION) && defined(CONFIG_SB)
+#if !defined(DISABLE_SB_EMULATION) && defined(CONFIG_SB)
 
 	  sb_dsp_disable_midi ();	/*
 					 * The SB emulation don't support *
diff -u --recursive --new-file v1.3.68/linux/drivers/sound/pas2_pcm.c linux/drivers/sound/pas2_pcm.c
--- v1.3.68/linux/drivers/sound/pas2_pcm.c	Wed Feb 14 14:37:15 1996
+++ linux/drivers/sound/pas2_pcm.c	Fri Feb 23 14:55:11 1996
@@ -57,6 +57,7 @@
 static unsigned short pcm_bitsok = 8;	/* mask of OK bits */
 static int      pcm_busy = 0;
 static int      my_devnum = 0;
+static int      open_mode = 0;
 
 int
 pcm_set_speed (int arg)
@@ -258,6 +259,7 @@
 
 
   pcm_count = 0;
+  open_mode = mode;
 
   return 0;
 }
@@ -322,7 +324,6 @@
       pcm_count = count;
     }
   pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER | F_F_PCM_RATE_COUNTER, FILTER_FREQUENCY);
-  pas_write (pas_read (PCM_CONTROL) | P_C_PCM_ENABLE | P_C_PCM_DAC_MODE, PCM_CONTROL);
 
   pcm_mode = PCM_DAC;
 
@@ -369,18 +370,37 @@
       pcm_count = count;
     }
   pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER | F_F_PCM_RATE_COUNTER, FILTER_FREQUENCY);
-  pas_write ((pas_read (PCM_CONTROL) | P_C_PCM_ENABLE) & ~P_C_PCM_DAC_MODE, PCM_CONTROL);
 
   pcm_mode = PCM_ADC;
 
   restore_flags (flags);
 }
 
+static void
+pas_pcm_trigger (int dev, int state)
+{
+  unsigned long   flags;
+
+  save_flags (flags);
+  cli ();
+  state &= open_mode;
+
+  if (state & PCM_ENABLE_OUTPUT)
+    pas_write (pas_read (PCM_CONTROL) | P_C_PCM_ENABLE | P_C_PCM_DAC_MODE, PCM_CONTROL);
+  else if (state & PCM_ENABLE_INPUT)
+    pas_write ((pas_read (PCM_CONTROL) | P_C_PCM_ENABLE) & ~P_C_PCM_DAC_MODE, PCM_CONTROL);
+  else
+    pas_write (pas_read (PCM_CONTROL) & ~P_C_PCM_ENABLE, PCM_CONTROL);
+
+  restore_flags (flags);
+}
+
 static int
 pas_pcm_prepare_for_input (int dev, int bsize, int bcount)
 {
   return 0;
 }
+
 static int
 pas_pcm_prepare_for_output (int dev, int bsize, int bcount)
 {
@@ -403,7 +423,10 @@
   pas_pcm_reset,
   pas_pcm_reset,
   NULL,
-  NULL
+  NULL,
+  NULL,
+  NULL,
+  pas_pcm_trigger
 };
 
 long
diff -u --recursive --new-file v1.3.68/linux/drivers/sound/pss.c linux/drivers/sound/pss.c
--- v1.3.68/linux/drivers/sound/pss.c	Wed Feb 14 14:37:15 1996
+++ linux/drivers/sound/pss.c	Fri Feb 23 14:55:15 1996
@@ -749,6 +749,7 @@
        timeout < 100000 && (inb (hw_config->io_base + 3) & 0x3f) != 0x04;
        timeout++);
 
+  outb (0x0b, hw_config->io_base + 4);	/* Required by some cards */
   return probe_ms_sound (hw_config);
 }
 
diff -u --recursive --new-file v1.3.68/linux/drivers/sound/sb16_dsp.c linux/drivers/sound/sb16_dsp.c
--- v1.3.68/linux/drivers/sound/sb16_dsp.c	Sat Feb 17 16:02:52 1996
+++ linux/drivers/sound/sb16_dsp.c	Fri Feb 23 17:33:14 1996
@@ -536,7 +536,7 @@
     {
       Jazz16_set_dma16 (hw_config->dma);
       sb16_dsp_ok = 1;
-      return 0;
+      return 1;
     }
 
   if (!(sb_config = sound_getconf (SNDCARD_SB)))
@@ -580,6 +580,10 @@
 void
 unload_sb16 (struct address_info *hw_config)
 {
+  extern int      Jazz16_detected;
+
+  if (Jazz16_detected)
+    return;
 
   sound_free_dma (dma8);
 
diff -u --recursive --new-file v1.3.68/linux/drivers/sound/sb16_midi.c linux/drivers/sound/sb16_midi.c
--- v1.3.68/linux/drivers/sound/sb16_midi.c	Wed Feb 14 14:37:15 1996
+++ linux/drivers/sound/sb16_midi.c	Fri Feb 23 14:55:16 1996
@@ -40,7 +40,7 @@
 #define	STATPORT   (sb16midi_base+1)
 
 extern int     *sb_osp;
-static int      sb16midi_base = 0x330;
+static int      sb16midi_base = 0;
 
 static int 
 sb16midi_status (void)
@@ -100,6 +100,9 @@
 void
 sb16midiintr (int unit)
 {
+  if (sb16midi_base == 0)
+    return;
+
   if (input_avail ())
     sb16midi_input_loop ();
 }
diff -u --recursive --new-file v1.3.68/linux/drivers/sound/sb_dsp.c linux/drivers/sound/sb_dsp.c
--- v1.3.68/linux/drivers/sound/sb_dsp.c	Sat Feb 17 16:02:53 1996
+++ linux/drivers/sound/sb_dsp.c	Fri Feb 23 14:55:18 1996
@@ -71,7 +71,7 @@
 				 * initialization  *  */
 static int      midi_disabled = 0;
 int             sb_dsp_highspeed = 0;
-int             sbc_major = 1, sbc_minor = 0;
+int             sbc_major = 0, sbc_minor = 0;
 static int      dsp_stereo = 0;
 static int      dsp_current_speed = DSP_DEFAULT_SPEED;
 static int      sb16 = 0;
@@ -292,7 +292,7 @@
 static int
 ess_speed (int speed)
 {
-  int             rate;
+  int             divider;
   unsigned char   bits = 0;
 
   if (speed < 4000)
@@ -303,20 +303,27 @@
   if (speed > 22000)
     {
       bits = 0x80;
-      rate = 256 - (795500 + speed / 2) / speed;
-      speed = 795500 / (256 - rate);
+      divider = 256 - (795500 + speed / 2) / speed;
+      dsp_current_speed = 795500 / (256 - divider);
     }
   else
     {
-      rate = 128 - (397700 + speed / 2) / speed;
-      speed = 397700 / (128 - rate);
+      divider = 128 - (397700 + speed / 2) / speed;
+      dsp_current_speed = 397700 / (128 - divider);
     }
 
-  bits |= (unsigned char) rate;
+  bits |= (unsigned char) divider;
   ess_write (0xa1, bits);
 
-  dsp_current_speed = speed;
-  return speed;
+/*
+ * Set filter divider register
+ */
+
+  speed = (speed * 9) / 20;	/* Set filter rolloff to 90% of speed/2 */
+  divider = 256 - 7160000 / (speed * 82);
+  ess_write (0xa2, divider);
+
+  return dsp_current_speed;
 }
 
 static int
@@ -481,10 +488,10 @@
 
   if (AudioDrive)
     {
-      int             c = 0xffff - nr_bytes;	/* ES1688 increments the count */
+      short           c = -nr_bytes;
 
-      ess_write (0xa4, (unsigned char) (c & 0xff));
-      ess_write (0xa5, (unsigned char) ((c >> 8) & 0xff));
+      ess_write (0xa4, (unsigned char) ((unsigned short) c & 0xff));
+      ess_write (0xa5, (unsigned char) (((unsigned short) c >> 8) & 0xff));
 
       ess_write (0xb8, ess_read (0xb8) | 0x01);		/* Go */
     }
@@ -571,10 +578,10 @@
 
   if (AudioDrive)
     {
-      int             c = 0xffff - nr_bytes;	/* ES1688 increments the count */
+      short           c = -nr_bytes;
 
-      ess_write (0xa4, (unsigned char) (c & 0xff));
-      ess_write (0xa5, (unsigned char) ((c >> 8) & 0xff));
+      ess_write (0xa4, (unsigned char) ((unsigned short) c & 0xff));
+      ess_write (0xa5, (unsigned char) (((unsigned short) c >> 8) & 0xff));
 
       ess_write (0xb8, ess_read (0xb8) | 0x01);		/* Go */
     }
@@ -798,6 +805,8 @@
 static void
 sb_dsp_halt_xfer (int dev)
 {
+  if (AudioDrive)
+    sb_reset_dsp ();
 }
 
 static int
@@ -884,6 +893,9 @@
 	sound_close_dma (dma16);
     }
 
+  if (AudioDrive)
+    sb_reset_dsp ();
+
   /* DMAbuf_close_dma (dev); */
   sb_free_irq ();
   /* sb_dsp_command (0xd4); */
@@ -923,7 +935,7 @@
     {
     case SOUND_PCM_WRITE_RATE:
       if (local)
-	return dsp_speed ((long) arg);
+	return dsp_speed ((int) arg);
       return snd_ioctl_return ((int *) arg, dsp_speed (get_fs_long ((long *) arg)));
       break;
 
@@ -935,7 +947,7 @@
 
     case SOUND_PCM_WRITE_CHANNELS:
       if (local)
-	return dsp_set_stereo ((long) arg - 1) + 1;
+	return dsp_set_stereo ((int) arg - 1) + 1;
       return snd_ioctl_return ((int *) arg, dsp_set_stereo (get_fs_long ((long *) arg) - 1) + 1);
       break;
 
@@ -947,7 +959,7 @@
 
     case SNDCTL_DSP_STEREO:
       if (local)
-	return dsp_set_stereo ((long) arg);
+	return dsp_set_stereo ((int) arg);
       return snd_ioctl_return ((int *) arg, dsp_set_stereo (get_fs_long ((long *) arg)));
       break;
 
@@ -956,7 +968,7 @@
        */
     case SNDCTL_DSP_SETFMT:
       if (local)
-	return dsp_set_bits ((long) arg);
+	return dsp_set_bits ((int) arg);
       return snd_ioctl_return ((int *) arg, dsp_set_bits (get_fs_long ((long *) arg)));
       break;
 
@@ -1214,15 +1226,15 @@
 int
 sb_dsp_detect (struct address_info *hw_config)
 {
-  sbc_base = hw_config->io_base;
-  sbc_irq = hw_config->irq;
-  sbc_dma = hw_config->dma;
-  sb_osp = hw_config->osp;
-
   if (sb_dsp_ok)
     return 0;			/*
 				 * Already initialized
 				 */
+
+  sbc_base = hw_config->io_base;
+  sbc_irq = hw_config->irq;
+  sbc_dma = hw_config->dma;
+  sb_osp = hw_config->osp;
   dma8 = dma16 = hw_config->dma;
 
   if (sb_reset_dsp ())
@@ -1273,7 +1285,6 @@
   unsigned char   cfg, irq_bits = 0, dma_bits = 0;
 
   AudioDrive = 1;
-  sb_no_recording = 1;		/* Temporary kludge */
 
   if (ess_minor >= 8)		/* ESS1688 doesn't support SB MIDI */
     midi_disabled = 1;
@@ -1466,7 +1477,8 @@
 
   int             mixer_type = 0;
 
-  dsp_get_vers (hw_config);
+  if (sbc_major == 0)
+    dsp_get_vers (hw_config);
 
   if (sbc_major == 0)
     {
diff -u --recursive --new-file v1.3.68/linux/drivers/sound/sb_mixer.c linux/drivers/sound/sb_mixer.c
--- v1.3.68/linux/drivers/sound/sb_mixer.c	Wed Feb 14 14:37:15 1996
+++ linux/drivers/sound/sb_mixer.c	Fri Feb 23 14:55:19 1996
@@ -275,13 +275,13 @@
       levels[dev] = left | (right << 8);
 
       /* Set left bass and treble values */
-      val = ((levels[SOUND_MIXER_TREBLE] & 0xff) * 16 / 100) << 4;
-      val |= ((levels[SOUND_MIXER_BASS] & 0xff) * 16 / 100) & 0x0f;
+      val = ((levels[SOUND_MIXER_TREBLE] & 0xff) * 16 / (unsigned) 100) << 4;
+      val |= ((levels[SOUND_MIXER_BASS] & 0xff) * 16 / (unsigned) 100) & 0x0f;
       sb_setmixer (0x0d, val);
 
       /* Set right bass and treble values */
-      val = (((levels[SOUND_MIXER_TREBLE] >> 8) & 0xff) * 16 / 100) << 4;
-      val |= (((levels[SOUND_MIXER_BASS] >> 8) & 0xff) * 16 / 100) & 0x0f;
+      val = (((levels[SOUND_MIXER_TREBLE] >> 8) & 0xff) * 16 / (unsigned) 100) << 4;
+      val |= (((levels[SOUND_MIXER_BASS] >> 8) & 0xff) * 16 / (unsigned) 100) & 0x0f;
       sb_setmixer (0x0e, val);
       break;
 
diff -u --recursive --new-file v1.3.68/linux/drivers/sound/sequencer.c linux/drivers/sound/sequencer.c
--- v1.3.68/linux/drivers/sound/sequencer.c	Wed Feb 14 14:37:15 1996
+++ linux/drivers/sound/sequencer.c	Fri Feb 23 14:55:22 1996
@@ -866,7 +866,7 @@
     case SEQ_WAIT:
       delay = (unsigned int *) q;	/*
 					 * Bytes 1 to 3 are containing the *
-					 * delay in jiffies
+					 * delay in 'ticks'
 					 */
       *delay = (*delay >> 8) & 0xffffff;
 
@@ -1097,22 +1097,8 @@
 
   if (dev)			/* Patch manager device */
     {
-      int             err;
-
       printk ("Patch manager interface is currently broken. Sorry\n");
       return -ENXIO;
-
-      dev--;
-
-      if (dev >= MAX_SYNTH_DEV)
-	return -ENXIO;
-      if (pmgr_present[dev])
-	return -EBUSY;
-      if ((err = pmgr_open (dev)) < 0)
-	return err;
-
-      pmgr_present[dev] = 1;
-      return err;
     }
 
   save_flags (flags);
diff -u --recursive --new-file v1.3.68/linux/drivers/sound/sound_switch.c linux/drivers/sound/sound_switch.c
--- v1.3.68/linux/drivers/sound/sound_switch.c	Wed Feb 14 14:37:16 1996
+++ linux/drivers/sound/sound_switch.c	Fri Feb 23 14:55:23 1996
@@ -387,11 +387,9 @@
       return MIDIbuf_write (dev, file, buf, count);
 #endif
 
-    default:
-      return -EPERM;
     }
 
-  return count;
+  return -EPERM;
 }
 
 int
@@ -569,9 +567,6 @@
       break;
 #endif
 
-    default:
-      return -EPERM;
-      break;
     }
 
   return -EPERM;
diff -u --recursive --new-file v1.3.68/linux/drivers/sound/soundcard.c linux/drivers/sound/soundcard.c
--- v1.3.68/linux/drivers/sound/soundcard.c	Wed Feb 14 14:37:16 1996
+++ linux/drivers/sound/soundcard.c	Fri Feb 23 15:16:56 1996
@@ -34,6 +34,7 @@
 #include <linux/major.h>
 
 static int      chrdev_registered = 0;
+static int      sound_major = SOUND_MAJOR;
 
 static int      is_unloading = 0;
 
@@ -143,6 +144,7 @@
 
   dev = inode_get_rdev (inode);
   dev = MINOR (dev);
+
   files[dev].flags = file_get_flags (file);
 
   sound_release_sw (dev, &files[dev]);
@@ -336,7 +338,7 @@
 soundcard_init (void)
 {
 #ifndef MODULE
-  module_register_chrdev (SOUND_MAJOR, "sound", &sound_fops);
+  module_register_chrdev (sound_major, "sound", &sound_fops);
   chrdev_registered = 1;
 #endif
 
@@ -345,7 +347,6 @@
   sndtable_init (0);		/* Initialize call tables and
 				   * detect cards */
 
-
   if (sndtable_get_cardcount () == 0)
     return;			/* No cards detected */
 
@@ -380,7 +381,7 @@
   for (i = 0; i < 31; i++)
     if (irqs & (1ul << i))
       {
-	printk ("Sound warning: IRQ%d was left allocated. Fixed.\n", i);
+	printk ("Sound warning: IRQ%d was left allocated - fixed.\n", i);
 	snd_release_irq (i);
       }
   irqs = 0;
@@ -419,7 +420,7 @@
   if (i)
     sound_setup ("sound=", ints);
 
-  err = module_register_chrdev (SOUND_MAJOR, "sound", &sound_fops);
+  err = module_register_chrdev (sound_major, "sound", &sound_fops);
   if (err)
     {
       printk ("sound: driver already loaded/included in kernel\n");
@@ -448,7 +449,7 @@
       int             i;
 
       if (chrdev_registered)
-	module_unregister_chrdev (SOUND_MAJOR, "sound");
+	module_unregister_chrdev (sound_major, "sound");
 
 #ifdef CONFIG_SEQUENCER
       sound_stop_timer ();
@@ -463,10 +464,9 @@
       for (i = 0; i < 8; i++)
 	if (dma_alloc_map[i] != DMA_MAP_UNAVAIL)
 	  {
-	    printk ("Sound: Hmm, DMA%d was left allocated\n", i);
+	    printk ("Sound: Hmm, DMA%d was left allocated - fixed\n", i);
 	    sound_free_dma (i);
 	  }
-
     }
 }
 #endif
@@ -627,7 +627,7 @@
   dmap->raw_buf = NULL;
 
   if (debugmem)
-    printk ("sound: buffsize%d %lu\n", dev, audio_devs[dev]->buffsize);
+    printk ("sound: buffsize[%d] = %lu\n", dev, audio_devs[dev]->buffsize);
 
   audio_devs[dev]->buffsize = dma_buffsize;
 
@@ -742,7 +742,7 @@
   printk ("<%s> at 0x%03x", name, hw_config->io_base);
 
   if (hw_config->irq)
-    printk (" irq %d", hw_config->irq);
+    printk (" irq %d", (hw_config->irq > 0) ? hw_config->irq : -hw_config->irq);
 
   if (hw_config->dma != -1 || hw_config->dma2 != -1)
     {
@@ -763,7 +763,7 @@
   printk ("<%s> at 0x%03x", name, base);
 
   if (irq)
-    printk (" irq %d", irq);
+    printk (" irq %d", (irq > 0) ? irq : -irq);
 
   if (dma != -1 || dma2 != -1)
     {
diff -u --recursive --new-file v1.3.68/linux/drivers/sound/soundvers.h linux/drivers/sound/soundvers.h
--- v1.3.68/linux/drivers/sound/soundvers.h	Wed Feb 14 14:37:16 1996
+++ linux/drivers/sound/soundvers.h	Fri Feb 23 14:54:40 1996
@@ -1,2 +1,2 @@
-#define SOUND_VERSION_STRING "3.5-beta2-960210"
+#define SOUND_VERSION_STRING "3.5-beta7-960223"
 #define SOUND_INTERNAL_VERSION 0x030505
diff -u --recursive --new-file v1.3.68/linux/drivers/sound/uart6850.c linux/drivers/sound/uart6850.c
--- v1.3.68/linux/drivers/sound/uart6850.c	Wed Feb 14 14:37:16 1996
+++ linux/drivers/sound/uart6850.c	Fri Feb 23 14:55:28 1996
@@ -36,6 +36,8 @@
 
 static int      uart6850_base = 0x330;
 
+static int     *uart6850_osp;
+
 #define	DATAPORT   (uart6850_base)
 #define	COMDPORT   (uart6850_base+1)
 #define	STATPORT   (uart6850_base+1)
@@ -77,8 +79,6 @@
 static int      reset_uart6850 (void);
 static void     (*midi_input_intr) (int dev, unsigned char data);
 static void     poll_uart6850 (unsigned long dummy);
-
-static int     *uart6850_osp;
 
 
 static struct timer_list uart6850_timer =
diff -u --recursive --new-file v1.3.68/linux/fs/binfmt_aout.c linux/fs/binfmt_aout.c
--- v1.3.68/linux/fs/binfmt_aout.c	Fri Feb  9 17:53:05 1996
+++ linux/fs/binfmt_aout.c	Sun Feb 25 11:17:59 1996
@@ -415,10 +415,13 @@
 		return error;
 	len = PAGE_ALIGN(ex.a_text + ex.a_data);
 	bss = ex.a_text + ex.a_data + ex.a_bss;
-	if (bss > len)
-		do_mmap(NULL, start_addr + len, bss-len,
-			PROT_READ|PROT_WRITE|PROT_EXEC,
-			MAP_PRIVATE|MAP_FIXED, 0);
+	if (bss > len) {
+		error = do_mmap(NULL, start_addr + len, bss-len,
+				PROT_READ|PROT_WRITE|PROT_EXEC,
+				MAP_PRIVATE|MAP_FIXED, 0);
+		if (error != start_addr + len)
+			return error;
+	}
 	return 0;
 }
 
diff -u --recursive --new-file v1.3.68/linux/fs/block_dev.c linux/fs/block_dev.c
--- v1.3.68/linux/fs/block_dev.c	Mon Nov 27 12:48:31 1995
+++ linux/fs/block_dev.c	Sun Feb 25 11:26:35 1996
@@ -61,8 +61,11 @@
 	else
 		size = INT_MAX;
 	while (count>0) {
-		if (block >= size)
+		if (block >= size) {
+			if (!written)
+				written = -ENOSPC;
 			return written;
+		}
 		chars = blocksize - offset;
 		if (chars > count)
 			chars=count;
diff -u --recursive --new-file v1.3.68/linux/fs/buffer.c linux/fs/buffer.c
--- v1.3.68/linux/fs/buffer.c	Tue Feb 20 14:37:26 1996
+++ linux/fs/buffer.c	Tue Feb 27 10:38:04 1996
@@ -72,13 +72,16 @@
 int nr_buffer_heads = 0;
 extern int *blksize_size[];
 
-/* Here is the parameter block for the bdflush process. */
+/* Here is the parameter block for the bdflush process. If you add or
+ * remove any of the parameters, make sure to update kernel/sysctl.c.
+ */
+
 static void wakeup_bdflush(int);
 
 #define N_PARAM 9
 #define LAV
 
-static union bdflush_param{
+union bdflush_param{
 	struct {
 		int nfract;  /* Percentage of buffer cache dirty to 
 				activate bdflush */
@@ -109,8 +112,8 @@
 
 
 /* These are the min and max parameter values that we will allow to be assigned */
-static int bdflush_min[N_PARAM] = {  0,  10,    5,   25,  0,   100,   100, 1, 1};
-static int bdflush_max[N_PARAM] = {100,5000, 2000, 2000,100, 60000, 60000, 2047, 5};
+int bdflush_min[N_PARAM] = {  0,  10,    5,   60,  0,   100,   100, 1, 1};
+int bdflush_max[N_PARAM] = {100,5000, 2000, 2000,100, 60000, 60000, 2047, 5};
 
 /*
  * Rewrote the wait-routines to use the "new" wait-queue functionality,
@@ -1250,11 +1253,6 @@
 
 	address = page_address(page);
 	page->count++;
-	wait_on_page(page);
-	if (page->uptodate) {
-		free_page(address);
-		return 0;
-	}
 	page->locked = 1;
 	
 	i = PAGE_SIZE >> inode->i_sb->s_blocksize_bits;
diff -u --recursive --new-file v1.3.68/linux/fs/ext/file.c linux/fs/ext/file.c
--- v1.3.68/linux/fs/ext/file.c	Tue Feb 20 14:37:26 1996
+++ linux/fs/ext/file.c	Sun Feb 25 11:17:58 1996
@@ -22,6 +22,7 @@
 #include <linux/fcntl.h>
 #include <linux/stat.h>
 #include <linux/locks.h>
+#include <linux/pagemap.h>
 
 #define	NBUF	32
 
@@ -241,13 +242,14 @@
 			}
 		}
 		p = (pos % BLOCK_SIZE) + bh->b_data;
+		memcpy_fromfs(p,buf,c);
+		update_vm_cache(inode, pos, p, c);
 		pos += c;
 		if (pos > inode->i_size) {
 			inode->i_size = pos;
 			inode->i_dirt = 1;
 		}
 		written += c;
-		memcpy_fromfs(p,buf,c);
 		buf += c;
 		mark_buffer_uptodate(bh, 1);
 		mark_buffer_dirty(bh, 0);
diff -u --recursive --new-file v1.3.68/linux/fs/ext2/ialloc.c linux/fs/ext2/ialloc.c
--- v1.3.68/linux/fs/ext2/ialloc.c	Thu Jan  4 21:54:56 1996
+++ linux/fs/ext2/ialloc.c	Mon Feb 26 08:36:27 1996
@@ -245,6 +245,7 @@
 		mark_buffer_dirty(bh2, 1);
 		es->s_free_inodes_count++;
 		mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
+		inode->i_dirt = 0;
 		set_inode_dtime (inode, gdp);
 	}
 	mark_buffer_dirty(bh, 1);
diff -u --recursive --new-file v1.3.68/linux/fs/ext2/super.c linux/fs/ext2/super.c
--- v1.3.68/linux/fs/ext2/super.c	Tue Jan 23 21:15:50 1996
+++ linux/fs/ext2/super.c	Mon Feb 26 08:38:08 1996
@@ -75,6 +75,10 @@
 	va_start (args, fmt);
 	vsprintf (error_buf, fmt, args);
 	va_end (args);
+	/* this is to prevent panic from syncing this filesystem */
+	if (sb->s_lock)
+		sb->s_lock=0;
+	sb->s_flags |= MS_RDONLY;
 	panic ("EXT2-fs panic (device %s): %s: %s\n",
 	       kdevname(sb->s_dev), function, error_buf);
 }
diff -u --recursive --new-file v1.3.68/linux/fs/fat/file.c linux/fs/fat/file.c
--- v1.3.68/linux/fs/fat/file.c	Tue Feb 20 14:37:26 1996
+++ linux/fs/fat/file.c	Sun Feb 25 11:17:58 1996
@@ -14,6 +14,7 @@
 #include <linux/fcntl.h>
 #include <linux/stat.h>
 #include <linux/string.h>
+#include <linux/pagemap.h>
 
 #include <asm/segment.h>
 #include <asm/system.h>
@@ -345,6 +346,7 @@
 			}
 			written -= left;
 		}
+		update_vm_cache(inode, filp->f_pos, bh->b_data + (filp->f_pos & (SECTOR_SIZE-1)), written);
 		filp->f_pos += written;
 		if (filp->f_pos > inode->i_size) {
 			inode->i_size = filp->f_pos;
diff -u --recursive --new-file v1.3.68/linux/fs/fat/inode.c linux/fs/fat/inode.c
--- v1.3.68/linux/fs/fat/inode.c	Fri Feb 23 13:54:37 1996
+++ linux/fs/fat/inode.c	Sun Feb 25 11:17:59 1996
@@ -13,7 +13,6 @@
 #include <linux/sched.h>
 #include <linux/errno.h>
 #include <linux/string.h>
-#include <linux/ctype.h>
 #include <linux/major.h>
 #include <linux/blkdev.h>
 #include <linux/fs.h>
diff -u --recursive --new-file v1.3.68/linux/fs/ncpfs/sock.c linux/fs/ncpfs/sock.c
--- v1.3.68/linux/fs/ncpfs/sock.c	Fri Feb 23 13:54:37 1996
+++ linux/fs/ncpfs/sock.c	Mon Feb 26 11:58:15 1996
@@ -257,7 +257,7 @@
 			ntohs(sender.sipx_port),
 			packet_buf[0], packet_buf[1]);
 
-		ncp_trigger_message(sk->ipx_ncp_server);
+		ncp_trigger_message(sk->protinfo.ipx.ncp_server);
 
 		set_fs(fs);
 	}
@@ -306,7 +306,7 @@
         }
 
         sk->data_ready = ncp_msg_data_ready;
-	sk->ipx_ncp_server = server;
+	sk->protinfo.ipx.ncp_server = server;
         return 0;
 }
                 
diff -u --recursive --new-file v1.3.68/linux/fs/nfs/file.c linux/fs/nfs/file.c
--- v1.3.68/linux/fs/nfs/file.c	Fri Feb 23 13:54:37 1996
+++ linux/fs/nfs/file.c	Tue Feb 27 10:37:48 1996
@@ -103,16 +103,16 @@
 	return 0;
 }
 
-static inline void do_read_nfs(struct inode * inode, char * buf, unsigned long pos)
+static inline int do_read_nfs(struct inode * inode, struct page * page,
+	char * buf, unsigned long pos)
 {
-	int refresh = 0;
+	int result, refresh = 0;
 	int count = PAGE_SIZE;
 	int rsize = NFS_SERVER(inode)->rsize;
 	struct nfs_fattr fattr;
 
+	page->locked = 1;
 	do {
-		int result;
-
 		if (count < rsize)
 			rsize = count;
 		result = nfs_proc_read(NFS_SERVER(inode), NFS_FH(inode), 
@@ -128,28 +128,26 @@
 	} while (count);
 
 	memset(buf, 0, count);
-	if (refresh)
+	if (refresh) {
 		nfs_refresh_inode(inode, &fattr);
+		result = 0;
+		page->uptodate = 1;
+	}
+	page->locked = 0;
+	wake_up(&page->wait);
+	return result;
 }
 
 static int nfs_readpage(struct inode * inode, struct page * page)
 {
+	int error;
 	unsigned long address;
 
 	address = page_address(page);
 	page->count++;
-	wait_on_page(page);
-	if (page->uptodate) {
-		free_page(address);
-		return 0;
-	}
-	page->locked = 1;
-	do_read_nfs(inode, (char *) address, page->offset);
-	page->locked = 0;
-	page->uptodate = 1;
-	wake_up(&page->wait);
+	error = do_read_nfs(inode, page, (char *) address, page->offset);
 	free_page(address);
-	return 0;
+	return error;
 }
 
 static int nfs_file_write(struct inode *inode, struct file *file, const char *buf,
diff -u --recursive --new-file v1.3.68/linux/fs/proc/array.c linux/fs/proc/array.c
--- v1.3.68/linux/fs/proc/array.c	Fri Feb 23 13:54:37 1996
+++ linux/fs/proc/array.c	Mon Feb 26 13:51:45 1996
@@ -854,8 +854,8 @@
  * f_pos = (number of the vma in the task->mm->mmap list) * MAPS_LINE_LENGTH
  *         + (index into the line)
  */
-#define MAPS_LINE_FORMAT	  "%08lx-%08lx %s %08lx %02x:%02x %lu\n"
-#define MAPS_LINE_MAX	49 /* sum of 8  1  8  1 4 1 8  1  2 1  2 1 10 1 */
+#define MAPS_LINE_FORMAT	  "%08lx-%08lx %s %08lx %s %lu\n"
+#define MAPS_LINE_MAX	49 /* sum of 8  1  8  1 4 1 8 1 5 1 10 1 */
 
 static int read_maps (int pid, struct file * file, char * buf, int count)
 {
@@ -909,7 +909,7 @@
 
 		len = sprintf(line, MAPS_LINE_FORMAT,
 			      map->vm_start, map->vm_end, str, map->vm_offset,
-			      MAJOR(dev),MINOR(dev), ino);
+			      kdevname(dev), ino);
 
 		if (column >= len) {
 			column = 0; /* continue with next line at column 0 */
@@ -958,6 +958,7 @@
 extern int get_dma_list(char *);
 extern int get_cpuinfo(char *);
 extern int get_pci_list(char*);
+extern int get_md_status (char *);
 #ifdef __SMP_PROF__
 extern int get_smp_prof_list(char *);
 #endif
@@ -1015,6 +1016,10 @@
 
 		case PROC_IOPORTS:
 			return get_ioport_list(page);
+#ifdef CONFIG_BLK_DEV_MD
+	        case PROC_MD:
+			return get_md_status(page);
+#endif
 #ifdef CONFIG_APM
 		case PROC_APM:
 			return apm_proc(page);
diff -u --recursive --new-file v1.3.68/linux/fs/super.c linux/fs/super.c
--- v1.3.68/linux/fs/super.c	Fri Feb 16 11:34:10 1996
+++ linux/fs/super.c	Mon Feb 26 12:39:10 1996
@@ -458,7 +458,7 @@
  * filesystems which don't use real block-devices.  -- jrs
  */
 
-static char unnamed_dev_in_use[256/8] = { 0, };
+static unsigned int unnamed_dev_in_use[256/32] = { 0, };
 
 kdev_t get_unnamed_dev(void)
 {
@@ -636,7 +636,10 @@
 		return -EBUSY;
 	}
 	vfsmnt = add_vfsmnt(dev, dev_name, dir_name);
-	vfsmnt->mnt_sb = sb;
+	if (vfsmnt) {
+		vfsmnt->mnt_sb = sb;
+		vfsmnt->mnt_flags = flags;
+	}
 	sb->s_covered = dir_i;
 	dir_i->i_mount = sb->s_mounted;
 	return 0;		/* we don't iput(dir_i) - see umount */
diff -u --recursive --new-file v1.3.68/linux/fs/sysv/file.c linux/fs/sysv/file.c
--- v1.3.68/linux/fs/sysv/file.c	Tue Feb 20 14:37:28 1996
+++ linux/fs/sysv/file.c	Sun Feb 25 11:17:58 1996
@@ -21,6 +21,7 @@
 #include <linux/stat.h>
 #include <linux/string.h>
 #include <linux/locks.h>
+#include <linux/pagemap.h>
 
 #include <asm/segment.h>
 
@@ -248,13 +249,14 @@
 		}
 		/* now either c==sb->sv_block_size or buffer_uptodate(bh) */
 		p = (pos & sb->sv_block_size_1) + bh->b_data;
+		memcpy_fromfs(p, buf, c);
+		update_vm_cache(inode, pos, p, c);
 		pos += c;
 		if (pos > inode->i_size) {
 			inode->i_size = pos;
 			inode->i_dirt = 1;
 		}
 		written += c;
-		memcpy_fromfs(p,buf,c);
 		buf += c;
 		mark_buffer_uptodate(bh, 1);
 		mark_buffer_dirty(bh, 0);
diff -u --recursive --new-file v1.3.68/linux/fs/sysv/inode.c linux/fs/sysv/inode.c
--- v1.3.68/linux/fs/sysv/inode.c	Tue Jan 23 21:15:51 1996
+++ linux/fs/sysv/inode.c	Sun Feb 25 11:17:59 1996
@@ -536,14 +536,15 @@
 {
 	struct statfs tmp;
 
-	tmp.f_type = sb->s_magic;
-	tmp.f_bsize = sb->sv_block_size;
-	tmp.f_blocks = sb->sv_ndatazones;
-	tmp.f_bfree = sysv_count_free_blocks(sb);
-	tmp.f_bavail = tmp.f_bfree;
-	tmp.f_files = sb->sv_ninodes;
-	tmp.f_ffree = sysv_count_free_inodes(sb);
+	tmp.f_type = sb->s_magic;			/* type of filesystem */
+	tmp.f_bsize = sb->sv_block_size;		/* block size */
+	tmp.f_blocks = sb->sv_ndatazones;		/* total data blocks in file system */
+	tmp.f_bfree = sysv_count_free_blocks(sb);	/* free blocks in fs */
+	tmp.f_bavail = tmp.f_bfree;			/* free blocks available to non-superuser */
+	tmp.f_files = sb->sv_ninodes;			/* total file nodes in file system */
+	tmp.f_ffree = sysv_count_free_inodes(sb);	/* free file nodes in fs */
 	tmp.f_namelen = SYSV_NAMELEN;
+	/* Don't know what value to put in tmp.f_fsid */ /* file system id */
 	memcpy_tofs(buf, &tmp, bufsiz);
 }
 
diff -u --recursive --new-file v1.3.68/linux/fs/umsdos/check.c linux/fs/umsdos/check.c
--- v1.3.68/linux/fs/umsdos/check.c	Thu Nov  9 11:23:52 1995
+++ linux/fs/umsdos/check.c	Sun Feb 25 11:17:59 1996
@@ -13,10 +13,9 @@
 #include <linux/types.h>
 #include <linux/ptrace.h>
 #include <linux/mman.h>
+#include <linux/mm.h>
 
 #include <asm/system.h>
-
-extern unsigned long high_memory;
 
 static int check_one_table(struct pde * page_dir)
 {
diff -u --recursive --new-file v1.3.68/linux/fs/umsdos/ioctl.c linux/fs/umsdos/ioctl.c
--- v1.3.68/linux/fs/umsdos/ioctl.c	Wed Feb  7 15:11:35 1996
+++ linux/fs/umsdos/ioctl.c	Sun Feb 25 11:17:59 1996
@@ -68,7 +68,7 @@
 		Well, not all case require write access, but it simplify the code
 		and let's face it, there is only one client (umssync) for all this
 	*/
-	if (err = verify_area(VERIFY_WRITE,(void*)data,sizeof(struct umsdos_ioctl)) < 0){
+	if ((err = verify_area(VERIFY_WRITE,(void*)data,sizeof(struct umsdos_ioctl))) < 0) {
 		ret = err;
 	}else if (current->euid == 0
 		|| cmd == UMSDOS_GETVERSION){
diff -u --recursive --new-file v1.3.68/linux/fs/umsdos/mangle.c linux/fs/umsdos/mangle.c
--- v1.3.68/linux/fs/umsdos/mangle.c	Thu Nov  9 11:23:52 1995
+++ linux/fs/umsdos/mangle.c	Sun Feb 25 11:17:59 1996
@@ -8,7 +8,6 @@
  */
 
 #include <linux/errno.h>
-#include <linux/ctype.h>
 #include <linux/string.h>
 #include <linux/kernel.h>
 #include <linux/umsdos_fs.h>
diff -u --recursive --new-file v1.3.68/linux/fs/xiafs/file.c linux/fs/xiafs/file.c
--- v1.3.68/linux/fs/xiafs/file.c	Tue Feb 20 14:37:28 1996
+++ linux/fs/xiafs/file.c	Sun Feb 25 11:17:58 1996
@@ -17,6 +17,7 @@
 #include <linux/fcntl.h>
 #include <linux/stat.h>
 #include <linux/locks.h>
+#include <linux/pagemap.h>
 
 #include <asm/segment.h>
 #include <asm/system.h>
@@ -234,13 +235,14 @@
 	    }
 	}
 	cp = (pos & (XIAFS_ZSIZE(inode->i_sb)-1)) + bh->b_data;
+	memcpy_fromfs(cp,buf,c);
+	update_vm_cache(inode,pos,cp,c);
 	pos += c;
 	if (pos > inode->i_size) {
 	    inode->i_size = pos;
 	    inode->i_dirt = 1;
 	}
 	written += c;
-	memcpy_fromfs(cp,buf,c);
 	buf += c;
 	mark_buffer_uptodate(bh, 1);
 	mark_buffer_dirty(bh, 0);
diff -u --recursive --new-file v1.3.68/linux/include/asm-i386/smp.h linux/include/asm-i386/smp.h
--- v1.3.68/linux/include/asm-i386/smp.h	Wed Feb  7 15:11:37 1996
+++ linux/include/asm-i386/smp.h	Mon Feb 26 11:58:16 1996
@@ -207,12 +207,12 @@
 
 extern __inline void apic_write(unsigned long reg, unsigned long v)
 {
-	*((unsigned long *)(apic_reg+reg))=v;
+	*((volatile unsigned long *)(apic_reg+reg))=v;
 }
 
 extern __inline unsigned long apic_read(unsigned long reg)
 {
-	return *((unsigned long *)(apic_reg+reg));
+	return *((volatile unsigned long *)(apic_reg+reg));
 }
 
 /*
diff -u --recursive --new-file v1.3.68/linux/include/asm-i386/string-486.h linux/include/asm-i386/string-486.h
--- v1.3.68/linux/include/asm-i386/string-486.h	Thu Jan  1 02:00:00 1970
+++ linux/include/asm-i386/string-486.h	Mon Feb 26 11:58:16 1996
@@ -0,0 +1,693 @@
+#ifndef _I386_STRING_I486_H_
+#define _I386_STRING_I486_H_
+
+/*
+ * This string-include defines all string functions as inline
+ * functions. Use gcc. It also assumes ds=es=data space, this should be
+ * normal. Most of the string-functions are rather heavily hand-optimized,
+ * see especially strtok,strstr,str[c]spn. They should work, but are not
+ * very easy to understand. Everything is done entirely within the register
+ * set, making the functions fast and clean. 
+ *
+ *		Copyright (C) 1991, 1992 Linus Torvalds
+ *		Revised and optimized for i486/pentium
+ *		1994/03/15 by Alberto Vignani/Davide Parodi @crf.it
+ *
+ *	Split into 2 CPU specific files by Alan Cox to keep #ifdef noise down.
+ */
+
+#define __HAVE_ARCH_STRCPY
+extern inline char * strcpy(char * dest,const char *src)
+{
+register char *tmp= (char *)dest;
+register char dummy;
+__asm__ __volatile__(
+	"\n1:\t"
+	"movb (%0),%2\n\t"
+	"incl %0\n\t"
+	"movb %2,(%1)\n\t"
+	"incl %1\n\t"
+	"testb %2,%2\n\t"
+	"jne 1b"
+	:"=r" (src), "=r" (tmp), "=q" (dummy)
+	:"0" (src), "1" (tmp)
+	:"memory");
+return dest;
+}
+
+#define __HAVE_ARCH_STRNCPY
+extern inline char * strncpy(char * dest,const char *src,size_t count)
+{
+register char *tmp= (char *)dest;
+register char dummy;
+if (count) {
+__asm__ __volatile__(
+	"\n1:\t"
+	"movb (%0),%2\n\t"
+	"incl %0\n\t"
+	"movb %2,(%1)\n\t"
+	"incl %1\n\t"
+	"decl %3\n\t"
+	"je 3f\n\t"
+	"testb %2,%2\n\t"
+	"jne 1b\n\t"
+	"2:\tmovb %2,(%1)\n\t"
+	"incl %1\n\t"
+	"decl %3\n\t"
+	"jne 2b\n\t"
+	"3:"
+	:"=r" (src), "=r" (tmp), "=q" (dummy), "=r" (count)
+	:"0" (src), "1" (tmp), "3" (count)
+	:"memory");
+    } /* if (count) */
+return dest;
+}
+
+#define __HAVE_ARCH_STRCAT
+extern inline char * strcat(char * dest,const char * src)
+{
+register char *tmp = (char *)(dest-1);
+register char dummy;
+__asm__ __volatile__(
+	"\n1:\tincl %1\n\t"
+	"cmpb $0,(%1)\n\t"
+	"jne 1b\n"
+	"2:\tmovb (%2),%b0\n\t"
+	"incl %2\n\t"
+	"movb %b0,(%1)\n\t"
+	"incl %1\n\t"
+	"testb %b0,%b0\n\t"
+	"jne 2b\n"
+	:"=q" (dummy), "=r" (tmp), "=r" (src)
+	:"1"  (tmp), "2"  (src)
+	:"memory");
+return dest;
+}
+
+#define __HAVE_ARCH_STRNCAT
+extern inline char * strncat(char * dest,const char * src,size_t count)
+{
+register char *tmp = (char *)(dest-1);
+register char dummy;
+__asm__ __volatile__(
+	"\n1:\tincl %1\n\t"
+	"cmpb $0,(%1)\n\t"
+	"jne 1b\n"
+	"2:\tdecl %3\n\t"
+	"js 3f\n\t"
+	"movb (%2),%b0\n\t"
+	"incl %2\n\t"
+	"movb %b0,(%1)\n\t"
+	"incl %1\n\t"
+	"testb %b0,%b0\n\t"
+	"jne 2b\n"
+	"3:\txorl %0,%0\n\t"
+	"movb %b0,(%1)\n\t"
+	:"=q" (dummy), "=r" (tmp), "=r" (src), "=r" (count)
+	:"1"  (tmp), "2"  (src), "3"  (count)
+	:"memory");
+return dest;
+}
+
+#define __HAVE_ARCH_STRCMP
+extern inline int strcmp(const char * cs,const char * ct)
+{
+register int __res;
+__asm__ __volatile__(
+	"\n1:\tmovb (%1),%b0\n\t"
+	"incl %1\n\t"
+	"cmpb %b0,(%2)\n\t"
+	"jne 2f\n\t"
+	"incl %2\n\t"
+	"testb %b0,%b0\n\t"
+	"jne 1b\n\t"
+	"xorl %0,%0\n\t"
+	"jmp 3f\n"
+	"2:\tmovl $1,%0\n\t"
+	"jb 3f\n\t"
+	"negl %0\n"
+	"3:"
+	:"=q" (__res), "=r" (cs), "=r" (ct)
+	:"1" (cs), "2" (ct)
+	: "memory" );
+return __res;
+}
+
+#define __HAVE_ARCH_STRNCMP
+extern inline int strncmp(const char * cs,const char * ct,size_t count)
+{
+register int __res;
+__asm__ __volatile__(
+	"\n1:\tdecl %3\n\t"
+	"js 2f\n\t"
+	"movb (%1),%b0\n\t"
+	"incl %1\n\t"
+	"cmpb %b0,(%2)\n\t"
+	"jne 3f\n\t"
+	"incl %2\n\t"
+	"testb %b0,%b0\n\t"
+	"jne 1b\n"
+	"2:\txorl %0,%0\n\t"
+	"jmp 4f\n"
+	"3:\tmovl $1,%0\n\t"
+	"jb 4f\n\t"
+	"negl %0\n"
+	"4:"
+	:"=q" (__res), "=r" (cs), "=r" (ct), "=r" (count)
+	:"1"  (cs), "2"  (ct),  "3" (count));
+return __res;
+}
+
+#define __HAVE_ARCH_STRCHR
+extern inline char * strchr(const char * s, int c)
+{
+register char * __res;
+__asm__ __volatile__(
+	"movb %%al,%%ah\n"
+	"1:\tmovb (%1),%%al\n\t"
+	"cmpb %%ah,%%al\n\t"
+	"je 2f\n\t"
+	"incl %1\n\t"
+	"testb %%al,%%al\n\t"
+	"jne 1b\n\t"
+	"xorl %1,%1\n"
+	"2:\tmovl %1,%0\n\t"
+	:"=a" (__res), "=r" (s)
+	:"0" (c),      "1"  (s));
+return __res;
+}
+
+#define __HAVE_ARCH_STRRCHR
+extern inline char * strrchr(const char * s, int c)
+{
+register char * __res;
+__asm__ __volatile__(
+	"cld\n\t"
+	"movb %%al,%%ah\n"
+	"1:\tlodsb\n\t"
+	"cmpb %%ah,%%al\n\t"
+	"jne 2f\n\t"
+	"leal -1(%%esi),%0\n"
+	"2:\ttestb %%al,%%al\n\t"
+	"jne 1b"
+	:"=d" (__res):"0" (0),"S" (s),"a" (c):"ax","si");
+return __res;
+}
+
+#define __HAVE_ARCH_STRSPN
+extern inline size_t strspn(const char * cs, const char * ct)
+{
+register char * __res;
+__asm__ __volatile__(
+	"cld\n\t"
+	"movl %4,%%edi\n\t"
+	"repne\n\t"
+	"scasb\n\t"
+	"notl %%ecx\n\t"
+	"decl %%ecx\n\t"
+	"movl %%ecx,%%edx\n"
+	"1:\tlodsb\n\t"
+	"testb %%al,%%al\n\t"
+	"je 2f\n\t"
+	"movl %4,%%edi\n\t"
+	"movl %%edx,%%ecx\n\t"
+	"repne\n\t"
+	"scasb\n\t"
+	"je 1b\n"
+	"2:\tdecl %0"
+	:"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct)
+	:"ax","cx","dx","di");
+return __res-cs;
+}
+
+#define __HAVE_ARCH_STRCSPN
+extern inline size_t strcspn(const char * cs, const char * ct)
+{
+register char * __res;
+__asm__ __volatile__(
+	"cld\n\t"
+	"movl %4,%%edi\n\t"
+	"repne\n\t"
+	"scasb\n\t"
+	"notl %%ecx\n\t"
+	"decl %%ecx\n\t"
+	"movl %%ecx,%%edx\n"
+	"1:\tlodsb\n\t"
+	"testb %%al,%%al\n\t"
+	"je 2f\n\t"
+	"movl %4,%%edi\n\t"
+	"movl %%edx,%%ecx\n\t"
+	"repne\n\t"
+	"scasb\n\t"
+	"jne 1b\n"
+	"2:\tdecl %0"
+	:"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct)
+	:"ax","cx","dx","di");
+return __res-cs;
+}
+
+#define __HAVE_ARCH_STRPBRK
+extern inline char * strpbrk(const char * cs,const char * ct)
+{
+register char * __res;
+__asm__ __volatile__(
+	"cld\n\t"
+	"movl %4,%%edi\n\t"
+	"repne\n\t"
+	"scasb\n\t"
+	"notl %%ecx\n\t"
+	"decl %%ecx\n\t"
+	"movl %%ecx,%%edx\n"
+	"1:\tlodsb\n\t"
+	"testb %%al,%%al\n\t"
+	"je 2f\n\t"
+	"movl %4,%%edi\n\t"
+	"movl %%edx,%%ecx\n\t"
+	"repne\n\t"
+	"scasb\n\t"
+	"jne 1b\n\t"
+	"decl %0\n\t"
+	"jmp 3f\n"
+	"2:\txorl %0,%0\n"
+	"3:"
+	:"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct)
+	:"ax","cx","dx","di");
+return __res;
+}
+
+#define __HAVE_ARCH_STRSTR
+extern inline char * strstr(const char * cs,const char * ct)
+{
+register char * __res;
+__asm__ __volatile__(
+	"cld\n\t" \
+	"movl %4,%%edi\n\t"
+	"repne\n\t"
+	"scasb\n\t"
+	"notl %%ecx\n\t"
+	"decl %%ecx\n\t"	/* NOTE! This also sets Z if searchstring='' */
+	"movl %%ecx,%%edx\n"
+	"1:\tmovl %4,%%edi\n\t"
+	"movl %%esi,%%eax\n\t"
+	"movl %%edx,%%ecx\n\t"
+	"repe\n\t"
+	"cmpsb\n\t"
+	"je 2f\n\t"		/* also works for empty string, see above */
+	"xchgl %%eax,%%esi\n\t"
+	"incl %%esi\n\t"
+	"cmpb $0,-1(%%eax)\n\t"
+	"jne 1b\n\t"
+	"xorl %%eax,%%eax\n\t"
+	"2:"
+	:"=a" (__res):"0" (0),"c" (0xffffffff),"S" (cs),"g" (ct)
+	:"cx","dx","di","si");
+return __res;
+}
+
+#define __HAVE_ARCH_STRLEN
+extern inline size_t strlen(const char * s)
+{
+/*
+ * slightly slower on a 486, but with better chances of
+ * register allocation
+ */
+register char dummy, *tmp= (char *)s;
+__asm__ __volatile__(
+	"\n1:\t"
+	"movb\t(%0),%1\n\t"
+	"incl\t%0\n\t"
+	"testb\t%1,%1\n\t"
+	"jne\t1b"
+	:"=r" (tmp),"=q" (dummy)
+	:"0" (s)
+	: "memory" );
+return (tmp-s-1);
+}
+
+/* Added by Gertjan van Wingerde to make minix and sysv module work */
+#define __HAVE_ARCH_STRNLEN
+extern inline size_t strnlen(const char * s, size_t count)
+{
+register int __res;
+__asm__ __volatile__(
+	"movl %1,%0\n\t"
+	"jmp 2f\n"
+	"1:\tcmpb $0,(%0)\n\t"
+	"je 3f\n\t"
+	"incl %0\n"
+	"2:\tdecl %2\n\t"
+	"cmpl $-1,%2\n\t"
+	"jne 1b\n"
+	"3:\tsubl %1,%0"
+	:"=a" (__res):"c" (s),"d" (count));
+return __res;
+}
+/* end of additional stuff */
+
+#define __HAVE_ARCH_STRTOK
+extern inline char * strtok(char * s,const char * ct)
+{
+register char * __res;
+__asm__ __volatile__(
+	"testl %1,%1\n\t"
+	"jne 1f\n\t"
+	"testl %0,%0\n\t"
+	"je 8f\n\t"
+	"movl %0,%1\n"
+	"1:\txorl %0,%0\n\t"
+	"movl $-1,%%ecx\n\t"
+	"xorl %%eax,%%eax\n\t"
+	"cld\n\t"
+	"movl %4,%%edi\n\t"
+	"repne\n\t"
+	"scasb\n\t"
+	"notl %%ecx\n\t"
+	"decl %%ecx\n\t"
+	"je 7f\n\t"			/* empty delimiter-string */
+	"movl %%ecx,%%edx\n"
+	"2:\tlodsb\n\t"
+	"testb %%al,%%al\n\t"
+	"je 7f\n\t"
+	"movl %4,%%edi\n\t"
+	"movl %%edx,%%ecx\n\t"
+	"repne\n\t"
+	"scasb\n\t"
+	"je 2b\n\t"
+	"decl %1\n\t"
+	"cmpb $0,(%1)\n\t"
+	"je 7f\n\t"
+	"movl %1,%0\n"
+	"3:\tlodsb\n\t"
+	"testb %%al,%%al\n\t"
+	"je 5f\n\t"
+	"movl %4,%%edi\n\t"
+	"movl %%edx,%%ecx\n\t"
+	"repne\n\t"
+	"scasb\n\t"
+	"jne 3b\n\t"
+	"decl %1\n\t"
+	"cmpb $0,(%1)\n\t"
+	"je 5f\n\t"
+	"movb $0,(%1)\n\t"
+	"incl %1\n\t"
+	"jmp 6f\n"
+	"5:\txorl %1,%1\n"
+	"6:\tcmpb $0,(%0)\n\t"
+	"jne 7f\n\t"
+	"xorl %0,%0\n"
+	"7:\ttestl %0,%0\n\t"
+	"jne 8f\n\t"
+	"movl %0,%1\n"
+	"8:"
+	:"=b" (__res),"=S" (___strtok)
+	:"0" (___strtok),"1" (s),"g" (ct)
+	:"ax","cx","dx","di","memory");
+return __res;
+}
+
+#define __memcpy_c(d,s,count) \
+((count%4==0) ? \
+ __memcpy_by4((d),(s),(count)) : \
+ ((count%2==0) ? \
+  __memcpy_by2((d),(s),(count)) : \
+  __memcpy_g((d),(s),(count))))
+
+#define __HAVE_ARCH_MEMCPY
+#define memcpy(d,s,count) \
+(__builtin_constant_p(count) ? \
+ __memcpy_c((d),(s),(count)) : \
+ __memcpy_g((d),(s),(count)))
+
+/*
+ *	These ought to get tweaked to do some cache priming.
+ */
+ 
+extern inline void * __memcpy_by4(void * to, const void * from, size_t n)
+{
+register void *tmp = (void *)to;
+register int dummy1,dummy2;
+__asm__ __volatile__ (
+	"\n1:\tmovl (%2),%0\n\t"
+	"addl $4,%2\n\t"
+	"movl %0,(%1)\n\t"
+	"addl $4,%1\n\t"
+	"decl %3\n\t"
+	"jnz 1b"
+	:"=r" (dummy1), "=r" (tmp), "=r" (from), "=r" (dummy2) 
+	:"1" (tmp), "2" (from), "3" (n/4)
+	:"memory");
+return (to);
+}
+
+extern inline void * __memcpy_by2(void * to, const void * from, size_t n)
+{
+register void *tmp = (void *)to;
+register int dummy1,dummy2;
+__asm__ __volatile__ (
+	"shrl $1,%3\n\t"
+	"jz 2f\n"                 /* only a word */
+	"1:\tmovl (%2),%0\n\t"
+	"addl $4,%2\n\t"
+	"movl %0,(%1)\n\t"
+	"addl $4,%1\n\t"
+	"decl %3\n\t"
+	"jnz 1b\n"
+	"2:\tmovw (%2),%w0\n\t"
+	"movw %w0,(%1)"
+	:"=r" (dummy1), "=r" (tmp), "=r" (from), "=r" (dummy2) 
+	:"1" (tmp), "2" (from), "3" (n/2)
+	:"memory");
+return (to);
+}
+
+extern inline void * __memcpy_g(void * to, const void * from, size_t n)
+{
+register void *tmp = (void *)to;
+__asm__ __volatile__ (
+	"cld\n\t"
+	"shrl $1,%%ecx\n\t"
+	"jnc 1f\n\t"
+	"movsb\n"
+	"1:\tshrl $1,%%ecx\n\t"
+	"jnc 2f\n\t"
+	"movsw\n"
+	"2:\trep\n\t"
+	"movsl"
+	: /* no output */
+	:"c" (n),"D" ((long) tmp),"S" ((long) from)
+	:"cx","di","si","memory");
+return (to);
+}
+
+
+#define __HAVE_ARCH_MEMMOVE
+extern inline void * memmove(void * dest,const void * src, size_t n)
+{
+register void *tmp = (void *)dest;
+if (dest<src)
+__asm__ __volatile__ (
+	"cld\n\t"
+	"rep\n\t"
+	"movsb"
+	: /* no output */
+	:"c" (n),"S" (src),"D" (tmp)
+	:"cx","si","di");
+else
+__asm__ __volatile__ (
+	"std\n\t"
+	"rep\n\t"
+	"movsb\n\t"
+	: /* no output */
+	:"c" (n), "S" (n-1+(const char *)src), "D" (n-1+(char *)tmp)
+	:"cx","si","di","memory");
+return dest;
+}
+
+extern inline int memcmp(const void * cs,const void * ct,size_t count)
+{
+register int __res;
+__asm__ __volatile__(
+	"cld\n\t"
+	"repe\n\t"
+	"cmpsb\n\t"
+	"je 1f\n\t"
+	"sbbl %0,%0\n\t"
+	"orb $1,%b0\n"
+	"1:"
+	:"=abd" (__res):"0" (0),"S" (cs),"D" (ct),"c" (count)
+	:"si","di","cx");
+return __res;
+}
+
+#define __HAVE_ARCH_MEMCHR
+extern inline void * memchr(const void * cs,int c,size_t count)
+{
+register void * __res;
+if (!count)
+	return NULL;
+__asm__ __volatile__(
+	"cld\n\t"
+	"repne\n\t"
+	"scasb\n\t"
+	"je 1f\n\t"
+	"movl $1,%0\n"
+	"1:\tdecl %0"
+	:"=D" (__res):"a" (c),"D" (cs),"c" (count)
+	:"cx");
+return __res;
+}
+
+#define __memset_cc(s,c,count) \
+((count%4==0) ? \
+ __memset_cc_by4((s),(c),(count)) : \
+ ((count%2==0) ? \
+  __memset_cc_by2((s),(c),(count)) : \
+  __memset_cg((s),(c),(count))))
+
+#define __memset_gc(s,c,count) \
+((count%4==0) ? \
+ __memset_gc_by4((s),(c),(count)) : \
+ ((count%2==0) ? \
+  __memset_gc_by2((s),(c),(count)) : \
+  __memset_gg((s),(c),(count))))
+
+#define __HAVE_ARCH_MEMSET
+#define memset(s,c,count) \
+(__builtin_constant_p(c) ? \
+ (__builtin_constant_p(count) ? \
+  __memset_cc((s),(c),(count)) : \
+  __memset_cg((s),(c),(count))) : \
+ (__builtin_constant_p(count) ? \
+  __memset_gc((s),(c),(count)) : \
+  __memset_gg((s),(c),(count))))
+
+extern inline void * __memset_cc_by4(void * s, char c, size_t count)
+{
+/*
+ * register char *tmp = s;
+ */
+register char *tmp = (char *)s;
+register int  dummy;
+__asm__ __volatile__ (
+	"\n1:\tmovl %2,(%0)\n\t"
+	"addl $4,%0\n\t"
+	"decl %1\n\t"
+	"jnz 1b"
+	:"=r" (tmp), "=r" (dummy)
+	:"q" (0x01010101UL * (unsigned char) c), "0" (tmp), "1" (count/4)
+	:"memory");
+return s;
+}
+
+extern inline void * __memset_cc_by2(void * s, char c, size_t count)
+{
+register void *tmp = (void *)s;
+register int  dummy;
+__asm__ __volatile__ (
+	"shrl $1,%1\n\t"          /* may be divisible also by 4 */
+	"jz 2f\n"
+	"\n1:\tmovl %2,(%0)\n\t"
+	"addl $4,%0\n\t"
+	"decl %1\n\t"
+	"jnz 1b\n"
+	"2:\tmovw %w2,(%0)"
+	:"=r" (tmp), "=r" (dummy)
+	:"q" (0x01010101UL * (unsigned char) c), "0" (tmp), "1" (count/2)
+	:"memory");
+return s;
+}
+
+extern inline void * __memset_gc_by4(void * s, char c, size_t count)
+{
+register void *tmp = (void *)s;
+register int dummy;
+__asm__ __volatile__ (
+	"movb %b0,%h0\n"
+	"pushw %w0\n\t"
+	"shll $16,%0\n\t"
+	"popw %w0\n"
+	"1:\tmovl %0,(%1)\n\t"
+	"addl $4,%1\n\t"
+	"decl %2\n\t"
+	"jnz 1b\n"
+	:"=q" (c), "=r" (tmp), "=r" (dummy)
+	:"0" ((unsigned) c),  "1"  (tmp), "2" (count/4)
+	:"memory");
+return s;
+}
+
+extern inline void * __memset_gc_by2(void * s, char c, size_t count)
+{
+register void *tmp = (void *)s;
+register int dummy1,dummy2;
+__asm__ __volatile__ (
+	"movb %b0,%h0\n\t"
+	"shrl $1,%2\n\t"          /* may be divisible also by 4 */
+	"jz 2f\n\t"
+	"pushw %w0\n\t"
+	"shll $16,%0\n\t"
+	"popw %w0\n"
+	"1:\tmovl %0,(%1)\n\t"
+	"addl $4,%1\n\t"
+	"decl %2\n\t"
+	"jnz 1b\n"
+	"2:\tmovw %w0,(%1)"
+	:"=q" (dummy1), "=r" (tmp), "=r" (dummy2)
+	:"0" ((unsigned) c),  "1"  (tmp), "2" (count/2)
+	:"memory");
+return s;
+}
+
+extern inline void * __memset_cg(void * s, char c, size_t count)
+{
+register void *tmp = (void *)s;
+__asm__ __volatile__ (
+	"shrl $1,%%ecx\n\t"
+	"rep\n\t"
+	"stosw\n\t"
+	"jnc 1f\n\t"
+	"movb %%al,(%%edi)\n"
+	"1:"
+	: /* no output */
+	:"c" (count),"D" (tmp), "a" (0x0101U * (unsigned char) c)
+	:"cx","di","memory");
+return s;
+}
+
+extern inline void * __memset_gg(void * s,char c,size_t count)
+{
+register void *tmp = (void *)s;
+__asm__ __volatile__ (
+	"movb %%al,%%ah\n\t"
+	"shrl $1,%%ecx\n\t"
+	"rep\n\t"
+	"stosw\n\t"
+	"jnc 1f\n\t"
+	"movb %%al,(%%edi)\n"
+	"1:"
+	: /* no output */
+	:"c" (count),"D" (tmp), "a" (c)
+	:"cx","di","memory");
+return s;
+}
+
+
+/*
+ * find the first occurrence of byte 'c', or 1 past the area if none
+ */
+#define __HAVE_ARCH_MEMSCAN
+extern inline void * memscan(void * addr, int c, size_t size)
+{
+	if (!size)
+		return addr;
+	__asm__("cld
+		repnz; scasb
+		jnz 1f
+		dec %%edi
+1:		"
+		: "=D" (addr), "=c" (size)
+		: "0" (addr), "1" (size), "a" (c));
+	return addr;
+}
+
+#endif
diff -u --recursive --new-file v1.3.68/linux/include/asm-i386/string.h linux/include/asm-i386/string.h
--- v1.3.68/linux/include/asm-i386/string.h	Mon Aug 28 14:52:22 1995
+++ linux/include/asm-i386/string.h	Mon Feb 26 12:25:57 1996
@@ -2,6 +2,16 @@
 #define _I386_STRING_H_
 
 /*
+ * On a 486 or Pentium, we are better off not using the
+ * byte string operations. But on a 386 or a PPro the
+ * byte string ops are faster than doing it by hand
+ * (MUCH faster on a Pentium).
+ */
+#if CPU == 486 || CPU == 586
+#include <asm/string-486.h>
+#else
+
+/*
  * This string-include defines all string functions as inline
  * functions. Use gcc. It also assumes ds=es=data space, this should be
  * normal. Most of the string-functions are rather heavily hand-optimized,
@@ -590,4 +600,5 @@
 	return addr;
 }
 
+#endif
 #endif
diff -u --recursive --new-file v1.3.68/linux/include/linux/blk.h linux/include/linux/blk.h
--- v1.3.68/linux/include/linux/blk.h	Wed Feb 14 14:37:18 1996
+++ linux/include/linux/blk.h	Tue Feb 27 13:36:27 1996
@@ -6,6 +6,13 @@
 #include <linux/config.h>
 
 /*
+ * NR_REQUEST is the number of entries in the request-queue.
+ * NOTE that writes may use only the low 2/3 of these: reads
+ * take precedence.
+ */
+#define NR_REQUEST	64
+
+/*
  * This is used in the elevator algorithm.  We don't prioritise reads
  * over writes any more --- although reads are more time-critical than
  * writes, by treating them equally we increase filesystem throughput.
@@ -19,7 +26,7 @@
  * These will have to be changed to be aware of different buffer
  * sizes etc.. It actually needs a major cleanup.
  */
-#ifdef IDE_DRIVER
+#if defined(IDE_DRIVER) || defined(MD_DRIVER)
 #define SECTOR_MASK ((BLOCK_SIZE >> 9) - 1)
 #else
 #define SECTOR_MASK (blksize_size[MAJOR_NR] &&     \
@@ -72,6 +79,12 @@
 #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
 
 extern void set_device_ro(kdev_t dev,int flag);
 void add_blkdev_randomness(int major);
@@ -144,6 +157,19 @@
 #define DEVICE_ON(device)
 #define DEVICE_OFF(device)
 
+/* Kludge to use the same number for both char and block major numbers */
+#elif  (MAJOR_NR == MD_MAJOR) && defined(MD_DRIVER)
+
+#ifndef MD_PERSONALITY
+
+#define DEVICE_NAME "Multiple devices driver"
+#define DEVICE_REQUEST do_md_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#endif
+
 #elif (MAJOR_NR == SCSI_TAPE_MAJOR)
 
 #define DEVICE_NAME "scsitape"
@@ -277,7 +303,7 @@
 
 #endif /* MAJOR_NR == whatever */
 
-#if (MAJOR_NR != SCSI_TAPE_MAJOR) && !defined(IDE_DRIVER)
+#if ((MAJOR_NR != SCSI_TAPE_MAJOR) && !defined(IDE_DRIVER) && !defined(MD_DRIVER))
 
 #ifndef CURRENT
 #define CURRENT (blk_dev[MAJOR_NR].current_request)
@@ -309,8 +335,10 @@
 
 #endif /* DEVICE_TIMEOUT */
 
+#ifndef MD_PERSONALITY
 static void (DEVICE_REQUEST)(void);
-
+#endif
+  
 #ifdef DEVICE_INTR
 #define CLEAR_INTR SET_INTR(NULL)
 #else
@@ -334,7 +362,7 @@
 /* end_request() - SCSI devices have their own version */
 /*               - IDE drivers have their own copy too */
 
-#if ! SCSI_MAJOR(MAJOR_NR)
+#if ! SCSI_MAJOR(MAJOR_NR) || (defined(MD_DRIVER) && !defined(MD_PERSONALITY))
 
 #if defined(IDE_DRIVER) && !defined(_IDE_C) /* shared copy for IDE modules */
 void ide_end_request(byte uptodate, ide_hwgroup_t *hwgroup);
@@ -343,6 +371,8 @@
 #ifdef IDE_DRIVER
 void ide_end_request(byte uptodate, ide_hwgroup_t *hwgroup) {
 	struct request *req = hwgroup->rq;
+#elif defined(MD_DRIVER)
+static void end_request (int uptodate, struct request * req) {
 #else
 static void end_request(int uptodate) {
 	struct request *req = CURRENT;
@@ -380,7 +410,7 @@
 #ifdef IDE_DRIVER
 	blk_dev[MAJOR(req->rq_dev)].current_request = req->next;
 	hwgroup->rq = NULL;
-#else
+#elif !defined(MD_DRIVER)
 	DEVICE_OFF(req->rq_dev);
 	CURRENT = req->next;
 #endif /* IDE_DRIVER */
@@ -391,6 +421,36 @@
 }
 #endif /* defined(IDE_DRIVER) && !defined(_IDE_C) */
 #endif /* ! SCSI_MAJOR(MAJOR_NR) */
+
+#ifdef MD_PERSONALITY
+extern inline void end_redirect (struct request *req)
+{
+  struct buffer_head * bh;
+
+  req->errors = 0;
+  
+  if ((bh = req->bh) != NULL)
+  {
+    req->bh = bh->b_reqnext;
+    bh->b_reqnext = NULL;
+    
+    if ((bh = req->bh) != NULL)
+    {
+      req->sector += req->current_nr_sectors;
+      req->current_nr_sectors = bh->b_size >> 9;
+      
+      if (req->nr_sectors < req->current_nr_sectors)
+      {
+	req->nr_sectors = req->current_nr_sectors;
+	printk("end_redirect : buffer-list destroyed\n");
+      }
+      
+      req->buffer = bh->b_data;
+      return;
+    }
+  }
+}
+#endif /* MD_PERSONALITY */
 
 #endif /* defined(MAJOR_NR) || defined(IDE_DRIVER) */
 
diff -u --recursive --new-file v1.3.68/linux/include/linux/blkdev.h linux/include/linux/blkdev.h
--- v1.3.68/linux/include/linux/blkdev.h	Mon Sep 18 14:54:09 1995
+++ linux/include/linux/blkdev.h	Tue Feb 27 13:35:21 1996
@@ -47,6 +47,10 @@
 extern struct wait_queue * wait_for_request;
 extern void resetup_one_dev(struct gendisk *dev, int drive);
 
+/* md needs those functions to requeue requests */
+extern void add_request(struct blk_dev_struct * dev, struct request * req);
+extern struct request *get_md_request (int max_req, kdev_t dev);
+
 extern int * blk_size[MAX_BLKDEV];
 
 extern int * blksize_size[MAX_BLKDEV];
diff -u --recursive --new-file v1.3.68/linux/include/linux/cdrom.h linux/include/linux/cdrom.h
--- v1.3.68/linux/include/linux/cdrom.h	Tue Nov 21 13:22:13 1995
+++ linux/include/linux/cdrom.h	Fri Feb 23 09:39:34 1996
@@ -27,6 +27,8 @@
 /* most drives don't deliver everything: */
 #define CD_FRAMESIZE_RAW1 (CD_FRAMESIZE_RAW-CD_SYNC_SIZE) /* 2340 */
 #define CD_FRAMESIZE_RAW0 (CD_FRAMESIZE_RAW-CD_SYNC_SIZE-CD_HEAD_SIZE) /* 2336 */
+/* Optics drive also has a 'read all' mode: */
+#define CD_FRAMESIZE_RAWER 2646 /* bytes per frame */
 
 #define CD_EDC_SIZE         4 /* bytes EDC per most raw data frame types */
 #define CD_ZERO_SIZE        8 /* bytes zero per yellow book mode 1 frame */
@@ -96,22 +98,26 @@
 	u_char	cdth_trk1;	/* end track */
 };
 
+struct cdrom_msf0		/* address in MSF format */
+{
+	u_char	minute;
+	u_char	second;
+	u_char	frame;
+};
+
+union cdrom_addr		/* address in either MSF or logical format */
+{
+	struct cdrom_msf0	msf;
+	int			lba;
+};
+
 struct cdrom_tocentry 
 {
 	u_char	cdte_track;
 	u_char	cdte_adr	:4;
 	u_char	cdte_ctrl	:4;
 	u_char	cdte_format;
-	union
-	{
-		struct
-		{
-			u_char	minute;
-			u_char	second;
-			u_char	frame;
-		} msf;
-		int	lba;
-	} cdte_addr;
+	union cdrom_addr cdte_addr;
 	u_char	cdte_datamode;
 };
 
@@ -139,26 +145,8 @@
 	u_char	cdsc_ctrl:	4;
 	u_char	cdsc_trk;
 	u_char	cdsc_ind;
-	union
-	{
-		struct 			
-		{
-			u_char	minute;
-			u_char	second;
-			u_char	frame;
-		} msf;
-		int	lba;
-	} cdsc_absaddr;
-	union 
-	{
-		struct 
-		{
-			u_char	minute;
-			u_char	second;
-			u_char	frame;
-		} msf;
-		int	lba;
-	} cdsc_reladdr;
+	union cdrom_addr cdsc_absaddr;
+	union cdrom_addr cdsc_reladdr;
 };
 
 /*
@@ -192,16 +180,7 @@
  */
 struct cdrom_read_audio
 {
-	union
-	{
-		struct 			
-		{
-			u_char minute;
-			u_char second;
-			u_char frame;
-		} msf;
-		int	lba;
-	} addr; /* frame address */
+	union cdrom_addr addr; /* frame address */
 	u_char addr_format; /* CDROM_LBA or CDROM_MSF */
 	int nframes; /* number of 2352-byte-frames to read at once, limited by the drivers */
 	u_char *buf; /* frame buffer (size: nframes*2352 bytes) */
@@ -214,16 +193,7 @@
  */
 struct cdrom_multisession
 {
-	union
-	{
-		struct 			
-		{
-			u_char minute;
-			u_char second;
-			u_char frame;
-		} msf;
-		int lba;
-	} addr; /* frame address: start-of-last-session (not the new "frame 16"!)*/
+	union cdrom_addr addr; /* frame address: start-of-last-session (not the new "frame 16"!)*/
 	u_char xa_flag; /* 1: "is XA disk" */
 	u_char addr_format; /* CDROM_LBA or CDROM_MSF */
 };
@@ -286,7 +256,7 @@
 					/* (struct cdrom_volctrl) */
 
 /* 
- * these ioctls are used in aztcd.c
+ * these ioctls are used in aztcd.c and optcd.c
  */
 #define CDROMREADRAW		0x5314	/* read data in raw mode */
 #define CDROMREADCOOKED		0x5315	/* read data in cooked mode */
@@ -296,6 +266,12 @@
  * for playing audio in logical block addressing mode
  */
 #define CDROMPLAYBLK		0x5317	/* (struct cdrom_blk) */
+
+/* 
+ * these ioctls are used in optcd.c
+ */
+#define CDROMREADALL		0x5318	/* read all 2646 bytes */
+#define CDROMCLOSETRAY		0x5319	/* pendant of CDROMEJECT */
 
 
 /*
diff -u --recursive --new-file v1.3.68/linux/include/linux/fs.h linux/include/linux/fs.h
--- v1.3.68/linux/include/linux/fs.h	Fri Feb 23 13:54:38 1996
+++ linux/include/linux/fs.h	Tue Feb 27 13:35:04 1996
@@ -154,6 +154,7 @@
 	/* First cache line: */
 	unsigned long b_blocknr;	/* block number */
 	kdev_t b_dev;			/* device (B_FREE = free) */
+	kdev_t b_rdev;		        /* Real device */
 	struct buffer_head * b_next;	/* Hash queue list */
 	struct buffer_head * b_this_page;	/* circular list of buffers in one page */
 
diff -u --recursive --new-file v1.3.68/linux/include/linux/isdn.h linux/include/linux/isdn.h
--- v1.3.68/linux/include/linux/isdn.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/isdn.h	Mon Feb 26 11:58:16 1996
@@ -0,0 +1,566 @@
+/* $Id: isdn.h,v 1.2 1996/02/11 02:10:02 fritz Exp fritz $
+ *
+ * Main header for the Linux ISDN subsystem (linklevel).
+ *
+ * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
+ * Copyright 1995,96    by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * 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.h,v $
+ * Revision 1.2  1996/02/11 02:10:02  fritz
+ * Changed IOCTL-names
+ * Added rx_netdev, st_netdev, first_skb, org_hcb, and org_hcu to
+ * Netdevice-local struct.
+ *
+ * Revision 1.1  1996/01/10 20:55:07  fritz
+ * Initial revision
+ *
+ */
+
+#ifndef isdn_h
+#define isdn_h
+
+#include <linux/ioctl.h>
+
+#define ISDN_TTY_MAJOR    43
+#define ISDN_TTYAUX_MAJOR 44
+#define ISDN_MAJOR        45
+
+/* The minor-devicenumbers for Channel 0 and 1 are used as arguments for
+ * physical Channel-Mapping, so they MUST NOT be changed without changing
+ * the correspondent code in isdn.c
+ */
+
+#define ISDN_MAX_DRIVERS    16
+#define ISDN_MAX_CHANNELS   16
+#define ISDN_MINOR_B        0
+#define ISDN_MINOR_BMAX     (ISDN_MAX_CHANNELS-1)
+#define ISDN_MINOR_CTRL     ISDN_MAX_CHANNELS
+#define ISDN_MINOR_CTRLMAX  (2*ISDN_MAX_CHANNELS-1)
+#define ISDN_MINOR_PPP      (2*ISDN_MAX_CHANNELS)
+#define ISDN_MINOR_PPPMAX   (3*ISDN_MAX_CHANNELS-1)
+#define ISDN_MINOR_STATUS   128
+
+/* New ioctl-codes */
+#define IIOCNETAIF  _IO('I',1)
+#define IIOCNETDIF  _IO('I',2)
+#define IIOCNETSCF  _IO('I',3)
+#define IIOCNETGCF  _IO('I',4)
+#define IIOCNETANM  _IO('I',5)
+#define IIOCNETDNM  _IO('I',6)
+#define IIOCNETGNM  _IO('I',7)
+#define IIOCGETSET  _IO('I',8)
+#define IIOCSETSET  _IO('I',9)
+#define IIOCSETVER  _IO('I',10)
+#define IIOCNETHUP  _IO('I',11)
+#define IIOCSETGST  _IO('I',12)
+#define IIOCSETBRJ  _IO('I',13)
+#define IIOCSIGPRF  _IO('I',14)
+#define IIOCGETPRF  _IO('I',15)
+#define IIOCSETPRF  _IO('I',16)
+#define IIOCGETMAP  _IO('I',17)
+#define IIOCSETMAP  _IO('I',18)
+#define IIOCNETASL  _IO('I',19)
+#define IIOCNETDIL  _IO('I',20)
+
+#define IIOCNETALN  _IO('I',32)
+#define IIOCNETDLN  _IO('I',33)
+
+#define IIOCDBGVAR  _IO('I',127)
+
+#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
+
+/* Facility which currently uses an ISDN-channel */
+#define ISDN_USAGE_NONE       0
+#define ISDN_USAGE_RAW        1
+#define ISDN_USAGE_MODEM      2
+#define ISDN_USAGE_NET        3
+#define ISDN_USAGE_VOICE      4
+#define ISDN_USAGE_FAX        5
+#define ISDN_USAGE_MASK       7 /* Mask to get plain usage */
+#define ISDN_USAGE_EXCLUSIVE 64 /* This bit is set, if channel is exclusive */
+#define ISDN_USAGE_OUTGOING 128 /* This bit is set, if channel is outgoing  */
+
+#define ISDN_MODEM_ANZREG    20        /* Number of Medem-Registers        */
+#define ISDN_MSNLEN          20
+
+typedef struct {
+  char drvid[25];
+  unsigned long arg;
+} isdn_ioctl_struct;
+
+typedef struct {
+  unsigned long isdndev;
+  unsigned long atmodem[ISDN_MAX_CHANNELS];
+  unsigned long info[ISDN_MAX_CHANNELS];
+} debugvar_addr;
+
+typedef struct {
+  char name[10];
+  char phone[20];
+  int  outgoing;
+} isdn_net_ioctl_phone;
+
+typedef struct {
+  char name[10];     /* Name of interface                     */
+  char master[10];   /* Name of Master for Bundling           */
+  char slave[10];    /* Name of Slave for Bundling            */
+  char eaz[256];     /* EAZ/MSN                               */
+  char drvid[25];    /* DriverId for Bindings                 */
+  int  secure;       /* Flag: Secure                          */
+  int  callback;     /* Flag: Callback                        */
+  int  onhtime;      /* Hangup-Timeout                        */
+  int  charge;       /* Charge-Units                          */
+  int  chargehup;    /* Flag: Charge-Hangup                   */
+  int  l2_proto;     /* Layer-2 protocol                      */
+  int  l3_proto;     /* Layer-3 protocol                      */
+  int  p_encap;      /* Encapsulation                         */
+  int  ihup;         /* Flag: Hangup-Timeout on incoming line */
+  int  exclusive;    /* Channel, if bound exclusive           */
+  int  slavedelay;   /* Delay until slave starts up           */
+} isdn_net_ioctl_cfg;
+
+#ifdef __KERNEL__
+
+#ifndef STANDALONE
+#include <linux/config.h>
+#endif
+#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/timer.h>
+#include <linux/wait.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_reg.h>
+#include <linux/fcntl.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/ip.h>
+#include <linux/in.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/tcp.h>
+
+#ifdef CONFIG_ISDN_PPP
+
+#ifdef CONFIG_ISDN_PPP_VJ
+#  include "/usr/src/linux/drivers/net/slhc.h"
+#endif
+
+#include <linux/ppp_defs.h>
+#include <linux/if_ppp.h>
+#include <linux/if_pppvar.h>
+
+#include <linux/isdn_ppp.h>
+#endif
+
+#include <linux/isdnif.h>
+
+#define ISDN_DRVIOCTL_MASK       0x7f  /* Mask for Device-ioctl */
+
+/* Until now unused */
+#define ISDN_SERVICE_VOICE 1
+#define ISDN_SERVICE_AB    1<<1 
+#define ISDN_SERVICE_X21   1<<2
+#define ISDN_SERVICE_G4    1<<3
+#define ISDN_SERVICE_BTX   1<<4
+#define ISDN_SERVICE_DFUE  1<<5
+#define ISDN_SERVICE_X25   1<<6
+#define ISDN_SERVICE_TTX   1<<7
+#define ISDN_SERVICE_MIXED 1<<8
+#define ISDN_SERVICE_FW    1<<9
+#define ISDN_SERVICE_GTEL  1<<10
+#define ISDN_SERVICE_BTXN  1<<11
+#define ISDN_SERVICE_BTEL  1<<12
+
+/* Macros checking plain usage */
+#define USG_NONE(x)     ((x & ISDN_USAGE_MASK)==ISDN_USAGE_NONE)
+#define USG_RAW(x)      ((x & ISDN_USAGE_MASK)==ISDN_USAGE_RAW)
+#define USG_MODEM(x)    ((x & ISDN_USAGE_MASK)==ISDN_USAGE_MODEM)
+#define USG_NET(x)      ((x & ISDN_USAGE_MASK)==ISDN_USAGE_NET)
+#define USG_OUTGOING(x) ((x & ISDN_USAGE_OUTGOING)==ISDN_USAGE_OUTGOING)
+
+/* Timer-delays and scheduling-flags */
+#define ISDN_TIMER_RES       3                     /* Main Timer-Resolution  */
+#define ISDN_TIMER_02SEC     (HZ/ISDN_TIMER_RES/5) /* Slow-Timer1 (0.2 sec.) */
+#define ISDN_TIMER_1SEC      (HZ/ISDN_TIMER_RES)   /* Slow-Timer2 (1 sec.)   */
+#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_FAST      (ISDN_TIMER_MODEMREAD | ISDN_TIMER_MODEMPLUS | \
+                              ISDN_TIMER_MODEMXMIT)
+#define ISDN_TIMER_SLOW      (ISDN_TIMER_MODEMRING | ISDN_TIMER_NETHANGUP | \
+                              ISDN_TIMER_NETDIAL)
+
+/* Timeout-Values for isdn_net_dial() */
+#define ISDN_TIMER_DTIMEOUT10 (10*HZ/(ISDN_TIMER_02SEC*ISDN_TIMER_RES))
+#define ISDN_TIMER_DTIMEOUT15 (15*HZ/(ISDN_TIMER_02SEC*ISDN_TIMER_RES))
+
+/* GLOBAL_FLAGS */
+#define ISDN_GLOBAL_STOPPED 1
+
+/*=================== Start of ip-over-ISDN stuff =========================*/
+
+/* Feature- and status-flags for a net-interface */
+#define ISDN_NET_CONNECTED  0x01       /* Bound to ISDN-Channel             */
+#define ISDN_NET_SECURE     0x02       /* Accept calls from phonelist only  */
+#define ISDN_NET_CALLBACK   0x04       /* callback incoming phonenumber     */
+#define ISDN_NET_CLONE      0x08       /* clone a tmp interface when called */
+#define ISDN_NET_TMP        0x10       /* tmp interface until getting an IP */
+#define ISDN_NET_DYNAMIC    0x20       /* this link is dynamically allocted */
+#define ISDN_NET_MAGIC      0x49344C02 /* for paranoia-checking             */
+
+/* Phone-list-element */
+typedef struct {
+  void *next;
+  char num[20];
+} isdn_net_phone;
+
+/* Local interface-data */
+typedef struct isdn_net_local_s {
+  ulong                  magic;
+  char                   name[10];     /* Name of device                   */
+  struct enet_statistics stats;        /* Ethernet Statistics              */
+  int                    isdn_device;  /* Index to isdn-device             */
+  int                    isdn_channel; /* Index to isdn-channel            */
+  int			 ppp_minor;    /* PPPD device minor number         */
+  int                    pre_device;   /* Preselected isdn-device          */
+  int                    pre_channel;  /* Preselected isdn-channel         */
+  int                    exclusive;    /* If non-zero idx to reserved chan.*/
+  int                    flags;        /* Connection-flags                 */
+  int                    dialstate;    /* State for dialing                */
+  int                    dialretry;    /* Counter for Dialout-retries      */
+  int                    dialmax;      /* Max. Nuber of Dial-retries       */
+  char                   msn[ISDN_MSNLEN]; /* MSNs/EAZs for this interface */
+  int                    dtimer;       /* Timeout-counter for dialing      */
+  u_char                 p_encap;      /* Packet encapsulation             */
+                                       /*   0 = Ethernet over ISDN         */
+				       /*   1 = RAW-IP                     */
+                                       /*   2 = IP with type field         */
+  u_char                 l2_proto;     /* Layer-2-protocol                 */
+				       /* See ISDN_PROTO_L2..-constants in */
+                                       /* isdnif.h                         */
+                                       /*   0 = X75/LAPB with I-Frames     */
+				       /*   1 = X75/LAPB with UI-Frames    */
+				       /*   2 = X75/LAPB with BUI-Frames   */
+				       /*   3 = HDLC                       */
+  u_char                 l3_proto;     /* Layer-3-protocol                 */
+				       /* See ISDN_PROTO_L3..-constants in */
+                                       /* isdnif.h                         */
+                                       /*   0 = Transparent                */
+  int                    huptimer;     /* Timeout-counter for auto-hangup  */
+  int                    charge;       /* Counter for charging units       */
+  int                    chargetime;   /* Timer for Charging info          */
+  int                    hupflags;     /* Flags for charge-unit-hangup:    */
+				       /* bit0: chargeint is invalid       */
+				       /* bit1: Getting charge-interval    */
+                                       /* bit2: Do charge-unit-hangup      */
+  int                    outgoing;     /* Flag: outgoing call              */
+  int                    onhtime;      /* Time to keep link up             */
+  int                    chargeint;    /* Interval between charge-infos    */
+  int                    onum;         /* Flag: at least 1 outgoing number */
+  int                    cps;          /* current speed of this interface  */
+  int                    transcount;   /* byte-counter for cps-calculation */
+  int                    sqfull;       /* Flag: netdev-queue overloaded    */
+  ulong                  sqfull_stamp; /* Start-Time of overload           */
+  ulong                  slavedelay;   /* Dynamic bundling delaytime       */
+  struct device          *srobin;      /* Ptr to Master device for slaves  */
+  isdn_net_phone         *phone[2];    /* List of remote-phonenumbers      */
+				       /* phone[0] = Incoming Numbers      */
+				       /* phone[1] = Outgoing Numbers      */
+  isdn_net_phone         *dial;        /* Pointer to dialed number         */
+  struct device          *master;      /* Ptr to Master device for slaves  */
+  struct device          *slave;       /* Ptr to Slave device for masters  */
+  struct isdn_net_local_s *next;       /* Ptr to next link in bundle       */
+  struct isdn_net_local_s *last;       /* Ptr to last link in bundle       */
+  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. header_cache_bind   */
+  void                   (*org_hcb)(struct hh_cache **, struct device *,
+                                    unsigned short, __u32);
+                                       /* Ptr to orig. header_cache_update */
+  void                   (*org_hcu)(struct hh_cache *, struct device *,
+                                    unsigned char *);
+} isdn_net_local;
+
+#ifdef CONFIG_ISDN_PPP
+struct ippp_bundle {
+  int mp_mrru;                        /* unused                             */
+  struct mpqueue *last;               /* currently defined in isdn_net_dev  */
+  int min;                            /* currently calculated 'on the fly'  */
+  long next_num;                      /* we wanna see this seq.-number next */
+  struct sqqueue *sq;
+  int modify:1;                       /* set to 1 while modifying sqqueue   */
+  int bundled:1;                      /* bundle active ?                    */
+};
+#endif
+
+/* the interface itself */
+typedef struct isdn_net_dev_s {
+  isdn_net_local  local;
+  isdn_net_local *queue;
+  void           *next;                /* Pointer to next isdn-interface   */
+  struct device   dev;	               /* interface to upper levels        */
+#ifdef CONFIG_ISDN_PPP
+  struct mpqueue *mp_last; 
+  struct ippp_bundle ib;
+#endif
+} isdn_net_dev;
+
+/*===================== End of ip-over-ISDN stuff ===========================*/
+
+/*======================= Start of ISDN-tty stuff ===========================*/
+
+#define ISDN_ASYNC_MAGIC          0x49344C01 /* for paranoia-checking        */
+#define ISDN_ASYNC_INITIALIZED	  0x80000000 /* port was initialized         */
+#define ISDN_ASYNC_CALLOUT_ACTIVE 0x40000000 /* Call out device active       */
+#define ISDN_ASYNC_NORMAL_ACTIVE  0x20000000 /* Normal device active         */
+#define ISDN_ASYNC_CLOSING	  0x08000000 /* Serial port is closing       */
+#define ISDN_ASYNC_CTS_FLOW	  0x04000000 /* Do CTS flow control          */
+#define ISDN_ASYNC_CHECK_CD	  0x02000000 /* i.e., CLOCAL                 */
+#define ISDN_ASYNC_HUP_NOTIFY         0x0001 /* Notify tty on hangups/closes */
+#define ISDN_ASYNC_SESSION_LOCKOUT    0x0100 /* Lock cua opens on session    */
+#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_PORT_16550A                   4 /* Type of faked Hardware       */
+#define ISDN_SERIAL_XMIT_SIZE           4000 /* Maximum bufsize for write    */
+#define ISDN_SERIAL_TYPE_NORMAL	           1 /* tty-type                     */
+#define ISDN_SERIAL_TYPE_CALLOUT           2 /* cua-type                     */
+
+/* Private data (similar to async_struct in <linux/serial.h>) */
+typedef struct {
+  int			magic;
+  int			flags;		 /* defined in tty.h               */
+  int			type;		 /* UART type                      */
+  struct tty_struct 	*tty;
+  int			x_char;		 /* xon/xoff character             */
+  int			close_delay;
+  int			MCR;		 /* Modem control register         */
+  int			line;
+  int			count;		 /* # of fd on device              */
+  int			blocked_open;	 /* # of blocked opens             */
+  long			session;	 /* Session of opening process     */
+  long			pgrp;		 /* pgrp of opening process        */
+  int                   isdn_driver;	 /* Index to isdn-driver           */
+  int                   isdn_channel;    /* Index to isdn-channel          */
+  int                   drv_index;       /* Index to dev->usage            */
+#if FUTURE
+  int                   send_outstanding;/* # of outstanding send-requests */
+#endif
+  int                   xmit_size;       /* max. # of chars in xmit_buf    */
+  int                   xmit_count;      /* # of chars in xmit_buf         */
+  u_char                *xmit_buf;       /* transmit-buffer                */
+  struct termios	normal_termios;
+  struct termios	callout_termios;
+  struct wait_queue	*open_wait;
+  struct wait_queue	*close_wait;
+} modem_info;
+
+#define ISDN_MODEM_WINSIZE 8
+
+/* Private data of AT-command-interpreter */
+typedef struct {
+  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                          */
+  int                 mdmcmdl;         /* Length of Modem-Commandbuffer    */
+  int                 pluscount;       /* Counter for +++ sequence         */
+  int                 lastplus;        /* Timestamp of last +              */
+  char                mdmcmd[255];     /* Modem-Commandbuffer              */
+} atemu;
+
+/* Descripion of one ISDN-tty */
+typedef struct {
+  int                msr[ISDN_MAX_CHANNELS];	  /* Modem-statusregister   */
+  int                mlr[ISDN_MAX_CHANNELS];	  /* Line-statusregister    */
+  int                refcount;			  /* Number of opens        */
+  int                online[ISDN_MAX_CHANNELS];	  /* B-Channel is up        */
+  int                dialing[ISDN_MAX_CHANNELS];  /* Dial in progress       */
+  int                rcvsched[ISDN_MAX_CHANNELS]; /* Receive needs schedule */
+  int                ncarrier[ISDN_MAX_CHANNELS]; /* Output NO CARRIER      */
+  struct tty_driver  tty_modem;			  /* tty-device             */
+  struct tty_driver  cua_modem;			  /* cua-device             */
+  struct tty_struct  *modem_table[ISDN_MAX_CHANNELS]; /* ?? copied from Orig */
+  struct termios     *modem_termios[ISDN_MAX_CHANNELS];
+  struct termios     *modem_termios_locked[ISDN_MAX_CHANNELS];
+  atemu              atmodem[ISDN_MAX_CHANNELS];  /* AT-Command-parser      */
+  modem_info         info[ISDN_MAX_CHANNELS];	  /* Private data           */
+} modem;
+
+/*======================= End of ISDN-tty stuff ============================*/
+
+/*======================= Start of sync-ppp stuff ==========================*/
+
+
+#define NUM_RCV_BUFFS     64
+#define PPP_HARD_HDR_LEN 4
+
+#define IPPP_OPEN        0x1
+#define IPPP_CONNECT     0x2
+#define IPPP_CLOSEWAIT   0x4
+#define IPPP_NOBLOCK     0x8
+
+#ifdef CONFIG_ISDN_PPP
+
+struct sqqueue {
+  struct sqqueue *next;
+  int sqno_start;
+  int sqno_end;
+  struct sk_buff *skb;
+  long timer;
+};
+  
+struct mpqueue {
+  struct mpqueue *next;
+  struct mpqueue *last;
+  int    sqno;
+  struct sk_buff *skb;
+  int BEbyte;
+  unsigned long time;
+}; 
+
+struct ippp_buf_queue {
+  struct ippp_buf_queue *next;
+  struct ippp_buf_queue *last;
+  char *buf;                 /* NULL here indicates end of queue */
+  int len;
+};
+
+struct ippp_struct {
+  struct ippp_struct *next_link;
+  int state;
+  struct ippp_buf_queue rq[NUM_RCV_BUFFS]; /* packet queue for isdn_ppp_read() */
+  struct ippp_buf_queue *first;  /* pointer to (current) first packet */
+  struct ippp_buf_queue *last;   /* pointer to (current) last used packet in queue */
+  struct wait_queue *wq;
+  struct wait_queue *wq1;
+  struct task_struct *tk;
+  unsigned int mpppcfg;
+  unsigned int pppcfg;
+  unsigned int mru;
+  unsigned int mpmru;
+  unsigned int mpmtu;
+  unsigned int maxcid;
+  isdn_net_local *lp;
+  int unit; 
+  long last_link_seqno;
+  long mp_seqno;
+  long range;
+#ifdef CONFIG_ISDN_PPP_VJ
+  unsigned char *cbuf;
+  struct slcompress *slcomp;
+#endif
+};
+
+#endif
+
+/*======================== End of sync-ppp stuff ===========================*/
+
+/*======================= Start of general stuff ===========================*/
+
+/* Packet-queue-element */
+typedef struct pqueue {
+  char   *next;				/* Pointer to next packet           */
+  short   length;			/* Packetlength                     */
+  short   size;                         /* Allocated size                   */
+  u_char *rptr;				/* Read-pointer for stream-reading  */
+  u_char  buffer[1];			/* The data (will be alloc'd)       */
+} pqueue;
+
+typedef struct {
+  char *next;
+  char *private;
+} infostruct;
+
+/* Description of hardware-level-driver */
+typedef struct {
+  ulong               flags;            /* Flags                            */
+  int                 channels;         /* Number of channels               */
+  int                 reject_bus;       /* Flag: Reject rejected call on bus*/
+  struct wait_queue  *st_waitq;         /* Wait-Queue for status-read's     */
+  int                 maxbufsize;       /* Maximum Buffersize supported     */
+  unsigned long       pktcount;         /* Until now: unused                */
+  int                 running;          /* Flag: Protocolcode running       */
+  int                 loaded;           /* Flag: Driver loaded              */
+  int                 stavail;          /* Chars avail on Status-device     */
+  isdn_if            *interface;        /* Interface to driver              */
+  int                *rcverr;           /* Error-counters for B-Ch.-receive */
+  int                *rcvcount;         /* Byte-counters for B-Ch.-receive  */
+  pqueue             **rpqueue;         /* Pointers to start of Rcv-Queue   */
+  struct wait_queue  **rcv_waitq;       /* Wait-Queues for B-Channel-Reads  */
+  struct wait_queue  **snd_waitq;       /* Wait-Queue for B-Channel-Send's  */
+  char               msn2eaz[10][ISDN_MSNLEN];  /* Mapping-Table MSN->EAZ   */
+} driver;
+
+/* Main driver-data */
+typedef struct isdn_devt {
+  unsigned short    flags;		       /* Bitmapped Flags:           */
+				               /*                            */
+  int               drivers;		       /* Current number of drivers  */
+  int               channels;		       /* Current number of channels */
+  int               net_verbose;               /* Verbose-Flag               */
+  int               modempoll;		       /* Flag: tty-read active      */
+  int               tflags;                    /* Timer-Flags:               */
+				               /*  see ISDN_TIMER_..defines  */
+  int               global_flags;
+  infostruct        *infochain;                /* List of open info-devs.    */
+  struct wait_queue *info_waitq;               /* Wait-Queue for isdninfo    */
+  struct timer_list timer;		       /* Misc.-function Timer       */
+  int               chanmap[ISDN_MAX_CHANNELS];/* Map minor->device-channel  */
+  int               drvmap[ISDN_MAX_CHANNELS]; /* Map minor->driver-index    */
+  int               usage[ISDN_MAX_CHANNELS];  /* Used by tty/ip/voice       */
+  char              num[ISDN_MAX_CHANNELS][20];/* Remote number of active ch.*/
+  int               m_idx[ISDN_MAX_CHANNELS];  /* Index for mdm....          */
+  driver            *drv[ISDN_MAX_DRIVERS];    /* Array of drivers           */
+  isdn_net_dev      *netdev;		       /* Linked list of net-if's    */
+  char              drvid[ISDN_MAX_DRIVERS][20];/* Driver-ID                 */
+  struct task_struct *profd;                   /* For iprofd                 */
+  modem             mdm;		       /* tty-driver-data            */
+  isdn_net_dev      *rx_netdev[ISDN_MAX_CHANNELS]; /* rx netdev-pointers     */
+  isdn_net_dev      *st_netdev[ISDN_MAX_CHANNELS]; /* stat netdev-pointers   */
+} isdn_dev;
+
+extern isdn_dev *dev;
+
+/* Utility-Macros */
+#define MIN(a,b) ((a<b)?a:b)
+#define MAX(a,b) ((a>b)?a:b)
+
+#endif /* __KERNEL__ */
+#endif /* isdn_h */
diff -u --recursive --new-file v1.3.68/linux/include/linux/isdn_ppp.h linux/include/linux/isdn_ppp.h
--- v1.3.68/linux/include/linux/isdn_ppp.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/isdn_ppp.h	Mon Feb 26 11:58:16 1996
@@ -0,0 +1,30 @@
+extern int isdn_ppp_dial_slave(char *);
+
+struct pppinfo
+{
+  int type; /* set by user */
+  union {
+    char clid[32]; /* calling ID */
+    int  bundles;
+    int  linknumber;
+  } info;
+};
+
+ 
+#define PPPIOCLINKINFO _IOWR('t',128,struct pppinfo)
+#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 PPP_MP         0x003d
+
+#define SC_MP_PROT       0x00000200
+#define SC_REJ_MP_PROT   0x00000400
+#define SC_OUT_SHORT_SEQ 0x00000800
+#define SC_IN_SHORT_SEQ  0x00004000
+
+#define MP_END_FRAG    0x40
+#define MP_BEGIN_FRAG  0x80
+
diff -u --recursive --new-file v1.3.68/linux/include/linux/isdnif.h linux/include/linux/isdnif.h
--- v1.3.68/linux/include/linux/isdnif.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/isdnif.h	Mon Feb 26 11:58:16 1996
@@ -0,0 +1,267 @@
+/* $Id: isdnif.h,v 1.1 1996/01/09 05:50:51 fritz Exp fritz $
+ *
+ * Linux ISDN subsystem
+ *
+ * Definition of the interface between the subsystem and it's lowlevel-drivers.
+ *
+ * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1995,96    Thinking Objects Software GmbH Wuerzburg
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * 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: isdnif.h,v $
+ * Revision 1.1  1996/01/09 05:50:51  fritz
+ * Initial revision
+ *
+ */
+
+#ifndef isdnif_h
+#define isdnif_h
+
+#ifdef STANDALONE
+#include <linux/k_compat.h>
+#endif
+
+/*
+ * Values for general protocol-selection
+ */
+#define ISDN_PTYPE_UNKNOWN   0   /* Protocol undefined   */
+#define ISDN_PTYPE_1TR6      1   /* german 1TR6-protocol */
+#define ISDN_PTYPE_EURO      2   /* EDSS1-protocol       */
+
+/*
+ * 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                        */
+
+/*
+ * Values for Layer-3-protocol-selection
+ */
+#define ISDN_PROTO_L3_TRANS  0   /* Transparent                 */
+
+#ifdef __KERNEL__
+
+#include <linux/skbuff.h>
+
+/*
+ * Commands from linklevel to lowlevel
+ *
+ */
+#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.            */
+#define ISDN_CMD_HANGUP  4       /* Hangup                                */
+#define ISDN_CMD_CLREAZ  5       /* Clear EAZ(s) of channel               */
+#define ISDN_CMD_SETEAZ  6       /* Set EAZ(s) of channel                 */
+#define ISDN_CMD_GETEAZ  7       /* Get EAZ(s) of channel                 */
+#define ISDN_CMD_SETSIL  8       /* Set Service-Indicator-List of channel */
+#define ISDN_CMD_GETSIL  9       /* Get Service-Indicator-List of channel */
+#define ISDN_CMD_SETL2  10       /* Set B-Chan. Layer2-Parameter          */
+#define ISDN_CMD_GETL2  11       /* Get B-Chan. Layer2-Parameter          */
+#define ISDN_CMD_SETL3  12       /* Set B-Chan. Layer3-Parameter          */
+#define ISDN_CMD_GETL3  13       /* Get B-Chan. Layer3-Parameter          */
+#define ISDN_CMD_LOCK   14       /* Signal usage by upper levels          */
+#define ISDN_CMD_UNLOCK 15       /* Release usage-lock                    */
+
+/*
+ * Status-Values delivered from lowlevel to linklevel via
+ * statcallb().
+ *
+ */
+#define ISDN_STAT_STAVAIL 256    /* Raw status-data available             */
+#define ISDN_STAT_ICALL   257    /* Incoming call detected                */
+#define ISDN_STAT_RUN     258    /* Signal protocol-code is running       */
+#define ISDN_STAT_STOP    259    /* Signal halt of protocol-code          */
+#define ISDN_STAT_DCONN   260    /* Signal D-Channel connect              */
+#define ISDN_STAT_BCONN   261    /* Signal B-Channel connect              */
+#define ISDN_STAT_DHUP    262    /* Signal D-Channel disconnect           */
+#define ISDN_STAT_BHUP    263    /* Signal B-Channel disconnect           */
+#define ISDN_STAT_CINF    264    /* Charge-Info                           */
+#define ISDN_STAT_LOAD    265    /* Signal new lowlevel-driver is loaded  */
+#define ISDN_STAT_UNLOAD  266    /* Signal unload of lowlevel-driver      */
+#define ISDN_STAT_BSENT   267    /* Signal packet sent                    */
+#define ISDN_STAT_NODCH   268    /* Signal no D-Channel                   */
+#define ISDN_STAT_ADDCH   269    /* Add more Channels                     */
+#define ISDN_STAT_CAUSE   270    /* Cause-Message                         */
+
+/*
+ * Values for feature-field of interface-struct.
+ */
+/* Layer 2 */
+#define ISDN_FEATURE_L2_X75I    (0x0001 << ISDN_PROTO_L2_X75I)
+#define ISDN_FEATURE_L2_X75UI   (0x0001 << ISDN_PROTO_L2_X75UI)
+#define ISDN_FEATURE_L2_X75BUI  (0x0001 << ISDN_PROTO_L2_X75BUI)
+#define ISDN_FEATURE_L2_HDLC    (0x0001 << ISDN_PROTO_L2_HDLC)
+
+/* Layer 3 */
+#define ISDN_FEATURE_L3_TRANS   (0x0100 << ISDN_PROTO_L3_TRANS)
+
+/* 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)
+
+/*
+ * Structure for exchanging above infos
+ *
+ */
+typedef struct {
+  int   driver;                  /* Lowlevel-Driver-ID                    */
+  int   command;                 /* Command or Status (see above)         */
+  ulong arg;                     /* Additional Data                       */
+  char  num[50];                 /* Additional Data                       */
+} isdn_ctrl;
+
+/*
+ * The interface-struct itself (initialized at load-time of lowlevel-driver)
+ *
+ * See Documentation/isdn/INTERFACE for a description, how the communication
+ * between the ISDN subsystem and it's drivers is done.
+ *
+ */
+typedef struct {
+  /* Number of channels supported by this driver
+   */
+  int channels;
+
+  /* 
+   * Maximum Size of transmit/receive-buffer this driver supports.
+   */
+  int maxbufsize;
+
+  /* Feature-Flags for this driver.
+   * See defines ISDN_FEATURE_... for Values
+   */
+  unsigned long features;
+
+  /*
+   * Needed for calculating
+   * dev->hard_header_len = linklayer header + hl_hdrlen;
+   * Drivers, not supporting sk_buff's should set this to 0.
+   */
+  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
+   */
+  void (*rcvcallb)(int, int, u_char*, int);
+
+  /*
+   * Receive-Callback using sk_buff's
+   * Parameters:
+   *             int                    Driver-ID
+   *             int                    local channel-number (0 ...)
+   *             struct sk_buff *skb    received Data
+   */
+  void (*rcvcallb_skb)(int, int, struct sk_buff *);
+
+  /* Status-Callback
+   * Parameters:
+   *             isdn_ctrl*
+   *                   driver  = Driver ID.
+   *                   command = One of above ISDN_STAT_... constants.
+   *                   arg     = depending on status-type.
+   *                   num     = depending on status-type.
+   */
+  int (*statcallb)(isdn_ctrl*);
+  /* Send command
+   * Parameters:
+   *             isdn_ctrl*
+   *                   driver  = Driver ID.
+   *                   command = One of above ISDN_CMD_... constants.
+   *                   arg     = depending on command.
+   *                   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)
+   */
+  int (*writebuf)(int, int, const u_char*, int, int);
+
+  /*
+   * Send data using sk_buff's
+   * Parameters:
+   *             int                    driverId
+   *             int                    local channel-number (0...)
+   *             struct sk_buff *skb    Data to send
+   */
+  int (*writebuf_skb) (int, int, struct sk_buff *);
+
+  /* Send raw D-Channel-Commands
+   * Parameters:
+   *             u_char pointer 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)
+   */
+  int (*writecmd)(const u_char*, int, int);
+  /* Read raw Status replies
+   *             u_char pointer data (volatile)
+   *             int    length of buffer
+   *             int    Flag: 0 = Call form Kernel-Space (use memcpy,
+   *                              no schedule allowed) 
+   *                          1 = Data is in User-Space (use memcpy_fromfs,
+   *                              may schedule)
+   */
+  int (*readstat)(u_char*, int, int);
+  char id[20];
+} isdn_if;
+
+/*
+ * Function which must be called by lowlevel-driver at loadtime with
+ * the following fields of above struct set:
+ *
+ * channels     Number of channels that will be supported.
+ * hl_hdrlen    Space to preserve in sk_buff's when sending. Drivers, not
+ *              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.
+ * writebuf_skb Address of Skbuff-Send-Handler. (NULL if not supported)
+ * writecmd        "    "  D-Channel  " which accepts raw D-Ch-Commands.
+ * readstat        "    "  D-Channel  " which delivers raw Status-Data.
+ *
+ * The linklevel-driver fills the following fields:
+ *
+ * channels      Driver-ID assigned to this driver. (Must be used on all
+ *               subsequent callbacks.
+ * rcvcallb      Address of handler for received data.
+ * rcvcallb_skb  Address of handler for received Skbuff's. (NULL if not supp.)
+ * statcallb        "    "     "    for status-changes.
+ *
+ */
+extern int register_isdn(isdn_if*);
+
+#endif /* __KERNEL__ */
+#endif /* isdnif_h */
+
diff -u --recursive --new-file v1.3.68/linux/include/linux/linear.h linux/include/linux/linear.h
--- v1.3.68/linux/include/linux/linear.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/linear.h	Mon Feb 26 13:51:45 1996
@@ -0,0 +1,34 @@
+
+#ifndef _LINEAR_H
+#define _LINEAR_H
+
+struct linear_hash
+{
+  struct real_dev *dev0, *dev1;
+};
+
+struct linear_data
+{
+  struct linear_hash *hash_table; /* Dynamically allocated */
+  struct real_dev *smallest;
+  int nr_zones;
+};
+
+#endif
+
+#ifndef _LINEAR_H
+#define _LINEAR_H
+
+struct linear_hash
+{
+  struct real_dev *dev0, *dev1;
+};
+
+struct linear_data
+{
+  struct linear_hash *hash_table; /* Dynamically allocated */
+  struct real_dev *smallest;
+  int nr_zones;
+};
+
+#endif
diff -u --recursive --new-file v1.3.68/linux/include/linux/major.h linux/include/linux/major.h
--- v1.3.68/linux/include/linux/major.h	Sun Dec 17 11:43:32 1995
+++ linux/include/linux/major.h	Mon Feb 26 13:51:45 1996
@@ -26,7 +26,7 @@
  *  6 - lp
  *  7 - /dev/vcs*
  *  8 -                        scsi disk
- *  9 - scsi tape
+ *  9 - scsi tape              multiple devices driver
  * 10 - mice
  * 11 -                        scsi cdrom
  * 12 - qic02 tape
@@ -67,6 +67,7 @@
 #define VCS_MAJOR	7
 #define SCSI_DISK_MAJOR	8
 #define SCSI_TAPE_MAJOR	9
+#define MD_MAJOR        9
 #define MOUSE_MAJOR	10
 #define SCSI_CDROM_MAJOR 11
 #define QIC02_TAPE_MAJOR 12
diff -u --recursive --new-file v1.3.68/linux/include/linux/md.h linux/include/linux/md.h
--- v1.3.68/linux/include/linux/md.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/md.h	Mon Feb 26 13:51:46 1996
@@ -0,0 +1,300 @@
+
+/*
+   md.h : Multiple Devices driver for Linux
+          Copyright (C) 1994-96 Marc ZYNGIER
+	  <zyngier@ufr-info-p7.ibp.fr> or
+	  <maz@gloups.fdn.fr>
+	  
+   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.
+   
+   You should have received a copy of the GNU General Public License
+   (for example /usr/src/linux/COPYING); if not, write to the Free
+   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
+*/
+
+#ifndef _MD_H
+#define _MD_H
+
+#include <linux/major.h>
+#include <linux/mm.h>
+#include <linux/ioctl.h>
+
+#define MD_VERSION "0.34"
+
+/* ioctls */
+#define REGISTER_DEV _IO (MD_MAJOR, 1)
+#define START_MD     _IO (MD_MAJOR, 2)
+#define STOP_MD      _IO (MD_MAJOR, 3)
+#define MD_INVALID   _IO (MD_MAJOR, 4)
+#define MD_VALID     _IO (MD_MAJOR, 5)
+
+/*
+   personalities :
+   Byte 0 : Chunk size factor
+   Byte 1 : Fault tolerance count for each physical device
+            (   0 means no fault tolerance,
+             0xFF means always tolerate faults)
+   Byte 2 : Personality
+   Byte 3 : Reserved.
+ */
+
+#define FAULT_SHIFT       8
+#define PERSONALITY_SHIFT 16
+
+#define FACTOR_MASK       0xFFUL
+#define FAULT_MASK        0xFF00UL
+#define PERSONALITY_MASK  0xFF0000UL
+
+#define MD_RESERVED       0	/* Not used by now */
+#define LINEAR            (1UL << PERSONALITY_SHIFT)
+#define STRIPED           (2UL << PERSONALITY_SHIFT)
+#define STRIPPED          STRIPED /* Long lasting spelling mistake... */
+#define RAID0             STRIPED
+#define RAID1             (3UL << PERSONALITY_SHIFT)
+#define RAID5             (4UL << PERSONALITY_SHIFT)
+#define MAX_PERSONALITY   5
+
+#ifdef __KERNEL__
+
+#include <linux/config.h>
+#include <sys/types.h>
+#include <linux/fs.h>
+#include <linux/blkdev.h>
+
+#undef MD_COUNT_SIZE		/* Define this to have stats about
+				   chunk size in /proc/mdstat */
+#define MAX_REAL     8		/* Max number of physical dev per md dev */
+#define MAX_MD_DEV   4		/* Max number of md dev */
+
+#define FACTOR(a)         ((a)->repartition & FACTOR_MASK)
+#define MAX_FAULT(a)      (((a)->repartition & FAULT_MASK)>>8)
+#define PERSONALITY(a)    ((a)->repartition & PERSONALITY_MASK)
+
+#define FACTOR_SHIFT(a) (PAGE_SHIFT + (a) - 10)
+
+/* Invalidation modes */
+#define VALID          0
+#define INVALID_NEXT   1
+#define INVALID_ALWAYS 2
+#define INVALID        3	/* Only useful to md_valid_device */
+
+/* Return values from personalities to md driver */
+#define REDIRECTED_BHREQ 0 /* Redirected individual buffers
+			      (shouldn't be used anymore since 0.31) */
+#define REDIRECTED_REQ   1 /* Redirected whole request */
+#define REDIRECT_FAILED -1 /* For RAID-1 */
+
+struct real_dev
+{
+  kdev_t dev;			/* Device number */
+  int size;			/* Device size (in blocks) */
+  int offset;			/* Real device offset (in blocks) in md dev
+				   (only used in linear mode) */
+  struct inode *inode;		/* Lock inode */
+  int fault_count;		/* Fault counter for invalidation */
+  int invalid;			/* Indicate if the device is disabled :
+				   VALID          - valid
+				   INVALID_NEXT   - disabled for next access
+				   INVALID_ALWAYS - permanently disabled
+				   (for redundancy modes only) */
+};
+
+struct md_dev;
+
+struct md_personality
+{
+  char *name;
+  int (*map)(int minor, struct md_dev *md_dev, struct request *req);
+  int (*run)(int minor, struct md_dev *md_dev);
+  int (*stop)(int minor, struct md_dev *md_dev);
+  int (*status)(char *page, int minor, struct md_dev *md_dev);
+  int (*ioctl)(struct inode *inode, struct file *file,
+	       unsigned int cmd, unsigned long arg);
+  int max_invalid_dev;
+};
+
+struct md_dev
+{
+  struct md_personality *pers;
+  int repartition;
+  int invalid_dev_count;
+  int busy;
+  int nb_dev;
+  void *private;
+#ifdef MD_COUNT_SIZE
+  unsigned int smallest_count;
+  unsigned int biggest_count;
+  unsigned int equal_count;
+#endif
+};
+
+extern struct real_dev devices[MAX_MD_DEV][MAX_REAL];
+extern struct md_dev md_dev[MAX_MD_DEV];
+extern int md_size[MAX_MD_DEV];
+
+extern void make_md_request(struct request *pending, int n);
+extern char *partition_name (kdev_t dev);
+
+#if defined(CONFIG_MD_SUPPORT_RAID1) || defined(CONFIG_MD_SUPPORT_RAID5)
+extern int md_valid_device (int minor, kdev_t dev, int mode);
+extern int md_can_reemit (int minor);
+#endif
+
+extern int register_md_personality (int p_num, struct md_personality *p);
+extern int unregister_md_personality (int p_num);
+
+#endif __KERNEL__
+#endif _MD_H
+
+/*
+   md.h : Multiple Devices driver for Linux
+          Copyright (C) 1994, 1995 Marc ZYNGIER
+	  <zyngier@amertume.ufr-info-p7.ibp.fr> or
+	  <maz@gloups.fdn.fr>
+	  
+   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.
+   
+   You should have received a copy of the GNU General Public License
+   (for example /usr/src/linux/COPYING); if not, write to the Free
+   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
+*/
+
+#ifndef _MD_H
+#define _MD_H
+
+#include <linux/major.h>
+#include <linux/mm.h>
+#include <linux/ioctl.h>
+
+#define MD_VERSION "0.33"
+
+/* ioctls */
+#define REGISTER_DEV _IO (MD_MAJOR, 1)
+#define START_MD     _IO (MD_MAJOR, 2)
+#define STOP_MD      _IO (MD_MAJOR, 3)
+#define MD_INVALID   _IO (MD_MAJOR, 4)
+#define MD_VALID     _IO (MD_MAJOR, 5)
+
+/*
+   personalities :
+   Byte 0 : Chunk size factor
+   Byte 1 : Fault tolerance count for each physical device
+            (   0 means no fault tolerance,
+             0xFF means always tolerate faults)
+   Byte 2 : Personality
+   Byte 3 : Reserved.
+ */
+
+#define FAULT_SHIFT       8
+#define PERSONALITY_SHIFT 16
+
+#define FACTOR_MASK       0xFFUL
+#define FAULT_MASK        0xFF00UL
+#define PERSONALITY_MASK  0xFF0000UL
+
+#define MD_RESERVED       0	/* Not used by now */
+#define LINEAR            (1UL << PERSONALITY_SHIFT)
+#define STRIPED           (2UL << PERSONALITY_SHIFT)
+#define STRIPPED          STRIPED /* Long lasting spelling mistake... */
+#define RAID0             STRIPED
+#define RAID1             (3UL << PERSONALITY_SHIFT)
+#define RAID5             (4UL << PERSONALITY_SHIFT)
+#define MAX_PERSONALITY   5
+
+#ifdef __KERNEL__
+
+#include <linux/config.h>
+#include <sys/types.h>
+#include <linux/fs.h>
+#include <linux/blkdev.h>
+
+#undef MD_COUNT_SIZE		/* Define this to have stats about
+				   chunk size in /proc/mdstat */
+#define MAX_REAL     8		/* Max number of physical dev per md dev */
+#define MAX_MD_DEV   4		/* Max number of md dev */
+
+#define FACTOR(a)         ((a)->repartition & FACTOR_MASK)
+#define MAX_FAULT(a)      (((a)->repartition & FAULT_MASK)>>8)
+#define PERSONALITY(a)    ((a)->repartition & PERSONALITY_MASK)
+
+#define FACTOR_SHIFT(a) (PAGE_SHIFT + (a) - 10)
+
+/* Invalidation modes */
+#define VALID          0
+#define INVALID_NEXT   1
+#define INVALID_ALWAYS 2
+#define INVALID        3	/* Only useful to md_valid_device */
+
+/* Return values from personalities to md driver */
+#define REDIRECTED_BHREQ 0 /* Redirected individual buffers
+			      (shouldn't be used anymore since 0.31) */
+#define REDIRECTED_REQ   1 /* Redirected whole request */
+#define REDIRECT_FAILED -1 /* For RAID-1 */
+
+struct real_dev
+{
+  kdev_t dev;			/* Device number */
+  int size;			/* Device size (in blocks) */
+  int offset;			/* Real device offset (in blocks) in md dev
+				   (only used in linear mode) */
+  struct inode *inode;		/* Lock inode */
+  int fault_count;		/* Fault counter for invalidation */
+  int invalid;			/* Indicate if the device is disabled :
+				   VALID          - valid
+				   INVALID_NEXT   - disabled for next access
+				   INVALID_ALWAYS - permanently disabled
+				   (for redundancy modes only) */
+};
+
+struct md_dev;
+
+struct md_personality
+{
+  char *name;
+  int (*map)(int minor, struct md_dev *md_dev, struct request *req);
+  int (*run)(int minor, struct md_dev *md_dev);
+  int (*stop)(int minor, struct md_dev *md_dev);
+  int (*status)(char *page, int minor, struct md_dev *md_dev);
+  int (*ioctl)(struct inode *inode, struct file *file,
+	       unsigned int cmd, unsigned long arg);
+  int max_invalid_dev;
+};
+
+struct md_dev
+{
+  struct md_personality *pers;
+  int repartition;
+  int invalid_dev_count;
+  int busy;
+  int nb_dev;
+  void *private;
+#ifdef MD_COUNT_SIZE
+  unsigned int smallest_count;
+  unsigned int biggest_count;
+  unsigned int equal_count;
+#endif
+};
+
+extern struct real_dev devices[MAX_MD_DEV][MAX_REAL];
+extern struct md_dev md_dev[MAX_MD_DEV];
+extern int md_size[MAX_MD_DEV];
+
+extern void make_md_request(struct request *pending, int n);
+extern char *partition_name (kdev_t dev);
+
+#if defined(CONFIG_MD_SUPPORT_RAID1) || defined(CONFIG_MD_SUPPORT_RAID5)
+extern int md_valid_device (int minor, kdev_t dev, int mode);
+extern int md_can_reemit (int minor);
+#endif
+
+extern int register_md_personality (int p_num, struct md_personality *p);
+extern int unregister_md_personality (int p_num);
+
+#endif __KERNEL__
+#endif _MD_H
diff -u --recursive --new-file v1.3.68/linux/include/linux/mm.h linux/include/linux/mm.h
--- v1.3.68/linux/include/linux/mm.h	Fri Feb 23 13:54:38 1996
+++ linux/include/linux/mm.h	Tue Feb 27 13:35:04 1996
@@ -239,8 +239,8 @@
 
 #define GFP_LEVEL_MASK 0xf
 
-#define avl_empty	(struct vm_area_struct *) NULL
-
+/* vma is the first one with  address < vma->vm_end,
+ * and even  address < vma->vm_start. Have to extend vma. */
 static inline int expand_stack(struct vm_area_struct * vma, unsigned long address)
 {
 	unsigned long grow;
@@ -256,6 +256,8 @@
 		vma->vm_mm->locked_vm += grow >> PAGE_SHIFT;
 	return 0;
 }
+
+#define avl_empty	(struct vm_area_struct *) NULL
 
 /* Look up the first VMA which satisfies  addr < vm_end,  NULL if none. */
 static inline struct vm_area_struct * find_vma (struct task_struct * task, unsigned long addr)
diff -u --recursive --new-file v1.3.68/linux/include/linux/optcd.h linux/include/linux/optcd.h
--- v1.3.68/linux/include/linux/optcd.h	Tue Jan 23 21:15:53 1996
+++ linux/include/linux/optcd.h	Fri Feb 23 09:39:34 1996
@@ -19,9 +19,6 @@
 
 /* enable / disable parts of driver by define / undef */
 #define	MULTISESSION		/* multisession support (ALPHA) */
-#define	PROBE_ISP16		/* ISP16 interface card probing */
-/* ISP16 probing can also be suppressed with kernel command line option
-   'noisp16', or with insmod parameter 'noisp16=1'. */
 
 
 /* Change 0 to 1 to debug various parts of the driver */
@@ -35,8 +32,9 @@
 #define	DEBUG_VFS	0	/* VFS interface */
 
 
-/* Various timeout loop repetition counts. Don't touch unless you know
-   what you're doing. */
+/* Don't touch these unless you know what you're doing. */
+
+/* Various timeout loop repetition counts. */
 #define BUSY_TIMEOUT		10000000	/* for busy wait */
 #define FAST_TIMEOUT		100000		/* ibid. for probing */
 #define SLEEP_TIMEOUT		3000		/* for timer wait */
@@ -44,5 +42,11 @@
 #define READ_TIMEOUT		3000		/* for poll wait */
 #define STOP_TIMEOUT		1000		/* for poll wait */
 #define RESET_WAIT		1000		/* busy wait at drive reset */
+
+/* # of buffer for block size conversion. 6 is optimal for my setup (P75),
+   giving 280 kb/s, with 0.4% CPU usage. Experiment to find your optimal
+   setting */
+#define N_BUFS		6
+
 
 #endif _LINUX_OPTCD_H
diff -u --recursive --new-file v1.3.68/linux/include/linux/pagemap.h linux/include/linux/pagemap.h
--- v1.3.68/linux/include/linux/pagemap.h	Wed Feb  7 15:11:38 1996
+++ linux/include/linux/pagemap.h	Tue Feb 27 13:35:21 1996
@@ -9,6 +9,9 @@
  * Copyright 1995 Linus Torvalds
  */
 
+#include <linux/mm.h>
+#include <linux/fs.h>
+
 static inline unsigned long page_address(struct page * page)
 {
 	return PAGE_OFFSET + PAGE_SIZE*(page - mem_map);
@@ -45,12 +48,13 @@
 {
 	struct page *page;
 	unsigned long flags;
-	
+
 	for (page = page_hash(inode, offset); page ; page = page->next_hash) {
 		if (page->inode != inode)
 			continue;
 		if (page->offset != offset)
 			continue;
+		/* Found the page. */
 		save_flags(flags);
 		cli();
 		page->referenced = 1;
diff -u --recursive --new-file v1.3.68/linux/include/linux/pipe_fs_i.h linux/include/linux/pipe_fs_i.h
--- v1.3.68/linux/include/linux/pipe_fs_i.h	Tue Dec  7 07:33:31 1993
+++ linux/include/linux/pipe_fs_i.h	Tue Feb 27 13:35:04 1996
@@ -1,7 +1,9 @@
 #ifndef _LINUX_PIPE_FS_I_H
 #define _LINUX_PIPE_FS_I_H
+#include <linux/nfs_fs_i.h>
 
 struct pipe_inode_info {
+	struct nfs_inode_info dummy;   /* NFS/fifo conflict workaround */
 	struct wait_queue * wait;
 	char * base;
 	unsigned int start;
diff -u --recursive --new-file v1.3.68/linux/include/linux/ppp_defs.h linux/include/linux/ppp_defs.h
--- v1.3.68/linux/include/linux/ppp_defs.h	Tue Oct 10 18:46:38 1995
+++ linux/include/linux/ppp_defs.h	Mon Feb 26 11:58:16 1996
@@ -28,7 +28,7 @@
  */
 
 /*
- *  ==FILEVERSION 4==
+ *  ==FILEVERSION 5==
  *
  *  NOTE TO MAINTAINERS:
  *     If you modify this file at all, increment the number above.
@@ -66,10 +66,12 @@
  * Protocol field values.
  */
 #define PPP_IP		0x21	/* Internet Protocol */
+#define PPP_IPX		0x2b	/* IPX protocol */
 #define	PPP_VJC_COMP	0x2d	/* VJ compressed TCP */
 #define	PPP_VJC_UNCOMP	0x2f	/* VJ uncompressed TCP */
 #define PPP_COMP	0xfd	/* compressed packet */
 #define PPP_IPCP	0x8021	/* IP Control Protocol */
+#define PPP_IPXCP	0x802b	/* IPX Control Protocol */
 #define PPP_CCP		0x80fd	/* Compression Control Protocol */
 #define PPP_LCP		0xc021	/* Link Control Protocol */
 #define PPP_PAP		0xc023	/* Password Authentication Protocol */
diff -u --recursive --new-file v1.3.68/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- v1.3.68/linux/include/linux/proc_fs.h	Fri Feb 23 13:54:39 1996
+++ linux/include/linux/proc_fs.h	Tue Feb 27 13:35:21 1996
@@ -41,7 +41,8 @@
 	PROC_PROFILE, /* whether enabled or not */
 	PROC_CMDLINE,
 	PROC_SYS,
-	PROC_MTAB
+	PROC_MTAB,
+	PROC_MD
 };
 
 enum pid_directory_inos {
diff -u --recursive --new-file v1.3.68/linux/include/linux/raid0.h linux/include/linux/raid0.h
--- v1.3.68/linux/include/linux/raid0.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/raid0.h	Mon Feb 26 13:51:46 1996
@@ -0,0 +1,56 @@
+
+#ifndef _RAID0_H
+#define _RAID0_H
+
+struct strip_zone
+{
+  int zone_offset;		/* Zone offset in md_dev */
+  int dev_offset;		/* Zone offset in real dev */
+  int size;			/* Zone size */
+  int nb_dev;			/* Number of devices attached to the zone */
+  struct real_dev *dev[MAX_REAL]; /* Devices attached to the zone */
+};
+
+struct raid0_hash
+{
+  struct strip_zone *zone0, *zone1;
+};
+
+struct raid0_data
+{
+  struct raid0_hash *hash_table; /* Dynamically allocated */
+  struct strip_zone *strip_zone; /* This one too */
+  int nr_strip_zones;
+  struct strip_zone *smallest;
+  int nr_zones;
+};
+
+#endif
+
+#ifndef _RAID0_H
+#define _RAID0_H
+
+struct strip_zone
+{
+  int zone_offset;		/* Zone offset in md_dev */
+  int dev_offset;		/* Zone offset in real dev */
+  int size;			/* Zone size */
+  int nb_dev;			/* Number of devices attached to the zone */
+  struct real_dev *dev[MAX_REAL]; /* Devices attached to the zone */
+};
+
+struct raid0_hash
+{
+  struct strip_zone *zone0, *zone1;
+};
+
+struct raid0_data
+{
+  struct raid0_hash *hash_table; /* Dynamically allocated */
+  struct strip_zone *strip_zone; /* This one too */
+  int nr_strip_zones;
+  struct strip_zone *smallest;
+  int nr_zones;
+};
+
+#endif
diff -u --recursive --new-file v1.3.68/linux/include/linux/soundcard.h linux/include/linux/soundcard.h
--- v1.3.68/linux/include/linux/soundcard.h	Wed Feb 14 14:37:19 1996
+++ linux/include/linux/soundcard.h	Fri Feb 23 14:54:40 1996
@@ -24,8 +24,8 @@
  * SUCH DAMAGE.
  */
 
-#define SOUND_VERSION	301
-#define VOXWARE
+#define SOUND_VERSION	350
+#define UNIX_SOUND_SYSTEM
 
 #include <sys/ioctl.h>
 
@@ -597,6 +597,7 @@
 							/* Sometimes it's a DSP */
 							/* but usually not */
 #	define DSP_CAP_TRIGGER		0x00001000	/* Supports SETTRIGGER */
+#	define DSP_CAP_MMAP		0x00002000	/* Supports mmap() */
 
 #define SNDCTL_DSP_GETTRIGGER		_IOR ('P',16, int)
 #define SNDCTL_DSP_SETTRIGGER		_IOW ('P',16, int)
@@ -730,17 +731,17 @@
 /* Not counted to SOUND_MIXER_NRDEVICES, but use the same number space */
 #define SOUND_ONOFF_MIN		28
 #define SOUND_ONOFF_MAX		30
-#define SOUND_MIXER_MUTE	28	/* 0 or 1 */
-#define SOUND_MIXER_LOUD	30	/* 0 or 1 */
 
 /* Note!	Number 31 cannot be used since the sign bit is reserved */
 
 
 /*
- * SOUND_MIXER_ENHANCE is an unsupported and undocumented call which
- * will be removed from the API in future.
+ * The following unsupported macros will be removed from the API in near
+ * future.
  */
 #define SOUND_MIXER_ENHANCE	29	/* Enhanced stereo (0, 40, 60 or 80) */
+#define SOUND_MIXER_MUTE	28	/* 0 or 1 */
+#define SOUND_MIXER_LOUD	30	/* 0 or 1 */
 
 
 
diff -u --recursive --new-file v1.3.68/linux/include/linux/swap.h linux/include/linux/swap.h
--- v1.3.68/linux/include/linux/swap.h	Fri Jan 26 01:37:07 1996
+++ linux/include/linux/swap.h	Sun Feb 25 11:17:59 1996
@@ -80,8 +80,6 @@
  */
 #define SHM_SWP_TYPE 0x40
 
-extern void shm_no_page (ulong *);
-
 /*
  * swap cache stuff (in linux/mm/swap_state.c)
  */
diff -u --recursive --new-file v1.3.68/linux/include/linux/sysctl.h linux/include/linux/sysctl.h
--- v1.3.68/linux/include/linux/sysctl.h	Wed Feb  7 15:11:38 1996
+++ linux/include/linux/sysctl.h	Mon Feb 26 11:58:16 1996
@@ -60,7 +60,8 @@
 #define VM_SWAPCTL	1	/* struct: Set vm swapping control */
 #define VM_KSWAPD	2	/* struct: control background pagout */
 #define VM_FREEPG	3	/* struct: Set free page thresholds */
-#define VM_MAXID	4
+#define VM_BDFLUSH	4	/* struct: Control buffer cache flushing */
+#define VM_MAXID	5
 
 /* CTL_NET names: */
 
@@ -91,6 +92,8 @@
 			 void *, size_t *);
 extern int proc_dointvec(ctl_table *, int, struct file *,
 			 void *, size_t *);
+extern int proc_dointvec_minmax(ctl_table *, int, struct file *,
+				void *, size_t *);
 
 extern int do_sysctl (int *name, int nlen,
 		      void *oldval, size_t *oldlenp,
@@ -102,6 +105,7 @@
 			       void *newval, size_t newlen, void ** context);
 
 extern ctl_handler sysctl_string;
+extern ctl_handler sysctl_intvec;
 
 extern int do_string (
 	void *oldval, size_t *oldlenp, void *newval, size_t newlen,
@@ -163,6 +167,8 @@
 	proc_handler *proc_handler;	/* Callback for text formatting */
 	ctl_handler *strategy;		/* Callback function for all r/w */
 	struct proc_dir_entry *de;	/* /proc control block */
+	void *extra1;
+	void *extra2;
 };
 
 /* struct ctl_table_header is used to maintain dynamic lists of
diff -u --recursive --new-file v1.3.68/linux/include/linux/sysv_fs.h linux/include/linux/sysv_fs.h
--- v1.3.68/linux/include/linux/sysv_fs.h	Tue Jan 23 21:15:54 1996
+++ linux/include/linux/sysv_fs.h	Tue Feb 27 13:43:51 1996
@@ -316,8 +316,6 @@
 #define SYSV_DIRSIZE	sizeof(struct sysv_dir_entry)	/* size of every directory entry */
 
 
-#ifdef __KERNEL__
-
 /* Operations */
 /* ========== */
 
@@ -334,6 +332,8 @@
 #define SYSV4_SUPER_MAGIC	(SYSV_MAGIC_BASE+FSTYPE_SYSV4)
 #define SYSV2_SUPER_MAGIC	(SYSV_MAGIC_BASE+FSTYPE_SYSV2)
 #define COH_SUPER_MAGIC		(SYSV_MAGIC_BASE+FSTYPE_COH)
+
+#ifdef __KERNEL__
 
 /* sv_get_hash_table(sb,dev,block) is equivalent to  get_hash_table(dev,block,block_size)  */
 static inline struct buffer_head *
diff -u --recursive --new-file v1.3.68/linux/include/linux/timer.h linux/include/linux/timer.h
--- v1.3.68/linux/include/linux/timer.h	Sun Jan 14 16:30:15 1996
+++ linux/include/linux/timer.h	Fri Feb 23 09:39:35 1996
@@ -35,8 +35,6 @@
  *
  * GSCD_TIMER		Goldstar CD-ROM Timer
  *
- * OPTCD_TIMER		Optics Storage CD-ROM Timer
- *
  */
 
 #define BLANK_TIMER	0
@@ -56,7 +54,6 @@
 
 #define HD_TIMER2	24
 #define GSCD_TIMER	25
-#define OPTCD_TIMER	26
 
 struct timer_struct {
 	unsigned long expires;
diff -u --recursive --new-file v1.3.68/linux/include/linux/ultrasound.h linux/include/linux/ultrasound.h
--- v1.3.68/linux/include/linux/ultrasound.h	Wed Feb 14 14:37:19 1996
+++ linux/include/linux/ultrasound.h	Fri Feb 23 14:54:40 1996
@@ -29,6 +29,7 @@
  * SUCH DAMAGE.
  */
 
+
 /*
  *	Private events for Gravis Ultrasound (GUS)
  *
diff -u --recursive --new-file v1.3.68/linux/include/net/sock.h linux/include/net/sock.h
--- v1.3.68/linux/include/net/sock.h	Fri Feb 23 13:54:40 1996
+++ linux/include/net/sock.h	Tue Feb 27 13:35:21 1996
@@ -86,7 +86,61 @@
 	char			device_name[15];
 };
 
+/*
+ *	Once the IPX ncpd patches are in these are going into protinfo
+ */
 
+#ifdef CONFIG_IPX 
+struct ipx_opt
+{
+	ipx_address		dest_addr;
+	ipx_interface		*intrfc;
+	unsigned short		port;
+#ifdef CONFIG_IPX_INTERN
+	unsigned char           node[IPX_NODE_LEN];
+#endif
+	unsigned short		type;
+/* 
+ * To handle asynchronous messages from the NetWare server, we have to
+ * know the connection this socket belongs to. 
+ */
+	struct ncp_server       *ncp_server;
+	
+};
+#endif
+
+#ifdef CONFIG_NUTCP
+struct tcp_opt
+{
+/*
+ *	RFC793 variables by their proper names. This means you can
+ *	read the code and the spec side by side (and laugh ...)
+ *	See RFC793 and RFC1122. The RFC writes these in capitals.
+ */
+ 	__u32	rcv_nxt;	/* What we want to receive next 	*/
+ 	__u32	rcv_up;		/* The urgent point (may not be valid) 	*/
+ 	__u32	rcv_wnd;	/* Current receiver window		*/
+ 	__u32	snd_nxt;	/* Next sequence we send		*/
+ 	__u32	snd_una;	/* First byte we want an ack for	*/
+	__u32	snd_up;		/* Outgoing urgent pointer		*/
+	__u32	snd_wl1;	/* Sequence for window update		*/
+	__u32	snd_wl2;	/* Ack sequence for update		*/
+/*
+ *	Slow start and congestion control (see also Nagle, and Karn & Partridge)
+ */
+ 	__u32	snd_cwnd;	/* Sending congestion window		*/
+ 	__u32	snd_ssthresh;	/* Slow start size threshold		*/
+/*
+ *	Timers used by the TCP protocol layer
+ */
+ 	struct timer_list	delack_timer;		/* Ack delay 	*/
+ 	struct timer_list	idle_timer;		/* Idle watch 	*/
+ 	struct timer_list	completion_timer;	/* Up/Down timer */
+ 	struct timer_list	probe_timer;		/* Probes	*/
+ 	struct timer_list	retransmit_timer;	/* Resend (no ack) */
+};
+#endif
+ 	
 /*
  * This structure really needs to be cleaned up.
  * Most of it is for TCP, and not used by any of
@@ -190,24 +244,6 @@
 	unsigned short		sndbuf;
 	unsigned short		type;
 	unsigned char		localroute;	/* Route locally only */
-#ifdef CONFIG_IPX
-/*
- *	Once the IPX ncpd patches are in these are going into protinfo
- */
-	ipx_address		ipx_dest_addr;
-	ipx_interface		*ipx_intrfc;
-	unsigned short		ipx_port;
-
-/* To handle asynchronous messages from the NetWare server, we have to
- * know the connection this socket belongs to. Sorry to blow up this
- * structure even more. */
-	struct ncp_server       *ipx_ncp_server;
-
-#ifdef CONFIG_IPX_INTERN
-	unsigned char           ipx_node[IPX_NODE_LEN];
-#endif
-	unsigned short		ipx_type;
-#endif
 #ifdef CONFIG_AX25
 	ax25_cb			*ax25;
 #ifdef CONFIG_NETROM
@@ -226,8 +262,14 @@
 #ifdef CONFIG_ATALK
 		struct atalk_sock	af_at;
 #endif
+#ifdef CONFIG_IPX
+		struct ipx_opt		af_ipx;
+#endif		
 #ifdef CONFIG_INET
 		struct inet_packet_opt  af_packet;
+#ifdef CONFIG_NUTCP		
+		struct tcp_opt		af_tcp;
+#endif		
 #endif
 	} protinfo;  		
 
@@ -331,18 +373,18 @@
 #define TIME_DESTROY	4
 #define TIME_DONE	5	/* Used to absorb those last few packets */
 #define TIME_PROBE0	6
+
 /*
  *	About 10 seconds 
  */
-#define SOCK_DESTROY_TIME (10*HZ)
 
+#define SOCK_DESTROY_TIME (10*HZ)
 
 /*
  *	Sockets 0-1023 can't be bound too unless you are superuser 
  */
  
 #define PROT_SOCK	1024
-
 
 #define SHUTDOWN_MASK	3
 #define RCV_SHUTDOWN	1
diff -u --recursive --new-file v1.3.68/linux/init/main.c linux/init/main.c
--- v1.3.68/linux/init/main.c	Wed Feb 14 14:37:19 1996
+++ linux/init/main.c	Mon Feb 26 11:58:17 1996
@@ -116,6 +116,12 @@
 static void load_ramdisk(char *str, int *ints);
 static void prompt_ramdisk(char *str, int *ints);
 #endif CONFIG_BLK_DEV_RAM
+#ifdef CONFIG_ISDN_DRV_ICN
+extern void icn_setup(char *str, int *ints);
+#endif
+#ifdef CONFIG_ISDN_DRV_TELES
+extern void teles_setup(char *str, int *ints);
+#endif
 
 #if defined(CONFIG_SYSVIPC) || defined(CONFIG_KERNELD)
 extern void ipc_init(void);
@@ -297,6 +303,12 @@
 #endif CONFIG_ISP16_CDI
 #ifdef CONFIG_SOUND
 	{ "sound=", sound_setup },
+#endif
+#ifdef CONFIG_ISDN_DRV_ICN
+	{ "icn=", icn_setup },
+#endif
+#ifdef CONFIG_ISDN_DRV_TELES
+	{ "teles=", teles_setup },
 #endif
 	{ 0, 0 }
 };
diff -u --recursive --new-file v1.3.68/linux/ipc/msg.c linux/ipc/msg.c
--- v1.3.68/linux/ipc/msg.c	Wed Feb  7 15:11:39 1996
+++ linux/ipc/msg.c	Sun Feb 25 11:17:59 1996
@@ -189,14 +189,15 @@
 	while (!nmsg) {
 		if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI)
 			return -EIDRM;
-		if ((msgflg & IPC_KERNELD) == 0)
+		if ((msgflg & IPC_KERNELD) == 0) {
 			/*
 			 * Non-root processes may receive from kerneld! 
 			 * i.e. no permission check if called from the kernel
 			 * otoh we don't want user level non-root snoopers...
 			 */
-		if (ipcperms (ipcp, S_IRUGO))
-			return -EACCES;
+			if (ipcperms (ipcp, S_IRUGO))
+				return -EACCES;
+		}
 		if (msgtyp == 0) 
 			nmsg = msq->msg_first;
 		else if (msgtyp > 0) {
diff -u --recursive --new-file v1.3.68/linux/ipc/shm.c linux/ipc/shm.c
--- v1.3.68/linux/ipc/shm.c	Sun Jan 14 16:30:15 1996
+++ linux/ipc/shm.c	Sun Feb 25 11:17:59 1996
@@ -351,7 +351,7 @@
  * The per process internal structure for managing segments is
  * `struct vm_area_struct'.
  * A shmat will add to and shmdt will remove from the list.
- * shmd->vm_task	the attacher
+ * shmd->vm_mm		the attacher
  * shmd->vm_start	virt addr of attach, multiple of SHMLBA
  * shmd->vm_end		multiple of SHMLBA
  * shmd->vm_next	next attach for task
@@ -765,7 +765,7 @@
 		mem_map[MAP_NR(pte_page(pte))].count--;
 		if (shmd->vm_mm->rss > 0)
 			shmd->vm_mm->rss--;
-		invalidate_range(shmd->vm_mm, shmd->vm_start, shmd->vm_end);
+		invalidate_page(shmd, tmp);
 	    /* continue looping through circular list */
 	    } while (0);
 	    if ((shmd = shmd->vm_next_share) == shp->attaches)
diff -u --recursive --new-file v1.3.68/linux/kernel/ksyms.c linux/kernel/ksyms.c
--- v1.3.68/linux/kernel/ksyms.c	Fri Feb 23 13:54:41 1996
+++ linux/kernel/ksyms.c	Mon Feb 26 11:58:17 1996
@@ -226,11 +226,11 @@
 	X(bmap),
 	X(sync_dev),
 	X(get_blkfops),
-	
+#ifdef CONFIG_SERIAL	
 	/* Module creation of serial units */
 	X(register_serial),
 	X(unregister_serial),
-
+#endif
 	/* tty routines */
 	X(tty_hangup),
 	X(tty_wait_until_sent),
diff -u --recursive --new-file v1.3.68/linux/kernel/sysctl.c linux/kernel/sysctl.c
--- v1.3.68/linux/kernel/sysctl.c	Wed Feb  7 15:11:40 1996
+++ linux/kernel/sysctl.c	Mon Feb 26 11:58:17 1996
@@ -3,6 +3,7 @@
  *
  * Begun 24 March 1995, Stephen Tweedie
  * Added /proc support, Dec 1995
+ * Added bdflush entry and intvec min/max checking, 2/23/96, Tom Dyas.
  */
 
 #include <linux/config.h>
@@ -81,6 +82,8 @@
 static void unregister_proc_table(ctl_table *, struct proc_dir_entry *);
 #endif
 
+extern int bdf_prm[], bdflush_min[], bdflush_max[];
+
 static int do_securelevel_strategy (ctl_table *, int *, int, void *, size_t *,
 				    void *, size_t, void **);
 
@@ -123,6 +126,9 @@
 	 &kswapd_ctl, sizeof(kswapd_ctl), 0600, NULL, &proc_dointvec},
 	{VM_FREEPG, "freepages", 
 	 &min_free_pages, 3*sizeof(int), 0600, NULL, &proc_dointvec},
+	{VM_BDFLUSH, "bdflush", &bdf_prm, 9*sizeof(int), 0600, NULL,
+	 &proc_dointvec_minmax, &sysctl_intvec, NULL,
+	 &bdflush_min, &bdflush_max},
 	{0}
 };
 
@@ -582,6 +588,88 @@
 	return 0;
 }
 
+int proc_dointvec_minmax(ctl_table *table, int write, struct file *filp,
+		  void *buffer, size_t *lenp)
+{
+	int *i, *min, *max, vleft, first=1, len, left, neg, val;
+	#define TMPBUFLEN 20
+	char buf[TMPBUFLEN], *p;
+	
+	if (!table->data || !table->maxlen || !*lenp ||
+	    (filp->f_pos && !write)) {
+		*lenp = 0;
+		return 0;
+	}
+	
+	i = (int *) table->data;
+	min = (int *) table->extra1;
+	max = (int *) table->extra2;
+	vleft = table->maxlen / sizeof(int);
+	left = *lenp;
+	
+	for (; left && vleft--; i++, first=0) {
+		if (write) {
+			while (left && isspace(get_user((char *) buffer)))
+				left--, ((char *) buffer)++;
+			if (!left)
+				break;
+			neg = 0;
+			len = left;
+			if (len > TMPBUFLEN-1)
+				len = TMPBUFLEN-1;
+			memcpy_fromfs(buf, buffer, len);
+			buf[len] = 0;
+			p = buf;
+			if (*p == '-' && left > 1) {
+				neg = 1;
+				left--, p++;
+			}
+			if (*p < '0' || *p > '9')
+				break;
+			val = simple_strtoul(p, &p, 0);
+			len = p-buf;
+			if ((len < left) && *p && !isspace(*p))
+				break;
+			if (neg)
+				val = -val;
+			buffer += len;
+			left -= len;
+
+			if (min && val < *min++)
+				continue;
+			if (max && val > *max++)
+				continue;
+			*i = val;
+		} else {
+			p = buf;
+			if (!first)
+				*p++ = '\t';
+			sprintf(p, "%d", *i);
+			len = strlen(buf);
+			if (len > left)
+				len = left;
+			memcpy_tofs(buffer, buf, len);
+			left -= len;
+			buffer += len;
+		}
+	}
+
+	if (!write && !first && left) {
+		put_user('\n', (char *) buffer);
+		left--, buffer++;
+	}
+	if (write) {
+		p = (char *) buffer;
+		while (left && isspace(get_user(p++)))
+			left--;
+	}
+	if (write && first)
+		return -EINVAL;
+	*lenp -= left;
+	filp->f_pos += *lenp;
+	return 0;
+}
+
 #else /* CONFIG_PROC_FS */
 
 int proc_dostring(ctl_table *table, int write, struct file *filp,
@@ -596,6 +684,12 @@
 	return -ENOSYS;
 }
 
+int proc_dointvec_minmax(ctl_table *table, int write, struct file *filp,
+		    void *buffer, size_t *lenp)
+{
+	return -ENOSYS;
+}
+
 #endif /* CONFIG_PROC_FS */
 
 
@@ -631,6 +725,43 @@
 		if (len == table->maxlen)
 			len--;
 		((char *) table->data)[len] = 0;
+	}
+	return 0;
+}
+
+/*
+ * This function makes sure that all of the integers in the vector
+ * are between the minimum and maximum values given in the arrays
+ * table->extra1 and table->extra2, respectively.
+ */
+int sysctl_intvec(ctl_table *table, int *name, int nlen,
+		void *oldval, size_t *oldlenp,
+		void *newval, size_t newlen, void **context)
+{
+	int i, length, *vec, *min, *max;
+
+	if (newval && newlen) {
+		if (newlen % sizeof(int) != 0)
+			return -EINVAL;
+
+		if (!table->extra1 && !table->extra2)
+			return 0;
+
+		if (newlen > table->maxlen)
+			newlen = table->maxlen;
+		length = newlen / sizeof(int);
+
+		vec = (int *) newval;
+		min = (int *) table->extra1;
+		max = (int *) table->extra2;
+
+		for (i = 0; i < length; i++) {
+			int value = get_user(vec + i);
+			if (min && value < min[i])
+				return -EINVAL;
+			if (max && value > max[i])
+				return -EINVAL;
+		}
 	}
 	return 0;
 }
diff -u --recursive --new-file v1.3.68/linux/mm/filemap.c linux/mm/filemap.c
--- v1.3.68/linux/mm/filemap.c	Fri Feb 23 13:54:41 1996
+++ linux/mm/filemap.c	Tue Feb 27 11:06:13 1996
@@ -293,19 +293,24 @@
  * This is a generic file read routine, and uses the
  * inode->i_op->readpage() function for the actual low-level
  * stuff.
+ *
+ * This is really ugly. But the goto's actually try to clarify some
+ * of the logic when it comes to error handling etc.
  */
 #define MAX_READAHEAD (PAGE_SIZE*4)
 int generic_file_read(struct inode * inode, struct file * filp, char * buf, int count)
 {
-	int read = 0;
-	unsigned long pos;
-	unsigned long page_cache = 0;
+	int error, read;
+	unsigned long pos, page_cache;
 	
 	if (count <= 0)
 		return 0;
+	error = 0;
+	read = 0;
+	page_cache = 0;
 
 	pos = filp->f_pos;
-	do {
+	for (;;) {
 		struct page *page;
 		unsigned long offset, addr, nr;
 
@@ -324,14 +329,14 @@
 		 * Ok, it wasn't cached, so we need to create a new
 		 * page..
 		 */
-		if (!page_cache) {
-			page_cache = __get_free_page(GFP_KERNEL);
-			if (!page_cache) {
-				if (!read)
-					read = -ENOMEM;
-				break;
-			}
-		}
+		if (page_cache)
+			goto new_page;
+
+		error = -ENOMEM;
+		page_cache = __get_free_page(GFP_KERNEL);
+		if (!page_cache)
+			break;
+		error = 0;
 
 		/*
 		 * That could have slept, so we need to check again..
@@ -339,22 +344,8 @@
 		if (pos >= inode->i_size)
 			break;
 		page = find_page(inode, pos & PAGE_MASK);
-		if (page)
-			goto found_page;
-
-		/*
-		 * Ok, add the new page to the hash-queues...
-		 */
-		page = mem_map + MAP_NR(page_cache);
-		page_cache = 0;
-		page->count++;
-		page->uptodate = 0;
-		page->error = 0;
-		page->offset = pos & PAGE_MASK;
-		add_page_to_inode_queue(inode, page);
-		add_page_to_hash_queue(inode, page);
-
-		inode->i_op->readpage(inode, page);
+		if (!page)
+			goto new_page;
 
 found_page:
 		addr = page_address(page);
@@ -369,15 +360,20 @@
 		 *  - if "f_reada" is set
 		 */
 		if (page->locked) {
-			if (nr < count || filp->f_reada) {
-				unsigned long ahead = 0;
-				do {
-					ahead += PAGE_SIZE;
-					page_cache = try_to_read_ahead(inode, pos + ahead, page_cache);
-				} while (ahead < MAX_READAHEAD);
+			unsigned long max_ahead, ahead;
+
+			max_ahead = count - nr;
+			if (filp->f_reada || max_ahead > MAX_READAHEAD)
+				max_ahead = MAX_READAHEAD;
+			ahead = 0;
+			while (ahead < max_ahead) {
+				ahead += PAGE_SIZE;
+				page_cache = try_to_read_ahead(inode, pos + ahead, page_cache);
 			}
 			__wait_on_page(page);
 		}
+		if (!page->uptodate)
+			goto read_page;
 		if (nr > inode->i_size - pos)
 			nr = inode->i_size - pos;
 		memcpy_tofs(buf, (void *) (addr + offset), nr);
@@ -386,7 +382,40 @@
 		pos += nr;
 		read += nr;
 		count -= nr;
-	} while (count);
+		if (count)
+			continue;
+		break;
+	
+
+new_page:
+		/*
+		 * Ok, add the new page to the hash-queues...
+		 */
+		addr = page_cache;
+		page = mem_map + MAP_NR(page_cache);
+		page_cache = 0;
+		page->count++;
+		page->uptodate = 0;
+		page->error = 0;
+		page->offset = pos & PAGE_MASK;
+		add_page_to_inode_queue(inode, page);
+		add_page_to_hash_queue(inode, page);
+
+		/*
+		 * Error handling is tricky. If we get a read error,
+		 * the cached page stays in the cache (but uptodate=0),
+		 * and the next process that accesses it will try to
+		 * re-read it. This is needed for NFS etc, where the
+		 * identity of the reader can decide if we can read the
+		 * page or not..
+		 */
+read_page:
+		error = inode->i_op->readpage(inode, page);
+		if (!error)
+			goto found_page;
+		free_page(addr);
+		break;
+	}
 
 	filp->f_pos = pos;
 	filp->f_reada = 1;
@@ -396,6 +425,8 @@
 		inode->i_atime = CURRENT_TIME;
 		inode->i_dirt = 1;
 	}
+	if (!read)
+		read = error;
 	return read;
 }
 
diff -u --recursive --new-file v1.3.68/linux/mm/kmalloc.c linux/mm/kmalloc.c
--- v1.3.68/linux/mm/kmalloc.c	Mon Sep 18 14:54:11 1995
+++ linux/mm/kmalloc.c	Tue Feb 27 15:19:02 1996
@@ -19,6 +19,8 @@
 #include <asm/system.h>
 #include <asm/dma.h>
 
+/* Define this is you want slow routines that try to trip errors */
+#undef SADISTIC_KMALLOC
 
 /* Private flags. */
 
@@ -270,6 +272,9 @@
 	sizes[order].nbytesmalloced += size;
 	p->bh_flags = type;	/* As of now this block is officially in use */
 	p->bh_length = size;
+#ifdef SADISTIC_KMALLOC
+	memset(p+1, 0xf0, size);
+#endif
 	return p + 1;		/* Pointer arithmetic: increments past header */
 }
 
@@ -302,6 +307,9 @@
 	}
 	size = p->bh_length;
 	p->bh_flags = MF_FREE;	/* As of now this block is officially free */
+#ifdef SADISTIC_KMALLOC
+	memset(p+1, 0xe0, size);
+#endif
 	save_flags(flags);
 	cli();
 	p->bh_next = page->firstfree;
diff -u --recursive --new-file v1.3.68/linux/mm/memory.c linux/mm/memory.c
--- v1.3.68/linux/mm/memory.c	Fri Feb 23 13:54:41 1996
+++ linux/mm/memory.c	Sun Feb 25 11:17:59 1996
@@ -431,6 +431,7 @@
 {
 	int error = 0;
 	pgd_t * dir;
+	unsigned long beg = address;
 	unsigned long end = address + size;
 	pte_t zero_pte;
 
@@ -447,7 +448,7 @@
 		address = (address + PGDIR_SIZE) & PGDIR_MASK;
 		dir++;
 	}
-	invalidate_range(current->mm, end - size, end);
+	invalidate_range(current->mm, beg, end);
 	return error;
 }
 
@@ -502,6 +503,7 @@
 {
 	int error = 0;
 	pgd_t * dir;
+	unsigned long beg = from;
 	unsigned long end = from + size;
 
 	offset -= from;
@@ -517,7 +519,7 @@
 		from = (from + PGDIR_SIZE) & PGDIR_MASK;
 		dir++;
 	}
-	invalidate_range(current->mm, from - size, from);
+	invalidate_range(current->mm, beg, from);
 	return error;
 }
 
diff -u --recursive --new-file v1.3.68/linux/mm/mlock.c linux/mm/mlock.c
--- v1.3.68/linux/mm/mlock.c	Tue Nov 21 13:22:14 1995
+++ linux/mm/mlock.c	Sun Feb 25 11:17:59 1996
@@ -106,12 +106,6 @@
 	if (newflags == vma->vm_flags)
 		return 0;
 
-	/* keep track of amount of locked VM */
-	pages = (end - start) >> PAGE_SHIFT;
-	if (!(newflags & VM_LOCKED))
-		pages = -pages;
-	vma->vm_mm->locked_vm += pages;
-
 	if (start == vma->vm_start) {
 		if (end == vma->vm_end)
 			retval = mlock_fixup_all(vma, newflags);
@@ -123,12 +117,19 @@
 		else
 			retval = mlock_fixup_middle(vma, start, end, newflags);
 	}
-	if (!retval && (newflags & VM_LOCKED)) {
-		while (start < end) {
-			char c = get_user((char *) start);
-			__asm__ __volatile__("": :"r" (c));
-			start += PAGE_SIZE;
-		}
+	if (!retval) {
+		/* keep track of amount of locked VM */
+		pages = (end - start) >> PAGE_SHIFT;
+		if (!(newflags & VM_LOCKED))
+			pages = -pages;
+		vma->vm_mm->locked_vm += pages;
+
+		if (newflags & VM_LOCKED)
+			while (start < end) {
+				char c = get_user((char *) start);
+				__asm__ __volatile__("": :"r" (c));
+				start += PAGE_SIZE;
+			}
 	}
 	return retval;
 }
diff -u --recursive --new-file v1.3.68/linux/mm/mmap.c linux/mm/mmap.c
--- v1.3.68/linux/mm/mmap.c	Tue Feb 20 14:37:29 1996
+++ linux/mm/mmap.c	Sun Feb 25 11:17:59 1996
@@ -208,18 +208,13 @@
 		addr = TASK_SIZE / 3;
 	addr = PAGE_ALIGN(addr);
 
-	for (vmm = current->mm->mmap; ; vmm = vmm->vm_next) {
+	for (vmm = find_vma(current, addr); ; vmm = vmm->vm_next) {
+		/* At this point:  (!vmm || addr < vmm->vm_end). */
 		if (TASK_SIZE - len < addr)
 			return 0;
-		if (!vmm)
+		if (!vmm || addr + len <= vmm->vm_start)
 			return addr;
-		if (addr > vmm->vm_end)
-			continue;
-		if (addr + len > vmm->vm_start) {
-			addr = vmm->vm_end;
-			continue;
-		}
-		return addr;
+		addr = vmm->vm_end;
 	}
 }
 
diff -u --recursive --new-file v1.3.68/linux/mm/page_io.c linux/mm/page_io.c
--- v1.3.68/linux/mm/page_io.c	Wed Feb  7 15:11:40 1996
+++ linux/mm/page_io.c	Sun Feb 25 11:17:59 1996
@@ -109,7 +109,7 @@
 		}
 		ll_rw_swap_file(rw,swapf->i_dev, zones, i,buf);
 	} else
-		printk("re_swap_page: no swap file or device\n");
+		printk("rw_swap_page: no swap file or device\n");
 	if (offset && !clear_bit(offset,p->swap_lockmap))
 		printk("rw_swap_page: lock already cleared\n");
 	wake_up(&lock_queue);
diff -u --recursive --new-file v1.3.68/linux/mm/vmscan.c linux/mm/vmscan.c
--- v1.3.68/linux/mm/vmscan.c	Wed Feb  7 15:11:40 1996
+++ linux/mm/vmscan.c	Sun Feb 25 11:17:59 1996
@@ -346,7 +346,7 @@
 			if (swap_out(i, limit, wait))
 				return 1;
 			state = 0;
-		} while(i--);
+		} while (i--);
 	}
 	return 0;
 }
diff -u --recursive --new-file v1.3.68/linux/net/core/dev.c linux/net/core/dev.c
--- v1.3.68/linux/net/core/dev.c	Fri Feb 23 13:54:41 1996
+++ linux/net/core/dev.c	Mon Feb 26 11:58:20 1996
@@ -216,7 +216,7 @@
 
 extern __inline__ void dev_load(const char *name)
 {
-        char *sptr;
+        const char *sptr;
  
         if(!dev_get(name)) {
 #ifdef CONFIG_NET_ALIAS
@@ -1317,6 +1317,7 @@
  *
  */
 extern int lance_init(void);
+extern int ni65_init(void);
 extern int pi_init(void);
 extern int dec21040_init(void);
 
@@ -1337,6 +1338,9 @@
 	 */
 #if defined(CONFIG_LANCE)
 	lance_init();
+#endif
+#if defined(CONFIG_NI65)
+        ni65_init();
 #endif
 #if defined(CONFIG_PI)
 	pi_init();
diff -u --recursive --new-file v1.3.68/linux/net/core/sock.c linux/net/core/sock.c
--- v1.3.68/linux/net/core/sock.c	Fri Feb 23 13:54:42 1996
+++ linux/net/core/sock.c	Mon Feb 26 11:58:20 1996
@@ -520,8 +520,6 @@
 			if (sk->wmem_alloc + size >= sk->sndbuf) 
 #endif
 			{
-				if (sk->wmem_alloc < 0)
-				  printk("sock.c: Look where I am %ld<%ld\n", tmp, sk->wmem_alloc);
 				sk->socket->flags &= ~SO_NOSPACE;
 				interruptible_sleep_on(sk->sleep);
 				if (current->signal & ~current->blocked) 
diff -u --recursive --new-file v1.3.68/linux/net/ipv4/af_inet.c linux/net/ipv4/af_inet.c
--- v1.3.68/linux/net/ipv4/af_inet.c	Fri Feb 23 13:54:42 1996
+++ linux/net/ipv4/af_inet.c	Fri Feb 23 10:46:33 1996
@@ -1162,7 +1162,7 @@
 		return(-EINVAL);
 	if (sock->state == SS_CONNECTING && sk->state == TCP_ESTABLISHED)
 		sock->state = SS_CONNECTED;
-	if (!tcp_connected(sk->state)) 
+	if (!sk || !tcp_connected(sk->state)) 
 		return(-ENOTCONN);
 	sk->shutdown |= how;
 	if (sk->prot->shutdown)
diff -u --recursive --new-file v1.3.68/linux/net/ipv4/arp.c linux/net/ipv4/arp.c
--- v1.3.68/linux/net/ipv4/arp.c	Sat Feb 17 16:02:58 1996
+++ linux/net/ipv4/arp.c	Mon Feb 26 11:58:20 1996
@@ -51,6 +51,7 @@
  *					of host down events.
  *		Alan Cox	:	Missing unlock in device events.
  *		Eckes		:	ARP ioctl control errors.
+ *		Alexey Kuznetsov:	Arp free fix.
  */
 
 /* RFC1122 Status:
@@ -331,6 +332,7 @@
 	{
 		next = hh->hh_next;
 		hh->hh_arp = NULL;
+		hh->hh_uptodate = 0;
 		if (!--hh->hh_refcnt)
 			kfree_s(hh, sizeof(struct(struct hh_cache)));
 	}
diff -u --recursive --new-file v1.3.68/linux/net/ipv4/icmp.c linux/net/ipv4/icmp.c
--- v1.3.68/linux/net/ipv4/icmp.c	Mon Nov 27 12:48:36 1995
+++ linux/net/ipv4/icmp.c	Mon Feb 26 11:58:20 1996
@@ -17,6 +17,7 @@
  *		Mike Shaver	:	RFC1122 checks.
  *		Alan Cox	:	Multicast ping reply as self.
  *		Alan Cox	:	Fix atomicity lockup in ip_build_xmit call
+ *		Alan Cox	:	Added 216,128 byte paths to the MTU code.
  *
  *
  *
@@ -356,8 +357,8 @@
 			case ICMP_HOST_UNREACH:
 				break;
 			case ICMP_PROT_UNREACH:
-				printk(KERN_INFO "ICMP: %s:%d: protocol unreachable.\n",
-					in_ntoa(iph->daddr), ntohs(iph->protocol));
+/*				printk(KERN_INFO "ICMP: %s:%d: protocol unreachable.\n",
+					in_ntoa(iph->daddr), ntohs(iph->protocol));*/
 				break;
 			case ICMP_PORT_UNREACH:
 				break;
@@ -396,7 +397,18 @@
 						new_mtu = 576;
 					else if (old_mtu > 296)
 						new_mtu = 296;
+					/*
+					 *	These two are not from the RFC but
+					 *	are needed for AMPRnet AX.25 paths.
+					 */
+					else if (old_mtu > 216)
+						old_mtu = 216;
+					else if (old_mtu > 128)
+						old_mtu = 128;
 					else
+					/*
+					 *	Despair..
+					 */
 						new_mtu = 68;
 				}
 				/*
diff -u --recursive --new-file v1.3.68/linux/net/ipv4/packet.c linux/net/ipv4/packet.c
--- v1.3.68/linux/net/ipv4/packet.c	Tue Feb 20 14:37:30 1996
+++ linux/net/ipv4/packet.c	Mon Feb 26 11:58:20 1996
@@ -240,10 +240,10 @@
 }
 
 /*
- *	Attach a packer hook to a device.
+ *	Attach a packet hook to a device.
  */
 
-int packet_attach(struct sock *sk)
+int packet_attach(struct sock *sk, struct device *dev)
 {
 	struct packet_type *p = (struct packet_type *) kmalloc(sizeof(*p), GFP_KERNEL);
 	if (p == NULL) 
@@ -252,7 +252,7 @@
 	p->func = packet_rcv;
 	p->type = sk->num;
 	p->data = (void *)sk;
-	p->dev = NULL;
+	p->dev = dev;
 	dev_add_pack(p);
    
 	/*
@@ -260,6 +260,7 @@
 	 */
    
 	sk->protinfo.af_packet.prot_hook = p;
+	sk->protinfo.af_packet.bound_dev = dev;
 	return 0;	
 }
  
@@ -269,7 +270,8 @@
 
 static int packet_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
 {
-	char dev[15];
+	char name[15];
+	struct device *dev;
 	
 	/*
 	 *	Check legality
@@ -277,8 +279,8 @@
 	 
 	if(addr_len!=sizeof(struct sockaddr))
 		return -EINVAL;
-	strncpy(dev,uaddr->sa_data,14);
-	dev[14]=0;
+	strncpy(name,uaddr->sa_data,14);
+	name[14]=0;
 	
 	/*
 	 *	Lock the device chain while we sanity check
@@ -286,12 +288,14 @@
 	 */
 	 
 	dev_lock_list();
-	if((sk->protinfo.af_packet.bound_dev=dev_get(dev))==NULL)
+	dev=dev_get(name);
+	if(dev==NULL)
 	{
 		dev_unlock_list();
 		return -ENODEV;
 	}
-	if(!(sk->protinfo.af_packet.bound_dev->flags&IFF_UP))
+	
+	if(!(dev->flags&IFF_UP))
 	{
 		dev_unlock_list();
 		return -ENETDOWN;
@@ -301,20 +305,28 @@
 	 *	Perform the request.
 	 */
 	 
-	memcpy(sk->protinfo.af_packet.device_name,dev,15);
+	memcpy(sk->protinfo.af_packet.device_name,name,15);
+	
+	/*
+	 *	Rewrite an existing hook if present.
+	 */
+	 
 	if(sk->protinfo.af_packet.prot_hook)
+	{
 		dev_remove_pack(sk->protinfo.af_packet.prot_hook);
+		sk->protinfo.af_packet.prot_hook->dev=dev;
+		sk->protinfo.af_packet.bound_dev=dev;
+		dev_add_pack(sk->protinfo.af_packet.prot_hook);
+	}
 	else
 	{
-		int err=packet_attach(sk);
+		int err=packet_attach(sk, dev);
 		if(err)
 		{
 			dev_unlock_list();
 			return err;
 		}
 	}
-	sk->protinfo.af_packet.prot_hook->dev=sk->protinfo.af_packet.bound_dev;
-	dev_add_pack(sk->protinfo.af_packet.prot_hook);
 	/*
 	 *	Now the notifier is set up right this lot is safe.
 	 */
@@ -354,7 +366,7 @@
 	 *	Attach a protocol block
 	 */
 	 
-	int err=packet_attach(sk);
+	int err=packet_attach(sk, NULL);
 	if(err)
 		return err;
 		
diff -u --recursive --new-file v1.3.68/linux/net/ipv4/tcp_input.c linux/net/ipv4/tcp_input.c
--- v1.3.68/linux/net/ipv4/tcp_input.c	Tue Feb 20 14:37:30 1996
+++ linux/net/ipv4/tcp_input.c	Mon Feb 26 11:58:21 1996
@@ -18,6 +18,9 @@
  *		Matthew Dillon, <dillon@apollo.west.oic.com>
  *		Arnt Gulbrandsen, <agulbra@nvg.unit.no>
  *		Jorge Cwik, <jorge@laser.satlink.net>
+ *
+ * FIXES
+ *		Pedro Roque	:	Double ACK bug
  */
 
 #include <linux/config.h>
@@ -1420,19 +1423,21 @@
 			 *      - must send at least every 2 full sized packets
 			 */
 			if (!sk->delay_acks ||
-			    sk->ack_backlog >= sk->max_ack_backlog || 
+			    /* sk->ack_backlog >= sk->max_ack_backlog || */
 			    sk->bytes_rcv > sk->max_unacked || th->fin ||
 			    sk->ato > HZ/2 ||
 			    tcp_raise_window(sk)) {
-	/*			tcp_send_ack(sk->sent_seq, sk->acked_seq,sk,th, saddr); */
+				tcp_send_ack(sk->sent_seq, sk->acked_seq,sk,th, saddr);
 			}
 			else 
-			{
+			{	
 				sk->ack_backlog++;
-				
+			
 				if(sk->debug)				
 					printk("Ack queued.\n");
+				
 				tcp_reset_xmit_timer(sk, TIME_WRITE, sk->ato);
+				
 			}
 		}
 	}
@@ -1475,11 +1480,7 @@
 		}
 		tcp_send_ack(sk->sent_seq, sk->acked_seq, sk, th, saddr);
 		sk->ack_backlog++;
-		tcp_reset_xmit_timer(sk, TIME_WRITE, min(sk->ato, 0.5 * HZ));
-	}
-	else
-	{
-		tcp_send_ack(sk->sent_seq, sk->acked_seq, sk, th, saddr);
+		tcp_reset_xmit_timer(sk, TIME_WRITE, min(sk->ato, HZ/2));
 	}
 
 	/*
diff -u --recursive --new-file v1.3.68/linux/net/ipx/af_ipx.c linux/net/ipx/af_ipx.c
--- v1.3.68/linux/net/ipx/af_ipx.c	Fri Feb 23 13:54:44 1996
+++ linux/net/ipx/af_ipx.c	Mon Feb 26 11:58:21 1996
@@ -39,6 +39,8 @@
  *	Revision 0.31:	New sk_buffs. This still needs a lot of testing. <Alan Cox>
  *	Revision 0.32:  Using sock_alloc_send_skb, firewall hooks. <Alan Cox>
  *			Supports sendmsg/recvmsg
+ *	Revision 0.33:	Internal network support, routing changes, uses a
+ *			protocol private area for ipx data.
  *
  * 	Portions Copyright (c) 1995 Caldera, Inc. <greg@caldera.com>
  *	Neither Greg Page nor Caldera, Inc. admit liability nor provide 
@@ -143,7 +145,7 @@
 	cli();
 	
 	/* Determine interface with which socket is associated */
-	intrfc = sk->ipx_intrfc;
+	intrfc = sk->protinfo.af_ipx.intrfc;
 	if (intrfc == NULL) {
 		restore_flags(flags);
 		return;
@@ -234,7 +236,7 @@
 {
 	ipx_socket	*s;
 
-	sk->ipx_intrfc = intrfc;
+	sk->protinfo.af_ipx.intrfc = intrfc;
 	sk->next = NULL;
 	if (intrfc->if_sklist == NULL) {
 		intrfc->if_sklist = sk;
@@ -251,7 +253,7 @@
 	ipx_socket	*s;
 
 	for (s=intrfc->if_sklist; 
-		(s != NULL) && (s->ipx_port != port); 
+		(s != NULL) && (s->protinfo.af_ipx.port != port); 
 		s=s->next)
 		;
 
@@ -268,8 +270,8 @@
 
 	while (s != NULL)
 	{
-		if (   (s->ipx_port == port)
-		    && (memcmp(node, s->ipx_node, IPX_NODE_LEN) == 0))
+		if (   (s->protinfo.af_ipx.port == port)
+		    && (memcmp(node, s->protinfo.af_ipx.node, IPX_NODE_LEN) == 0))
 		{
 			break;
 		}
@@ -294,8 +296,8 @@
 	for (s = intrfc->if_sklist; s != NULL; ) {
 		s->err = ENOLINK;
 		s->error_report(s);
-		s->ipx_intrfc = NULL;
-		s->ipx_port = 0;
+		s->protinfo.af_ipx.intrfc = NULL;
+		s->protinfo.af_ipx.port = 0;
 		s->zapped=1;	/* Indicates it is no longer bound */
 		t = s;
 		s = s->next;
@@ -378,9 +380,9 @@
 
 	while (s != NULL)
 	{
-		if (   (s->ipx_port == ipx->ipx_dest.sock)
+		if (   (s->protinfo.af_ipx.port == ipx->ipx_dest.sock)
 		    && (   is_broadcast
-			|| (memcmp(ipx->ipx_dest.node, s->ipx_node,
+			|| (memcmp(ipx->ipx_dest.node, s->protinfo.af_ipx.node,
 				   IPX_NODE_LEN) == 0)))
 		{
 			/* We found a socket to which to send */
@@ -1200,11 +1202,11 @@
 	ipx->ipx_type=usipx->sipx_type;
 	skb->h.raw = (unsigned char *)ipx;
 
-	ipx->ipx_source.net = sk->ipx_intrfc->if_netnum;
+	ipx->ipx_source.net = sk->protinfo.af_ipx.intrfc->if_netnum;
 #ifdef CONFIG_IPX_INTERN
-	memcpy(ipx->ipx_source.node, sk->ipx_node, IPX_NODE_LEN);
+	memcpy(ipx->ipx_source.node, sk->protinfo.af_ipx.node, IPX_NODE_LEN);
 #else
-	if ((err = ntohs(sk->ipx_port)) == 0x453 || err == 0x452)  
+	if ((err = ntohs(sk->protinfo.af_ipx.port)) == 0x453 || err == 0x452)  
 	{
 		/* RIP/SAP special handling for mars_nwe */
 		ipx->ipx_source.net = intrfc->if_netnum;
@@ -1212,11 +1214,11 @@
 	}
 	else
 	{
-		ipx->ipx_source.net = sk->ipx_intrfc->if_netnum;
-		memcpy(ipx->ipx_source.node, sk->ipx_intrfc->if_node, IPX_NODE_LEN);
+		ipx->ipx_source.net = sk->protinfo.af_ipx.intrfc->if_netnum;
+		memcpy(ipx->ipx_source.node, sk->protinfo.af_ipx.intrfc->if_node, IPX_NODE_LEN);
 	}
 #endif
-	ipx->ipx_source.sock = sk->ipx_port;
+	ipx->ipx_source.sock = sk->protinfo.af_ipx.port;
 	ipx->ipx_dest.net=usipx->sipx_network;
 	memcpy(ipx->ipx_dest.node,usipx->sipx_node,IPX_NODE_LEN);
 	ipx->ipx_dest.sock=usipx->sipx_port;
@@ -1384,25 +1386,31 @@
 			len += sprintf(buffer+len,
 				       "%08lX:%02X%02X%02X%02X%02X%02X:%04X  ", 
 				       htonl(s->ipx_intrfc->if_netnum),
-				       s->ipx_node[0], s->ipx_node[1], 
-				       s->ipx_node[2], s->ipx_node[3], 
-				       s->ipx_node[4], s->ipx_node[5],
-				       htons(s->ipx_port));
+				       s->protinfo.af_ipx.node[0],
+				       s->protinfo.af_ipx.node[1], 
+				       s->protinfo.af_ipx.node[2], 
+				       s->protinfo.af_ipx.node[3], 
+				       s->protinfo.af_ipx.node[4], 
+				       s->protinfo.af_ipx.node[5],
+				       htons(s->protinfo.af_ipx.port));
 #else
 			len += sprintf(buffer+len,"%08lX:%04X  ", 
 				       htonl(i->if_netnum),
-				       htons(s->ipx_port));
+				       htons(s->protinfo.af_ipx.port));
 #endif
 			if (s->state!=TCP_ESTABLISHED) {
 				len += sprintf(buffer+len, "%-28s", "Not_Connected");
 			} else {
 				len += sprintf (buffer+len,
 					"%08lX:%02X%02X%02X%02X%02X%02X:%04X  ", 
-					htonl(s->ipx_dest_addr.net),
-					s->ipx_dest_addr.node[0], s->ipx_dest_addr.node[1], 
-					s->ipx_dest_addr.node[2], s->ipx_dest_addr.node[3], 
-					s->ipx_dest_addr.node[4], s->ipx_dest_addr.node[5],
-					htons(s->ipx_dest_addr.sock));
+					htonl(s->protinfo.af_ipx.dest_addr.net),
+					s->protinfo.af_ipx.dest_addr.node[0],
+					s->protinfo.af_ipx.dest_addr.node[1], 
+					s->protinfo.af_ipx.dest_addr.node[2],
+					s->protinfo.af_ipx.dest_addr.node[3], 
+					s->protinfo.af_ipx.dest_addr.node[4],
+					s->protinfo.af_ipx.dest_addr.node[5],
+					htons(s->protinfo.af_ipx.dest_addr.sock));
 			}
 			len += sprintf (buffer+len,"%08lX  %08lX  ", 
 				s->wmem_alloc, s->rmem_alloc);
@@ -1506,7 +1514,7 @@
 			switch(optname)
 			{
 				case IPX_TYPE:
-					sk->ipx_type=opt;
+					sk->protinfo.af_ipx.type=opt;
 					return 0;
 				default:
 					return -EOPNOTSUPP;
@@ -1537,7 +1545,7 @@
 			switch(optname)
 			{
 				case IPX_TYPE:
-					val=sk->ipx_type;
+					val=sk->protinfo.af_ipx.type;
 					break;
 				default:
 					return -ENOPROTOOPT;
@@ -1613,12 +1621,13 @@
 	sk->state=TCP_CLOSE;
 	sk->socket=sock;
 	sk->type=sock->type;
-	sk->ipx_type=0;		/* General user level IPX */
-	sk->ipx_ncp_server = NULL;
+	sk->protinfo.af_ipx.type=0;		/* General user level IPX */
 	sk->debug=0;
-	sk->ipx_intrfc = NULL;
-	memset(&sk->ipx_dest_addr,'\0',sizeof(sk->ipx_dest_addr));
-	sk->ipx_port = 0;
+	sk->protinfo.af_ipx.intrfc = NULL;
+	memset(&sk->protinfo.af_ipx.dest_addr,'\0',
+		sizeof(sk->protinfo.af_ipx.dest_addr));
+	sk->protinfo.af_ipx.port = 0;
+	sk->protinfo.af_ipx.ncp_server = 0;
 	sk->mtu=IPX_MTU;
 	
 	if(sock!=NULL)
@@ -1699,7 +1708,7 @@
 	if(ntohs(addr->sipx_port)<IPX_MIN_EPHEMERAL_SOCKET && !suser())
 		return -EPERM;	/* protect IPX system stuff like routing/sap */
 
-	sk->ipx_port=addr->sipx_port;
+	sk->protinfo.af_ipx.port=addr->sipx_port;
 
 #ifdef CONFIG_IPX_INTERN
 	if (intrfc == ipx_internal_net)
@@ -1716,15 +1725,16 @@
 		}
 		if (memcmp(addr->sipx_node, ipx_this_node, IPX_NODE_LEN) == 0)
 		{
-			memcpy(sk->ipx_node, intrfc->if_node,
+			memcpy(sk->protinfo.af_ipx.node, intrfc->if_node,
 			       IPX_NODE_LEN);
 		}
 		else
 		{
-			memcpy(sk->ipx_node, addr->sipx_node, IPX_NODE_LEN);
+			memcpy(sk->protinfo.af_ipx.node, addr->sipx_node, IPX_NODE_LEN);
 		}
-		if (ipxitf_find_internal_socket(intrfc, sk->ipx_node,
-						sk->ipx_port) != NULL)
+		if (ipxitf_find_internal_socket(intrfc, 
+			sk->protinfo.af_ipx.node, 
+			sk->protinfo.af_ipx.port) != NULL)
 		{
 			if(sk->debug)
 				printk("IPX: bind failed because port %X in"
@@ -1739,7 +1749,8 @@
 		 * with the ipx routing ioctl()
 		 */
 
-		memcpy(sk->ipx_node, intrfc->if_node, IPX_NODE_LEN);
+		memcpy(sk->protinfo.af_ipx.node, intrfc->if_node, 
+			IPX_NODE_LEN);
 		
 		if(ipxitf_find_socket(intrfc, addr->sipx_port)!=NULL) {
 			if(sk->debug)
@@ -1783,7 +1794,7 @@
 		return(-EINVAL);
 	addr=(struct sockaddr_ipx *)uaddr;
 	
-	if(sk->ipx_port==0)
+	if(sk->protinfo.af_ipx.port==0)
 	/* put the autobinding in */
 	{
 		struct sockaddr_ipx uaddr;
@@ -1792,7 +1803,7 @@
 		uaddr.sipx_port = 0;
 		uaddr.sipx_network = 0L;
 #ifdef CONFIG_IPX_INTERN
-		memcpy(uaddr.sipx_node, sk->ipx_intrfc->if_node,
+		memcpy(uaddr.sipx_node, sk->protinfo.af_ipx.intrfc->if_node,
 		       IPX_NODE_LEN);
 #endif
 		ret = ipx_bind (sock, (struct sockaddr *)&uaddr,
@@ -1802,10 +1813,11 @@
 	
 	if(ipxrtr_lookup(addr->sipx_network)==NULL)
 		return -ENETUNREACH;
-	sk->ipx_dest_addr.net=addr->sipx_network;
-	sk->ipx_dest_addr.sock=addr->sipx_port;
-	memcpy(sk->ipx_dest_addr.node,addr->sipx_node,IPX_NODE_LEN);
-	sk->ipx_type=addr->sipx_type;
+	sk->protinfo.af_ipx.dest_addr.net=addr->sipx_network;
+	sk->protinfo.af_ipx.dest_addr.sock=addr->sipx_port;
+	memcpy(sk->protinfo.af_ipx.dest_addr.node,
+		addr->sipx_node,IPX_NODE_LEN);
+	sk->protinfo.af_ipx.type=addr->sipx_type;
 	sock->state = SS_CONNECTED;
 	sk->state=TCP_ESTABLISHED;
 	return 0;
@@ -1837,29 +1849,29 @@
 	if(peer) {
 		if(sk->state!=TCP_ESTABLISHED)
 			return -ENOTCONN;
-		addr=&sk->ipx_dest_addr;
+		addr=&sk->protinfo.af_ipx.dest_addr;
 		sipx.sipx_network = addr->net;
 		memcpy(sipx.sipx_node,addr->node,IPX_NODE_LEN);
 		sipx.sipx_port = addr->sock;
 	} else {
-		if (sk->ipx_intrfc != NULL) {
-			sipx.sipx_network = sk->ipx_intrfc->if_netnum;
+		if (sk->protinfo.af_ipx.intrfc != NULL) {
+			sipx.sipx_network = sk->protinfo.af_ipx.intrfc->if_netnum;
 #ifdef CONFIG_IPX_INTERN
-			memcpy(sipx.sipx_node, sk->ipx_node, IPX_NODE_LEN);
+			memcpy(sipx.sipx_node, sk->protinfo.af_ipx.node, IPX_NODE_LEN);
 #else
-			memcpy(sipx.sipx_node, sk->ipx_intrfc->if_node,
-			       IPX_NODE_LEN);
+			memcpy(sipx.sipx_node, 
+				sk->protinfo.af_ipx.intrfc->if_node, IPX_NODE_LEN);
 #endif
 
 		} else {
 			sipx.sipx_network = 0L;
 			memset(sipx.sipx_node, '\0', IPX_NODE_LEN);
 		}
-		sipx.sipx_port = sk->ipx_port;
+		sipx.sipx_port = sk->protinfo.af_ipx.port;
 	}
 		
 	sipx.sipx_family = AF_IPX;
-	sipx.sipx_type = sk->ipx_type;
+	sipx.sipx_type = sk->protinfo.af_ipx.type;
 	memcpy(uaddr,&sipx,sizeof(sipx));
 	return 0;
 }
@@ -1974,7 +1986,7 @@
 		
 	if(usipx) 
 	{
-		if(sk->ipx_port == 0) 
+		if(sk->protinfo.af_ipx.port == 0) 
 		{
 			struct sockaddr_ipx uaddr;
 			int ret;
@@ -1982,8 +1994,8 @@
 			uaddr.sipx_port = 0;
 			uaddr.sipx_network = 0L; 
 #ifdef CONFIG_IPX_INTERN
-			memcpy(uaddr.sipx_node, sk->ipx_intrfc->if_node,
-			       IPX_NODE_LEN);
+			memcpy(uaddr.sipx_node, sk->protinfo.af_ipx.intrfc
+				->if_node, IPX_NODE_LEN);
 #endif
 			ret = ipx_bind (sock, (struct sockaddr *)&uaddr,
 					sizeof(struct sockaddr_ipx));
@@ -2001,10 +2013,10 @@
 			return -ENOTCONN;
 		usipx=&local_sipx;
 		usipx->sipx_family=AF_IPX;
-		usipx->sipx_type=sk->ipx_type;
-		usipx->sipx_port=sk->ipx_dest_addr.sock;
-		usipx->sipx_network=sk->ipx_dest_addr.net;
-		memcpy(usipx->sipx_node,sk->ipx_dest_addr.node,IPX_NODE_LEN);
+		usipx->sipx_type=sk->protinfo.af_ipx.type;
+		usipx->sipx_port=sk->protinfo.af_ipx.dest_addr.sock;
+		usipx->sipx_network=sk->protinfo.af_ipx.dest_addr.net;
+		memcpy(usipx->sipx_node,sk->protinfo.af_ipx.dest_addr.node,IPX_NODE_LEN);
 	}
 	
 	retval = ipxrtr_route_packet(sk, usipx, msg->msg_iov, len);
diff -u --recursive --new-file v1.3.68/linux/scripts/Menuconfig linux/scripts/Menuconfig
--- v1.3.68/linux/scripts/Menuconfig	Fri Feb 23 13:54:44 1996
+++ linux/scripts/Menuconfig	Sun Feb 25 11:17:58 1996
@@ -56,8 +56,8 @@
      var=$(echo "$1"|sed 's/[][\/.^$*]/\\&/g')
      #now pick out the right help text:
      text=$(sed -n "/^$var[ 	]*\$/,\${
-                        /^$var[ 	]*\$/b
-                        /^#.*/b;/^[ 	]*\$/q
+                        /^$var[ 	]*\$/d
+                        /^#.*/d;/^[ 	]*\$/q
                         p
                     }" Documentation/Configure.help)
 
diff -u --recursive --new-file v1.3.68/linux/scripts/header.tk linux/scripts/header.tk
--- v1.3.68/linux/scripts/header.tk	Wed Feb  7 15:11:44 1996
+++ linux/scripts/header.tk	Fri Feb 23 09:27:14 1996
@@ -87,8 +87,7 @@
 	label $w.bm -bitmap questhead
 	pack  $w.bm -pady 10 -side top -padx 10
 	message $w.m -width 400 -aspect 300 \
-		-text "Changes will be lost.  Are you sure?"  -relief raised \
-		-fg black
+		-text "Changes will be lost.  Are you sure?"  -relief raised
 	pack  $w.m -pady 10 -side top -padx 10
 	wm title $w "Are you sure?" 
 
@@ -115,7 +114,7 @@
 		toplevel $w -class Dialog
 		message $w.m -width 400 -aspect 300 -text \
 			"Unable to read file $loadfile" \
-			 -relief raised -fg black
+			 -relief raised 
 		label $w.bm -bitmap error
 		pack $w.bm $w.m -pady 10 -side top -padx 10
 		wm title $w "Oops" 
@@ -143,7 +142,7 @@
 		toplevel $w -class Dialog
 		message $w.m -width 400 -aspect 300 -text \
 			"Unable to write file $loadfile" \
-			 -relief raised -fg black
+			 -relief raised 
 		label $w.bm -bitmap error
 		pack $w.bm $w.m -pady 10 -side top -padx 10
 		wm title $w "Oops" 
@@ -303,17 +302,17 @@
 	if { $found == 0 } then {
 		if { $filefound == 0 } then {
 		message $w.m -width 750 -aspect 300 -text \
-			"No help available - unable to open file Documentation/Configure.help.  This file is available from http://math-www.uni-paderborn.de/~axel/config_help.html or ftp://sunsite.unc.edu/pub/Linux/kernel/config/krnl_cnfg_hlp_1.X.XX.tgz"  -relief raised -fg black
+			"No help available - unable to open file Documentation/Configure.help.  This file is available from http://math-www.uni-paderborn.de/~axel/config_help.html or ftp://sunsite.unc.edu/pub/Linux/kernel/config/krnl_cnfg_hlp_1.X.XX.tgz"  -relief raised 
 		} else {
 		message $w.m -width 400 -aspect 300 -text \
-			"No help available for $varname"  -relief raised -fg black
+			"No help available for $varname"  -relief raised 
 		}
 		label $w.bm -bitmap error
 		pack $w.bm $w.m -pady 10 -side top -padx 10
 		wm title $w "RTFM" 
 	} else {
 		message $w.m -width 400 -aspect 300 -text $message \
-			 -relief raised -fg black
+			 -relief raised 
 		label $w.bm -bitmap info
 		pack $w.bm $w.m -pady 10 -side top -padx 10
 		wm title $w "Configuration help" 
@@ -334,7 +333,7 @@
 	catch {destroy $w}
 	toplevel $w -class Dialog
 	message $w.m -width 400 -aspect 300 -text \
-		"The linux kernel is now hopefully configured for your setup. Check the top-level Makefile for additional configuration, and do a 'make dep ; make clean' if you want to be sure all the files are correctly re-made."  -relief raised -fg black
+		"The linux kernel is now hopefully configured for your setup. Check the top-level Makefile for additional configuration, and do a 'make dep ; make clean' if you want to be sure all the files are correctly re-made."  -relief raised 
 	label $w.bm -bitmap info
 	pack $w.bm $w.m -pady 10 -side top -padx 10
 	wm title $w "Kernel build instructions" 
diff -u --recursive --new-file v1.3.68/linux/scripts/lxdialog/checklist.c linux/scripts/lxdialog/checklist.c
--- v1.3.68/linux/scripts/lxdialog/checklist.c	Wed Feb  7 15:11:46 1996
+++ linux/scripts/lxdialog/checklist.c	Sun Feb 25 11:17:58 1996
@@ -332,11 +332,7 @@
 		for (i = 0; i < item_no; i++) {
 		    if (status[i]) {
 			if (flag == FLAG_CHECK) {
-			    if (separate_output) {
-				fprintf (stderr, "%s\n", items[i * 3]);
-			    } else {
-				fprintf (stderr, "\"%s\" ", items[i * 3]);
-			    }
+			    fprintf (stderr, "\"%s\" ", items[i * 3]);
 			} else {
 			    fprintf (stderr, "%s", items[i * 3]);
 			}
diff -u --recursive --new-file v1.3.68/linux/scripts/lxdialog/dialog.h linux/scripts/lxdialog/dialog.h
--- v1.3.68/linux/scripts/lxdialog/dialog.h	Fri Feb  9 17:53:13 1996
+++ linux/scripts/lxdialog/dialog.h	Sun Feb 25 11:17:58 1996
@@ -141,7 +141,7 @@
 		const char * const * items);
 int dialog_checklist (const char *title, const char *prompt, int height,
 		int width, int list_height, int item_no,
-		const char * const * items, int flag, int separate_output);
+		const char * const * items, int flag);
 extern unsigned char dialog_input_result[];
 int dialog_inputbox (const char *title, const char *prompt, int height,
 		int width, const char *init);
diff -u --recursive --new-file v1.3.68/linux/scripts/lxdialog/lxdialog.c linux/scripts/lxdialog/lxdialog.c
--- v1.3.68/linux/scripts/lxdialog/lxdialog.c	Wed Feb  7 15:11:46 1996
+++ linux/scripts/lxdialog/lxdialog.c	Sun Feb 25 11:17:58 1996
@@ -23,8 +23,6 @@
 
 static void Usage (const char *name);
 
-static int separate_output = 0;
-
 typedef int (jumperFn) (const char *title, int argc, const char * const * argv);
 
 struct Mode {
@@ -87,9 +85,6 @@
                 backtitle = argv[offset + 2];
                 offset += 2;
             }
-	} else if (!strcmp (argv[offset + 1], "--separate-output")) {
-	    separate_output = 1;
-	    offset++;
 	} else if (!strcmp (argv[offset + 1], "--clear")) {
 	    if (clear_screen) {	/* Hey, "--clear" can't appear twice! */
 		Usage (argv[0]);
@@ -153,7 +148,7 @@
 \n* Display dialog boxes from shell scripts *\
 \n\
 \nUsage: %s --clear\
-\n       %s [--title <title>] [--separate-output] [--backtitle <backtitle>] --clear <Box options>\
+\n       %s [--title <title>] [--backtitle <backtitle>] --clear <Box options>\
 \n\
 \nBox options:\
 \n\
@@ -182,14 +177,14 @@
 j_checklist (const char *t, int ac, const char * const * av)
 {
     return dialog_checklist (t, av[2], atoi (av[3]), atoi (av[4]),
-	atoi (av[5]), (ac - 6) / 3, av + 6, FLAG_CHECK, separate_output);
+	atoi (av[5]), (ac - 6) / 3, av + 6, FLAG_CHECK);
 }
 
 int
 j_radiolist (const char *t, int ac, const char * const * av)
 {
     return dialog_checklist (t, av[2], atoi (av[3]), atoi (av[4]),
-	atoi (av[5]), (ac - 6) / 3, av + 6, FLAG_RADIO, separate_output);
+	atoi (av[5]), (ac - 6) / 3, av + 6, FLAG_RADIO);
 }
 
 int
diff -u --recursive --new-file v1.3.68/linux/scripts/patch-kernel linux/scripts/patch-kernel
--- v1.3.68/linux/scripts/patch-kernel	Wed Feb  7 15:11:46 1996
+++ linux/scripts/patch-kernel	Sun Feb 25 11:17:59 1996
@@ -36,13 +36,16 @@
     fi
 
     echo "Applying $patch..."
-    gunzip -dc $patchdir/$patch | patch -p1 -s -E -d $sourcedir
-# used to be patch -p1 -s -E -f -d $sourcedir
-    if [ "`find $sourcedir -name \*.rej -print`" ]
+    if (gunzip -dc $patchdir/$patch | grep -v '^\\' | patch -p1 -s -E -d $sourcedir)
+    then
+        echo "Patch failed.  Clean up yourself."
+        break
+    fi
+    if [ "`find $sourcedir '(' -name '*.rej' -o -name '.*.rej' ')' -print`" ]
     then
         echo "Aborting.  Reject files found."
         break
     fi
     # Remove backup files
-    find $sourcedir -name \*.orig -print | xargs rm -f
+    find $sourcedir '(' -name '*.orig' -o -name '.*.orig' ')' -print | xargs rm -f
 done
diff -u --recursive --new-file v1.3.68/linux/scripts/tkgen.c linux/scripts/tkgen.c
--- v1.3.68/linux/scripts/tkgen.c	Fri Feb  9 17:53:13 1996
+++ linux/scripts/tkgen.c	Fri Feb 23 09:27:14 1996
@@ -196,7 +196,7 @@
       break;
     case tok_int:
       printf("} then { ");
-      printf(".menu%d.config.f.x%d.x configure -state normal -fore black; ", menu_num, line_num);
+      printf(".menu%d.config.f.x%d.x configure -state normal; ", menu_num, line_num);
       printf(".menu%d.config.f.x%d.l configure -state normal; ", menu_num, line_num);
       printf("} else { ");
       printf(".menu%d.config.f.x%d.x configure -state disabled -fore gray60;", menu_num, line_num );
