diff -u --recursive --new-file v2.3.3/linux/CREDITS linux/CREDITS
--- v2.3.3/linux/CREDITS	Mon May 17 09:55:20 1999
+++ linux/CREDITS	Mon May 24 22:47:43 1999
@@ -767,11 +767,8 @@
 S: The Netherlands
 
 N: Kai Harrekilde-Petersen
-E: khp@dolphinics.no
+E: khp@olicom.dk
 D: Original author of the ftape-HOWTO, i82078 fdc detection code.
-S: Peder Holters vei 13
-S: 1168 Oslo
-S: Norway
 
 N: Andrew Haylett
 E: ajh@primag.co.uk
@@ -1439,8 +1436,8 @@
 S: Germany
 
 N: David Mosberger-Tang
-E: David.Mosberger@acm.org
-D: Linux/Alpha
+E: davidm@hpl.hp.com if IA-64 related, else David.Mosberger@acm.org
+D: Linux/Alpha and Linux/ia64
 S: 35706 Runckel Lane
 S: Fremont, California 94536
 S: USA
diff -u --recursive --new-file v2.3.3/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.3.3/linux/Documentation/Configure.help	Mon May 17 09:55:20 1999
+++ linux/Documentation/Configure.help	Wed May 26 10:01:43 1999
@@ -3110,6 +3110,44 @@
   The module will be called af_spx.o. If you want to compile it as a
   module, say M here and read Documentation/modules.txt.
 
+DECnet networking
+CONFIG_DECNET
+  The DECnet networking protocol was used in many products made by
+  Digital (now Compaq). It provides reliable stream and sequenced
+  packet communications over which run a variety of services similar
+  to those which run over TCP/IP.
+
+  To find some tools to use with the kernel layer support, please
+  look at Patrick Caulfield's web site: 
+  http://linux.dreamtime.org/decnet/
+
+  More detailed documentation is available in the
+  Documentation/networking/decnet.txt file.
+
+  Be sure to turn on the CONFIG_PROCFS and CONFIG_SYSCTL options
+  when using DECnet, since you will need sysctl support to aid in
+  configuration at run time.
+
+DECnet SIOCFIGCONF support
+CONFIG_DECNET_SIOCGIFCONF
+   This option should only be turned on if you are really sure that
+   you know what you are doing. It can break other applications which
+   use this system call and the proper way to get the information
+   provided by this call is to use rtnetlink.
+
+DECnet Router Support
+CONFIG_DECNET_ROUTER
+   Add support for turning your DECnet Endnode into a level 1 or 2
+   router. This is an unfinished option for developers only. If you
+   do turn it on, then make sure you also have rtnetlink configured
+   in, since thats the only current method of configuration.
+
+DECnet Raw Socket Support
+CONFIG_DECNET_RAW
+   Add support for the SOCK_RAW type under DECnet. Used by userland
+   routing programs to receive routing messages from the kernel and
+   also as a general debugging aid to see whats going on "under the hood".
+
 AppleTalk DDP
 CONFIG_ATALK
   AppleTalk is the way Apple computers speak to each other on a
@@ -7525,6 +7563,18 @@
   Say Y here if you want to try writing to UFS partitions. This is
   experimental, so you should back up your UFS partitions beforehand.
 
+EFS filesystem support (experimental)
+CONFIG_EFS_FS
+  EFS is the filesystem used for CDROMs and filesystems by SGI's IRIX.
+  This implementation only offers read-only access. If you don't know
+  what all this is about, it's safe to say N. For more information
+  about EFS see its homepage at http://aeschi.ch.eu.org/efs/.
+
+SGI disklabel support
+CONFIG_SGI_DISKLABEL
+  Say Y to this only if you plan on mounting disks with SGI disklabels.
+  This is not required to mount EFS-format CDROMs.
+
 BSD disklabel (FreeBSD partition tables) support
 CONFIG_BSD_DISKLABEL
   FreeBSD uses its own hard disk partition scheme on your PC. It
@@ -11559,6 +11609,64 @@
   This is the driver for the serial ports on the BVME4000 and BVME6000
   boards from BVM Ltd.  Everyone using one of these boards should say
   Y here.
+
+Universal Serial Bus (USB) Support (Experimental)
+CONFIG_USB
+  This will enable support for the Universal Serial Bus (USB).  USB 
+  allows you to connect up to 127 external devices such as keyboards, 
+  mice, printers and modems to your computer through a USB connector.
+  This code can also be built as a module (i.e. code outside the 
+  kernel that can be added and removed at runtime), consult 
+  Documentation/modules.txt for more information.
+
+UHCI (intel PIIX4 and others) support
+CONFIG_USB_UHCI
+  The UHCI is the USB host controller for many computers, and is built
+  into all recent boards with intel PCI chipsets. This driver is
+  needed for these motherboards to support USB.
+
+OHCI (compaq and some others) support
+CONFIG_USB_OHCI
+  The OHCI (Open Host Controller Interface) is the USB host controller
+  on most non-Intel architectures, several USB-adding PCI cards, and
+  is on several x86 compatibles with non-Intel chipsets. This driver
+  is needed for these motherboards to support USB. There is presently
+  two OHCI drivers in development. Say 'N' here if you would like to
+  compile the other OHCI driver into the linux kernel.
+
+OHCI-HCD (other OHCI opt. Virt. Root Hub) support
+CONFIG_USB_OHCI_HCD
+  The OHCI (Open Host Controller Interface) is the USB host controller
+  on most non-Intel architectures, several USB-adding PCI cards, and
+  is on several x86 compatibles with non-Intel chipsets. This driver
+  is needed for these motherboards to support USB. There is presently
+  two OHCI drivers in development. 
+
+USB hub support
+CONFIG_USB_HUB
+  To expand beyond the USB ports on the computer, a device called a 
+  hub is used. This driver supports hubs, allowing them to be used. 
+  Say 'Y'
+
+USB mouse support
+CONFIG_USB_MOUSE  
+  This driver allows mice to work under the USB stack. It currently
+  patches into /dev/ps2aux, although it does not require the ps2 
+  mouse code to be included in the kernel as well.
+
+USB keyboard support
+CONFIG_USB_KBD
+  This driver allows usb keyboards to work under the USB stack.
+
+USB audio parsing support (Preliminary)
+CONFIG_USB_AUDIO
+  This driver will eventually handle audio devices, such 
+  as USB speakers.
+
+USB Abstract Control Model support (Preliminary)
+CONFIG_USB_ACM
+  This driver allows for devices which support the Abstract Control Model,
+  including many USB-based modems, ISDN adapters, and network adapters.
 
 #
 # A couple of things I keep forgetting:
diff -u --recursive --new-file v2.3.3/linux/Documentation/filesystems/isofs.txt linux/Documentation/filesystems/isofs.txt
--- v2.3.3/linux/Documentation/filesystems/isofs.txt	Wed Jun 24 14:30:07 1998
+++ linux/Documentation/filesystems/isofs.txt	Wed May 26 10:01:43 1999
@@ -27,3 +27,5 @@
   nojoliet      Ignore Joliet extensions if they are present.
   norock        Ignore Rock Ridge extensions if they are present.
   unhide        Show hidden files.
+  session=x     Select number of session on multisession CD
+  sbsector=xxx  Session begins from sector xxx
diff -u --recursive --new-file v2.3.3/linux/Documentation/isdn/HiSax.cert linux/Documentation/isdn/HiSax.cert
--- v2.3.3/linux/Documentation/isdn/HiSax.cert	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/isdn/HiSax.cert	Sun May 23 10:03:41 1999
@@ -0,0 +1,76 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+
+First:
+
+    HiSax is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+However, if you wish to modify the HiSax sources, please note the following:
+
+HiSax has passed the ITU approval test suite with ELSA Quickstep ISDN cards.
+The certification is only valid for the combination of the tested software
+version and the tested hardware. Any changes to the HiSax source code may
+therefore affect the certification.
+
+If you change the main files of the HiSax ISDN stack, the certification will
+become invalid. Because in most countries it is illegal to connect
+unapproved ISDN equipment to the public network, I have to guarantee that
+changes in HiSax do not affect the certification.
+
+In order to make a valid certification apparent to the user, I have built in
+some validation checks that are made during the make process. The HiSax main
+files are protected by md5 checksums and the md5sum file is pgp signed by
+myself:
+
+KeyID 1024/FF992F6D 1997/01/16 Karsten Keil <keil@temic-ech.spacenet.de>
+Key fingerprint = 92 6B F7 58 EE 86 28 C8  C4 1A E6 DC 39 89 F2 AA
+
+Only if the checksums are OK, and the signature of the file
+"drivers/isdn/hisax/md5sums.asc" match, is the certification valid; a
+message confirming this is then displayed during the hisax init process.
+
+The affected files are:
+
+drivers/isdn/hisax/isac.c
+drivers/isdn/hisax/isdnl1.c
+drivers/isdn/hisax/isdnl2.c
+drivers/isdn/hisax/isdnl3.c
+drivers/isdn/hisax/tei.c
+drivers/isdn/hisax/callc.c
+drivers/isdn/hisax/l3dss1.c
+drivers/isdn/hisax/l3_1tr6.c
+drivers/isdn/hisax/cert.c
+drivers/isdn/hisax/elsa.c
+
+Please send any changes, bugfixes and patches to me rather than implementing
+them directly into the HiSax sources.
+
+This does not reduce your rights granted by the GNU General Public License.
+If you wish to change the sources, go ahead; but note that then the
+certification is invalid even if you use ELSA Quickstep cards.
+
+Here are the certification registration numbers for ELSA Quickstep cards:
+German   D133361J CETECOM ICT Services GmbH 0682
+European D133362J CETECOM ICT Services GmbH 0682
+
+
+Karsten Keil
+keil@isdn4linux.de
+
+-----BEGIN PGP SIGNATURE-----
+Version: 2.6.3i
+Charset: noconv
+
+iQCVAwUBNj5OKDpxHvX/mS9tAQFHuQP/WeImlqCcDZ2d132yAvRBWFULlJoSf1P/
+c1lVTeaWvsSaY5Cu9hrKhXXhPzeEaitUbcUBPXdpzFWCA5CE902lnz7AhgRC+HF1
+0qiKgkZZyc/5HKasFymR1+IWSLw30GesP3Di/ZMR3NJi8SlY9PIjx7hnEMunGSRO
+1ufPvfWWuu8=
+=nGJk
+-----END PGP SIGNATURE-----
diff -u --recursive --new-file v2.3.3/linux/Documentation/isdn/INTERFACE linux/Documentation/isdn/INTERFACE
--- v2.3.3/linux/Documentation/isdn/INTERFACE	Tue Apr 28 14:22:04 1998
+++ linux/Documentation/isdn/INTERFACE	Sun May 23 10:03:41 1999
@@ -1,4 +1,4 @@
-$Id: INTERFACE,v 1.8 1998/02/20 17:38:20 fritz Exp $
+$Id: INTERFACE,v 1.11 1999/03/02 12:14:51 armin Exp $
 
 Description of the Interface between Linklevel and Hardwarelevel
   of isdn4linux:
@@ -478,6 +478,14 @@
       1           = At least one device matching this call (RING on ttyI).
                     HL-driver may send ALERTING on the D-channel in this case.
       2           = Call will be rejected.
+      3           = Incomplete number.
+                    The CalledNumber would match, if more digits are appended.
+                    This feature is needed for Number-Blocks assigned to
+                    a line. In this case, the LL driver should assemble the
+					CalledNumber by handling keypad protocol and try again
+					later with a longer CalledNumber.
+					HL drivers serving ordinary lines should interpret this
+					return code like 0 (nothing matches).
       -1          = An error happened. (Invalid parameters for example.)
 
   ISDN_STAT_RUN:
@@ -526,7 +534,9 @@
       driver      = driver-Id
       command     = ISDN_STAT_BCONN
       arg         = channel-number, locally to the driver. (starting with 0)
-      para        = unused.
+      para.num    = ASCII-String, containing type of connection (for analog
+		    modem only). This will be appended to the CONNECT message
+		    e.g. 14400/V.32bis
 
   ISDN_STAT_DHUP:
 
diff -u --recursive --new-file v2.3.3/linux/Documentation/isdn/README linux/Documentation/isdn/README
--- v2.3.3/linux/Documentation/isdn/README	Wed May 20 18:54:34 1998
+++ linux/Documentation/isdn/README	Sun May 23 10:03:41 1999
@@ -31,7 +31,7 @@
    This mailinglist is bidirectionally gated to the newsgroup
 
      de.alt.comm.isdn4linux
-   
+
   There is also a well maintained FAQ (both english and german) available
   at ftp.franken.de in /pub/isdn4linux/FAQ/
   This FAQ is also available at http://www.lrz-muenchen.de/~ui161ab/www/isdn/
@@ -41,11 +41,11 @@
   In the following Text, the terms MSN and EAZ are used.
 
   MSN is the abbreviation for (M)ultiple(S)ubscriber(N)umber, and applies
-  to Euro(EDSS1)-type lines. Usually it is simply the phone-number.
+  to Euro(EDSS1)-type lines. Usually it is simply the phone number.
 
   EAZ is the abbreviation of (E)ndgeraete(A)uswahl(Z)iffer and
   applies to German 1TR6-type lines. This is a one-digit string,
-  simply appended to the base phone-number
+  simply appended to the base phone number
 
   The internal handling is nearly identical, so replace the appropriate
   term to that one, which applies to your local ISDN-environment.
@@ -56,13 +56,13 @@
   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, i.e. for the ICN-driver, the base-address of
             the ports and the shared memory on the card can be set and read
-            also the boot-code and the protocol software can be loaded into 
+            also the boot-code and the protocol software can be loaded into
             the card.
 
    O N L Y !!!  for debugging (no locking against other devices):
@@ -74,38 +74,38 @@
 
    128 tty-devices (64 cuix and 64 ttyIx) with integrated modem-emulator:
    The functionality is almost the same as that of a serial device
-   (the line-discs are handled by the kernel), which lets you run 
-   SLIP, CSLIP and asynchronous PPP through the devices. We have tested 
+   (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:
+               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 'S' must precede the number, if
                         the line is a SPV (German 1TR6).
-               ATE0	Echo off.
-	       ATE1     Echo on (default).
+               ATE0     Echo off.
+               ATE1     Echo on (default).
                ATH      Hang-up.
-	       ATH1     Off hook (ignored).
+               ATH1     Off hook (ignored).
                ATH0     Hang-up.
-	       ATI      Return "ISDN for Linux...".
+               ATI      Return "ISDN for Linux...".
                ATI0        "
                ATI1        "
-	       ATI2     Report of last connection.
+               ATI2     Report of last connection.
                ATO      On line (data mode).
                ATQ0     Enable result codes (default).
                ATQ1     Disable result codes (default).
-               ATSx=y	Set register x to y.
-	       ATSx?    Show contents of register x.
+               ATSx=y   Set register x to y.
+               ATSx?    Show contents of register x.
                ATV0     Numeric responses.
                ATV1     English responses (default).
-	       ATZ      Load registers and EAZ/MSN from Profile.
-	       AT&Bx    Set Send-Packet-size to x (max. 4000)
+               ATZ      Load registers and EAZ/MSN from Profile.
+               AT&Bx    Set Send-Packet-size to x (max. 4000)
                         The real packet-size may be limited by the
                         low-level-driver used. e.g. the HiSax-Module-
                         limit is 2000. You will get NO Error-Message,
@@ -114,13 +114,13 @@
                         driver may not be selected (see "Automatic
                         Assignment") however the size of outgoing packets
                         will be limited correctly.
-	       AT&D0    Ignore DTR
-	       AT&D2    DTR-low-edge: Hang up and return to 
+               AT&D0    Ignore DTR
+               AT&D2    DTR-low-edge: Hang up and return to
                         command mode (default).
                AT&D3    Same as AT&D2 but also resets all registers.
-	       AT&Ex    Set the EAZ/MSN for this channel to x.
-	       AT&F     Reset all registers and profile to "factory-defaults"
-	       AT&Rx    Select V.110 bitrate adaption.
+               AT&Ex    Set the EAZ/MSN for this channel to x.
+               AT&F     Reset all registers and profile to "factory-defaults"
+               AT&Rx    Select V.110 bitrate adaption.
                         This command enables V.110 protocol with 9600 baud
                         (x=9600), 19200 baud (x=19200) or 38400 baud
                         (x=38400). A value of x=0 disables V.110 switching
@@ -142,24 +142,24 @@
                             The value 198 is choosen arbitrarily. Users
                             _MUST_ negotiate this value before establishing
                             a connection.
-	       AT&Sx    Set window-size (x = 1..8) (not yet implemented)
-	       AT&V     Show all settings.
+               AT&Sx    Set window-size (x = 1..8) (not yet implemented)
+               AT&V     Show all settings.
                AT&W0    Write registers and EAZ/MSN to profile. See also
                         iprofd (5.c in this README).
-               AT&X0	BTX-mode and T.70-mode off (default)
-	       AT&X1    BTX-mode on. (S13.1=1, S13.5=0 S14=0, S16=7, S18=7, S19=0)
-	       AT&X2    T.70-mode on. (S13.1=1, S13.5=1, S14=0, S16=7, S18=7, S19=0)
+               AT&X0    BTX-mode and T.70-mode off (default)
+               AT&X1    BTX-mode on. (S13.1=1, S13.5=0 S14=0, S16=7, S18=7, S19=0)
+               AT&X2    T.70-mode on. (S13.1=1, S13.5=1, S14=0, S16=7, S18=7, S19=0)
 
            For voice-mode commands refer to README.audio
 
-	   1.3.2 Escape sequence:
+           1.3.2 Escape sequence:
                During a connection, the emulation reacts just like
                a normal modem to the escape sequence <DELAY>+++<DELAY>.
-	       (The escape character - default '+' - can be set in the
+               (The escape character - default '+' - can be set in the
                register 2).
-               The DELAY must at least be 1.5 seconds long and delay 
+               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
@@ -172,7 +172,7 @@
               4    10       Line feed character (ASCII).
               5    8        Backspace character (ASCII).
               6    3        Delay in seconds before dialing.
-              7    60       Wait for carrier (ignored).
+              7    60       Wait for carrier.
               8    2        Pause time for comma (ignored)
               9    6        Carrier detect time (ignored)
              10    7        Carrier loss to disconnect time (ignored).
@@ -203,8 +203,8 @@
                                       1 = T.70 protocol (Only for BTX!) on
                             Bit 2:    0 = Don't hangup on DTR low.
                                       1 = Hangup on DTR low.
-			    Bit 3:    0 = Standard response messages
-				      1 = Extended response messages
+                            Bit 3:    0 = Standard response messages
+                                      1 = Extended response messages
                             Bit 4:    0 = CALLER NUMBER before every RING.
                                       1 = CALLER NUMBER after first RING.
                             Bit 5:    0 = T.70 extended protocol off
@@ -215,18 +215,19 @@
                                           an incoming call happened (RING) and
                                           the remote party hung up before any
                                           local ATA was given.
-	     14   0         Layer-2 protocol:
-				      0 = X75/LAPB with I-frames
-				      1 = X75/LAPB with UI-frames
+             14   0         Layer-2 protocol:
+                                      0 = X75/LAPB with I-frames
+                                      1 = X75/LAPB with UI-frames
                                       2 = X75/LAPB with BUI-frames
                                       3 = HDLC
                                       4 = Transparent (audio)
                                       7 = V.110, 9600 baud
                                       8 = V.110, 19200 baud
                                       9 = V.110, 38400 baud
+                                     10 = Analog Modem (only if hardware supports this)
              15   0         Layer-3 protocol: (at the moment always 0)
                                       0 = transparent
-	     16   250       Send-Packet-size/16
+             16   250       Send-Packet-size/16
              17   8         Window-size (not yet implemented)
              18   4         Bit coded register, Service-Octet-1 to accept,
                             or to be used on dialout:
@@ -262,7 +263,7 @@
 
   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 
+  When an incoming call is detected, the driver looks first for a network
   interface and then for an opened tty which:
 
   1. is configured for the same EAZ.
@@ -270,7 +271,7 @@
   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 exactly this Interface.
+     it is bound AND the other checks apply to exactly this interface.
      (For usage of the bind-features, refer to the isdnctrl-man-page)
 
   Only when a matching interface or tty is found is the call accepted
@@ -301,7 +302,7 @@
   Always use the latest module utilities. The current version is
   named in Documentation/Changes. Some old versions of insmod
   are not capable of setting the driver-Ids correctly.
- 
+
 3. Lowlevel-driver configuration.
 
    Configuration depends on how the drivers are built. See the
@@ -310,7 +311,7 @@
 4. Device-inodes
 
    The major and minor numbers and their names are described in
-   Documentation/devices.txt. The major-numbers are:
+   Documentation/devices.txt. The major numbers are:
 
      43 for the ISDN-tty's.
      44 for the ISDN-callout-tty's.
@@ -350,45 +351,64 @@
    g) Set the timeout for hang-up:
        isdnctrl huptimeout isdn0 <timeout_in_seconds>
 
-   h) additionally you may activate charge-hang-up (= Hang up before 
+   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):
        isdnctrl chargehup isdn0 on
 
-   i) Setup the interface with ifconfig as usual, and set a route to it.
+   i) Set the dial mode of the interface:
+       isdnctrl dialmode isdn0 auto
+      "off" means that you (or the system) cannot make any connection
+        (neither incoming or outgoing connections are possible). Use
+        this if you want to be sure that no connections will be made.
+      "auto" means that the interface is in auto-dial mode, and will
+        attempt to make a connection whenever a network data packet needs
+        the interface's link. Note that this can cause unexpected dialouts,
+        and lead to a high phone bill! Some daemons or other pc's that use
+        this interface can cause this.
+        Incoming connections are also possible.
+      "manual" is a dial mode created to prevent the unexpected dialouts.
+        In this mode, the interface will never make any connections on its
+        own. You must explicitly initiate a connection with "isdnctrl dial
+        isdn0". However, after an idle time of no traffic as configured for
+	the huptimeout value with isdnctrl, the connection _will_ be ended.
+	If you don't want any automatic hangup, set the huptimeout value to 0.
+        "manual" is the default.
 
-   j) (optional) If you run X11 and have Tcl/Tk-wish Version 4.0, you can use
+   j) Setup the interface with ifconfig as usual, and set a route to it.
+
+   k) (optional) If you run X11 and have Tcl/Tk-wish version 4.0, you can use
      the script tools/tcltk/isdnmon. You can add actions for line-status
      changes. See the comments at the beginning of the script for how to
      do that. There are other tty-based tools in the tools-subdirectory
      contributed by Michael Knigge (imon), Volker Götz (imontty) and
      Andreas Kool (isdnmon).
 
-   k) For initial testing, you can set the verbose-level to 2 (default: 0).
+   l) For initial testing, you can set the verbose-level to 2 (default: 0).
       Then all incoming calls are logged, even if they are not addressed
       to one of the configured net-interfaces:
       isdnctrl verbose 2
 
-  Now you are ready! A ping to the set address should now result in a
-  dial-out (look at syslog kernel-messages).
-  The phone-numbers and EAZs can be assigned at any time with isdnctrl.
+  Now you are ready! A ping to the set address should now result in an
+  automatic dial-out (look at syslog kernel-messages).
+  The phone numbers and EAZs can be assigned at any time with isdnctrl.
   You can add as many interfaces as you like with addif following the
-  directions above. Of course, there may be some limitations. But we have 
-  tested as many as 20 interfaces without any problem. However, if you 
-  don't give an interface name to addif, the  kernel will assign a name 
+  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" 
+   "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 number
-   to configure here is "9311234567". The pattern-match function 
+   to configure here is "9311234567". The pattern-match function
    works similar to the shell mechanism.
 
      ?     one arbitrary digit
@@ -398,23 +418,23 @@
            a '^' as the first character in a list inverts the list
 
 
-   "isdnctrl secure <InterfaceName> off" 
+   "isdnctrl secure <InterfaceName> off"
    Switch off secure operation (default).
 
-   "isdnctrl ihup <InterfaceName> [on|off]" 
+   "isdnctrl ihup <InterfaceName> [on|off]"
    Switch the hang-up-timer for incoming calls on or off.
 
-   "isdnctrl eaz <InterfaceName>" 
+   "isdnctrl eaz <InterfaceName>"
    Returns the EAZ of an interface.
 
-   "isdnctrl delphone <InterfaceName> in|out <number>" 
+   "isdnctrl delphone <InterfaceName> in|out <number>"
    Deletes a number from one of the access-lists of the interface.
 
-   "isdnctrl delif <InterfaceName>" 
+   "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]" 
+   "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
@@ -435,14 +455,14 @@
    only while an interface is down.
 
    At the moment the following values are supported:
- 
+
    rawip    (Default) Selects raw-IP-encapsulation. This means, MAC-headers
-            are stripped off.  
+            are stripped off.
    ip       IP with type-field. Same as IP but the type-field of the MAC-header
             is preserved.
    x25iface X.25 interface encapsulation (first byte semantics as defined in
-	    ../networking/x25-iface.txt). Use this for running the linux
-	    X.25 network protocol stack (AF_X25 sockets) on top of isdn.
+            ../networking/x25-iface.txt). Use this for running the linux
+            X.25 network protocol stack (AF_X25 sockets) on top of isdn.
    cisco-h  A special-mode for communicating with a Cisco, which is configured
             to do "hdlc"
    ethernet No stripping. Packets are sent with full MAC-header.
@@ -454,7 +474,7 @@
 
 
    NOTE:    x25iface encapsulation is currently experimental. Please
-	    read README.x25 for further details    
+            read README.x25 for further details
 
 
    Watching packets, using standard-tcpdump will fail for all encapsulations
@@ -462,16 +482,16 @@
    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. 
+   "isdnctrl l2_prot <InterfaceName> <L2-ProtocolName>"
+   Selects a layer-2-protocol.
    (With the ICN-driver and the HiSax-driver, "x75i" and "hdlc" is available.
    With other drivers, "x75ui", "x75bui", "x25dte", "x25dce" may be
    possible too. See README.x25 for x25 related l2 protocols.)
 
-   isdnctrl l3_prot <InterfaceName> <L3-ProtocolName> 
+   isdnctrl l3_prot <InterfaceName> <L3-ProtocolName>
    The same for layer-3. (At the moment only "trans" is allowed)
 
-   "isdnctrl list <InterfaceName>" 
+   "isdnctrl list <InterfaceName>"
    Shows all parameters of an interface and the charge-info.
    Try "all" as the interface name.
 
@@ -479,13 +499,13 @@
    Forces hangup of an interface.
 
    "isdnctrl bind <InterfaceName> <DriverId>,<ChannelNumber> [exclusive]"
-   If you are using more than one ISDN-Card, it is sometimes necessary to
-   dial out using a specific Card or even preserve a specific Channel for
-   Dialout of a specific net-interface. This can be done with the above
+   If you are using more than one ISDN card, it is sometimes necessary to
+   dial out using a specific card or even preserve a specific channel for
+   dialout of a specific net-interface. This can be done with the above
    command. Replace <DriverId> by whatever you assigned while loading the
-   module. The <ChannelNumber> is counting from zero. The upper Limit
-   depends on the card used. At the Moment no card supports more than
-   2 Channels, so the upper limit is one.
+   module. The <ChannelNumber> is counted from zero. The upper limit
+   depends on the card used. At the moment no card supports more than
+   2 channels, so the upper limit is one.
 
    "isdnctrl unbind <InterfaceName>"
    unbinds a previously bound interface.
@@ -494,10 +514,10 @@
    If switched on, isdn4linux replies a REJECT to incoming calls, it
    cannot match to any configured interface.
    If switched off, nothing happens in this case.
-   You normally should NOT enable this feature, if the ISDN-adaptor is not
-   the only device, connected to the S0-bus. Otherwise it could happen, that
+   You normally should NOT enable this feature, if the ISDN adapter is not
+   the only device connected to the S0-bus. Otherwise it could happen that
    isdn4linux rejects an incoming call, which belongs to another device on
-   the bus. 
+   the bus.
 
    "isdnctrl addslave <InterfaceName> <SlaveName>
    Creates a slave interface for channel-bundling. Slave interfaces are
@@ -539,9 +559,9 @@
    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
diff -u --recursive --new-file v2.3.3/linux/Documentation/isdn/README.HiSax linux/Documentation/isdn/README.HiSax
--- v2.3.3/linux/Documentation/isdn/README.HiSax	Tue Apr 28 14:22:04 1998
+++ linux/Documentation/isdn/README.HiSax	Sun May 23 10:03:41 1999
@@ -1,7 +1,7 @@
 HiSax is a Linux hardware-level driver for passive ISDN cards with Siemens
 chipset (ISAC_S 2085/2086/2186, HSCX SAB 82525). It is based on the Teles
 driver from Jan den Ouden.
-It is meant to be used with isdn4linux, an ISDN link-level module for Linux 
+It is meant to be used with isdn4linux, an ISDN link-level module for Linux
 written by Fritz Elfert.
 
     This program is free software; you can redistribute it and/or modify
@@ -25,24 +25,33 @@
 Teles 8.0/16.0/16.3 and compatible ones
 Teles 16.3c
 Teles S0/PCMCIA
-Creatix PnP S0 
+Teles PCI
+Teles S0Box
+Creatix S0Box
+Creatix PnP S0
 Compaq ISDN S0 ISA card
 AVM A1 (Fritz, Teledat 150)
+AVM Fritz PCMCIA
+AVM Fritz PnP
+AVM Fritz PCI
 ELSA Microlink PCC-16, PCF, PCF-Pro, PCC-8
 ELSA Quickstep 1000
 ELSA Quickstep 1000PCI
 ELSA Quickstep 3000 (same settings as QS1000)
+ELSA Quickstep 3000PCI
 ELSA PCMCIA
 ITK ix1-micro Rev.2
 Eicon.Diehl Diva 2.0 ISA and PCI (S0 and U interface, no PRO version)
 Eicon.Diehl Diva Piccola
 ASUSCOM NETWORK INC. ISDNLink 128K PC adapter (order code I-IN100-ST-D)
 Dynalink IS64PH (OEM version of ASUSCOM NETWORK INC. ISDNLink 128K adapter)
+PCBIT-DP (OEM version of ASUSCOM NETWORK INC. ISDNLink)
 HFC-2BS0 based cards (TeleInt SA1)
-Sedlbauer Speed Card (Speed Win, Teledat 100)
-Sedlbauer Speed Star (PCMCIA)
+Sedlbauer Speed Card (Speed Win, Teledat 100, PCI, Fax+)
+Sedlbauer Speed Star/Speed Star2 (PCMCIA)
+Sedlbauer ISDN-Controller PC/104
 USR Sportster internal TA (compatible Stollmann tina-pp V3)
-ith Kommunikationstechnik GmbH MIC 16 ISA card 
+ith Kommunikationstechnik GmbH MIC 16 ISA card
 Traverse Technologie NETjet PCI S0 card
 Dr. Neuhaus Niccy PnP/PCI
 
@@ -50,6 +59,8 @@
       PCC-8: not tested yet
       Teles PCMCIA is EXPERIMENTAL
       Teles 16.3c is EXPERIMENTAL
+      Teles PCI is EXPERIMENTAL
+      Teles S0Box is EXPERIMENTAL
       Eicon.Diehl Diva U interface not tested
 
 If you know other passive cards with the Siemens chipset, please let me know.
@@ -113,7 +124,7 @@
 correct one during kernel config. Valid values are "1" for German 1TR6,
 "2" for EDSS1 (Euro ISDN) and "3" for leased lines (no D-Channel).
 
-The Creatix/Teles PnP cards use io1= and io2= instead of io= for specifying 
+The Creatix/Teles PnP cards use io1= and io2= instead of io= for specifying
 the I/O addresses of the ISAC and HSCX chips, respectively.
 
 Card types:
@@ -135,24 +146,29 @@
    11   Eicon.Diehl Diva ISA PnP irq, io
    11   Eicon.Diehl Diva PCI     no parameter
    12   ASUS COM ISDNLink        irq, io  (from isapnp setup)
-   13   HFC-2BS0 based cards     irq, io  
+   13   HFC-2BS0 based cards     irq, io
    14   Teles 16.3c PnP          irq, io
-   15   Sedlbauer Speed Card     irq, io  
-   16   USR Sportster internal   irq, io  
-   17   MIC card                 irq, io  
+   15   Sedlbauer Speed Card     irq, io
+   15   Sedlbauer PC/104         irq, io
+   15   Sedlbauer Speed PCI	 no parameter
+   16   USR Sportster internal   irq, io
+   17   MIC card                 irq, io
    18   ELSA Quickstep 1000PCI   no parameter
    19   Compaq ISDN S0 ISA card  irq, io0, io1, io (from isapnp setup io=IO2)
    20   NETjet PCI card          no parameter
+   21   Teles PCI                no parameter
    22   Sedlbauer Speed Star (PCMCIA) irq, io (set with card manager)
    24   Dr. Neuhaus Niccy PnP    irq, io0, io1 (from isapnp setup)
    24   Dr. Neuhaus Niccy PCI    no parameter
+   25   Teles S0Box              irq, io (of the used lpt port)
+   26   AVM A1 PCMCIA (Fritz!)   irq, io (set with card manager)
+   27   AVM PnP (Fritz!PnP)      irq, io  (from isapnp setup)
+   27   AVM PCI (Fritz!PCI)      no parameter
+   28   Sedlbauer Speed Fax+     irq, io (from isapnp setup)
 
 
-        
-At the moment IRQ sharing is not possible. Please make sure that your IRQ
-is free and enabled for ISA use.
-Note: For using the ELSA PCMCIA you need the cardmanager under MSDOS for
-enabling at the moment, then boot linux with loadlin.
+At the moment IRQ sharing is only possible with PCI cards. Please make sure
+that your IRQ is free and enabled for ISA use.
 
 
 Examples for module loading
@@ -169,7 +185,7 @@
 4. Any ELSA PCC/PCF card, Euro ISDN
    modprobe hisax type=6 protocol=2
 
-5. Teles 16.3 PnP, Euro ISDN, with isapnp configured 
+5. Teles 16.3 PnP, Euro ISDN, with isapnp configured
    isapnp config:  (INT 0 (IRQ 10 (MODE +E)))
  		   (IO 0 (BASE 0x0580))
                    (IO 1 (BASE 0x0180))
@@ -210,8 +226,8 @@
                 Note: the ID string must start with an alphabetical character!
 
 Card types:
-       
-  type  
+
+type
     1 	Teles 16.0     	        pa=irq  pb=membase  pc=iobase
     2 	Teles  8.0              pa=irq  pb=membase
     3 	Teles 16.3              pa=irq  pb=iobase
@@ -227,22 +243,33 @@
    12   ASUS COM ISDNLink       ONLY WORKS AS A MODULE !
    13	HFC-2BS0 based cards    pa=irq  pb=io
    14   Teles 16.3c PnP         ONLY WORKS AS A MODULE !
-   15	Sedlbauer Speed Card    pa=irq  pb=io (Speed Win only as module !)  
+   15	Sedlbauer Speed Card    pa=irq  pb=io (Speed Win only as module !)
+   15   Sedlbauer PC/104        pa=irq  pb=io
+   15   Sedlbauer Speed PCI	no parameter
    16   USR Sportster internal  pa=irq  pb=io
    17   MIC card                pa=irq  pb=io
    18   ELSA Quickstep 1000PCI  no parameter
    19   Compaq ISDN S0 ISA card ONLY WORKS AS A MODULE !
    20   NETjet PCI card         no parameter
-   21   Sedlbauer Speed Star (PCMCIA)  pa=irq, pb=io  (set with card manager)
+   21   Teles PCI               no parameter
+   22   Sedlbauer Speed Star (PCMCIA)  pa=irq, pb=io  (set with card manager)
+   24   Dr. Neuhaus Niccy PnP   ONLY WORKS AS A MODULE !
+   24   Dr. Neuhaus Niccy PCI   no parameter
+   25   Teles S0Box             irq, io (of the used lpt port)
+   26   AVM A1 PCMCIA (Fritz!)  irq, io (set with card manager)
+   27   AVM PnP (Fritz!PnP)     ONLY WORKS AS A MODULE !
+   27   AVM PCI (Fritz!PCI)     no parameter
+   28   Sedlbauer Speed Fax+    ONLY WORKS AS A MODULE !
+
 
 Running the driver
 ------------------
 
-When you insmod isdn.o and hisax.o (or with the in-kernel version, during 
+When you insmod isdn.o and hisax.o (or with the in-kernel version, during
 boot time), a few lines should appear in your syslog. Look for something like:
 
 Apr 13 21:01:59 kke01 kernel: HiSax: Driver for Siemens chip set ISDN cards
-Apr 13 21:01:59 kke01 kernel: HiSax: Version 2.1
+Apr 13 21:01:59 kke01 kernel: HiSax: Version 2.9
 Apr 13 21:01:59 kke01 kernel: HiSax: Revisions 1.14/1.9/1.10/1.25/1.8
 Apr 13 21:01:59 kke01 kernel: HiSax: Total 1 card defined
 Apr 13 21:01:59 kke01 kernel: HiSax: Card 1 Protocol EDSS1 Id=HiSax1 (0)
@@ -257,7 +284,7 @@
 Apr 13 21:01:59 kke01 kernel: HiSax: 2 channels added
 
 This means that the card is ready for use.
-Cabling problems or line-downs are not detected, and only some ELSA cards can 
+Cabling problems or line-downs are not detected, and only some ELSA cards can
 detect the S0 power.
 
 Remember that, according to the new strategy for accessing low-level drivers
@@ -265,8 +292,7 @@
 insmod: Simply append hisax_id=<SomeString> to the insmod command line. This
 string MUST NOT start with a digit or a small 'x'!
 
-At this point you can run a 'cat /dev/isdnctrl0' and view debugging 
-messages. 
+At this point you can run a 'cat /dev/isdnctrl0' and view debugging messages.
 
 At the moment, debugging messages are enabled with the hisaxctrl tool:
 
@@ -283,28 +309,35 @@
 
 With DebugCmd set to 1:
 
-         1  Link-level <--> hardware-level communication
-         2  Top state machine
-         4  D-Channel Q.931 (call control messages)
-         8  D-Channel Q.921
-        16  B-Channel X.75
-        32  D-Channel l2
-        64  B-Channel l2
-       128  D-Channel link state debugging
-       256  B-Channel link state debugging
-       512  TEI debug
-      1024  LOCK debug in callc.c
-      2048  More paranoid debug in callc.c (not for normal use)
+   0x0001  Link-level <--> hardware-level communication
+   0x0002  Top state machine
+   0x0004  D-Channel Frames for isdnlog
+   0x0008  D-Channel Q.921
+   0x0010  B-Channel X.75
+   0x0020  D-Channel l2
+   0x0040  B-Channel l2
+   0x0080  D-Channel link state debugging
+   0x0100  B-Channel link state debugging
+   0x0200  TEI debug
+   0x0400  LOCK debug in callc.c
+   0x0800  More paranoid debug in callc.c (not for normal use)
+   0x1000  D-Channel l1 state debugging
+   0x2000  B-Channel l1 state debugging
 
 With DebugCmd set to 11:
 
-         1  Warnings (default: on)
-         2  IRQ status
-         4  ISAC
-         8  ISAC FIFO
-        16  HSCX
-        32  HSCX FIFO (attention: full B-Channel output!)
-        64  D-Channel LAPD frame types
+   0x0001  Warnings (default: on)
+   0x0002  IRQ status
+   0x0004  ISAC
+   0x0008  ISAC FIFO
+   0x0010  HSCX
+   0x0020  HSCX FIFO (attention: full B-Channel output!)
+   0x0040  D-Channel LAPD frame types
+   0x0080  IPAC debug
+   0x0100  HFC receive debug
+   0x0200  ISAC monitor debug
+   0x0400  D-Channel frames for isdnlog (set with 1 0x4 too)
+   0x0800  D-Channel message verbose
 
 With DebugCmd set to 13:
 
@@ -317,27 +350,39 @@
 
 Because of some obscure problems with some switch equipment, the delay
 between the CONNECT message and sending the first data on the B-channel is now
-configurable with 
+configurable with
 
 hisaxctrl <DriverId> 2 <delay>
 <delay> in ms Value between 50 and 800 ms is recommended.
 
+Downloading Firmware
+--------------------
+At the moment, the Sedlbauer speed fax+ is the only card, which
+needs to download firmware.
+The firmware is downloaded with the hisaxctrl tool:
+
+    hisaxctrl <DriverId> 9 <firmware_filename>
+
+<DriverId> default is HiSax, if you didn't specify one,
+
+where <firmware_filename> is the filename of the firmware file.
+
+For example, 'hisaxctrl HiSax 9 ISAR.BIN' downloads the firmware for
+ISAR based cards (like the Sedlbauer speed fax+).
 
 Warning
 -------
-HiSax is a work in progress and may crash your machine. It has not been
-certified and therefore operation on your PTT's ISDN network is probably
-illegal.
-
+HiSax is a work in progress and may crash your machine.
+For certification look at HiSax.cert file.
 
 Limitations
 -----------
 At this time, HiSax only works on Euro ISDN lines and German 1TR6 lines.
-For leased lines see appendix. 
+For leased lines see appendix.
 
-Bugs 
+Bugs
 ----
-If you find any, please let me know. 
+If you find any, please let me know.
 
 
 Thanks
@@ -354,8 +399,10 @@
 	Stephan von Krawczynski
 	Juergen Quade for the Leased Line part
 	Klaus Lichtenwalder (Klaus.Lichtenwalder@WebForum.DE), for ELSA PCMCIA support
+	Enrik Berkhan (enrik@starfleet.inka.de) for S0BOX specific stuff
+	Ton van Rosmalen for Teles PCI
         and more people who are hunting bugs. (If I forgot somebody, please
-	send me a mail). 
+	send me a mail).
 
         Firma ELSA GmbH
         Firma Eicon.Diehl GmbH
@@ -364,20 +411,23 @@
 	Firma S.u.S.E
 	Firma ith Kommunikationstechnik GmbH
 	Firma Traverse Technologie Australia
-        
+	Firma Medusa GmbH  (www.medusa.de).
+	Firma Quant-X Austria for sponsoring a DEC Alpha board+CPU
+	Firma Cologne Chip Designs GmbH
+
         My girl friend and partner in life Ute for her patience with me.
 
 
 Enjoy,
 
-Karsten Keil   
-keil@temic-ech.spacenet.de
+Karsten Keil
+keil@isdn4linux.de
 
 
 Appendix: Teles PCMCIA driver
 -----------------------------
 
-See 
+See
    http://www.stud.uni-wuppertal.de/~ea0141/pcmcia.html
 for instructions.
 
@@ -451,7 +501,7 @@
       /sbin/isdnctrl l2_prot isdn0 hdlc
       # Attention you must not set an outgoing number !!! This won't work !!!
       # The incomming number is LEASED0 for the first card, LEASED1 for the
-      # second and so on. 
+      # second and so on.
       /sbin/isdnctrl addphone isdn0 in LEASED0
       # Here is no need to bind the channel.
    c) in case the remote partner is a CISCO:
@@ -461,11 +511,12 @@
    e) set the routes
       /sbin/route add -host ${REMOTE_IP} isdn0
       /sbin/route add default gw ${REMOTE_IP}
-   f) switch the card into leased mode for each used B-channel 
+   f) switch the card into leased mode for each used B-channel
       /sbin/hisaxctrl HiSax 5 1
- 
+
 Remarks:
 a) If you have a CISCO don't forget to switch off the KEEP ALIVE option!
+b) Use state of the art isdn4k-utils
 
 Here an example script:
 #!/bin/sh
@@ -517,6 +568,7 @@
 			/sbin/isdnctrl encap isdn0s cisco-h
 		fi
 	fi
+	/sbin/isdnctrl dialmode isdn0 auto
 	# configure tcp/ip
 	/sbin/ifconfig isdn0 ${LOCAL_IP} pointopoint ${REMOTE_IP}
 	/sbin/route add -host ${REMOTE_IP} isdn0
diff -u --recursive --new-file v2.3.3/linux/Documentation/isdn/README.act2000 linux/Documentation/isdn/README.act2000
--- v2.3.3/linux/Documentation/isdn/README.act2000	Thu Jan  7 08:41:54 1999
+++ linux/Documentation/isdn/README.act2000	Sun May 23 10:03:41 1999
@@ -1,4 +1,4 @@
-$Id: README.act2000,v 1.1 1997/09/24 23:50:16 fritz Exp $
+$Id: README.act2000,v 1.2 1998/04/29 19:49:06 he Exp $
 
 This document describes the ACT2000 driver for the
 IBM Active 2000 ISDN card.
@@ -7,7 +7,7 @@
 Version. Currently, only the ISA-Bus version of the card is supported.
 However MCA and PCMCIA will follow soon.
 
-The ISA-Bus Version uses 8 IO-ports. The base port address has to be set
+The ISA-Bus Version uses 8 IO-ports. The base port adress has to be set
 manually using the DIP switches.
 
 Setting up the DIP switches for the IBM Active 2000 ISDN card:
diff -u --recursive --new-file v2.3.3/linux/Documentation/isdn/README.audio linux/Documentation/isdn/README.audio
--- v2.3.3/linux/Documentation/isdn/README.audio	Tue Apr 28 14:22:04 1998
+++ linux/Documentation/isdn/README.audio	Sun May 23 10:03:41 1999
@@ -1,4 +1,4 @@
-$Id: README.audio,v 1.5 1997/02/23 23:53:46 fritz Exp $
+$Id: README.audio,v 1.7 1998/07/26 18:45:34 armin Exp $
 
 ISDN subsystem for Linux.
   Description of audio mode.
@@ -50,10 +50,11 @@
                          the application. See below for data format
 
         AT+VSD=x,y       Set silence-detection parameters.
-                         NO EFFECT, supported for compatibility
-                         only. Possible parameters:
-                           x = 0 ... 31
-                           y = 0 ... 255
+                         Possible parameters:
+                           x = 0 ... 31  sensitivity threshold level.
+                                         (default 0 , deactivated)
+                           y = 0 ... 255 range of interval in units
+                                         of 0.1 second. (default 70)
         AT+VSD=?         Report possible parameters.
         AT+VSD?          Show current parameters.
 
@@ -102,13 +103,14 @@
     <DLE>C                  Touchtone "C" received.
     <DLE>D                  Touchtone "D" received.
 
+    <DLE>q                  quiet. Silence detected after non-silence.
+    <DLE>s                  silence. Silence detected from the
+                            start of recording.
+
     Currently unsupported DLE sequences:
 
     <DLE>c                  FAX calling tone received.
     <DLE>b                  busy tone received.
-    <DLE>q                  quiet. Silence detected after non-silence.
-    <DLE>s                  silence. Silence detected from the
-                            start of recording.
 
   Audio playback.
 
diff -u --recursive --new-file v2.3.3/linux/Documentation/isdn/README.concap linux/Documentation/isdn/README.concap
--- v2.3.3/linux/Documentation/isdn/README.concap	Thu Jan  7 08:41:54 1999
+++ linux/Documentation/isdn/README.concap	Sun May 23 10:03:41 1999
@@ -256,3 +256,4 @@
 of the concap interface when a trivial concap protocol is used.
 Nevertheless, the device remains able to support encapsulation
 protocol configuration.
+
diff -u --recursive --new-file v2.3.3/linux/Documentation/isdn/README.eicon linux/Documentation/isdn/README.eicon
--- v2.3.3/linux/Documentation/isdn/README.eicon	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/isdn/README.eicon	Sun May 23 10:03:41 1999
@@ -0,0 +1,92 @@
+$Id: README.eicon,v 1.3 1999/03/29 11:10:04 armin Exp $
+
+(c) 1999 Cytronics & Melware
+
+This document describes the eicon driver for the
+Eicon.Diehl active ISDN cards.
+
+It is meant to be used with isdn4linux, an ISDN link-level module for Linux.
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+NOTE : Since the eicon driver is still experimental, this README file
+       may be incomplete and not up to date.
+
+
+However, the driver should work under following conditions :
+
+Supported Cards
+---------------
+
+- S-Card ISA
+- SX-Card ISA
+- SXn-Card ISA
+- SCOM-Card ISA
+- Quadro-Card ISA
+- S2M-Card ISA
+- DIVA Server BRI/PCI 2M
+- DIVA Server PRI/PCI 2M (9M 23M 30M)
+  (Only analog modem functions of the DSPs are currently implemented)
+
+ISDN D-Channel Protocols
+------------------------
+
+- ETSI (Euro-DSS1) 
+- 1TR6 (German ISDN) *not testet*
+
+
+
+You can load the module simply by using the insmod or modprobe function :
+
+  insmod eicon [id=driverid] [membase=<membase>] [irq=<irq>]
+
+
+The module will automatically probe the PCI-cards. If the id-options
+is omitted, the driver will assume 'eicon0' for the first pci card and
+increases the digit with each further card. With a given driver-id
+the module appends a number starting with '0'.
+
+For ISA-cards you have to specify membase, irq and id. If id or
+membase is missing/invalid, the driver will not be loaded except
+PCI-cards were found. Additional ISA-cards and irq/membase changes
+can be done with the eiconctrl utility.
+
+After loading the module, you have to download the protocol and
+dsp-code by using the eiconctrl utility of isdn4k-utils.
+
+
+Example for loading and starting a BRI card with E-DSS1 Protocol.
+
+	eiconctrl [-d DriverId] load etsi
+
+
+Example for loading and starting a PRI card with E-DSS1 Protocol.
+
+	eiconctrl [-d DriverId] load etsi -s2 -n
+
+
+Details about using the eiconctrl utility are in 'man eiconctrl'
+or will be printed by starting eiconctrl without any parameters.
+
+
+
+Any reports about bugs, errors and even wishes are welcome.
+
+
+Have fun !
+
+Armin Schindler
+mac@melware.de
+http://www.melware.de
diff -u --recursive --new-file v2.3.3/linux/Documentation/isdn/README.icn linux/Documentation/isdn/README.icn
--- v2.3.3/linux/Documentation/isdn/README.icn	Tue Apr 28 14:22:04 1998
+++ linux/Documentation/isdn/README.icn	Sun May 23 10:03:41 1999
@@ -1,4 +1,4 @@
-$Id: README.icn,v 1.5 1997/04/23 18:55:55 fritz Exp $
+$Id: README.icn,v 1.6 1998/04/29 19:49:08 he Exp $
 
 You can get the ICN-ISDN-card from:
 
diff -u --recursive --new-file v2.3.3/linux/Documentation/isdn/README.x25 linux/Documentation/isdn/README.x25
--- v2.3.3/linux/Documentation/isdn/README.x25	Thu Jan  7 08:41:54 1999
+++ linux/Documentation/isdn/README.x25	Sun May 23 10:03:41 1999
@@ -1,29 +1,24 @@
- 
-X25 support within isdn4linux
-
-
-This is experimental code and should be used with linux version 2.1.72.
-or later. Use it completely at your own risk.
-
+  
+X.25 support within isdn4linux
+==============================
 
+This is alpha/beta test code. Use it completely at your own risk.
 As new versions appear, the stuff described here might suddenly change
 or become invalid without notice.
 
 Keep in mind:
 
-You are using an experimental kernel (2.1.x series) with an experimental
-x25 protocol implementation and experimental x25-on-top-of-isdn extensions.
-Thus, be prepared to face problems related therefrom.
+You are using several new parts of the 2.2.x kernel series which
+have not been tested in a large scale. Therefore, you might encounter
+more bugs as usual.
 
-- If you connect to an x25 neighbour not operated by yourself, ASK the
+- If you connect to an X.25 neighbour not operated by yourself, ASK the
   other side first. Be prepared that bugs in the protocol implementation
-  might result in problems (even crashing the peer, however such ugly events
-  should only happen if your peer's protocol implementation has serious bugs).
+  might result in problems.
 
 - This implementation has never wiped out my whole hard disk yet. But as
-  this is experimental code, don't blame me if that happened to you. Take
-  appropriate actions (such as backing up important data) before
-  trying this code.
+  this is experimental code, don't blame me if that happened to you.
+  Backing up important data will never harm.
 
 - Monitor your isdn connections while using this software. This should
   prevent you from undesired phone bills in case of driver problems.
@@ -32,7 +27,7 @@
 
 
 How to configure the kernel
-
+===========================
  
 The ITU-T (former CCITT) X.25 network protocol layer has been implemented
 in the Linux source tree since version 2.1.16. The isdn subsystem might be 
@@ -48,32 +43,33 @@
 compilation. You currently also need to enable
 "Prompt for development and/or incomplete code/drivers" from the
 "Code maturity level options" menu. For the x25trace utility to work
-you also need to enable "Packet socket" (I recommend to choose "y",
-not "m" for testing) from the networking options.
+you also need to enable "Packet socket".
 
+For local testing it is also recommended to enable the isdnloop driver
+from the isdn subsystem's configuration menu.
 
-For testing you should also select the isdnloop driver from the
-isdn subsystem's configuration menu.
+For testing, it is recommended that all isdn drivers and the X.25 PLP
+protocol are compiled as loadable modules. Like this, you can recover
+from certain errors by simply unloading and reloading the modules.
 
 
 
 What's it for? How to use it?
+=============================
 
-
-X25 on top of isdn might be useful with two different scenarios:
+X.25 on top of isdn might be useful with two different scenarios:
 
 - You might want to access a public X.25 data network from your Linux box.
   You can use i4l if you were physically connected to the X.25 switch
-  by an ISDN line (leased line as well as dial up connection should work,
-  but connecting to x.25 network switches is currently untested. Testing
-  needs to be done by somebody with access to such a switch.) 
-
-- Or you might want to operate certain ISDN teleservices on 
-  your linux box. A lot of those teleservices run on top of the ISO-8208 
-  network layer protocol. ISO-8208 is essentially the same as ITU-T X.25.
+  by an ISDN line (leased line as well as dial up connection should work)
+
+- Or you might want to operate certain ISDN teleservices on your linux
+  box. A lot of those teleservices run on top of the ISO-8208
+  (DTE-DTE mode) network layer protocol. ISO-8208 is essentially the
+  same as ITU-T X.25.
 
-  Popular candidates of such teleservices are EUROFILE transfer or any
-  teleservice applying ITU-T recommendation T.90 (i.e., AFAIK, G4 Fax).
+  Popular candidates of such teleservices are EUROfile transfer or any
+  teleservice applying ITU-T recommendation T.90.
 
 To use the X.25 protocol on top of isdn, just create an isdn network
 interface as usual, configure your own and/or peer's ISDN numbers,
@@ -81,21 +77,18 @@
 
    isdnctrl encap <iface-name> x25iface.
 
-Once encap is set like this, the device can be used by the x25 packet layer.
+Once encap is set like this, the device can be used by the X.25 packet layer.
 
-All the stuff needed for x25 is implemented inside the isdn link
+All the stuff needed for X.25 is implemented inside the isdn link
 level (mainly isdn_net.c and some new source files). Thus, it should
-work with every existing HL driver. I was able to successfully open x25
+work with every existing HL driver. I was able to successfully open X.25
 connections on top of the isdnloop driver and the hisax driver.
 "x25iface"-encapsulation bypasses demand dialing. Dialing will be
-initiated when the upper (x25 packet) layer requests the lapb datalink to
-be established. But hangup timeout is still active. The connection
-will not automatically be re-established by the isdn_net module
-itself when new data arrives after the hangup timeout. But
-the x25 network code will re-establish the datalink connection
-(resulting in re-dialing and an x25 protocol reset) when new data is
-to be transmitted. (This currently does not work properly with the
-isdnloop driver, see "known problems" below)
+initiated when the upper (X.25 packet) layer requests the lapb datalink to
+be established. But hangup timeout is still active. Whenever a hangup
+occurs, all existing X.25 connections on that link will be cleared
+It is recommended to use sufficiently large hangup-timeouts for the
+isdn interfaces.
 
 
 In order to set up a conforming protocol stack you also need to
@@ -114,104 +107,64 @@
    isdnctrl l2_prot <iface-name> x25dce
 
 However, x25dte or x25dce is currently not supported by any real HL
-level driver. The main difference between x75 and x25dte/dce is that
+level driver. The main difference between x75i and x25dte/dce is that
 x25d[tc]e uses fixed lap_b addresses. With x75i, the side which
 initiates the isdn connection uses the DTE's lap_b address while the
-called side used the DCE's lap_b address. Thus, l2_prot x75i will
-probably work if you access a public x25 network as long as the
-corresponding isdn connection is set up by you. However, I've never
-tested this.
-
-
+called side used the DCE's lap_b address. Thus, l2_prot x75i might
+probably work if you access a public X.25 network as long as the
+corresponding isdn connection is set up by you. At least one test
+was successful to connect via isdn4linux to an X.25 switch using this
+trick. At the switch side, a terminal adapter X.21 was used to connect
+it to the isdn.
 
-How to use the test installation?
 
+How to set up a test installation?
+==================================
 
-To test x25 on top of isdn, you need to get
+To test X.25 on top of isdn, you need to get
 
-- a patched version of the "isdnctrl" program that supports setting the new
-  x25 specific parameters.
+- a recent version of the "isdnctrl" program that supports setting the new
+  X.25 specific parameters.
 
-- the x25-utils-2.1.x package from ftp.pspt.fi/pub/ham/linux/ax25
-  or any mirror site (i.e. ftp://ftp.gwdg.de/pub/linux/misc/ax25/).
+- the x25-utils-2.X package from 
+  ftp://ftp.hes.iki.fi/pub/ham/linux/ax25/x25utils-*
+  (don't confuse the x25-utils with the ax25-utils)
 
-- a kernel patch that enhances isdn4linux to provide x25 network
-  interface support. (This file is part of that kernel patch).
-
-- an application that uses linux AF_X25 sockets program. 
+- an application program that uses linux PF_X25 sockets (some are
+  contained in the x25-util package).
 
 Before compiling the user level utilities make sure that the compiler/
-preprocessor will fetch the proper (patched) kernel header files. Either make
-/usr/include/linux a symbolic link pointing to your developer kernel's
-include/linux directory or set the appropriate compiler flags.
-
-It is recommended that all isdn drivers and the x25 PLP protocol 
-are compiled as loadable modules. Like this, you can recover
-from certain errors by simply unloading and reloading the modules.
+preprocessor will fetch the proper kernel header files of this kernel
+source tree. Either make /usr/include/linux a symbolic link pointing to 
+this kernel's include/linux directory or set the appropriate compiler flags.
 
 When all drivers and interfaces are loaded and configured you need to
-ifconfig the network interfaces up and add x25-routes to them. Use
+ifconfig the network interfaces up and add X.25-routes to them. Use
 the usual ifconfig tool.
 
 ifconfig <iface-name> up
 
 But a special x25route tool (distributed with the x25-util package)
-is needed to set up x25 routes. I.e. 
+is needed to set up X.25 routes. I.e. 
 
 x25route add 01 <iface-name>
 
-will cause all x.25 connections to the destination x.25-address
+will cause all x.25 connections to the destination X.25-address
 "01" to be routed to your created isdn network interface.
 
-
-There are currently no real x25 applications available. However, for
+There are currently no real X.25 applications available. However, for
 tests, the x25-utils package contains a modified version of telnet
-and telnetd that uses x25 sockets instead of tcp/ip sockets. Use
-this for your first tests. Furthermore, there is an x25.echod and a client
-named "eftp" (which contains some experimental code to download files
-from a remote eft server using the EUROfile transfer protocol).
-It available at ftp://ftp.hamburg.pop.de/pub/LOCAL/linux/i4l-eft/eftp4linux-*
+and telnetd that uses X.25 sockets instead of tcp/ip sockets. You can
+use those for your first tests. Furthermore, you might check
+ftp://ftp.hamburg.pop.de/pub/LOCAL/linux/i4l-eft/ which contains some
+alpha-test implementation ("eftp4linux") of the EUROfile transfer
+protocol.
+
+The scripts distributed with the eftp4linux test releases might also
+provide useful examples for setting up X.25 on top of isdn.
 
 The x25-utility package also contains an x25trace tool that can be
-used to monitor x25 packets received by the network interfaces.
+used to monitor X.25 packets received by the network interfaces.
 The /proc/net/x25* files also contain useful information. 
 
-The eftp4linux test release also contains an "ix25test" script that can
-be used for testing x25 on top of isdn4linux. Edit
-this script according to your local needs and then call it as
-
-ix25test start
-
-This will set up a sample configuration using the isdnloop and hisax
-driver and create some isdn network interfaces.
-It is recommended that all other isdn drivers and the
-x25 module are unloaded before calling this script.
-
-
-
-Known problems and deficiencies:
-
-The isdnloop HL driver apparently has problems to re-establish a
-connection that has been hung up from the outgoing device. You have to
-unload the isdnloop driver after the faked isdn-connection is closed
-and insmod it again. With the Hisax driver, this problem is not present.
-
-Sometimes the x25 module cannot be unloaded (decrementation of its use
-count seems to get lost occasionally).
-
-Using the x25 based telnet and telnetd programm to establish connection
-from your own to your own computer repeatedly sometimes totally locked
-up my system. However, this kernel patch also modifies
-net/x25/af_x25.c to include a workaround. With this workaround
-enabled, my system is stable. (If you want to disable the
-workaround, just undefine ISDN_X25_FIXES in af_x25.c).
-
-The latter problem could be reproduced by using hisax as well as the
-isdnloop driver. It seems that it is not caused by the isdn code.
-Somehow, the inode of a socket is freed while a process still refers
-the socket's wait queue. This causes problems when the process tries to
-remove itself from the wait queue (referred by the dangling
-sock->sleep pointer) before returning from a select() system call.
-
 - Henner
-
diff -u --recursive --new-file v2.3.3/linux/Documentation/networking/decnet.txt linux/Documentation/networking/decnet.txt
--- v2.3.3/linux/Documentation/networking/decnet.txt	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/networking/decnet.txt	Wed May 26 09:36:36 1999
@@ -0,0 +1,146 @@
+                    Linux DECnet Networking Layer Information
+                   ===========================================
+
+1) Other documentation....
+
+   o Project Home Pages
+       http://www.sucs.swan.ac.uk/~rohan/DECnet/index.html - Kernel info
+       http://linux.dreamtime.org/decnet/                  - Userland tools
+
+   o FTP sites
+       ftp://ftp.sucs.swan.ac.uk/pub/Linux/DECnet/
+                - Swansea University Computer Society DECnet Archive
+                  (contains kernel patches and info)
+                - Mirror of userland tools on ftp.dreamtime.org
+
+        ftp://ftp.dreamtime.org/pub/linux/decnet/
+                - Patrick Caulfield's archive of userland tools and
+                  Eduardo Serrat's kernel patches
+
+2) Configuring the kernel
+
+Be sure to turn on the following options:
+
+    CONFIG_DECNET (obviously)
+    CONFIG_PROCFS (to see what's going on)
+    CONFIG_SYSCTL (for easy configuration)
+
+if you want to try out router support (not properly debugged and not
+complete yet), you'll need the following options as well...
+
+    CONFIG_DECNET_RAW (to receive routing packets)
+    CONFIG_DECNET_ROUTER (to be able to add/delete routes)
+    CONFIG_NETLINK (to allow rtnetlink)
+    CONFIG_RTNETLINK (for communication with the kernel routing layer)
+
+3) Command line options
+
+The kernel command line takes options looking like the following:
+
+    decnet=1,2,1
+
+the first two numbers are the node address 1,2 = 1.2 (yes, you must use
+commas when specifying them). The third number is the level number for routers
+and is optional. It is probably a good idea to set the DECnet address on boot
+like this rather than trying to do it later.
+
+There are also equivalent options for modules. The node address and type can
+also be set through the /proc/sys/net/decnet/ files, as can other system
+parameters.
+
+Currently the only supported device is ethernet. You'll have to set the
+ethernet address of your ethernet card according to the DECnet address
+of the node in order for it to be recognised (and thus appear in
+/proc/net/decnet_dev). There is a utility available at the above
+FTP sites called dn2ethaddr which can compute the correct ethernet
+address to use. The address can be set by ifconfig either before at
+at the time the device is brought up. If you are using RedHat you can
+add the line:
+
+    MACADDR=AA:00:04:00:03:04
+
+or something similar, to /etc/sysconfig/network-scripts/ifcfg-eth0 or
+wherever your network card's configuration lives.
+
+You will also need to set /proc/sys/net/decnet/default_device to the
+device you want DECnet to route packets out of when no specific route
+is available. Usually this will be eth0, for example:
+
+    echo -n "eth0" >/proc/sys/net/decnet/default_device
+
+There is a list of what the other files under /proc/sys/net/decnet/ do
+on the kernel patch web site (shown above).
+
+4) How can I tell if its working ?
+
+Here is a quick guide of what to look for in order to know if your DECnet
+kernel subsystem is working.
+
+   - Is the node address set (see /proc/sys/net/decnet/node_address)
+   - Is the node of the correct type (see /proc/sys/net/decnet/node_type)
+   - Is the Ethernet MAC address of each Ethernet card set to match
+     the DECnet address. If in doubt use the dn2ethaddr utility available
+     at the ftp archive.
+   - If the previous two steps are satisfied, and the Ethernet card is up,
+     you should find that it is listed in /proc/net/decnet_dev and also
+     that it appears as a directory in /proc/sys/net/decnet/conf/. The
+     loopback device (lo) should also appear and is required to communicate
+     within a node.
+   - If you have any DECnet routers on your network, they should appear
+     in /proc/net/decnet_neigh, otherwise this file will only contain the
+     entry for the node itself (if it doesn't check to see if lo is up).
+   - If you want to send to any node which is not listed in the
+     /proc/net/decnet_neigh file, you'll need to set the default device
+     to point to an Ethernet card with connection to a router. This is
+     again done with the /proc/sys/net/decnet/default_device file.
+   - Try starting a simple server and client, like the dnping/dnmirror
+     over the loopback interface. With luck they should communicate.
+     For this step and those after, you'll need the DECnet library
+     which can be obtained from the above ftp sites as well as the
+     actual utilities themselves.
+   - If this seems to work, then try talking to a node on your local
+     network, and see if you can obtain the same results.
+   - At this point you are on your own... :-)
+
+5) How to send a bug report
+
+If you've found a bug and want to report it, then there are several things
+you can do to help me work out exactly what it is that is wrong. Useful
+information (a lot of which is essential) includes:
+
+ - What kernel version are you running ?
+ - What version of the patch are you running ?
+ - How far though the above set of tests can you get ?
+ - What is in the /proc/decnet* files and /proc/sys/net/decnet/* files ?
+ - Which services are you running ?
+ - Which client caused the problem ?
+ - How much data was being transfered ?
+ - Was the network congested ?
+ - If there was a kernel panic, please run the output through ksymoops
+   before sending it to me, otherwise its _useless_.
+ - How can the problem be reproduced ?
+ - Can you use tcpdump to get a trace ? (N.B. Most (all?) versions of 
+   tcpdump don't understand how to dump DECnet properly, so including
+   the hex listing of the packet contents is essential, usually the -x flag.
+   You may also need to increase the length grabbed with the -s flag)
+
+6) Mailing list
+
+If you are keen to get involved in development, or want to ask questions
+about configuration, or even just report bugs, then there is a mailing
+list that you can join: send mail to majordomo@dreamtime.org with
+
+subscribe linux-decnet
+
+as the body of the message.
+
+7) Legal Info
+
+The Linux DECnet project team have placed their code under the GPL. The
+software is provided "as is" and without warranty express or implied.
+DECnet is a trademark of Compaq. This software is not a product of
+Compaq. We acknowledge the help of people at Compaq in providing extra
+documentation above and beyond what was previously publicly available.
+
+Steve Whitehouse <SteveW@ACM.org>
+
diff -u --recursive --new-file v2.3.3/linux/Documentation/sound/CMI8330 linux/Documentation/sound/CMI8330
--- v2.3.3/linux/Documentation/sound/CMI8330	Sun Mar  7 15:22:06 1999
+++ linux/Documentation/sound/CMI8330	Wed May 26 09:29:34 1999
@@ -1,46 +1,48 @@
-How to enable CMI 8330 soundchip on Linux
+How to enable CMI 8330 (SOUNDPRO) soundchip on Linux
 ------------------------------------------
 Stefan Laudat <Stefan.Laudat@asit.ro>
 
-Hello folks, 
-	
-	The CMI8330 soundchip is a very small chip found on many recent 
-	motherboards. In order to use it you just have to use a proper 
-        isapnp.conf and a little bit of patience. 
+[Note: The CMI 8338 is unrelated and right now unsupported]
+
 	
-	Of course you will have to compile kernel sound support as module, 
-        as shown below:
+	In order to use CMI8330 under Linux  you just have to use a proper isapnp.conf, a good isapnp and a little bit of patience.  I use isapnp 1.17, but
+you may get a better one I guess at http://www.roestock.demon.co.uk/isapnptools/.
+
+	Of course you will have to compile kernel sound support as module, as shown below:
 
 CONFIG_SOUND=m
 CONFIG_SOUND_OSS=m
 CONFIG_SOUND_SB=m
 CONFIG_SOUND_ADLIB=m
 CONFIG_SOUND_MPU401=m
-# Just for fun :)
+# Mikro$chaft sound system (kinda useful here ;))	
 CONFIG_SOUND_MSS=m
 
 	The /etc/isapnp.conf file will be:
 
 <snip below>
 
+
 (READPORT 0x0203)
 (ISOLATE PRESERVE)
 (IDENTIFY *)
 (VERBOSITY 2)
 (CONFLICT (IO FATAL)(IRQ FATAL)(DMA FATAL)(MEM FATAL)) # or WARNING
 (VERIFYLD N)
+
+
 # WSS 
 
 (CONFIGURE CMI0001/16777472 (LD 0
 (IO 0 (SIZE 8) (BASE 0x0530))
 (IO 1 (SIZE 8) (BASE 0x0388))
-(INT 0 (IRQ 5 (MODE +E)))
+(INT 0 (IRQ 7 (MODE +E)))
 (DMA 0 (CHANNEL 0))
 (NAME "CMI0001/16777472[0]{CMI8330/C3D Audio Adapter}")
 (ACT Y)
 ))
 
-# Control device ? 
+# MPU
 
 (CONFIGURE CMI0001/16777472 (LD 1
 (IO 0 (SIZE 2) (BASE 0x0330))
@@ -57,10 +59,11 @@
 (ACT Y)
 ))
 
-#  SB... 
+# SoundBlaster 
+ 
 (CONFIGURE CMI0001/16777472 (LD 3
 (IO 0 (SIZE 16) (BASE 0x0220))
-(INT 0 (IRQ 7 (MODE +E)))
+(INT 0 (IRQ 5 (MODE +E)))
 (DMA 0 (CHANNEL 1))
 (DMA 1 (CHANNEL 5))
 (NAME "CMI0001/16777472[3]{CMI8330/C3D Audio Adapter}")
@@ -74,13 +77,22 @@
 
 	The module sequence is trivial:
 
-/sbin/modprobe sound
-# You need to load the ad1848 module first. That matters, otherwise the 
-# chip falls into soundblaster compatibility and you won't get it back out
-/sbin/insmod ad1848 io=0x530 dma=0 irq=5 soundpro=1
+/sbin/insmod soundcore
+/sbin/insmod sound
 /sbin/insmod uart401
-/sbin/insmod sb io=0x220 irq=5 dma=1 dma16=-1
-/sbin/insmod mpu401 io=0x330
-/sbin/insmod opl3 io=0x388
+# insert this first
+/sbin/insmod ad1848 io=0x530 irq=7 dma=0 soundpro=1
+# The sb module is an alternative to the ad1848 (Microsoft Sound System)
+# Anyhow, this is full duplex and has MIDI
+/sbin/insmod sb io=0x220 dma=1 dma16=5 irq=5 mpu_io=0x330
+
+
 
-	The soundchip is now fully initialized. Enjoy it.
+Alma Chao <elysian@ethereal.torsion.org> suggests the following /etc/conf.modules:
+
+alias sound ad1848
+alias synth0 opl3
+options ad1848 io=0x530 irq=7 dma=0 soundpro=1
+options opl3 io=0x388
+
+	
diff -u --recursive --new-file v2.3.3/linux/Documentation/sound/CMI8338 linux/Documentation/sound/CMI8338
--- v2.3.3/linux/Documentation/sound/CMI8338	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/sound/CMI8338	Wed May 26 09:36:36 1999
@@ -0,0 +1,32 @@
+Audio driver for CM8338/CM8738 chips by Chen-Li Tien
+
+   Be aware: C-Media Electronics Inc. is basically an IC design house,
+   and whose development of software drivers is mainly for use by its OEM
+   customers in their products. C-Media Electronics Inc. itself does not
+   manufacture end-user products, such as PC or sound cards, so it can
+   not fully control the drivers provided to consumers. Drivers provided
+   at this site, therefore, MAY NOT BE APPLICABLE to all sound cards.
+   Drivers you download from this site may function well at certain
+   situation, but C-Media Electronics Inc. does not give any guarantee or
+   assurances. Please be aware that these drivers might cause some
+   technical difficulties when installed
+
+
+1. Config cm8338 driver by 'make menuconfig' or 'make config' command.
+
+2. Please select Sound Card (CONFIG_SOUND=m) support and CMPCI driver (CONFIG_SOUND_CMPCI=m) as modules. Resident mode not tested.
+
+3. Compile the kernel if necessary.
+
+4. Compile the modules by 'make modules'.
+
+5. Install the modules by 'make modules_install'
+
+6. Before first time to run the driver, create module dependency by 'depmod -a'
+
+7. To install the driver, enter 'modprobe cmpci'.
+
+
+Bugs:
+
+1. Real player cannot be run (the same as es1371).
diff -u --recursive --new-file v2.3.3/linux/Makefile linux/Makefile
--- v2.3.3/linux/Makefile	Sat May 15 23:46:02 1999
+++ linux/Makefile	Mon May 17 09:55:04 1999
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 3
-SUBLEVEL = 2
+SUBLEVEL = 4
 EXTRAVERSION =
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
diff -u --recursive --new-file v2.3.3/linux/README linux/README
--- v2.3.3/linux/README	Fri May 14 18:55:11 1999
+++ linux/README	Sun May 30 10:17:43 1999
@@ -46,11 +46,11 @@
  - There is a lot of documentation available both in electronic form on
    the Internet and in books, both Linux-specific and pertaining to
    general UNIX questions.  I'd recommend looking into the documentation
-   subdirectories on any Linux ftp site for the LDP (Linux Documentation
+   subdirectories on any Linux FTP site for the LDP (Linux Documentation
    Project) books.  This README is not meant to be documentation on the
    system: there are much better sources available.
 
- - There are various readme's in the kernel Documentation/ subdirectory:
+ - There are various README files in the Documentation/ subdirectory:
    these typically contain kernel-specific installation notes for some 
    drivers for example. See ./Documentation/00-INDEX for a list of what
    is contained in each file.  Please read the Changes file, as it
@@ -62,7 +62,7 @@
  - If you install the full sources, do a
 
 		cd /usr/src
-		gzip -cd linux-2.3.XX.tar.gz | tar xfv -
+		gzip -cd linux-2.3.XX.tar.gz | tar xvf -
 
    to get it all put in place. Replace "XX" with the version number of the
    latest kernel.
@@ -233,7 +233,7 @@
    isn't anyone listed there, then the second best thing is to mail
    them to me (torvalds@transmeta.com), and possibly to any other
    relevant mailing-list or to the newsgroup.  The mailing-lists are
-   useful especially for SCSI and NETworking problems, as I can't test
+   useful especially for SCSI and networking problems, as I can't test
    either of those personally anyway. 
 
  - In all bug-reports, *please* tell what kernel you are talking about,
diff -u --recursive --new-file v2.3.3/linux/arch/alpha/kernel/process.c linux/arch/alpha/kernel/process.c
--- v2.3.3/linux/arch/alpha/kernel/process.c	Sat May 15 23:46:03 1999
+++ linux/arch/alpha/kernel/process.c	Sat May 22 13:46:08 1999
@@ -58,10 +58,10 @@
 static struct file * init_fd_array[NR_OPEN] = { NULL, };
 static struct files_struct init_files = INIT_FILES;
 static struct signal_struct init_signals = INIT_SIGNALS;
-struct mm_struct init_mm = INIT_MM;
+struct mm_struct init_mm = INIT_MM(init_mm);
 
 union task_union init_task_union __attribute__((section("init_task")))
-	 = { task: INIT_TASK };
+	 = { task: INIT_TASK(init_task_union.task) };
 
 /*
  * No need to acquire the kernel lock, we're entirely local..
diff -u --recursive --new-file v2.3.3/linux/arch/alpha/kernel/ptrace.c linux/arch/alpha/kernel/ptrace.c
--- v2.3.3/linux/arch/alpha/kernel/ptrace.c	Tue Dec 29 16:17:00 1998
+++ linux/arch/alpha/kernel/ptrace.c	Sat May 22 12:42:04 1999
@@ -506,7 +506,8 @@
 		     (current->uid != child->uid) ||
 		     (current->gid != child->egid) ||
 		     (current->gid != child->sgid) ||
-		     (current->gid != child->gid))
+		     (current->gid != child->gid) ||
+		     (!cap_issubset(child->cap_permitted, current->cap_permitted)))
 		    && !capable(CAP_SYS_PTRACE))
 			goto out;
 		/* the same process cannot be attached many times */
diff -u --recursive --new-file v2.3.3/linux/arch/arm/kernel/ptrace.c linux/arch/arm/kernel/ptrace.c
--- v2.3.3/linux/arch/arm/kernel/ptrace.c	Wed Sep  9 08:56:58 1998
+++ linux/arch/arm/kernel/ptrace.c	Sat May 22 12:42:04 1999
@@ -580,6 +580,7 @@
 		    (current->uid != child->uid) ||
 	 	    (current->gid != child->egid) ||
 	 	    (current->gid != child->sgid) ||
+		    (!cap_issubset(child->cap_permitted, current->cap_permitted)) ||
 	 	    (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE))
 			goto out;
 		/* the same process cannot be attached many times */
diff -u --recursive --new-file v2.3.3/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v2.3.3/linux/arch/i386/defconfig	Sat May 15 23:46:03 1999
+++ linux/arch/i386/defconfig	Mon May 31 09:01:50 1999
@@ -304,8 +304,10 @@
 # CONFIG_USB_OHCI_HCD is not set
 CONFIG_USB_MOUSE=y
 CONFIG_USB_KBD=y
+CONFIG_USB_HUB=y
 # CONFIG_USB_AUDIO is not set
 # CONFIG_USB_ACM is not set
+# CONFIG_USB_PRINTER is not set
 
 #
 # Filesystems
diff -u --recursive --new-file v2.3.3/linux/arch/mips/kernel/ptrace.c linux/arch/mips/kernel/ptrace.c
--- v2.3.3/linux/arch/mips/kernel/ptrace.c	Tue Oct 20 13:52:54 1998
+++ linux/arch/mips/kernel/ptrace.c	Sat May 22 12:42:04 1999
@@ -297,6 +297,7 @@
 		    (current->uid != child->uid) ||
 	 	    (current->gid != child->egid) ||
 		    (current->gid != child->sgid) ||
+		    (!cap_issubset(child->cap_permitted, current->cap_permitted)) ||
 	 	    (current->gid != child->gid)) && 
 		    !capable(CAP_SYS_PTRACE)) {
 			res = -EPERM;
diff -u --recursive --new-file v2.3.3/linux/arch/ppc/Makefile linux/arch/ppc/Makefile
--- v2.3.3/linux/arch/ppc/Makefile	Fri Mar 19 10:50:03 1999
+++ linux/arch/ppc/Makefile	Sat May 22 13:03:00 1999
@@ -29,6 +29,10 @@
 CFLAGS := $(CFLAGS) -mcpu=860
 endif
 
+ifdef CONFIG_PPC64
+CFLAGS := $(CFLAGS) -Wa,-mppc64bridge #-Wa,-mppc64
+#CFLAGS := $(CFLAGS) -Wa,-mppc64 -mpowerpc64
+endif
 
 HEAD := arch/ppc/kernel/head.o
 
diff -u --recursive --new-file v2.3.3/linux/arch/ppc/boot/Makefile linux/arch/ppc/boot/Makefile
--- v2.3.3/linux/arch/ppc/boot/Makefile	Thu Apr 29 12:39:01 1999
+++ linux/arch/ppc/boot/Makefile	Sat May 22 13:03:00 1999
@@ -26,9 +26,15 @@
 ISZ = 0
 
 ifeq ($(CONFIG_SMP),y)
-TFTPIMAGE=/tftpboot/zImage.prep.smp
+TFTPIMAGE=/tftpboot/zImage.prep.smp$(MSIZE)
 else
-TFTPIMAGE=/tftpboot/zImage.prep
+TFTPIMAGE=/tftpboot/zImage.prep$(MSIZE)
+endif
+
+ifeq ($(CONFIG_PPC64),y)
+MSIZE=.64
+else
+MSIZE=
 endif
 
 ZLINKFLAGS = -T ../vmlinux.lds -Ttext 0x00800000
diff -u --recursive --new-file v2.3.3/linux/arch/ppc/boot/misc.c linux/arch/ppc/boot/misc.c
--- v2.3.3/linux/arch/ppc/boot/misc.c	Tue May 11 08:24:32 1999
+++ linux/arch/ppc/boot/misc.c	Sat May 22 13:03:00 1999
@@ -1,7 +1,7 @@
 /*
  * misc.c
  *
- * $Id: misc.c,v 1.64 1999/04/30 05:52:46 cort Exp $
+ * $Id: misc.c,v 1.65 1999/05/17 19:11:13 cort Exp $
  * 
  * Adapted for PowerPC by Gary Thomas
  *
@@ -363,7 +363,7 @@
 			if (board_type == 0xe0) {	
 				base_mod = inb(0x803);
 				/* if a MVME2300/2400 or a Sitka then no keyboard */
-				if((base_mod == 0x9) || (base_mod == 0xF9) ||
+				if((base_mod == 0xFA) || (base_mod == 0xF9) ||
 				   (base_mod == 0xE1)) {
 					keyb_present = 0;	/* no keyboard */
 				}
diff -u --recursive --new-file v2.3.3/linux/arch/ppc/chrpboot/Makefile linux/arch/ppc/chrpboot/Makefile
--- v2.3.3/linux/arch/ppc/chrpboot/Makefile	Fri Mar 19 10:50:03 1999
+++ linux/arch/ppc/chrpboot/Makefile	Sat May 22 13:03:00 1999
@@ -23,15 +23,21 @@
 OBJS = crt0.o start.o main.o misc.o ../coffboot/string.o ../coffboot/zlib.o image.o # initrd.o
 LIBS = $(TOPDIR)/lib/lib.a
 
+ifeq ($(CONFIG_PPC64),y)
+MSIZE=.64
+else
+MSIZE=
+endif
+
 ifeq ($(CONFIG_ALL_PPC),y)
 # yes, we want to build chrp stuff
 CONFIG_CHRP = y
 endif
 
 ifeq ($(CONFIG_SMP),y)
-TFTPIMAGE=/tftpboot/zImage.chrp.smp
+TFTPIMAGE=/tftpboot/zImage.chrp.smp$(MSIZE)
 else
-TFTPIMAGE=/tftpboot/zImage.chrp
+TFTPIMAGE=/tftpboot/zImage.chrp$(MSIZE)
 endif
 
 all:	$(TOPDIR)/zImage
diff -u --recursive --new-file v2.3.3/linux/arch/ppc/coffboot/Makefile linux/arch/ppc/coffboot/Makefile
--- v2.3.3/linux/arch/ppc/coffboot/Makefile	Fri Mar 19 10:50:03 1999
+++ linux/arch/ppc/coffboot/Makefile	Sat May 22 13:03:00 1999
@@ -23,10 +23,16 @@
 CONFIG_PMAC = y
 endif
 
+ifeq ($(CONFIG_PPC64),y)
+MSIZE=.64
+else
+MSIZE=
+endif
+
 ifeq ($(CONFIG_SMP),y)
-TFTPIMAGE=/tftpboot/zImage.pmac.smp
+TFTPIMAGE=/tftpboot/zImage.pmac.smp$(MSIZE)
 else
-TFTPIMAGE=/tftpboot/zImage.pmac
+TFTPIMAGE=/tftpboot/zImage.pmac$(MSIZE)
 endif
 
 ifeq ($(CONFIG_PMAC),y)
diff -u --recursive --new-file v2.3.3/linux/arch/ppc/common_defconfig linux/arch/ppc/common_defconfig
--- v2.3.3/linux/arch/ppc/common_defconfig	Tue May 11 08:24:32 1999
+++ linux/arch/ppc/common_defconfig	Sat May 22 13:03:00 1999
@@ -7,21 +7,24 @@
 #
 CONFIG_PPC=y
 CONFIG_6xx=y
+# CONFIG_PPC64 is not set
 # CONFIG_8xx is not set
-# CONFIG_PMAC is not set
+CONFIG_PMAC=y
 # CONFIG_PREP is not set
 # CONFIG_CHRP is not set
-CONFIG_ALL_PPC=y
+# CONFIG_ALL_PPC is not set
 # CONFIG_APUS is not set
 # CONFIG_MBX is not set
 # CONFIG_SMP is not set
+CONFIG_MACH_SPECIFIC=y
+CONFIG_6xx=y
 
 #
 # General setup
 #
 CONFIG_EXPERIMENTAL=y
 CONFIG_MODULES=y
-# CONFIG_MODVERSIONS is not set
+CONFIG_MODVERSIONS=y
 CONFIG_KMOD=y
 CONFIG_PCI=y
 # CONFIG_PCI_QUIRKS is not set
@@ -32,7 +35,7 @@
 # CONFIG_BSD_PROCESS_ACCT is not set
 CONFIG_BINFMT_ELF=y
 CONFIG_KERNEL_ELF=y
-CONFIG_BINFMT_MISC=m
+# CONFIG_BINFMT_MISC is not set
 # CONFIG_BINFMT_JAVA is not set
 # CONFIG_PARPORT is not set
 CONFIG_VGA_CONSOLE=y
@@ -48,7 +51,6 @@
 # CONFIG_TOTALMP is not set
 CONFIG_BOOTX_TEXT=y
 # CONFIG_MOTOROLA_HOTSWAP is not set
-# CONFIG_CMDLINE_BOOL is not set
 
 #
 # Plug and Play support
@@ -66,7 +68,9 @@
 #
 # CONFIG_BLK_DEV_HD_IDE is not set
 CONFIG_BLK_DEV_IDEDISK=y
+# CONFIG_IDEDISK_MULTI_MODE is not set
 CONFIG_BLK_DEV_IDECD=y
+# CONFIG_IDECD_SLOTS is not set
 # CONFIG_BLK_DEV_IDETAPE is not set
 CONFIG_BLK_DEV_IDEFLOPPY=y
 # CONFIG_BLK_DEV_IDESCSI is not set
@@ -75,13 +79,15 @@
 # CONFIG_BLK_DEV_IDEPCI is not set
 # CONFIG_BLK_DEV_SL82C105 is not set
 CONFIG_BLK_DEV_IDE_PMAC=y
-# CONFIG_BLK_DEV_IDEDMA_PMAC is not set
+CONFIG_BLK_DEV_IDEDMA_PMAC=y
+CONFIG_BLK_DEV_IDEDMA=y
+CONFIG_PMAC_IDEDMA_AUTO=y
 # CONFIG_IDE_CHIPSETS is not set
 
 #
 # Additional Block Devices
 #
-CONFIG_BLK_DEV_LOOP=y
+# CONFIG_BLK_DEV_LOOP is not set
 # CONFIG_BLK_DEV_NBD is not set
 # CONFIG_BLK_DEV_MD is not set
 CONFIG_BLK_DEV_RAM=y
@@ -89,6 +95,7 @@
 # CONFIG_BLK_DEV_XD is not set
 CONFIG_PARIDE_PARPORT=y
 # CONFIG_PARIDE is not set
+CONFIG_BLK_DEV_IDE_MODES=y
 # CONFIG_BLK_DEV_HD is not set
 
 #
@@ -151,12 +158,12 @@
 CONFIG_CHR_DEV_ST=y
 CONFIG_BLK_DEV_SR=y
 CONFIG_BLK_DEV_SR_VENDOR=y
-CONFIG_CHR_DEV_SG=y
+# CONFIG_CHR_DEV_SG is not set
 
 #
 # Some SCSI devices (e.g. CD jukebox) support multiple LUNs
 #
-CONFIG_SCSI_MULTI_LUN=y
+# CONFIG_SCSI_MULTI_LUN is not set
 CONFIG_SCSI_CONSTANTS=y
 # CONFIG_SCSI_LOGGING is not set
 
@@ -191,14 +198,8 @@
 # CONFIG_SCSI_NCR53C406A is not set
 # CONFIG_SCSI_SYM53C416 is not set
 # CONFIG_SCSI_NCR53C7xx is not set
-CONFIG_SCSI_NCR53C8XX=y
+# CONFIG_SCSI_NCR53C8XX is not set
 # CONFIG_SCSI_SYM53C8XX is not set
-CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=8
-CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32
-CONFIG_SCSI_NCR53C8XX_SYNC=20
-# CONFIG_SCSI_NCR53C8XX_PROFILE is not set
-# CONFIG_SCSI_NCR53C8XX_IOMAPPED is not set
-# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set
 # CONFIG_SCSI_PAS16 is not set
 # CONFIG_SCSI_PCI2000 is not set
 # CONFIG_SCSI_PCI2220I is not set
@@ -236,7 +237,7 @@
 # CONFIG_ACENIC is not set
 # CONFIG_NET_ISA is not set
 CONFIG_NET_EISA=y
-CONFIG_PCNET32=y
+# CONFIG_PCNET32 is not set
 # CONFIG_AC3200 is not set
 # CONFIG_APRICOT is not set
 # CONFIG_CS89x0 is not set
@@ -296,32 +297,18 @@
 CONFIG_FB_CONTROL=y
 CONFIG_FB_PLATINUM=y
 CONFIG_FB_VALKYRIE=y
-# CONFIG_FB_ATY is not set
+CONFIG_FB_ATY=y
 CONFIG_FB_IMSTT=y
 CONFIG_FB_CT65550=y
 # CONFIG_FB_S3TRIO is not set
-CONFIG_FB_MATROX=y
-CONFIG_FB_MATROX_MILLENIUM=y
-CONFIG_FB_MATROX_MYSTIQUE=y
-CONFIG_FB_MATROX_G100=y
-# CONFIG_FB_MATROX_MULTIHEAD is not set
-# CONFIG_FB_ATY is not set
+# CONFIG_FB_MATROX is not set
+CONFIG_FB_ATY=y
 # CONFIG_FB_VIRTUAL is not set
-CONFIG_FBCON_ADVANCED=y
-# CONFIG_FBCON_MFB is not set
-# CONFIG_FBCON_CFB2 is not set
-# CONFIG_FBCON_CFB4 is not set
+# CONFIG_FBCON_ADVANCED is not set
 CONFIG_FBCON_CFB8=y
 CONFIG_FBCON_CFB16=y
 CONFIG_FBCON_CFB24=y
 CONFIG_FBCON_CFB32=y
-# CONFIG_FBCON_AFB is not set
-# CONFIG_FBCON_ILBM is not set
-# CONFIG_FBCON_IPLAN2P2 is not set
-# CONFIG_FBCON_IPLAN2P4 is not set
-# CONFIG_FBCON_IPLAN2P8 is not set
-# CONFIG_FBCON_MAC is not set
-# CONFIG_FBCON_VGA is not set
 # CONFIG_FBCON_FONTWIDTH8_ONLY is not set
 CONFIG_FBCON_FONTS=y
 # CONFIG_FONT_8x8 is not set
@@ -337,25 +324,15 @@
 #
 CONFIG_VT=y
 CONFIG_VT_CONSOLE=y
-CONFIG_SERIAL=m
+# CONFIG_SERIAL is not set
 # CONFIG_SERIAL_EXTENDED is not set
 # CONFIG_SERIAL_NONSTANDARD is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
-CONFIG_MOUSE=y
-
-#
-# Mice
-#
-# CONFIG_ATIXL_BUSMOUSE is not set
-# CONFIG_BUSMOUSE is not set
-# CONFIG_MS_BUSMOUSE is not set
-CONFIG_PSMOUSE=y
-# CONFIG_82C710_MOUSE is not set
-# CONFIG_PC110_PAD is not set
+# CONFIG_MOUSE is not set
 # CONFIG_QIC02_TAPE is not set
 # CONFIG_WATCHDOG is not set
-# CONFIG_NVRAM is not set
+CONFIG_NVRAM=y
 # CONFIG_RTC is not set
 
 #
@@ -390,10 +367,11 @@
 # CONFIG_ADFS_FS is not set
 # CONFIG_AFFS_FS is not set
 CONFIG_HFS_FS=y
-CONFIG_FAT_FS=m
-CONFIG_MSDOS_FS=m
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=y
 # CONFIG_UMSDOS_FS is not set
-CONFIG_VFAT_FS=m
+CONFIG_VFAT_FS=y
+# CONFIG_EFS_FS is not set
 CONFIG_ISO9660_FS=y
 # CONFIG_JOLIET is not set
 # CONFIG_MINIX_FS is not set
@@ -477,4 +455,4 @@
 #
 CONFIG_MAGIC_SYSRQ=y
 # CONFIG_KGDB is not set
-# CONFIG_XMON is not set
+CONFIG_XMON=y
diff -u --recursive --new-file v2.3.3/linux/arch/ppc/config.in linux/arch/ppc/config.in
--- v2.3.3/linux/arch/ppc/config.in	Tue May 11 08:24:32 1999
+++ linux/arch/ppc/config.in	Sat May 22 13:03:00 1999
@@ -1,4 +1,4 @@
-# $Id: config.in,v 1.92 1999/04/30 05:41:43 cort Exp $
+# $Id: config.in,v 1.93 1999/05/14 22:36:58 cort Exp $
 # For a description of the syntax of this configuration file,
 # see the Configure script.
 #
@@ -8,8 +8,9 @@
 comment 'Platform support'
 define_bool CONFIG_PPC y
 choice 'Processor type' \
-	"6xx/7xx	CONFIG_6xx \
-	 860/821	CONFIG_8xx" 6xx/7xx
+	"6xx/7xx		CONFIG_6xx \
+	 630/Power3(64-Bit) 	CONFIG_PPC64 \
+	 860/821		CONFIG_8xx" 6xx/7xx
 
 choice 'Machine Type' \
 	"PowerMac	CONFIG_PMAC \
@@ -22,6 +23,10 @@
 bool 'Symmetric multi-processing support' CONFIG_SMP
 if [ "$CONFIG_ALL_PPC" != "y" ];then
   define_bool CONFIG_MACH_SPECIFIC y
+fi
+
+if [ "$CONFIG_PPC64" != "y" ];then
+  define_bool CONFIG_6xx y
 fi
 endmenu
 
diff -u --recursive --new-file v2.3.3/linux/arch/ppc/defconfig linux/arch/ppc/defconfig
--- v2.3.3/linux/arch/ppc/defconfig	Tue May 11 08:24:32 1999
+++ linux/arch/ppc/defconfig	Sat May 22 13:03:00 1999
@@ -7,21 +7,24 @@
 #
 CONFIG_PPC=y
 CONFIG_6xx=y
+# CONFIG_PPC64 is not set
 # CONFIG_8xx is not set
-# CONFIG_PMAC is not set
+CONFIG_PMAC=y
 # CONFIG_PREP is not set
 # CONFIG_CHRP is not set
-CONFIG_ALL_PPC=y
+# CONFIG_ALL_PPC is not set
 # CONFIG_APUS is not set
 # CONFIG_MBX is not set
 # CONFIG_SMP is not set
+CONFIG_MACH_SPECIFIC=y
+CONFIG_6xx=y
 
 #
 # General setup
 #
 CONFIG_EXPERIMENTAL=y
 CONFIG_MODULES=y
-# CONFIG_MODVERSIONS is not set
+CONFIG_MODVERSIONS=y
 CONFIG_KMOD=y
 CONFIG_PCI=y
 # CONFIG_PCI_QUIRKS is not set
@@ -32,7 +35,7 @@
 # CONFIG_BSD_PROCESS_ACCT is not set
 CONFIG_BINFMT_ELF=y
 CONFIG_KERNEL_ELF=y
-CONFIG_BINFMT_MISC=m
+# CONFIG_BINFMT_MISC is not set
 # CONFIG_BINFMT_JAVA is not set
 # CONFIG_PARPORT is not set
 CONFIG_VGA_CONSOLE=y
@@ -48,7 +51,6 @@
 # CONFIG_TOTALMP is not set
 CONFIG_BOOTX_TEXT=y
 # CONFIG_MOTOROLA_HOTSWAP is not set
-# CONFIG_CMDLINE_BOOL is not set
 
 #
 # Plug and Play support
@@ -66,7 +68,9 @@
 #
 # CONFIG_BLK_DEV_HD_IDE is not set
 CONFIG_BLK_DEV_IDEDISK=y
+# CONFIG_IDEDISK_MULTI_MODE is not set
 CONFIG_BLK_DEV_IDECD=y
+# CONFIG_IDECD_SLOTS is not set
 # CONFIG_BLK_DEV_IDETAPE is not set
 CONFIG_BLK_DEV_IDEFLOPPY=y
 # CONFIG_BLK_DEV_IDESCSI is not set
@@ -75,13 +79,15 @@
 # CONFIG_BLK_DEV_IDEPCI is not set
 # CONFIG_BLK_DEV_SL82C105 is not set
 CONFIG_BLK_DEV_IDE_PMAC=y
-# CONFIG_BLK_DEV_IDEDMA_PMAC is not set
+CONFIG_BLK_DEV_IDEDMA_PMAC=y
+CONFIG_BLK_DEV_IDEDMA=y
+CONFIG_PMAC_IDEDMA_AUTO=y
 # CONFIG_IDE_CHIPSETS is not set
 
 #
 # Additional Block Devices
 #
-CONFIG_BLK_DEV_LOOP=y
+# CONFIG_BLK_DEV_LOOP is not set
 # CONFIG_BLK_DEV_NBD is not set
 # CONFIG_BLK_DEV_MD is not set
 CONFIG_BLK_DEV_RAM=y
@@ -89,6 +95,7 @@
 # CONFIG_BLK_DEV_XD is not set
 CONFIG_PARIDE_PARPORT=y
 # CONFIG_PARIDE is not set
+CONFIG_BLK_DEV_IDE_MODES=y
 # CONFIG_BLK_DEV_HD is not set
 
 #
@@ -151,12 +158,12 @@
 CONFIG_CHR_DEV_ST=y
 CONFIG_BLK_DEV_SR=y
 CONFIG_BLK_DEV_SR_VENDOR=y
-CONFIG_CHR_DEV_SG=y
+# CONFIG_CHR_DEV_SG is not set
 
 #
 # Some SCSI devices (e.g. CD jukebox) support multiple LUNs
 #
-CONFIG_SCSI_MULTI_LUN=y
+# CONFIG_SCSI_MULTI_LUN is not set
 CONFIG_SCSI_CONSTANTS=y
 # CONFIG_SCSI_LOGGING is not set
 
@@ -191,14 +198,8 @@
 # CONFIG_SCSI_NCR53C406A is not set
 # CONFIG_SCSI_SYM53C416 is not set
 # CONFIG_SCSI_NCR53C7xx is not set
-CONFIG_SCSI_NCR53C8XX=y
+# CONFIG_SCSI_NCR53C8XX is not set
 # CONFIG_SCSI_SYM53C8XX is not set
-CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=8
-CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32
-CONFIG_SCSI_NCR53C8XX_SYNC=20
-# CONFIG_SCSI_NCR53C8XX_PROFILE is not set
-# CONFIG_SCSI_NCR53C8XX_IOMAPPED is not set
-# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set
 # CONFIG_SCSI_PAS16 is not set
 # CONFIG_SCSI_PCI2000 is not set
 # CONFIG_SCSI_PCI2220I is not set
@@ -236,7 +237,7 @@
 # CONFIG_ACENIC is not set
 # CONFIG_NET_ISA is not set
 CONFIG_NET_EISA=y
-CONFIG_PCNET32=y
+# CONFIG_PCNET32 is not set
 # CONFIG_AC3200 is not set
 # CONFIG_APRICOT is not set
 # CONFIG_CS89x0 is not set
@@ -296,32 +297,18 @@
 CONFIG_FB_CONTROL=y
 CONFIG_FB_PLATINUM=y
 CONFIG_FB_VALKYRIE=y
-# CONFIG_FB_ATY is not set
+CONFIG_FB_ATY=y
 CONFIG_FB_IMSTT=y
 CONFIG_FB_CT65550=y
 # CONFIG_FB_S3TRIO is not set
-CONFIG_FB_MATROX=y
-CONFIG_FB_MATROX_MILLENIUM=y
-CONFIG_FB_MATROX_MYSTIQUE=y
-CONFIG_FB_MATROX_G100=y
-# CONFIG_FB_MATROX_MULTIHEAD is not set
-# CONFIG_FB_ATY is not set
+# CONFIG_FB_MATROX is not set
+CONFIG_FB_ATY=y
 # CONFIG_FB_VIRTUAL is not set
-CONFIG_FBCON_ADVANCED=y
-# CONFIG_FBCON_MFB is not set
-# CONFIG_FBCON_CFB2 is not set
-# CONFIG_FBCON_CFB4 is not set
+# CONFIG_FBCON_ADVANCED is not set
 CONFIG_FBCON_CFB8=y
 CONFIG_FBCON_CFB16=y
 CONFIG_FBCON_CFB24=y
 CONFIG_FBCON_CFB32=y
-# CONFIG_FBCON_AFB is not set
-# CONFIG_FBCON_ILBM is not set
-# CONFIG_FBCON_IPLAN2P2 is not set
-# CONFIG_FBCON_IPLAN2P4 is not set
-# CONFIG_FBCON_IPLAN2P8 is not set
-# CONFIG_FBCON_MAC is not set
-# CONFIG_FBCON_VGA is not set
 # CONFIG_FBCON_FONTWIDTH8_ONLY is not set
 CONFIG_FBCON_FONTS=y
 # CONFIG_FONT_8x8 is not set
@@ -337,25 +324,15 @@
 #
 CONFIG_VT=y
 CONFIG_VT_CONSOLE=y
-CONFIG_SERIAL=m
+# CONFIG_SERIAL is not set
 # CONFIG_SERIAL_EXTENDED is not set
 # CONFIG_SERIAL_NONSTANDARD is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
-CONFIG_MOUSE=y
-
-#
-# Mice
-#
-# CONFIG_ATIXL_BUSMOUSE is not set
-# CONFIG_BUSMOUSE is not set
-# CONFIG_MS_BUSMOUSE is not set
-CONFIG_PSMOUSE=y
-# CONFIG_82C710_MOUSE is not set
-# CONFIG_PC110_PAD is not set
+# CONFIG_MOUSE is not set
 # CONFIG_QIC02_TAPE is not set
 # CONFIG_WATCHDOG is not set
-# CONFIG_NVRAM is not set
+CONFIG_NVRAM=y
 # CONFIG_RTC is not set
 
 #
@@ -390,10 +367,11 @@
 # CONFIG_ADFS_FS is not set
 # CONFIG_AFFS_FS is not set
 CONFIG_HFS_FS=y
-CONFIG_FAT_FS=m
-CONFIG_MSDOS_FS=m
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=y
 # CONFIG_UMSDOS_FS is not set
-CONFIG_VFAT_FS=m
+CONFIG_VFAT_FS=y
+# CONFIG_EFS_FS is not set
 CONFIG_ISO9660_FS=y
 # CONFIG_JOLIET is not set
 # CONFIG_MINIX_FS is not set
@@ -477,4 +455,4 @@
 #
 CONFIG_MAGIC_SYSRQ=y
 # CONFIG_KGDB is not set
-# CONFIG_XMON is not set
+CONFIG_XMON=y
diff -u --recursive --new-file v2.3.3/linux/arch/ppc/kernel/Makefile linux/arch/ppc/kernel/Makefile
--- v2.3.3/linux/arch/ppc/kernel/Makefile	Thu Apr 29 12:39:01 1999
+++ linux/arch/ppc/kernel/Makefile	Sat May 22 13:03:00 1999
@@ -65,7 +65,7 @@
 	$(HOSTCC) -o find_name find_name.c
 
 checks: checks.c
-	$(HOSTCC) -fno-builtin -I$(TOPDIR)/include -D__KERNEL__ -o checks checks.c
+	$(HOSTCC) ${CFLAGS} -D__KERNEL__ -o checks checks.c
 	./checks
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.3.3/linux/arch/ppc/kernel/chrp_pci.c linux/arch/ppc/kernel/chrp_pci.c
--- v2.3.3/linux/arch/ppc/kernel/chrp_pci.c	Tue May 11 08:24:32 1999
+++ linux/arch/ppc/kernel/chrp_pci.c	Sat May 22 13:03:00 1999
@@ -8,6 +8,7 @@
 #include <linux/string.h>
 #include <linux/init.h>
 #include <linux/openpic.h>
+#include <linux/ide.h>
 
 #include <asm/io.h>
 #include <asm/pgtable.h>
diff -u --recursive --new-file v2.3.3/linux/arch/ppc/kernel/chrp_setup.c linux/arch/ppc/kernel/chrp_setup.c
--- v2.3.3/linux/arch/ppc/kernel/chrp_setup.c	Fri May 14 18:55:12 1999
+++ linux/arch/ppc/kernel/chrp_setup.c	Sat May 22 13:03:00 1999
@@ -286,29 +286,6 @@
 	if ( !strncmp("MOT", get_property(find_path_device("/"),
 					  "model", NULL),3) )
 		*memory_start_p = pmac_find_bridges(*memory_start_p, *memory_end_p);
-	/*
-	 * The f50 has a lot of IO space - we need to map some in that
-	 * isn't covered by the BAT mappings in MMU_init() -- Cort
-	 */
-	if ( !strncmp("F5", get_property(find_path_device("/"),
-					 "ibm,model-class", NULL),2) )
-	{
-#if 0		
-		/*
-		 * This ugly hack allows us to force ioremap() to
-		 * create a 1-to-1 mapping for us, even though
-		 * the address is < ioremap_base.  This is necessary
-		 * since we want our PCI IO space to have contiguous
-		 * virtual addresses and I think it's worse to have
-		 * calls to map_page() here.
-		 * -- Cort
-		 */
-		unsigned long hold = ioremap_base;
-		ioremap_base = 0;
-		__ioremap(0x90000000, 0x10000000, _PAGE_NO_CACHE);
-		ioremap_base = hold;
-#endif		
-	}
 }
 
 void
diff -u --recursive --new-file v2.3.3/linux/arch/ppc/kernel/head.S linux/arch/ppc/kernel/head.S
--- v2.3.3/linux/arch/ppc/kernel/head.S	Tue May 11 08:24:32 1999
+++ linux/arch/ppc/kernel/head.S	Sat May 22 13:03:00 1999
@@ -1,7 +1,7 @@
 /*
  *  arch/ppc/kernel/head.S
  *
- *  $Id: head.S,v 1.130 1999/05/09 19:16:43 cort Exp $
+ *  $Id: head.S,v 1.131 1999/05/14 22:37:21 cort Exp $
  *
  *  PowerPC version 
  *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
@@ -97,18 +97,32 @@
 	bdnz	0b
 #endif
 
+#ifdef CONFIG_PPC64
+#define LOAD_BAT(n, offset, reg, RA, RB) \
+	ld	RA,offset+0(reg);	\
+	ld	RB,offset+8(reg);	\
+	mtspr	IBAT##n##U,RA;		\
+	mtspr	IBAT##n##L,RB;		\
+	ld	RA,offset+16(reg);	\
+	ld	RB,offset+24(reg);	\
+	mtspr	DBAT##n##U,RA;		\
+	mtspr	DBAT##n##L,RB;		\
+	
+#else /* CONFIG_PPC64 */
+	
 /* 601 only have IBAT cr0.eq is set on 601 when using this macro */ 
 #define LOAD_BAT(n, offset, reg, RA, RB) \
-	lwz	RA,offset+0(reg); \
+	lwz	RA,offset+0(reg);	\
 	lwz	RB,offset+4(reg);	\
-	mtspr	IBAT##n##U,RA;	\
-	mtspr	IBAT##n##L,RB;	\
-	beq	1f;		\
+	mtspr	IBAT##n##U,RA;		\
+	mtspr	IBAT##n##L,RB;		\
+	beq	1f;			\
 	lwz	RA,offset+8(reg);	\
 	lwz	RB,offset+12(reg);	\
-	mtspr	DBAT##n##U,RA;	\
-	mtspr	DBAT##n##L,RB;	\
-1:	 
+	mtspr	DBAT##n##U,RA;		\
+	mtspr	DBAT##n##L,RB;		\
+1:	
+#endif /* CONFIG_PPC64 */	 
 
 #ifndef CONFIG_APUS
 #define tophys(rd,rs,rt)	addis	rd,rs,-KERNELBASE@h
@@ -206,6 +220,16 @@
 	
 	.globl	__start
 __start:
+#ifdef CONFIG_PPC64
+/* 
+ * Go into 32-bit mode to boot.  OF should do this for
+ * us already but just in case...
+ * -- Cort 
+ */
+	mfmsr	r10
+	clrldi	r10,r10,3
+	mtmsr	r10
+#endif	
 /*
  * We have to do any OF calls before we map ourselves to KERNELBASE,
  * because OF may have I/O devices mapped in in that area
@@ -226,10 +250,11 @@
  * of RAM to KERNELBASE.  From this point on we can't safely
  * call OF any more.
  */
+	lis	r11,KERNELBASE@h
+#ifndef CONFIG_PPC64
 	mfspr	r9,PVR
 	rlwinm	r9,r9,16,16,31		/* r9 = 1 for 601, 4 for 604 */
 	cmpi	0,r9,1
-	lis	r11,KERNELBASE@h
 	bne	4f
 	ori	r11,r11,4		/* set up BAT registers for 601 */
 	li	r8,0x7f			/* valid, block length = 8MB */
@@ -240,6 +265,7 @@
 	mtspr	IBAT1U,r9
 	mtspr	IBAT1L,r10
 	b	5f
+#endif /* CONFIG_PPC64 */
 4:
 #ifdef CONFIG_APUS
 	ori	r11,r11,BL_8M<<2|0x2	/* set up an 8MB mapping */
@@ -248,9 +274,17 @@
 	lwz	r8,0(r8)
 	addis	r8,r8,KERNELBASE@h
 	addi	r8,r8,2
-#else
+#else	
 	ori	r11,r11,BL_256M<<2|0x2	/* set up BAT registers for 604 */
 	li	r8,2			/* R/W access */
+#ifdef CONFIG_PPC64
+	/* clear out the high 32 bits in the BAT */
+	clrldi	r11,r11,32
+	clrldi	r8,r8,32
+	/* turn off the pagetable mappings just in case */
+	clrldi	r16,r16,63
+	mtsdr1	r16
+#else /* CONFIG_PPC64 */
 	/* 
 	 * allow secondary cpus to get at all of ram in early bootup
 	 * since their init_task may be up there -- Cort
@@ -268,6 +302,7 @@
 	mtspr	DBAT2U,r21		/* bit in upper BAT register */
 	mtspr	IBAT2L,r28
 	mtspr	IBAT2U,r21
+#endif /* CONFIG_PPC64 */
 #endif
 	mtspr	DBAT0L,r8		/* N.B. 6xx (not 601) have valid */
 	mtspr	DBAT0U,r11		/* bit in upper BAT register */
@@ -1246,7 +1281,7 @@
 	eieio
 	lis	r2,hash_table_lock@h
 	ori	r2,r2,hash_table_lock@l
-	tophys(r2,r2,r6)
+ 	tophys(r2,r2,r6)
 	lis	r6,100000000@h
 	mtctr	r6
 	lwz	r0,PROCESSOR-TSS(r5)
@@ -1294,6 +1329,11 @@
 	stw	r6,0(r2)		/* update PTE (accessed/dirty bits) */
 
 	/* Convert linux-style PTE to low word of PPC-style PTE */
+#ifdef CONFIG_PPC64
+	/* clear the high 32 bits just in case */
+	clrldi	r6,r6,32
+	clrldi	r4,r4,32
+#endif /* CONFIG_PPC64 */
 	rlwinm	r4,r6,32-9,31,31	/* _PAGE_HWWRITE -> PP lsb */
 	rlwimi	r6,r6,32-1,31,31	/* _PAGE_USER -> PP (both bits now) */
 	ori	r4,r4,0xe04		/* clear out reserved bits */
@@ -1301,16 +1341,34 @@
 
 	/* Construct the high word of the PPC-style PTE */
 	mfsrin	r5,r3			/* get segment reg for segment */
+#ifdef CONFIG_PPC64
+	sldi	r5,r5,12
+#else /* CONFIG_PPC64 */
 	rlwinm	r5,r5,7,1,24		/* put VSID in 0x7fffff80 bits */
+#endif /* CONFIG_PPC64 */
+	
 #ifndef __SMP__				/* do this later for SMP */
+#ifdef CONFIG_PPC64
+	ori	r5,r5,1			/* set V (valid) bit */
+#else /* CONFIG_PPC64 */
 	oris	r5,r5,0x8000		/* set V (valid) bit */
+#endif /* CONFIG_PPC64 */
 #endif
+	
+#ifdef CONFIG_PPC64
+/* XXX:	 does this insert the api correctly? -- Cort */
+	rlwimi	r5,r3,17,21,25		/* put in API (abbrev page index) */
+#else /* CONFIG_PPC64 */
 	rlwimi	r5,r3,10,26,31		/* put in API (abbrev page index) */
-
+#endif /* CONFIG_PPC64 */
 	/* Get the address of the primary PTE group in the hash table */
 	.globl	hash_page_patch_A
 hash_page_patch_A:
 	lis	r4,Hash_base@h		/* base address of hash table */
+#ifdef CONFIG_PPC64
+	/* just in case */
+	clrldi	r4,r4,32
+#endif	
 	rlwimi	r4,r5,32-1,26-Hash_bits,25	/* (VSID & hash_mask) << 6 */
 	rlwinm	r0,r3,32-6,26-Hash_bits,25	/* (PI & hash_mask) << 6 */
 	xor	r4,r4,r0		/* make primary hash */
@@ -1799,7 +1857,11 @@
  */
 #ifndef CONFIG_8xx
 	lis	r6,_SDR1@ha
+#ifdef CONFIG_PPC64	
+	ld	r6,_SDR1@l(r6)
+#else
 	lwz	r6,_SDR1@l(r6)
+#endif		
 #else
 	/* The right way to do this would be to track it down through
 	 * init's TSS like the context switch code does, but this is
@@ -1828,6 +1890,14 @@
 #endif
 #ifndef CONFIG_8xx
 	mtspr	SDR1,r6
+#ifdef CONFIG_PPC64
+	/* clear the v bit in the ASR so we can
+	 * behave as if we have segment registers 
+	 * -- Cort
+	 */
+	clrldi	r6,r6,63
+	mtasr	r6
+#endif /* CONFIG_PPC64 */
 	li	r0,16		/* load up segment register values */
 	mtctr	r0		/* for context 0 */
 	lis	r3,0x2000	/* Ku = 1, VSID = 0 */
@@ -1844,10 +1914,17 @@
 	lis	r3,BATS@ha
 	addi	r3,r3,BATS@l
 	tophys(r3,r3,r4)
+#ifdef CONFIG_PPC64
+	LOAD_BAT(0,0,r3,r4,r5)
+	LOAD_BAT(1,32,r3,r4,r5)
+	LOAD_BAT(2,64,r3,r4,r5)
+	LOAD_BAT(3,96,r3,r4,r5)
+#else /* CONFIG_PPC64 */
 	LOAD_BAT(0,0,r3,r4,r5)
 	LOAD_BAT(1,16,r3,r4,r5)
 	LOAD_BAT(2,32,r3,r4,r5)
 	LOAD_BAT(3,48,r3,r4,r5)
+#endif /* CONFIG_PPC64 */
 #endif /* CONFIG_8xx */
 /* Set up for using our exception vectors */
 	/* ptr to phys current tss */
diff -u --recursive --new-file v2.3.3/linux/arch/ppc/kernel/mbx_setup.c linux/arch/ppc/kernel/mbx_setup.c
--- v2.3.3/linux/arch/ppc/kernel/mbx_setup.c	Fri May 14 18:55:12 1999
+++ linux/arch/ppc/kernel/mbx_setup.c	Sat May 22 13:03:00 1999
@@ -1,5 +1,5 @@
 /*
- * $Id: mbx_setup.c,v 1.9 1999/04/28 11:54:09 davem Exp $
+ * $Id: mbx_setup.c,v 1.10 1999/05/14 07:24:19 davem Exp $
  *
  *  linux/arch/ppc/kernel/setup.c
  *
diff -u --recursive --new-file v2.3.3/linux/arch/ppc/kernel/misc.S linux/arch/ppc/kernel/misc.S
--- v2.3.3/linux/arch/ppc/kernel/misc.S	Thu Apr 29 12:39:01 1999
+++ linux/arch/ppc/kernel/misc.S	Sat May 22 13:03:00 1999
@@ -649,15 +649,6 @@
         blr
 
 /*
- * Fetch the current SR register
- *   get_SR(int index)
- */
-_GLOBAL(get_SR)
-	mfsrin	r4,r3
-	mr	r3,r4
-	blr
-
-/*
  * Create a kernel thread
  *   __kernel_thread(flags, fn, arg)
  */
diff -u --recursive --new-file v2.3.3/linux/arch/ppc/kernel/pmac_setup.c linux/arch/ppc/kernel/pmac_setup.c
--- v2.3.3/linux/arch/ppc/kernel/pmac_setup.c	Fri May 14 18:55:12 1999
+++ linux/arch/ppc/kernel/pmac_setup.c	Sat May 22 13:03:00 1999
@@ -41,6 +41,7 @@
 #include <linux/blk.h>
 #include <linux/vt_kern.h>
 #include <linux/console.h>
+#include <linux/ide.h>
 #include <asm/prom.h>
 #include <asm/system.h>
 #include <asm/pgtable.h>
@@ -543,17 +544,18 @@
         ppc_generic_ide_fix_driveid(id);
 }
 
+#if defined(CONFIG_BLK_DEV_IDE_PMAC)
 /* This is declared in drivers/block/ide-pmac.c */
 void pmac_ide_init_hwif_ports (hw_regs_t *hw, ide_ioreg_t data_port, ide_ioreg_t ctrl_port, int *irq);
-
+#else
 /*
  * This registers the standard ports for this architecture with the IDE
  * driver.
  */
-void
-ide_init_default_hwifs(void)
+void pmac_ide_init_hwif_ports(hw_regs_t *hw, ide_ioreg_t data_port, ide_ioreg_t ctrl_port, int *irq)
 {
 }
+#endif
 #endif
 
 __initfunc(void
diff -u --recursive --new-file v2.3.3/linux/arch/ppc/kernel/prep_pci.c linux/arch/ppc/kernel/prep_pci.c
--- v2.3.3/linux/arch/ppc/kernel/prep_pci.c	Tue May 11 08:24:32 1999
+++ linux/arch/ppc/kernel/prep_pci.c	Sat May 22 13:03:00 1999
@@ -1,5 +1,5 @@
 /*
- * $Id: prep_pci.c,v 1.33 1999/05/09 20:15:54 cort Exp $
+ * $Id: prep_pci.c,v 1.35 1999/05/10 23:31:03 cort Exp $
  * PReP pci functions.
  * Originally by Gary Thomas
  * rewritten and updated by Cort Dougan (cort@cs.nmt.edu)
@@ -38,6 +38,8 @@
 /* Used for Motorola to store system config register */
 static unsigned long	*ProcInfo;
 
+extern void chrp_do_IRQ(struct pt_regs *,int , int);
+
 /* Tables for known hardware */   
 
 /* Motorola PowerStackII - Utah */
@@ -731,6 +733,8 @@
 	OpenPIC_InitSenses = mvme2600_openpic_initsenses;
 	OpenPIC_NumInitSenses = sizeof(mvme2600_openpic_initsenses);
 
+	ppc_md.do_IRQ = chrp_do_IRQ;
+	
 	/* If raven is present on Motorola store the system config register
 	 * for later use.
 	 */
diff -u --recursive --new-file v2.3.3/linux/arch/ppc/kernel/prep_setup.c linux/arch/ppc/kernel/prep_setup.c
--- v2.3.3/linux/arch/ppc/kernel/prep_setup.c	Fri May 14 18:55:12 1999
+++ linux/arch/ppc/kernel/prep_setup.c	Sat May 22 13:03:00 1999
@@ -33,6 +33,7 @@
 #include <linux/timex.h>
 #include <linux/pci.h>
 #include <linux/openpic.h>
+#include <linux/ide.h>
 
 #include <asm/mmu.h>
 #include <asm/processor.h>
@@ -247,7 +248,7 @@
 	case _PREP_Motorola:
 		/* Enable L2.  Assume we don't need to flush -- Cort*/
 		*(unsigned char *)(0x8000081c) |= 3;
-		ROOT_DEV = to_kdev_t(0x0801); /* sda1 */
+		ROOT_DEV = to_kdev_t(0x0802); /* sda2 */
 		break;
 	case _PREP_Radstone:
 		ROOT_DEV = to_kdev_t(0x0801); /* sda1 */
@@ -774,10 +775,8 @@
 	ppc_md.get_cpuinfo    = prep_get_cpuinfo;
 	ppc_md.irq_cannonicalize = prep_irq_cannonicalize;
 	ppc_md.init_IRQ       = prep_init_IRQ;
-	if ( !OpenPIC )
-		ppc_md.do_IRQ         = prep_do_IRQ;
-	else
-		ppc_md.do_IRQ         = chrp_do_IRQ;
+	/* this gets changed later on if we have an OpenPIC -- Cort */
+	ppc_md.do_IRQ         = prep_do_IRQ;
 	ppc_md.init           = NULL;
 
 	ppc_md.restart        = prep_restart;
diff -u --recursive --new-file v2.3.3/linux/arch/ppc/kernel/process.c linux/arch/ppc/kernel/process.c
--- v2.3.3/linux/arch/ppc/kernel/process.c	Tue May 11 08:24:32 1999
+++ linux/arch/ppc/kernel/process.c	Sat May 22 13:03:00 1999
@@ -1,5 +1,5 @@
 /*
- * $Id: process.c,v 1.83 1999/05/10 04:43:43 cort Exp $
+ * $Id: process.c,v 1.85 1999/05/16 21:27:08 cort Exp $
  *
  *  linux/arch/ppc/kernel/process.c
  *
@@ -51,8 +51,8 @@
 static struct file * init_fd_array[NR_OPEN] = { NULL, };
 static struct files_struct init_files = INIT_FILES;
 static struct signal_struct init_signals = INIT_SIGNALS;
-struct mm_struct init_mm = INIT_MM;
-union task_union init_task_union = { INIT_TASK };
+struct mm_struct init_mm = INIT_MM(init_mm);
+union task_union init_task_union = { INIT_TASK(init_task_union.task) };
 /* only used to get secondary processor up */
 struct task_struct *current_set[NR_CPUS] = {&init_task, };
 
@@ -233,24 +233,6 @@
 		}
 	}
 out:
-}
-
-void instruction_dump (unsigned long *pc)
-{
-	int i;
-
-	if((((unsigned long) pc) & 3))
-                return;
-
-	printk("Instruction DUMP:");
-	for(i = -3; i < 6; i++)
-	{
-		unsigned long p;
-		if (__get_user( p, &pc[i] ))
-			break;
-		printk("%c%08lx%c",i?' ':'<',p,i?' ':'>');
-	}
-	printk("\n");
 }
 
 void exit_thread(void)
diff -u --recursive --new-file v2.3.3/linux/arch/ppc/kernel/ptrace.c linux/arch/ppc/kernel/ptrace.c
--- v2.3.3/linux/arch/ppc/kernel/ptrace.c	Thu Apr 29 12:39:01 1999
+++ linux/arch/ppc/kernel/ptrace.c	Sat May 22 12:42:04 1999
@@ -330,8 +330,12 @@
 		if ((!child->dumpable ||
 		    (current->uid != child->euid) ||
 		    (current->uid != child->uid) ||
+		    (current->uid != child->suid) ||
 	 	    (current->gid != child->egid) ||
-	 	    (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE))
+	 	    (current->gid != child->gid) ||
+		    (current->gid != child->sgid) ||
+		    (!cap_issubset(child->cap_permitted, current->cap_permitted)))
+		    && !capable(CAP_SYS_PTRACE))
 			goto out;
 		/* the same process cannot be attached many times */
 		if (child->flags & PF_PTRACED)
diff -u --recursive --new-file v2.3.3/linux/arch/ppc/kernel/residual.c linux/arch/ppc/kernel/residual.c
--- v2.3.3/linux/arch/ppc/kernel/residual.c	Fri May 14 18:55:12 1999
+++ linux/arch/ppc/kernel/residual.c	Sat May 22 13:03:00 1999
@@ -1,5 +1,5 @@
 /*
- * $Id: residual.c,v 1.14 1998/10/11 17:38:10 cort Exp $
+ * $Id: residual.c,v 1.15 1999/05/14 07:24:27 davem Exp $
  *
  * Code to deal with the PReP residual data.
  *
diff -u --recursive --new-file v2.3.3/linux/arch/ppc/kernel/setup.c linux/arch/ppc/kernel/setup.c
--- v2.3.3/linux/arch/ppc/kernel/setup.c	Fri May 14 18:55:12 1999
+++ linux/arch/ppc/kernel/setup.c	Sat May 22 13:03:00 1999
@@ -1,5 +1,5 @@
 /*
- * $Id: setup.c,v 1.132 1999/03/24 00:32:19 cort Exp $
+ * $Id: setup.c,v 1.133 1999/05/14 07:24:30 davem Exp $
  * Common prep/pmac/chrp boot and setup code.
  */
 
diff -u --recursive --new-file v2.3.3/linux/arch/ppc/kernel/softemu8xx.c linux/arch/ppc/kernel/softemu8xx.c
--- v2.3.3/linux/arch/ppc/kernel/softemu8xx.c	Wed Mar 10 21:30:32 1999
+++ linux/arch/ppc/kernel/softemu8xx.c	Sat May 22 13:03:00 1999
@@ -34,6 +34,7 @@
 
 /* Eventually we may need a look-up table, but this works for now.
 */
+#define LFS	48
 #define LFD	50
 #define LFDU	51
 #define STFD	54
@@ -82,6 +83,12 @@
 			retval = EFAULT;
 		else
 			regs->gpr[idxreg] = (uint)ea;
+		break;
+	case LFS:
+		sdisp = (instword & 0xffff);
+		ea = (uint *)(regs->gpr[idxreg] + sdisp);
+		if (copy_from_user(ip, ea, sizeof(float)))
+			retval = EFAULT;
 		break;
 	case STFD:
 		/* this is a 16 bit quantity that is sign extended
diff -u --recursive --new-file v2.3.3/linux/arch/ppc/kernel/syscalls.c linux/arch/ppc/kernel/syscalls.c
--- v2.3.3/linux/arch/ppc/kernel/syscalls.c	Sat May  8 11:14:01 1999
+++ linux/arch/ppc/kernel/syscalls.c	Sat May 22 13:03:00 1999
@@ -205,15 +205,12 @@
 
 	lock_kernel();
 	if (!(flags & MAP_ANONYMOUS)) {
-		file = fget(fd);
-		if (!file)
+		if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
 			goto out;
 	}
 	
 	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
 	ret = do_mmap(file, addr, len, prot, flags, offset);
-	if (file)
-		fput(file);
 out:
 	unlock_kernel();
 	return ret;
diff -u --recursive --new-file v2.3.3/linux/arch/ppc/kernel/traps.c linux/arch/ppc/kernel/traps.c
--- v2.3.3/linux/arch/ppc/kernel/traps.c	Thu Apr 29 12:39:01 1999
+++ linux/arch/ppc/kernel/traps.c	Sat May 22 13:03:00 1999
@@ -79,7 +79,6 @@
 		debugger(regs);
 #endif
 		print_backtrace((unsigned long *)regs->gpr[1]);
-		instruction_dump((unsigned long *)regs->nip);
 		panic("Exception in kernel pc %lx signal %d",regs->nip,signr);
 	}
 	force_sig(signr, current);
@@ -127,7 +126,6 @@
 		debugger(regs);
 #endif
 		print_backtrace((unsigned long *)regs->gpr[1]);
-		instruction_dump((unsigned long *)regs->nip);
 		panic("machine check");
 	}
 	_exception(SIGSEGV, regs);	
@@ -216,7 +214,6 @@
 #endif
 	show_regs(regs);
 	print_backtrace((unsigned long *)regs->gpr[1]);
-	instruction_dump((unsigned long *)regs->nip);
 	panic("kernel stack overflow");
 }
 
diff -u --recursive --new-file v2.3.3/linux/arch/ppc/mm/fault.c linux/arch/ppc/mm/fault.c
--- v2.3.3/linux/arch/ppc/mm/fault.c	Tue May 11 08:24:32 1999
+++ linux/arch/ppc/mm/fault.c	Sat May 22 13:03:00 1999
@@ -89,7 +89,6 @@
 			printk("page fault in interrupt handler, addr=%lx\n",
 			       address);
 			show_regs(regs);
-			instruction_dump((unsigned long *)regs->nip);
 #if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
 			if (debugger_kernel_faults)
 				debugger(regs);
@@ -176,7 +175,6 @@
 	/* kernel has accessed a bad area */
 	show_regs(regs);
 	print_backtrace( (unsigned long *)regs->gpr[1] );
-	instruction_dump((unsigned long *)regs->nip);
 #if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
 	if (debugger_kernel_faults)
 		debugger(regs);
diff -u --recursive --new-file v2.3.3/linux/arch/ppc/mm/init.c linux/arch/ppc/mm/init.c
--- v2.3.3/linux/arch/ppc/mm/init.c	Tue May 11 08:24:32 1999
+++ linux/arch/ppc/mm/init.c	Sat May 22 13:03:00 1999
@@ -1,5 +1,5 @@
 /*
- *  $Id: init.c,v 1.164 1999/05/05 17:33:55 cort Exp $
+ *  $Id: init.c,v 1.165 1999/05/14 22:37:29 cort Exp $
  *
  *  PowerPC version 
  *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
@@ -1510,7 +1510,7 @@
 	for (h = 256<<10; h < ramsize / 256 && h < 4<<20; h *= 2, Hash_mask++)
 		;
 	Hash_size = h;
-	Hash_mask << 10;  /* so setting _SDR1 works the same -- Cort */
+	Hash_mask <<= 10;  /* so setting _SDR1 works the same -- Cort */
 #else
 	for (h = 64<<10; h < ramsize / 256 && h < 2<<20; h *= 2)
 		;
diff -u --recursive --new-file v2.3.3/linux/arch/ppc/xmon/Makefile linux/arch/ppc/xmon/Makefile
--- v2.3.3/linux/arch/ppc/xmon/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/xmon/Makefile	Sat May 22 13:03:00 1999
@@ -0,0 +1,6 @@
+# Makefile for xmon
+
+O_TARGET = x.o
+O_OBJS = start.o xmon.o ppc-dis.o ppc-opc.o subr_prf.o setjmp.o
+
+include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.3.3/linux/arch/ppc/xmon/adb.c linux/arch/ppc/xmon/adb.c
--- v2.3.3/linux/arch/ppc/xmon/adb.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/xmon/adb.c	Sat May 22 13:03:00 1999
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 1996 Paul Mackerras.
+ */
+#include "nonstdio.h"
+#include "privinst.h"
+
+#define scanhex	xmon_scanhex
+#define skipbl	xmon_skipbl
+
+#define ADB_B		(*(volatile unsigned char *)0xf3016000)
+#define ADB_SR		(*(volatile unsigned char *)0xf3017400)
+#define ADB_ACR		(*(volatile unsigned char *)0xf3017600)
+#define ADB_IFR		(*(volatile unsigned char *)0xf3017a00)
+
+static inline void eieio(void) { asm volatile ("eieio" : :); }
+
+#define N_ADB_LOG	1000
+struct adb_log {
+    unsigned char b;
+    unsigned char ifr;
+    unsigned char acr;
+    unsigned int time;
+} adb_log[N_ADB_LOG];
+int n_adb_log;
+
+void
+init_adb_log(void)
+{
+    adb_log[0].b = ADB_B;
+    adb_log[0].ifr = ADB_IFR;
+    adb_log[0].acr = ADB_ACR;
+    adb_log[0].time = get_dec();
+    n_adb_log = 0;
+}
+
+void
+dump_adb_log(void)
+{
+    unsigned t, t0;
+    struct adb_log *ap;
+    int i;
+
+    ap = adb_log;
+    t0 = ap->time;
+    for (i = 0; i <= n_adb_log; ++i, ++ap) {
+	t = t0 - ap->time;
+	printf("b=%x ifr=%x acr=%x at %d.%.7d\n", ap->b, ap->ifr, ap->acr,
+	       t / 1000000000, (t % 1000000000) / 100);
+    }
+}
+
+void
+adb_chklog(void)
+{
+    struct adb_log *ap = &adb_log[n_adb_log + 1];
+
+    ap->b = ADB_B;
+    ap->ifr = ADB_IFR;
+    ap->acr = ADB_ACR;
+    if (ap->b != ap[-1].b || (ap->ifr & 4) != (ap[-1].ifr & 4)
+	|| ap->acr != ap[-1].acr) {
+	ap->time = get_dec();
+	++n_adb_log;
+    }
+}
+
+int
+adb_bitwait(int bmask, int bval, int fmask, int fval)
+{
+    int i;
+    struct adb_log *ap;
+
+    for (i = 10000; i > 0; --i) {
+	adb_chklog();
+	ap = &adb_log[n_adb_log];
+	if ((ap->b & bmask) == bval && (ap->ifr & fmask) == fval)
+	    return 0;
+    }
+    return -1;
+}
+
+int
+adb_wait(void)
+{
+    if (adb_bitwait(0, 0, 4, 4) < 0) {
+	printf("adb: ready wait timeout\n");
+	return -1;
+    }
+    return 0;
+}
+
+void
+adb_readin(void)
+{
+    int i, j;
+    unsigned char d[64];
+
+    if (ADB_B & 8) {
+	printf("ADB_B: %x\n", ADB_B);
+	return;
+    }
+    i = 0;
+    adb_wait();
+    j = ADB_SR;
+    eieio();
+    ADB_B &= ~0x20;
+    eieio();
+    for (;;) {
+	if (adb_wait() < 0)
+	    break;
+	d[i++] = ADB_SR;
+	eieio();
+	if (ADB_B & 8)
+	    break;
+	ADB_B ^= 0x10;
+	eieio();
+    }
+    ADB_B |= 0x30;
+    if (adb_wait() == 0)
+	j = ADB_SR;
+    for (j = 0; j < i; ++j)
+	printf("%.2x ", d[j]);
+    printf("\n");
+}
+
+int
+adb_write(unsigned char *d, int i)
+{
+    int j;
+    unsigned x;
+
+    if ((ADB_B & 8) == 0) {
+	printf("r: ");
+	adb_readin();
+    }
+    for (;;) {
+	ADB_ACR = 0x1c;
+	eieio();
+	ADB_SR = d[0];
+	eieio();
+	ADB_B &= ~0x20;
+	eieio();
+	if (ADB_B & 8)
+	    break;
+	ADB_ACR = 0xc;
+	eieio();
+	ADB_B |= 0x20;
+	eieio();
+	adb_readin();
+    }
+    adb_wait();
+    for (j = 1; j < i; ++j) {
+	ADB_SR = d[j];
+	eieio();
+	ADB_B ^= 0x10;
+	eieio();
+	if (adb_wait() < 0)
+	    break;
+    }
+    ADB_ACR = 0xc;
+    eieio();
+    x = ADB_SR;
+    eieio();
+    ADB_B |= 0x30;
+    return j;
+}
+
+void
+adbcmds(void)
+{
+    char cmd;
+    unsigned rtcu, rtcl, dec, pdec, x;
+    int i, j;
+    unsigned char d[64];
+
+    cmd = skipbl();
+    switch (cmd) {
+    case 't':
+	for (;;) {
+	    rtcl = get_rtcl();
+	    rtcu = get_rtcu();
+	    dec = get_dec();
+	    printf("rtc u=%u l=%u dec=%x (%d = %d.%.7d)\n",
+		   rtcu, rtcl, dec, pdec - dec, (pdec - dec) / 1000000000,
+		   ((pdec - dec) % 1000000000) / 100);
+	    pdec = dec;
+	    if (cmd == 'x')
+		break;
+	    while (xmon_read(stdin, &cmd, 1) != 1)
+		;
+	}
+	break;
+    case 'r':
+	init_adb_log();
+	while (adb_bitwait(8, 0, 0, 0) == 0)
+	    adb_readin();
+	break;
+    case 'w':
+	i = 0;
+	while (scanhex(&x))
+	    d[i++] = x;
+	init_adb_log();
+	j = adb_write(d, i);
+	printf("sent %d bytes\n", j);
+	while (adb_bitwait(8, 0, 0, 0) == 0)
+	    adb_readin();
+	break;
+    case 'l':
+	dump_adb_log();
+	break;
+    }
+}
diff -u --recursive --new-file v2.3.3/linux/arch/ppc/xmon/ansidecl.h linux/arch/ppc/xmon/ansidecl.h
--- v2.3.3/linux/arch/ppc/xmon/ansidecl.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/xmon/ansidecl.h	Sat May 22 13:03:00 1999
@@ -0,0 +1,141 @@
+/* ANSI and traditional C compatability macros
+   Copyright 1991, 1992 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+/* ANSI and traditional C compatibility macros
+
+   ANSI C is assumed if __STDC__ is #defined.
+
+   Macro	ANSI C definition	Traditional C definition
+   -----	---- - ----------	----------- - ----------
+   PTR		`void *'		`char *'
+   LONG_DOUBLE	`long double'		`double'
+   VOLATILE	`volatile'		`'
+   SIGNED	`signed'		`'
+   PTRCONST	`void *const'		`char *'
+   ANSI_PROTOTYPES  1			not defined
+
+   CONST is also defined, but is obsolete.  Just use const.
+
+   DEFUN (name, arglist, args)
+
+	Defines function NAME.
+
+	ARGLIST lists the arguments, separated by commas and enclosed in
+	parentheses.  ARGLIST becomes the argument list in traditional C.
+
+	ARGS list the arguments with their types.  It becomes a prototype in
+	ANSI C, and the type declarations in traditional C.  Arguments should
+	be separated with `AND'.  For functions with a variable number of
+	arguments, the last thing listed should be `DOTS'.
+
+   DEFUN_VOID (name)
+
+	Defines a function NAME, which takes no arguments.
+
+   obsolete --     EXFUN (name, (prototype))	-- obsolete.
+
+	Replaced by PARAMS.  Do not use; will disappear someday soon.
+	Was used in external function declarations.
+	In ANSI C it is `NAME PROTOTYPE' (so PROTOTYPE should be enclosed in
+	parentheses).  In traditional C it is `NAME()'.
+	For a function that takes no arguments, PROTOTYPE should be `(void)'.
+
+    PARAMS ((args))
+
+	We could use the EXFUN macro to handle prototype declarations, but
+	the name is misleading and the result is ugly.  So we just define a
+	simple macro to handle the parameter lists, as in:
+
+	      static int foo PARAMS ((int, char));
+
+	This produces:  `static int foo();' or `static int foo (int, char);'
+
+	EXFUN would have done it like this:
+
+	      static int EXFUN (foo, (int, char));
+
+	but the function is not external...and it's hard to visually parse
+	the function name out of the mess.   EXFUN should be considered
+	obsolete; new code should be written to use PARAMS.
+
+    For example:
+	extern int printf PARAMS ((CONST char *format DOTS));
+	int DEFUN(fprintf, (stream, format),
+		  FILE *stream AND CONST char *format DOTS) { ... }
+	void DEFUN_VOID(abort) { ... }
+*/
+
+#ifndef	_ANSIDECL_H
+
+#define	_ANSIDECL_H	1
+
+
+/* Every source file includes this file,
+   so they will all get the switch for lint.  */
+/* LINTLIBRARY */
+
+
+#if defined (__STDC__) || defined (_AIX) || (defined (__mips) && defined (_SYSTYPE_SVR4)) || defined(WIN32)
+/* All known AIX compilers implement these things (but don't always
+   define __STDC__).  The RISC/OS MIPS compiler defines these things
+   in SVR4 mode, but does not define __STDC__.  */
+
+#define	PTR		void *
+#define	PTRCONST	void *CONST
+#define	LONG_DOUBLE	long double
+
+#define	AND		,
+#define	NOARGS		void
+#define	CONST		const
+#define	VOLATILE	volatile
+#define	SIGNED		signed
+#define	DOTS		, ...
+
+#define	EXFUN(name, proto)		name proto
+#define	DEFUN(name, arglist, args)	name(args)
+#define	DEFUN_VOID(name)		name(void)
+
+#define PROTO(type, name, arglist)	type name arglist
+#define PARAMS(paramlist)		paramlist
+#define ANSI_PROTOTYPES			1
+
+#else	/* Not ANSI C.  */
+
+#define	PTR		char *
+#define	PTRCONST	PTR
+#define	LONG_DOUBLE	double
+
+#define	AND		;
+#define	NOARGS
+#define	CONST
+#ifndef const /* some systems define it in header files for non-ansi mode */
+#define	const
+#endif
+#define	VOLATILE
+#define	SIGNED
+#define	DOTS
+
+#define	EXFUN(name, proto)		name()
+#define	DEFUN(name, arglist, args)	name arglist args;
+#define	DEFUN_VOID(name)		name()
+#define PROTO(type, name, arglist) type name ()
+#define PARAMS(paramlist)		()
+
+#endif	/* ANSI C.  */
+
+#endif	/* ansidecl.h	*/
diff -u --recursive --new-file v2.3.3/linux/arch/ppc/xmon/nonstdio.h linux/arch/ppc/xmon/nonstdio.h
--- v2.3.3/linux/arch/ppc/xmon/nonstdio.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/xmon/nonstdio.h	Sat May 22 13:03:00 1999
@@ -0,0 +1,22 @@
+typedef int	FILE;
+extern FILE *xmon_stdin, *xmon_stdout;
+#define EOF	(-1)
+#define stdin	xmon_stdin
+#define stdout	xmon_stdout
+#define printf	xmon_printf
+#define fprintf	xmon_fprintf
+#define fputs	xmon_fputs
+#define fgets	xmon_fgets
+#define putchar	xmon_putchar
+#define getchar	xmon_getchar
+#define putc	xmon_putc
+#define getc	xmon_getc
+#define fopen(n, m)	NULL
+#define fflush(f)	do {} while (0)
+#define fclose(f)	do {} while (0)
+extern char *fgets(char *, int, void *);
+extern void xmon_printf(const char *, ...);
+extern void xmon_fprintf(void *, const char *, ...);
+extern void xmon_sprintf(char *, const char *, ...);
+
+#define perror(s)	printf("%s: no files!\n", (s))
diff -u --recursive --new-file v2.3.3/linux/arch/ppc/xmon/ppc-dis.c linux/arch/ppc/xmon/ppc-dis.c
--- v2.3.3/linux/arch/ppc/xmon/ppc-dis.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/xmon/ppc-dis.c	Sat May 22 13:03:00 1999
@@ -0,0 +1,190 @@
+/* ppc-dis.c -- Disassemble PowerPC instructions
+   Copyright 1994 Free Software Foundation, Inc.
+   Written by Ian Lance Taylor, Cygnus Support
+
+This file is part of GDB, GAS, and the GNU binutils.
+
+GDB, GAS, and the GNU binutils are free software; you can redistribute
+them and/or modify them 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.
+
+GDB, GAS, and the GNU binutils are distributed in the hope that they
+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 file; see the file COPYING.  If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#include "nonstdio.h"
+#include "ansidecl.h"
+#include "ppc.h"
+
+static int print_insn_powerpc PARAMS ((FILE *, unsigned long insn,
+				       unsigned memaddr, int dialect));
+
+extern void print_address PARAMS((unsigned memaddr));
+
+/* Print a big endian PowerPC instruction.  For convenience, also
+   disassemble instructions supported by the Motorola PowerPC 601.  */
+
+int
+print_insn_big_powerpc (FILE *out, unsigned long insn, unsigned memaddr)
+{
+  return print_insn_powerpc (out, insn, memaddr,
+			     PPC_OPCODE_PPC | PPC_OPCODE_601);
+}
+
+/* Print a PowerPC or POWER instruction.  */
+
+static int
+print_insn_powerpc (FILE *out, unsigned long insn, unsigned memaddr,
+		    int dialect)
+{
+  const struct powerpc_opcode *opcode;
+  const struct powerpc_opcode *opcode_end;
+  unsigned long op;
+
+  /* Get the major opcode of the instruction.  */
+  op = PPC_OP (insn);
+
+  /* Find the first match in the opcode table.  We could speed this up
+     a bit by doing a binary search on the major opcode.  */
+  opcode_end = powerpc_opcodes + powerpc_num_opcodes;
+  for (opcode = powerpc_opcodes; opcode < opcode_end; opcode++)
+    {
+      unsigned long table_op;
+      const unsigned char *opindex;
+      const struct powerpc_operand *operand;
+      int invalid;
+      int need_comma;
+      int need_paren;
+
+      table_op = PPC_OP (opcode->opcode);
+      if (op < table_op)
+		break;
+      if (op > table_op)
+		continue;
+
+      if ((insn & opcode->mask) != opcode->opcode
+	  || (opcode->flags & dialect) == 0)
+		continue;
+
+      /* Make two passes over the operands.  First see if any of them
+		 have extraction functions, and, if they do, make sure the
+		 instruction is valid.  */
+      invalid = 0;
+      for (opindex = opcode->operands; *opindex != 0; opindex++)
+		{
+		  operand = powerpc_operands + *opindex;
+		  if (operand->extract)
+		    (*operand->extract) (insn, &invalid);
+		}
+      if (invalid)
+		continue;
+
+      /* The instruction is valid.  */
+      fprintf(out, "%s", opcode->name);
+      if (opcode->operands[0] != 0)
+		fprintf(out, "\t");
+
+      /* Now extract and print the operands.  */
+      need_comma = 0;
+      need_paren = 0;
+      for (opindex = opcode->operands; *opindex != 0; opindex++)
+		{
+		  long value;
+
+		  operand = powerpc_operands + *opindex;
+
+		  /* Operands that are marked FAKE are simply ignored.  We
+		     already made sure that the extract function considered
+		     the instruction to be valid.  */
+		  if ((operand->flags & PPC_OPERAND_FAKE) != 0)
+		    continue;
+
+		  /* Extract the value from the instruction.  */
+		  if (operand->extract)
+		    value = (*operand->extract) (insn, (int *) 0);
+		  else
+		    {
+		      value = (insn >> operand->shift) & ((1 << operand->bits) - 1);
+		      if ((operand->flags & PPC_OPERAND_SIGNED) != 0
+			  && (value & (1 << (operand->bits - 1))) != 0)
+			value -= 1 << operand->bits;
+		    }
+
+		  /* If the operand is optional, and the value is zero, don't
+		     print anything.  */
+		  if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0
+		      && (operand->flags & PPC_OPERAND_NEXT) == 0
+		      && value == 0)
+		    continue;
+
+		  if (need_comma)
+		    {
+		      fprintf(out, ",");
+		      need_comma = 0;
+		    }
+
+		  /* Print the operand as directed by the flags.  */
+		  if ((operand->flags & PPC_OPERAND_GPR) != 0)
+		    fprintf(out, "r%ld", value);
+		  else if ((operand->flags & PPC_OPERAND_FPR) != 0)
+		    fprintf(out, "f%ld", value);
+		  else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0)
+		    print_address (memaddr + value);
+		  else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0)
+		    print_address (value & 0xffffffff);
+		  else if ((operand->flags & PPC_OPERAND_CR) == 0
+			   || (dialect & PPC_OPCODE_PPC) == 0)
+		    fprintf(out, "%ld", value);
+		  else
+		    {
+		      if (operand->bits == 3)
+				fprintf(out, "cr%d", value);
+		      else
+			{
+			  static const char *cbnames[4] = { "lt", "gt", "eq", "so" };
+			  int cr;
+			  int cc;
+
+			  cr = value >> 2;
+			  if (cr != 0)
+			    fprintf(out, "4*cr%d", cr);
+			  cc = value & 3;
+			  if (cc != 0)
+			    {
+			      if (cr != 0)
+					fprintf(out, "+");
+			      fprintf(out, "%s", cbnames[cc]);
+			    }
+			}
+	    }
+
+	  if (need_paren)
+	    {
+	      fprintf(out, ")");
+	      need_paren = 0;
+	    }
+
+	  if ((operand->flags & PPC_OPERAND_PARENS) == 0)
+	    need_comma = 1;
+	  else
+	    {
+	      fprintf(out, "(");
+	      need_paren = 1;
+	    }
+	}
+
+      /* We have found and printed an instruction; return.  */
+      return 4;
+    }
+
+  /* We could not find a match.  */
+  fprintf(out, ".long 0x%lx", insn);
+
+  return 4;
+}
diff -u --recursive --new-file v2.3.3/linux/arch/ppc/xmon/ppc-opc.c linux/arch/ppc/xmon/ppc-opc.c
--- v2.3.3/linux/arch/ppc/xmon/ppc-opc.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/xmon/ppc-opc.c	Sat May 22 13:03:00 1999
@@ -0,0 +1,2816 @@
+/* ppc-opc.c -- PowerPC opcode list
+   Copyright 1994 Free Software Foundation, Inc.
+   Written by Ian Lance Taylor, Cygnus Support
+
+This file is part of GDB, GAS, and the GNU binutils.
+
+GDB, GAS, and the GNU binutils are free software; you can redistribute
+them and/or modify them 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.
+
+GDB, GAS, and the GNU binutils are distributed in the hope that they
+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 file; see the file COPYING.  If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#include <linux/posix_types.h>
+#include "ansidecl.h"
+#include "ppc.h"
+
+/* This file holds the PowerPC opcode table.  The opcode table
+   includes almost all of the extended instruction mnemonics.  This
+   permits the disassembler to use them, and simplifies the assembler
+   logic, at the cost of increasing the table size.  The table is
+   strictly constant data, so the compiler should be able to put it in
+   the .text section.
+
+   This file also holds the operand table.  All knowledge about
+   inserting operands into instructions and vice-versa is kept in this
+   file.  */
+
+/* Local insertion and extraction functions.  */
+
+static unsigned long insert_bat PARAMS ((unsigned long, long, const char **));
+static long extract_bat PARAMS ((unsigned long, int *));
+static unsigned long insert_bba PARAMS ((unsigned long, long, const char **));
+static long extract_bba PARAMS ((unsigned long, int *));
+static unsigned long insert_bd PARAMS ((unsigned long, long, const char **));
+static long extract_bd PARAMS ((unsigned long, int *));
+static unsigned long insert_bdm PARAMS ((unsigned long, long, const char **));
+static long extract_bdm PARAMS ((unsigned long, int *));
+static unsigned long insert_bdp PARAMS ((unsigned long, long, const char **));
+static long extract_bdp PARAMS ((unsigned long, int *));
+static unsigned long insert_bo PARAMS ((unsigned long, long, const char **));
+static long extract_bo PARAMS ((unsigned long, int *));
+static unsigned long insert_boe PARAMS ((unsigned long, long, const char **));
+static long extract_boe PARAMS ((unsigned long, int *));
+static unsigned long insert_ds PARAMS ((unsigned long, long, const char **));
+static long extract_ds PARAMS ((unsigned long, int *));
+static unsigned long insert_li PARAMS ((unsigned long, long, const char **));
+static long extract_li PARAMS ((unsigned long, int *));
+static unsigned long insert_mbe PARAMS ((unsigned long, long, const char **));
+static long extract_mbe PARAMS ((unsigned long, int *));
+static unsigned long insert_mb6 PARAMS ((unsigned long, long, const char **));
+static long extract_mb6 PARAMS ((unsigned long, int *));
+static unsigned long insert_nb PARAMS ((unsigned long, long, const char **));
+static long extract_nb PARAMS ((unsigned long, int *));
+static unsigned long insert_nsi PARAMS ((unsigned long, long, const char **));
+static long extract_nsi PARAMS ((unsigned long, int *));
+static unsigned long insert_ral PARAMS ((unsigned long, long, const char **));
+static unsigned long insert_ram PARAMS ((unsigned long, long, const char **));
+static unsigned long insert_ras PARAMS ((unsigned long, long, const char **));
+static unsigned long insert_rbs PARAMS ((unsigned long, long, const char **));
+static long extract_rbs PARAMS ((unsigned long, int *));
+static unsigned long insert_sh6 PARAMS ((unsigned long, long, const char **));
+static long extract_sh6 PARAMS ((unsigned long, int *));
+static unsigned long insert_spr PARAMS ((unsigned long, long, const char **));
+static long extract_spr PARAMS ((unsigned long, int *));
+static unsigned long insert_tbr PARAMS ((unsigned long, long, const char **));
+static long extract_tbr PARAMS ((unsigned long, int *));
+
+/* The operands table.
+
+   The fields are bits, shift, signed, insert, extract, flags.  */
+
+const struct powerpc_operand powerpc_operands[] =
+{
+  /* The zero index is used to indicate the end of the list of
+     operands.  */
+#define UNUSED (0)
+  { 0, 0, 0, 0, 0 },
+
+  /* The BA field in an XL form instruction.  */
+#define BA (1)
+#define BA_MASK (0x1f << 16)
+  { 5, 16, 0, 0, PPC_OPERAND_CR },
+
+  /* The BA field in an XL form instruction when it must be the same
+     as the BT field in the same instruction.  */
+#define BAT (2)
+  { 5, 16, insert_bat, extract_bat, PPC_OPERAND_FAKE },
+
+  /* The BB field in an XL form instruction.  */
+#define BB (3)
+#define BB_MASK (0x1f << 11)
+  { 5, 11, 0, 0, PPC_OPERAND_CR },
+
+  /* The BB field in an XL form instruction when it must be the same
+     as the BA field in the same instruction.  */
+#define BBA (4)
+  { 5, 11, insert_bba, extract_bba, PPC_OPERAND_FAKE },
+
+  /* The BD field in a B form instruction.  The lower two bits are
+     forced to zero.  */
+#define BD (5)
+  { 16, 0, insert_bd, extract_bd, PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED },
+
+  /* The BD field in a B form instruction when absolute addressing is
+     used.  */
+#define BDA (6)
+  { 16, 0, insert_bd, extract_bd, PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED },
+
+  /* The BD field in a B form instruction when the - modifier is used.
+     This sets the y bit of the BO field appropriately.  */
+#define BDM (7)
+  { 16, 0, insert_bdm, extract_bdm,
+      PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED },
+
+  /* The BD field in a B form instruction when the - modifier is used
+     and absolute address is used.  */
+#define BDMA (8)
+  { 16, 0, insert_bdm, extract_bdm,
+      PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED },
+
+  /* The BD field in a B form instruction when the + modifier is used.
+     This sets the y bit of the BO field appropriately.  */
+#define BDP (9)
+  { 16, 0, insert_bdp, extract_bdp,
+      PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED },
+
+  /* The BD field in a B form instruction when the + modifier is used
+     and absolute addressing is used.  */
+#define BDPA (10)
+  { 16, 0, insert_bdp, extract_bdp,
+      PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED },
+
+  /* The BF field in an X or XL form instruction.  */
+#define BF (11)
+  { 3, 23, 0, 0, PPC_OPERAND_CR },
+
+  /* An optional BF field.  This is used for comparison instructions,
+     in which an omitted BF field is taken as zero.  */
+#define OBF (12)
+  { 3, 23, 0, 0, PPC_OPERAND_CR | PPC_OPERAND_OPTIONAL },
+
+  /* The BFA field in an X or XL form instruction.  */
+#define BFA (13)
+  { 3, 18, 0, 0, PPC_OPERAND_CR },
+
+  /* The BI field in a B form or XL form instruction.  */
+#define BI (14)
+#define BI_MASK (0x1f << 16)
+  { 5, 16, 0, 0, PPC_OPERAND_CR },
+
+  /* The BO field in a B form instruction.  Certain values are
+     illegal.  */
+#define BO (15)
+#define BO_MASK (0x1f << 21)
+  { 5, 21, insert_bo, extract_bo, 0 },
+
+  /* The BO field in a B form instruction when the + or - modifier is
+     used.  This is like the BO field, but it must be even.  */
+#define BOE (16)
+  { 5, 21, insert_boe, extract_boe, 0 },
+
+  /* The BT field in an X or XL form instruction.  */
+#define BT (17)
+  { 5, 21, 0, 0, PPC_OPERAND_CR },
+
+  /* The condition register number portion of the BI field in a B form
+     or XL form instruction.  This is used for the extended
+     conditional branch mnemonics, which set the lower two bits of the
+     BI field.  This field is optional.  */
+#define CR (18)
+  { 3, 18, 0, 0, PPC_OPERAND_CR | PPC_OPERAND_OPTIONAL },
+
+  /* The D field in a D form instruction.  This is a displacement off
+     a register, and implies that the next operand is a register in
+     parentheses.  */
+#define D (19)
+  { 16, 0, 0, 0, PPC_OPERAND_PARENS | PPC_OPERAND_SIGNED },
+
+  /* The DS field in a DS form instruction.  This is like D, but the
+     lower two bits are forced to zero.  */
+#define DS (20)
+  { 16, 0, insert_ds, extract_ds, PPC_OPERAND_PARENS | PPC_OPERAND_SIGNED },
+
+  /* The FL1 field in a POWER SC form instruction.  */
+#define FL1 (21)
+  { 4, 12, 0, 0, 0 },
+
+  /* The FL2 field in a POWER SC form instruction.  */
+#define FL2 (22)
+  { 3, 2, 0, 0, 0 },
+
+  /* The FLM field in an XFL form instruction.  */
+#define FLM (23)
+  { 8, 17, 0, 0, 0 },
+
+  /* The FRA field in an X or A form instruction.  */
+#define FRA (24)
+#define FRA_MASK (0x1f << 16)
+  { 5, 16, 0, 0, PPC_OPERAND_FPR },
+
+  /* The FRB field in an X or A form instruction.  */
+#define FRB (25)
+#define FRB_MASK (0x1f << 11)
+  { 5, 11, 0, 0, PPC_OPERAND_FPR },
+
+  /* The FRC field in an A form instruction.  */
+#define FRC (26)
+#define FRC_MASK (0x1f << 6)
+  { 5, 6, 0, 0, PPC_OPERAND_FPR },
+
+  /* The FRS field in an X form instruction or the FRT field in a D, X
+     or A form instruction.  */
+#define FRS (27)
+#define FRT (FRS)
+  { 5, 21, 0, 0, PPC_OPERAND_FPR },
+
+  /* The FXM field in an XFX instruction.  */
+#define FXM (28)
+#define FXM_MASK (0xff << 12)
+  { 8, 12, 0, 0, 0 },
+
+  /* The L field in a D or X form instruction.  */
+#define L (29)
+  { 1, 21, 0, 0, PPC_OPERAND_OPTIONAL },
+
+  /* The LEV field in a POWER SC form instruction.  */
+#define LEV (30)
+  { 7, 5, 0, 0, 0 },
+
+  /* The LI field in an I form instruction.  The lower two bits are
+     forced to zero.  */
+#define LI (31)
+  { 26, 0, insert_li, extract_li, PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED },
+
+  /* The LI field in an I form instruction when used as an absolute
+     address.  */
+#define LIA (32)
+  { 26, 0, insert_li, extract_li, PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED },
+
+  /* The MB field in an M form instruction.  */
+#define MB (33)
+#define MB_MASK (0x1f << 6)
+  { 5, 6, 0, 0, 0 },
+
+  /* The ME field in an M form instruction.  */
+#define ME (34)
+#define ME_MASK (0x1f << 1)
+  { 5, 1, 0, 0, 0 },
+
+  /* The MB and ME fields in an M form instruction expressed a single
+     operand which is a bitmask indicating which bits to select.  This
+     is a two operand form using PPC_OPERAND_NEXT.  See the
+     description in opcode/ppc.h for what this means.  */
+#define MBE (35)
+  { 5, 6, 0, 0, PPC_OPERAND_OPTIONAL | PPC_OPERAND_NEXT },
+  { 32, 0, insert_mbe, extract_mbe, 0 },
+
+  /* The MB or ME field in an MD or MDS form instruction.  The high
+     bit is wrapped to the low end.  */
+#define MB6 (37)
+#define ME6 (MB6)
+#define MB6_MASK (0x3f << 5)
+  { 6, 5, insert_mb6, extract_mb6, 0 },
+
+  /* The NB field in an X form instruction.  The value 32 is stored as
+     0.  */
+#define NB (38)
+  { 6, 11, insert_nb, extract_nb, 0 },
+
+  /* The NSI field in a D form instruction.  This is the same as the
+     SI field, only negated.  */
+#define NSI (39)
+  { 16, 0, insert_nsi, extract_nsi,
+      PPC_OPERAND_NEGATIVE | PPC_OPERAND_SIGNED },
+
+  /* The RA field in an D, DS, X, XO, M, or MDS form instruction.  */
+#define RA (40)
+#define RA_MASK (0x1f << 16)
+  { 5, 16, 0, 0, PPC_OPERAND_GPR },
+
+  /* The RA field in a D or X form instruction which is an updating
+     load, which means that the RA field may not be zero and may not
+     equal the RT field.  */
+#define RAL (41)
+  { 5, 16, insert_ral, 0, PPC_OPERAND_GPR },
+
+  /* The RA field in an lmw instruction, which has special value
+     restrictions.  */
+#define RAM (42)
+  { 5, 16, insert_ram, 0, PPC_OPERAND_GPR },
+
+  /* The RA field in a D or X form instruction which is an updating
+     store or an updating floating point load, which means that the RA
+     field may not be zero.  */
+#define RAS (43)
+  { 5, 16, insert_ras, 0, PPC_OPERAND_GPR },
+
+  /* The RB field in an X, XO, M, or MDS form instruction.  */
+#define RB (44)
+#define RB_MASK (0x1f << 11)
+  { 5, 11, 0, 0, PPC_OPERAND_GPR },
+
+  /* The RB field in an X form instruction when it must be the same as
+     the RS field in the instruction.  This is used for extended
+     mnemonics like mr.  */
+#define RBS (45)
+  { 5, 1, insert_rbs, extract_rbs, PPC_OPERAND_FAKE },
+
+  /* The RS field in a D, DS, X, XFX, XS, M, MD or MDS form
+     instruction or the RT field in a D, DS, X, XFX or XO form
+     instruction.  */
+#define RS (46)
+#define RT (RS)
+#define RT_MASK (0x1f << 21)
+  { 5, 21, 0, 0, PPC_OPERAND_GPR },
+
+  /* The SH field in an X or M form instruction.  */
+#define SH (47)
+#define SH_MASK (0x1f << 11)
+  { 5, 11, 0, 0, 0 },
+
+  /* The SH field in an MD form instruction.  This is split.  */
+#define SH6 (48)
+#define SH6_MASK ((0x1f << 11) | (1 << 1))
+  { 6, 1, insert_sh6, extract_sh6, 0 },
+
+  /* The SI field in a D form instruction.  */
+#define SI (49)
+  { 16, 0, 0, 0, PPC_OPERAND_SIGNED },
+
+  /* The SI field in a D form instruction when we accept a wide range
+     of positive values.  */
+#define SISIGNOPT (50)
+  { 16, 0, 0, 0, PPC_OPERAND_SIGNED | PPC_OPERAND_SIGNOPT },
+
+  /* The SPR field in an XFX form instruction.  This is flipped--the
+     lower 5 bits are stored in the upper 5 and vice- versa.  */
+#define SPR (51)
+#define SPR_MASK (0x3ff << 11)
+  { 10, 11, insert_spr, extract_spr, 0 },
+
+  /* The BAT index number in an XFX form m[ft]ibat[lu] instruction.  */
+#define SPRBAT (52)
+#define SPRBAT_MASK (0x3 << 17)
+  { 2, 17, 0, 0, 0 },
+
+  /* The SPRG register number in an XFX form m[ft]sprg instruction.  */
+#define SPRG (53)
+#define SPRG_MASK (0x3 << 16)
+  { 2, 16, 0, 0, 0 },
+
+  /* The SR field in an X form instruction.  */
+#define SR (54)
+  { 4, 16, 0, 0, 0 },
+
+  /* The SV field in a POWER SC form instruction.  */
+#define SV (55)
+  { 14, 2, 0, 0, 0 },
+
+  /* The TBR field in an XFX form instruction.  This is like the SPR
+     field, but it is optional.  */
+#define TBR (56)
+  { 10, 11, insert_tbr, extract_tbr, PPC_OPERAND_OPTIONAL },
+
+  /* The TO field in a D or X form instruction.  */
+#define TO (57)
+#define TO_MASK (0x1f << 21)
+  { 5, 21, 0, 0, 0 },
+
+  /* The U field in an X form instruction.  */
+#define U (58)
+  { 4, 12, 0, 0, 0 },
+
+  /* The UI field in a D form instruction.  */
+#define UI (59)
+  { 16, 0, 0, 0, 0 },
+};
+
+/* The functions used to insert and extract complicated operands.  */
+
+/* The BA field in an XL form instruction when it must be the same as
+   the BT field in the same instruction.  This operand is marked FAKE.
+   The insertion function just copies the BT field into the BA field,
+   and the extraction function just checks that the fields are the
+   same.  */
+
+/*ARGSUSED*/
+static unsigned long 
+insert_bat (insn, value, errmsg)
+     unsigned long insn;
+     long value;
+     const char **errmsg;
+{
+  return insn | (((insn >> 21) & 0x1f) << 16);
+}
+
+static long
+extract_bat (insn, invalid)
+     unsigned long insn;
+     int *invalid;
+{
+  if (invalid != (int *) NULL
+      && ((insn >> 21) & 0x1f) != ((insn >> 16) & 0x1f))
+    *invalid = 1;
+  return 0;
+}
+
+/* The BB field in an XL form instruction when it must be the same as
+   the BA field in the same instruction.  This operand is marked FAKE.
+   The insertion function just copies the BA field into the BB field,
+   and the extraction function just checks that the fields are the
+   same.  */
+
+/*ARGSUSED*/
+static unsigned long
+insert_bba (insn, value, errmsg)
+     unsigned long insn;
+     long value;
+     const char **errmsg;
+{
+  return insn | (((insn >> 16) & 0x1f) << 11);
+}
+
+static long
+extract_bba (insn, invalid)
+     unsigned long insn;
+     int *invalid;
+{
+  if (invalid != (int *) NULL
+      && ((insn >> 16) & 0x1f) != ((insn >> 11) & 0x1f))
+    *invalid = 1;
+  return 0;
+}
+
+/* The BD field in a B form instruction.  The lower two bits are
+   forced to zero.  */
+
+/*ARGSUSED*/
+static unsigned long
+insert_bd (insn, value, errmsg)
+     unsigned long insn;
+     long value;
+     const char **errmsg;
+{
+  return insn | (value & 0xfffc);
+}
+
+/*ARGSUSED*/
+static long
+extract_bd (insn, invalid)
+     unsigned long insn;
+     int *invalid;
+{
+  if ((insn & 0x8000) != 0)
+    return (insn & 0xfffc) - 0x10000;
+  else
+    return insn & 0xfffc;
+}
+
+/* The BD field in a B form instruction when the - modifier is used.
+   This modifier means that the branch is not expected to be taken.
+   We must set the y bit of the BO field to 1 if the offset is
+   negative.  When extracting, we require that the y bit be 1 and that
+   the offset be positive, since if the y bit is 0 we just want to
+   print the normal form of the instruction.  */
+
+/*ARGSUSED*/
+static unsigned long
+insert_bdm (insn, value, errmsg)
+     unsigned long insn;
+     long value;
+     const char **errmsg;
+{
+  if ((value & 0x8000) != 0)
+    insn |= 1 << 21;
+  return insn | (value & 0xfffc);
+}
+
+static long
+extract_bdm (insn, invalid)
+     unsigned long insn;
+     int *invalid;
+{
+  if (invalid != (int *) NULL
+      && ((insn & (1 << 21)) == 0
+	  || (insn & (1 << 15)) == 0))
+    *invalid = 1;
+  if ((insn & 0x8000) != 0)
+    return (insn & 0xfffc) - 0x10000;
+  else
+    return insn & 0xfffc;
+}
+
+/* The BD field in a B form instruction when the + modifier is used.
+   This is like BDM, above, except that the branch is expected to be
+   taken.  */
+
+/*ARGSUSED*/
+static unsigned long
+insert_bdp (insn, value, errmsg)
+     unsigned long insn;
+     long value;
+     const char **errmsg;
+{
+  if ((value & 0x8000) == 0)
+    insn |= 1 << 21;
+  return insn | (value & 0xfffc);
+}
+
+static long
+extract_bdp (insn, invalid)
+     unsigned long insn;
+     int *invalid;
+{
+  if (invalid != (int *) NULL
+      && ((insn & (1 << 21)) == 0
+	  || (insn & (1 << 15)) != 0))
+    *invalid = 1;
+  if ((insn & 0x8000) != 0)
+    return (insn & 0xfffc) - 0x10000;
+  else
+    return insn & 0xfffc;
+}
+
+/* Check for legal values of a BO field.  */
+
+static int
+valid_bo (value)
+     long value;
+{
+  /* Certain encodings have bits that are required to be zero.  These
+     are (z must be zero, y may be anything):
+         001zy
+	 011zy
+	 1z00y
+	 1z01y
+	 1z1zz
+     */
+  switch (value & 0x14)
+    {
+    default:
+    case 0:
+      return 1;
+    case 0x4:
+      return (value & 0x2) == 0;
+    case 0x10:
+      return (value & 0x8) == 0;
+    case 0x14:
+      return value == 0x14;
+    }
+}
+
+/* The BO field in a B form instruction.  Warn about attempts to set
+   the field to an illegal value.  */
+
+static unsigned long
+insert_bo (insn, value, errmsg)
+     unsigned long insn;
+     long value;
+     const char **errmsg;
+{
+  if (errmsg != (const char **) NULL
+      && ! valid_bo (value))
+    *errmsg = "invalid conditional option";
+  return insn | ((value & 0x1f) << 21);
+}
+
+static long
+extract_bo (insn, invalid)
+     unsigned long insn;
+     int *invalid;
+{
+  long value;
+
+  value = (insn >> 21) & 0x1f;
+  if (invalid != (int *) NULL
+      && ! valid_bo (value))
+    *invalid = 1;
+  return value;
+}
+
+/* The BO field in a B form instruction when the + or - modifier is
+   used.  This is like the BO field, but it must be even.  When
+   extracting it, we force it to be even.  */
+
+static unsigned long
+insert_boe (insn, value, errmsg)
+     unsigned long insn;
+     long value;
+     const char **errmsg;
+{
+  if (errmsg != (const char **) NULL)
+    {
+      if (! valid_bo (value))
+	*errmsg = "invalid conditional option";
+      else if ((value & 1) != 0)
+	*errmsg = "attempt to set y bit when using + or - modifier";
+    }
+  return insn | ((value & 0x1f) << 21);
+}
+
+static long
+extract_boe (insn, invalid)
+     unsigned long insn;
+     int *invalid;
+{
+  long value;
+
+  value = (insn >> 21) & 0x1f;
+  if (invalid != (int *) NULL
+      && ! valid_bo (value))
+    *invalid = 1;
+  return value & 0x1e;
+}
+
+/* The DS field in a DS form instruction.  This is like D, but the
+   lower two bits are forced to zero.  */
+
+/*ARGSUSED*/
+static unsigned long
+insert_ds (insn, value, errmsg)
+     unsigned long insn;
+     long value;
+     const char **errmsg;
+{
+  return insn | (value & 0xfffc);
+}
+
+/*ARGSUSED*/
+static long
+extract_ds (insn, invalid)
+     unsigned long insn;
+     int *invalid;
+{
+  if ((insn & 0x8000) != 0)
+    return (insn & 0xfffc) - 0x10000;
+  else
+    return insn & 0xfffc;
+}
+
+/* The LI field in an I form instruction.  The lower two bits are
+   forced to zero.  */
+
+/*ARGSUSED*/
+static unsigned long
+insert_li (insn, value, errmsg)
+     unsigned long insn;
+     long value;
+     const char **errmsg;
+{
+  return insn | (value & 0x3fffffc);
+}
+
+/*ARGSUSED*/
+static long
+extract_li (insn, invalid)
+     unsigned long insn;
+     int *invalid;
+{
+  if ((insn & 0x2000000) != 0)
+    return (insn & 0x3fffffc) - 0x4000000;
+  else
+    return insn & 0x3fffffc;
+}
+
+/* The MB and ME fields in an M form instruction expressed as a single
+   operand which is itself a bitmask.  The extraction function always
+   marks it as invalid, since we never want to recognize an
+   instruction which uses a field of this type.  */
+
+static unsigned long
+insert_mbe (insn, value, errmsg)
+     unsigned long insn;
+     long value;
+     const char **errmsg;
+{
+  unsigned long uval;
+  int mb, me;
+
+  uval = value;
+
+  if (uval == 0)
+    {
+      if (errmsg != (const char **) NULL)
+	*errmsg = "illegal bitmask";
+      return insn;
+    }
+
+  me = 31;
+  while ((uval & 1) == 0)
+    {
+      uval >>= 1;
+      --me;
+    }
+
+  mb = me;
+  uval >>= 1;
+  while ((uval & 1) != 0)
+    {
+      uval >>= 1;
+      --mb;
+    }
+
+  if (uval != 0)
+    {
+      if (errmsg != (const char **) NULL)
+	*errmsg = "illegal bitmask";
+    }
+
+  return insn | (mb << 6) | (me << 1);
+}
+
+static long
+extract_mbe (insn, invalid)
+     unsigned long insn;
+     int *invalid;
+{
+  long ret;
+  int mb, me;
+  int i;
+
+  if (invalid != (int *) NULL)
+    *invalid = 1;
+
+  ret = 0;
+  mb = (insn >> 6) & 0x1f;
+  me = (insn >> 1) & 0x1f;
+  for (i = mb; i < me; i++)
+    ret |= 1 << (31 - i);
+  return ret;
+}
+
+/* The MB or ME field in an MD or MDS form instruction.  The high bit
+   is wrapped to the low end.  */
+
+/*ARGSUSED*/
+static unsigned long
+insert_mb6 (insn, value, errmsg)
+     unsigned long insn;
+     long value;
+     const char **errmsg;
+{
+  return insn | ((value & 0x1f) << 6) | (value & 0x20);
+}
+
+/*ARGSUSED*/
+static long
+extract_mb6 (insn, invalid)
+     unsigned long insn;
+     int *invalid;
+{
+  return ((insn >> 6) & 0x1f) | (insn & 0x20);
+}
+
+/* The NB field in an X form instruction.  The value 32 is stored as
+   0.  */
+
+static unsigned long
+insert_nb (insn, value, errmsg)
+     unsigned long insn;
+     long value;
+     const char **errmsg;
+{
+  if (value < 0 || value > 32)
+    *errmsg = "value out of range";
+  if (value == 32)
+    value = 0;
+  return insn | ((value & 0x1f) << 11);
+}
+
+/*ARGSUSED*/
+static long
+extract_nb (insn, invalid)
+     unsigned long insn;
+     int *invalid;
+{
+  long ret;
+
+  ret = (insn >> 11) & 0x1f;
+  if (ret == 0)
+    ret = 32;
+  return ret;
+}
+
+/* The NSI field in a D form instruction.  This is the same as the SI
+   field, only negated.  The extraction function always marks it as
+   invalid, since we never want to recognize an instruction which uses
+   a field of this type.  */
+
+/*ARGSUSED*/
+static unsigned long
+insert_nsi (insn, value, errmsg)
+     unsigned long insn;
+     long value;
+     const char **errmsg;
+{
+  return insn | ((- value) & 0xffff);
+}
+
+static long
+extract_nsi (insn, invalid)
+     unsigned long insn;
+     int *invalid;
+{
+  if (invalid != (int *) NULL)
+    *invalid = 1;
+  if ((insn & 0x8000) != 0)
+    return - ((insn & 0xffff) - 0x10000);
+  else
+    return - (insn & 0xffff);
+}
+
+/* The RA field in a D or X form instruction which is an updating
+   load, which means that the RA field may not be zero and may not
+   equal the RT field.  */
+
+static unsigned long
+insert_ral (insn, value, errmsg)
+     unsigned long insn;
+     long value;
+     const char **errmsg;
+{
+  if (value == 0
+      || value == ((insn >> 21) & 0x1f))
+    *errmsg = "invalid register operand when updating";
+  return insn | ((value & 0x1f) << 16);
+}
+
+/* The RA field in an lmw instruction, which has special value
+   restrictions.  */
+
+static unsigned long
+insert_ram (insn, value, errmsg)
+     unsigned long insn;
+     long value;
+     const char **errmsg;
+{
+  if (value >= ((insn >> 21) & 0x1f))
+    *errmsg = "index register in load range";
+  return insn | ((value & 0x1f) << 16);
+}
+
+/* The RA field in a D or X form instruction which is an updating
+   store or an updating floating point load, which means that the RA
+   field may not be zero.  */
+
+static unsigned long
+insert_ras (insn, value, errmsg)
+     unsigned long insn;
+     long value;
+     const char **errmsg;
+{
+  if (value == 0)
+    *errmsg = "invalid register operand when updating";
+  return insn | ((value & 0x1f) << 16);
+}
+
+/* The RB field in an X form instruction when it must be the same as
+   the RS field in the instruction.  This is used for extended
+   mnemonics like mr.  This operand is marked FAKE.  The insertion
+   function just copies the BT field into the BA field, and the
+   extraction function just checks that the fields are the same.  */
+
+/*ARGSUSED*/
+static unsigned long 
+insert_rbs (insn, value, errmsg)
+     unsigned long insn;
+     long value;
+     const char **errmsg;
+{
+  return insn | (((insn >> 21) & 0x1f) << 11);
+}
+
+static long
+extract_rbs (insn, invalid)
+     unsigned long insn;
+     int *invalid;
+{
+  if (invalid != (int *) NULL
+      && ((insn >> 21) & 0x1f) != ((insn >> 11) & 0x1f))
+    *invalid = 1;
+  return 0;
+}
+
+/* The SH field in an MD form instruction.  This is split.  */
+
+/*ARGSUSED*/
+static unsigned long
+insert_sh6 (insn, value, errmsg)
+     unsigned long insn;
+     long value;
+     const char **errmsg;
+{
+  return insn | ((value & 0x1f) << 11) | ((value & 0x20) >> 4);
+}
+
+/*ARGSUSED*/
+static long
+extract_sh6 (insn, invalid)
+     unsigned long insn;
+     int *invalid;
+{
+  return ((insn >> 11) & 0x1f) | ((insn << 4) & 0x20);
+}
+
+/* The SPR field in an XFX form instruction.  This is flipped--the
+   lower 5 bits are stored in the upper 5 and vice- versa.  */
+
+static unsigned long
+insert_spr (insn, value, errmsg)
+     unsigned long insn;
+     long value;
+     const char **errmsg;
+{
+  return insn | ((value & 0x1f) << 16) | ((value & 0x3e0) << 6);
+}
+
+static long
+extract_spr (insn, invalid)
+     unsigned long insn;
+     int *invalid;
+{
+  return ((insn >> 16) & 0x1f) | ((insn >> 6) & 0x3e0);
+}
+
+/* The TBR field in an XFX instruction.  This is just like SPR, but it
+   is optional.  When TBR is omitted, it must be inserted as 268 (the
+   magic number of the TB register).  These functions treat 0
+   (indicating an omitted optional operand) as 268.  This means that
+   ``mftb 4,0'' is not handled correctly.  This does not matter very
+   much, since the architecture manual does not define mftb as
+   accepting any values other than 268 or 269.  */
+
+#define TB (268)
+
+static unsigned long
+insert_tbr (insn, value, errmsg)
+     unsigned long insn;
+     long value;
+     const char **errmsg;
+{
+  if (value == 0)
+    value = TB;
+  return insn | ((value & 0x1f) << 16) | ((value & 0x3e0) << 6);
+}
+
+static long
+extract_tbr (insn, invalid)
+     unsigned long insn;
+     int *invalid;
+{
+  long ret;
+
+  ret = ((insn >> 16) & 0x1f) | ((insn >> 6) & 0x3e0);
+  if (ret == TB)
+    ret = 0;
+  return ret;
+}
+
+/* Macros used to form opcodes.  */
+
+/* The main opcode.  */
+#define OP(x) (((x) & 0x3f) << 26)
+#define OP_MASK OP (0x3f)
+
+/* The main opcode combined with a trap code in the TO field of a D
+   form instruction.  Used for extended mnemonics for the trap
+   instructions.  */
+#define OPTO(x,to) (OP (x) | (((to) & 0x1f) << 21))
+#define OPTO_MASK (OP_MASK | TO_MASK)
+
+/* The main opcode combined with a comparison size bit in the L field
+   of a D form or X form instruction.  Used for extended mnemonics for
+   the comparison instructions.  */
+#define OPL(x,l) (OP (x) | (((l) & 1) << 21))
+#define OPL_MASK OPL (0x3f,1)
+
+/* An A form instruction.  */
+#define A(op, xop, rc) (OP (op) | (((xop) & 0x1f) << 1) | ((rc) & 1))
+#define A_MASK A (0x3f, 0x1f, 1)
+
+/* An A_MASK with the FRB field fixed.  */
+#define AFRB_MASK (A_MASK | FRB_MASK)
+
+/* An A_MASK with the FRC field fixed.  */
+#define AFRC_MASK (A_MASK | FRC_MASK)
+
+/* An A_MASK with the FRA and FRC fields fixed.  */
+#define AFRAFRC_MASK (A_MASK | FRA_MASK | FRC_MASK)
+
+/* A B form instruction.  */
+#define B(op, aa, lk) (OP (op) | (((aa) & 1) << 1) | ((lk) & 1))
+#define B_MASK B (0x3f, 1, 1)
+
+/* A B form instruction setting the BO field.  */
+#define BBO(op, bo, aa, lk) (B ((op), (aa), (lk)) | (((bo) & 0x1f) << 21))
+#define BBO_MASK BBO (0x3f, 0x1f, 1, 1)
+
+/* A BBO_MASK with the y bit of the BO field removed.  This permits
+   matching a conditional branch regardless of the setting of the y
+   bit.  */
+#define Y_MASK (1 << 21)
+#define BBOY_MASK (BBO_MASK &~ Y_MASK)
+
+/* A B form instruction setting the BO field and the condition bits of
+   the BI field.  */
+#define BBOCB(op, bo, cb, aa, lk) \
+  (BBO ((op), (bo), (aa), (lk)) | (((cb) & 0x3) << 16))
+#define BBOCB_MASK BBOCB (0x3f, 0x1f, 0x3, 1, 1)
+
+/* A BBOCB_MASK with the y bit of the BO field removed.  */
+#define BBOYCB_MASK (BBOCB_MASK &~ Y_MASK)
+
+/* A BBOYCB_MASK in which the BI field is fixed.  */
+#define BBOYBI_MASK (BBOYCB_MASK | BI_MASK)
+
+/* The main opcode mask with the RA field clear.  */
+#define DRA_MASK (OP_MASK | RA_MASK)
+
+/* A DS form instruction.  */
+#define DSO(op, xop) (OP (op) | ((xop) & 0x3))
+#define DS_MASK DSO (0x3f, 3)
+
+/* An M form instruction.  */
+#define M(op, rc) (OP (op) | ((rc) & 1))
+#define M_MASK M (0x3f, 1)
+
+/* An M form instruction with the ME field specified.  */
+#define MME(op, me, rc) (M ((op), (rc)) | (((me) & 0x1f) << 1))
+
+/* An M_MASK with the MB and ME fields fixed.  */
+#define MMBME_MASK (M_MASK | MB_MASK | ME_MASK)
+
+/* An M_MASK with the SH and ME fields fixed.  */
+#define MSHME_MASK (M_MASK | SH_MASK | ME_MASK)
+
+/* An MD form instruction.  */
+#define MD(op, xop, rc) (OP (op) | (((xop) & 0x7) << 2) | ((rc) & 1))
+#define MD_MASK MD (0x3f, 0x7, 1)
+
+/* An MD_MASK with the MB field fixed.  */
+#define MDMB_MASK (MD_MASK | MB6_MASK)
+
+/* An MD_MASK with the SH field fixed.  */
+#define MDSH_MASK (MD_MASK | SH6_MASK)
+
+/* An MDS form instruction.  */
+#define MDS(op, xop, rc) (OP (op) | (((xop) & 0xf) << 1) | ((rc) & 1))
+#define MDS_MASK MDS (0x3f, 0xf, 1)
+
+/* An MDS_MASK with the MB field fixed.  */
+#define MDSMB_MASK (MDS_MASK | MB6_MASK)
+
+/* An SC form instruction.  */
+#define SC(op, sa, lk) (OP (op) | (((sa) & 1) << 1) | ((lk) & 1))
+#define SC_MASK (OP_MASK | (0x3ff << 16) | (1 << 1) | 1)
+
+/* An X form instruction.  */
+#define X(op, xop) (OP (op) | (((xop) & 0x3ff) << 1))
+
+/* An X form instruction with the RC bit specified.  */
+#define XRC(op, xop, rc) (X ((op), (xop)) | ((rc) & 1))
+
+/* The mask for an X form instruction.  */
+#define X_MASK XRC (0x3f, 0x3ff, 1)
+
+/* An X_MASK with the RA field fixed.  */
+#define XRA_MASK (X_MASK | RA_MASK)
+
+/* An X_MASK with the RB field fixed.  */
+#define XRB_MASK (X_MASK | RB_MASK)
+
+/* An X_MASK with the RT field fixed.  */
+#define XRT_MASK (X_MASK | RT_MASK)
+
+/* An X_MASK with the RA and RB fields fixed.  */
+#define XRARB_MASK (X_MASK | RA_MASK | RB_MASK)
+
+/* An X_MASK with the RT and RA fields fixed.  */
+#define XRTRA_MASK (X_MASK | RT_MASK | RA_MASK)
+
+/* An X form comparison instruction.  */
+#define XCMPL(op, xop, l) (X ((op), (xop)) | (((l) & 1) << 21))
+
+/* The mask for an X form comparison instruction.  */
+#define XCMP_MASK (X_MASK | (1 << 22))
+
+/* The mask for an X form comparison instruction with the L field
+   fixed.  */
+#define XCMPL_MASK (XCMP_MASK | (1 << 21))
+
+/* An X form trap instruction with the TO field specified.  */
+#define XTO(op, xop, to) (X ((op), (xop)) | (((to) & 0x1f) << 21))
+#define XTO_MASK (X_MASK | TO_MASK)
+
+/* An XFL form instruction.  */
+#define XFL(op, xop, rc) (OP (op) | (((xop) & 0x3ff) << 1) | ((rc) & 1))
+#define XFL_MASK (XFL (0x3f, 0x3ff, 1) | (1 << 25) | (1 << 16))
+
+/* An XL form instruction with the LK field set to 0.  */
+#define XL(op, xop) (OP (op) | (((xop) & 0x3ff) << 1))
+
+/* An XL form instruction which uses the LK field.  */
+#define XLLK(op, xop, lk) (XL ((op), (xop)) | ((lk) & 1))
+
+/* The mask for an XL form instruction.  */
+#define XL_MASK XLLK (0x3f, 0x3ff, 1)
+
+/* An XL form instruction which explicitly sets the BO field.  */
+#define XLO(op, bo, xop, lk) \
+  (XLLK ((op), (xop), (lk)) | (((bo) & 0x1f) << 21))
+#define XLO_MASK (XL_MASK | BO_MASK)
+
+/* An XL form instruction which explicitly sets the y bit of the BO
+   field.  */
+#define XLYLK(op, xop, y, lk) (XLLK ((op), (xop), (lk)) | (((y) & 1) << 21))
+#define XLYLK_MASK (XL_MASK | Y_MASK)
+
+/* An XL form instruction which sets the BO field and the condition
+   bits of the BI field.  */
+#define XLOCB(op, bo, cb, xop, lk) \
+  (XLO ((op), (bo), (xop), (lk)) | (((cb) & 3) << 16))
+#define XLOCB_MASK XLOCB (0x3f, 0x1f, 0x3, 0x3ff, 1)
+
+/* An XL_MASK or XLYLK_MASK or XLOCB_MASK with the BB field fixed.  */
+#define XLBB_MASK (XL_MASK | BB_MASK)
+#define XLYBB_MASK (XLYLK_MASK | BB_MASK)
+#define XLBOCBBB_MASK (XLOCB_MASK | BB_MASK)
+
+/* An XL_MASK with the BO and BB fields fixed.  */
+#define XLBOBB_MASK (XL_MASK | BO_MASK | BB_MASK)
+
+/* An XL_MASK with the BO, BI and BB fields fixed.  */
+#define XLBOBIBB_MASK (XL_MASK | BO_MASK | BI_MASK | BB_MASK)
+
+/* An XO form instruction.  */
+#define XO(op, xop, oe, rc) \
+  (OP (op) | (((xop) & 0x1ff) << 1) | (((oe) & 1) << 10) | ((rc) & 1))
+#define XO_MASK XO (0x3f, 0x1ff, 1, 1)
+
+/* An XO_MASK with the RB field fixed.  */
+#define XORB_MASK (XO_MASK | RB_MASK)
+
+/* An XS form instruction.  */
+#define XS(op, xop, rc) (OP (op) | (((xop) & 0x1ff) << 2) | ((rc) & 1))
+#define XS_MASK XS (0x3f, 0x1ff, 1)
+
+/* A mask for the FXM version of an XFX form instruction.  */
+#define XFXFXM_MASK (X_MASK | (1 << 20) | (1 << 11))
+
+/* An XFX form instruction with the FXM field filled in.  */
+#define XFXM(op, xop, fxm) \
+  (X ((op), (xop)) | (((fxm) & 0xff) << 12))
+
+/* An XFX form instruction with the SPR field filled in.  */
+#define XSPR(op, xop, spr) \
+  (X ((op), (xop)) | (((spr) & 0x1f) << 16) | (((spr) & 0x3e0) << 6))
+#define XSPR_MASK (X_MASK | SPR_MASK)
+
+/* An XFX form instruction with the SPR field filled in except for the
+   SPRBAT field.  */
+#define XSPRBAT_MASK (XSPR_MASK &~ SPRBAT_MASK)
+
+/* An XFX form instruction with the SPR field filled in except for the
+   SPRG field.  */
+#define XSPRG_MASK (XSPR_MASK &~ SPRG_MASK)
+
+/* The BO encodings used in extended conditional branch mnemonics.  */
+#define BODNZF	(0x0)
+#define BODNZFP	(0x1)
+#define BODZF	(0x2)
+#define BODZFP	(0x3)
+#define BOF	(0x4)
+#define BOFP	(0x5)
+#define BODNZT	(0x8)
+#define BODNZTP	(0x9)
+#define BODZT	(0xa)
+#define BODZTP	(0xb)
+#define BOT	(0xc)
+#define BOTP	(0xd)
+#define BODNZ	(0x10)
+#define BODNZP	(0x11)
+#define BODZ	(0x12)
+#define BODZP	(0x13)
+#define BOU	(0x14)
+
+/* The BI condition bit encodings used in extended conditional branch
+   mnemonics.  */
+#define CBLT	(0)
+#define CBGT	(1)
+#define CBEQ	(2)
+#define CBSO	(3)
+
+/* The TO encodings used in extended trap mnemonics.  */
+#define TOLGT	(0x1)
+#define TOLLT	(0x2)
+#define TOEQ	(0x4)
+#define TOLGE	(0x5)
+#define TOLNL	(0x5)
+#define TOLLE	(0x6)
+#define TOLNG	(0x6)
+#define TOGT	(0x8)
+#define TOGE	(0xc)
+#define TONL	(0xc)
+#define TOLT	(0x10)
+#define TOLE	(0x14)
+#define TONG	(0x14)
+#define TONE	(0x18)
+#define TOU	(0x1f)
+
+/* Smaller names for the flags so each entry in the opcodes table will
+   fit on a single line.  */
+#undef PPC
+#define PPC PPC_OPCODE_PPC
+#define POWER PPC_OPCODE_POWER
+#define POWER2 PPC_OPCODE_POWER2
+#define B32 PPC_OPCODE_32
+#define B64 PPC_OPCODE_64
+#define M601 PPC_OPCODE_601
+
+/* The opcode table.
+
+   The format of the opcode table is:
+
+   NAME	     OPCODE	MASK		FLAGS		{ OPERANDS }
+
+   NAME is the name of the instruction.
+   OPCODE is the instruction opcode.
+   MASK is the opcode mask; this is used to tell the disassembler
+     which bits in the actual opcode must match OPCODE.
+   FLAGS are flags indicated what processors support the instruction.
+   OPERANDS is the list of operands.
+
+   The disassembler reads the table in order and prints the first
+   instruction which matches, so this table is sorted to put more
+   specific instructions before more general instructions.  It is also
+   sorted by major opcode.  */
+
+const struct powerpc_opcode powerpc_opcodes[] = {
+{ "tdlgti",  OPTO(2,TOLGT), OPTO_MASK,	PPC|B64,	{ RA, SI } },
+{ "tdllti",  OPTO(2,TOLLT), OPTO_MASK,	PPC|B64,	{ RA, SI } },
+{ "tdeqi",   OPTO(2,TOEQ), OPTO_MASK,	PPC|B64,	{ RA, SI } },
+{ "tdlgei",  OPTO(2,TOLGE), OPTO_MASK,	PPC|B64,	{ RA, SI } },
+{ "tdlnli",  OPTO(2,TOLNL), OPTO_MASK,	PPC|B64,	{ RA, SI } },
+{ "tdllei",  OPTO(2,TOLLE), OPTO_MASK,	PPC|B64,	{ RA, SI } },
+{ "tdlngi",  OPTO(2,TOLNG), OPTO_MASK,	PPC|B64,	{ RA, SI } },
+{ "tdgti",   OPTO(2,TOGT), OPTO_MASK,	PPC|B64,	{ RA, SI } },
+{ "tdgei",   OPTO(2,TOGE), OPTO_MASK,	PPC|B64,	{ RA, SI } },
+{ "tdnli",   OPTO(2,TONL), OPTO_MASK,	PPC|B64,	{ RA, SI } },
+{ "tdlti",   OPTO(2,TOLT), OPTO_MASK,	PPC|B64,	{ RA, SI } },
+{ "tdlei",   OPTO(2,TOLE), OPTO_MASK,	PPC|B64,	{ RA, SI } },
+{ "tdngi",   OPTO(2,TONG), OPTO_MASK,	PPC|B64,	{ RA, SI } },
+{ "tdnei",   OPTO(2,TONE), OPTO_MASK,	PPC|B64,	{ RA, SI } },
+{ "tdi",     OP(2),	OP_MASK,	PPC|B64,	{ TO, RA, SI } },
+
+{ "twlgti",  OPTO(3,TOLGT), OPTO_MASK,	PPC,		{ RA, SI } },
+{ "tlgti",   OPTO(3,TOLGT), OPTO_MASK,	POWER,		{ RA, SI } },
+{ "twllti",  OPTO(3,TOLLT), OPTO_MASK,	PPC,		{ RA, SI } },
+{ "tllti",   OPTO(3,TOLLT), OPTO_MASK,	POWER,		{ RA, SI } },
+{ "tweqi",   OPTO(3,TOEQ), OPTO_MASK,	PPC,		{ RA, SI } },
+{ "teqi",    OPTO(3,TOEQ), OPTO_MASK,	POWER,		{ RA, SI } },
+{ "twlgei",  OPTO(3,TOLGE), OPTO_MASK,	PPC,		{ RA, SI } },
+{ "tlgei",   OPTO(3,TOLGE), OPTO_MASK,	POWER,		{ RA, SI } },
+{ "twlnli",  OPTO(3,TOLNL), OPTO_MASK,	PPC,		{ RA, SI } },
+{ "tlnli",   OPTO(3,TOLNL), OPTO_MASK,	POWER,		{ RA, SI } },
+{ "twllei",  OPTO(3,TOLLE), OPTO_MASK,	PPC,		{ RA, SI } },
+{ "tllei",   OPTO(3,TOLLE), OPTO_MASK,	POWER,		{ RA, SI } },
+{ "twlngi",  OPTO(3,TOLNG), OPTO_MASK,	PPC,		{ RA, SI } },
+{ "tlngi",   OPTO(3,TOLNG), OPTO_MASK,	POWER,		{ RA, SI } },
+{ "twgti",   OPTO(3,TOGT), OPTO_MASK,	PPC,		{ RA, SI } },
+{ "tgti",    OPTO(3,TOGT), OPTO_MASK,	POWER,		{ RA, SI } },
+{ "twgei",   OPTO(3,TOGE), OPTO_MASK,	PPC,		{ RA, SI } },
+{ "tgei",    OPTO(3,TOGE), OPTO_MASK,	POWER,		{ RA, SI } },
+{ "twnli",   OPTO(3,TONL), OPTO_MASK,	PPC,		{ RA, SI } },
+{ "tnli",    OPTO(3,TONL), OPTO_MASK,	POWER,		{ RA, SI } },
+{ "twlti",   OPTO(3,TOLT), OPTO_MASK,	PPC,		{ RA, SI } },
+{ "tlti",    OPTO(3,TOLT), OPTO_MASK,	POWER,		{ RA, SI } },
+{ "twlei",   OPTO(3,TOLE), OPTO_MASK,	PPC,		{ RA, SI } },
+{ "tlei",    OPTO(3,TOLE), OPTO_MASK,	POWER,		{ RA, SI } },
+{ "twngi",   OPTO(3,TONG), OPTO_MASK,	PPC,		{ RA, SI } },
+{ "tngi",    OPTO(3,TONG), OPTO_MASK,	POWER,		{ RA, SI } },
+{ "twnei",   OPTO(3,TONE), OPTO_MASK,	PPC,		{ RA, SI } },
+{ "tnei",    OPTO(3,TONE), OPTO_MASK,	POWER,		{ RA, SI } },
+{ "twi",     OP(3),	OP_MASK,	PPC,		{ TO, RA, SI } },
+{ "ti",      OP(3),	OP_MASK,	POWER,		{ TO, RA, SI } },
+
+{ "mulli",   OP(7),	OP_MASK,	PPC,		{ RT, RA, SI } },
+{ "muli",    OP(7),	OP_MASK,	POWER,		{ RT, RA, SI } },
+
+{ "subfic",  OP(8),	OP_MASK,	PPC,		{ RT, RA, SI } },
+{ "sfi",     OP(8),	OP_MASK,	POWER,		{ RT, RA, SI } },
+
+{ "dozi",    OP(9),	OP_MASK,	POWER|M601,	{ RT, RA, SI } },
+
+{ "cmplwi",  OPL(10,0),	OPL_MASK,	PPC,		{ OBF, RA, UI } },
+{ "cmpldi",  OPL(10,1), OPL_MASK,	PPC|B64,	{ OBF, RA, UI } },
+{ "cmpli",   OP(10),	OP_MASK,	PPC,		{ BF, L, RA, UI } },
+{ "cmpli",   OP(10),	OP_MASK,	POWER,		{ BF, RA, UI } },
+
+{ "cmpwi",   OPL(11,0),	OPL_MASK,	PPC,		{ OBF, RA, SI } },
+{ "cmpdi",   OPL(11,1),	OPL_MASK,	PPC|B64,	{ OBF, RA, SI } },
+{ "cmpi",    OP(11),	OP_MASK,	PPC,		{ BF, L, RA, SI } },
+{ "cmpi",    OP(11),	OP_MASK,	POWER,		{ BF, RA, SI } },
+
+{ "addic",   OP(12),	OP_MASK,	PPC,		{ RT, RA, SI } },
+{ "ai",	     OP(12),	OP_MASK,	POWER,		{ RT, RA, SI } },
+{ "subic",   OP(12),	OP_MASK,	PPC,		{ RT, RA, NSI } },
+
+{ "addic.",  OP(13),	OP_MASK,	PPC,		{ RT, RA, SI } },
+{ "ai.",     OP(13),	OP_MASK,	POWER,		{ RT, RA, SI } },
+{ "subic.",  OP(13),	OP_MASK,	PPC,		{ RT, RA, NSI } },
+
+{ "li",	     OP(14),	DRA_MASK,	PPC,		{ RT, SI } },
+{ "lil",     OP(14),	DRA_MASK,	POWER,		{ RT, SI } },
+{ "addi",    OP(14),	OP_MASK,	PPC,		{ RT, RA, SI } },
+{ "cal",     OP(14),	OP_MASK,	POWER,		{ RT, D, RA } },
+{ "subi",    OP(14),	OP_MASK,	PPC,		{ RT, RA, NSI } },
+{ "la",	     OP(14),	OP_MASK,	PPC,		{ RT, D, RA } },
+
+{ "lis",     OP(15),	DRA_MASK,	PPC,		{ RT, SISIGNOPT } },
+{ "liu",     OP(15),	DRA_MASK,	POWER,		{ RT, SISIGNOPT } },
+{ "addis",   OP(15),	OP_MASK,	PPC,		{ RT,RA,SISIGNOPT } },
+{ "cau",     OP(15),	OP_MASK,	POWER,		{ RT,RA,SISIGNOPT } },
+{ "subis",   OP(15),	OP_MASK,	PPC,		{ RT, RA, NSI } },
+
+{ "bdnz-",   BBO(16,BODNZ,0,0), BBOYBI_MASK, PPC,	{ BDM } },
+{ "bdnz+",   BBO(16,BODNZ,0,0), BBOYBI_MASK, PPC,	{ BDP } },
+{ "bdnz",    BBO(16,BODNZ,0,0), BBOYBI_MASK, PPC,	{ BD } },
+{ "bdn",     BBO(16,BODNZ,0,0), BBOYBI_MASK, POWER,	{ BD } },
+{ "bdnzl-",  BBO(16,BODNZ,0,1), BBOYBI_MASK, PPC,	{ BDM } },
+{ "bdnzl+",  BBO(16,BODNZ,0,1), BBOYBI_MASK, PPC,	{ BDP } },
+{ "bdnzl",   BBO(16,BODNZ,0,1), BBOYBI_MASK, PPC,	{ BD } },
+{ "bdnl",    BBO(16,BODNZ,0,1), BBOYBI_MASK, POWER,	{ BD } },
+{ "bdnza-",  BBO(16,BODNZ,1,0), BBOYBI_MASK, PPC,	{ BDMA } },
+{ "bdnza+",  BBO(16,BODNZ,1,0), BBOYBI_MASK, PPC,	{ BDPA } },
+{ "bdnza",   BBO(16,BODNZ,1,0), BBOYBI_MASK, PPC,	{ BDA } },
+{ "bdna",    BBO(16,BODNZ,1,0), BBOYBI_MASK, POWER,	{ BDA } },
+{ "bdnzla-", BBO(16,BODNZ,1,1), BBOYBI_MASK, PPC,	{ BDMA } },
+{ "bdnzla+", BBO(16,BODNZ,1,1), BBOYBI_MASK, PPC,	{ BDPA } },
+{ "bdnzla",  BBO(16,BODNZ,1,1), BBOYBI_MASK, PPC,	{ BDA } },
+{ "bdnla",   BBO(16,BODNZ,1,1), BBOYBI_MASK, POWER,	{ BDA } },
+{ "bdz-",    BBO(16,BODZ,0,0), BBOYBI_MASK, PPC,	{ BDM } },
+{ "bdz+",    BBO(16,BODZ,0,0), BBOYBI_MASK, PPC,	{ BDP } },
+{ "bdz",     BBO(16,BODZ,0,0), BBOYBI_MASK, PPC|POWER,	{ BD } },
+{ "bdzl-",   BBO(16,BODZ,0,1), BBOYBI_MASK, PPC,	{ BDM } },
+{ "bdzl+",   BBO(16,BODZ,0,1), BBOYBI_MASK, PPC,	{ BDP } },
+{ "bdzl",    BBO(16,BODZ,0,1), BBOYBI_MASK, PPC|POWER,	{ BD } },
+{ "bdza-",   BBO(16,BODZ,1,0), BBOYBI_MASK, PPC,	{ BDMA } },
+{ "bdza+",   BBO(16,BODZ,1,0), BBOYBI_MASK, PPC,	{ BDPA } },
+{ "bdza",    BBO(16,BODZ,1,0), BBOYBI_MASK, PPC|POWER,	{ BDA } },
+{ "bdzla-",  BBO(16,BODZ,1,1), BBOYBI_MASK, PPC,	{ BDMA } },
+{ "bdzla+",  BBO(16,BODZ,1,1), BBOYBI_MASK, PPC,	{ BDPA } },
+{ "bdzla",   BBO(16,BODZ,1,1), BBOYBI_MASK, PPC|POWER,	{ BDA } },
+{ "blt-",    BBOCB(16,BOT,CBLT,0,0), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "blt+",    BBOCB(16,BOT,CBLT,0,0), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "blt",     BBOCB(16,BOT,CBLT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bltl-",   BBOCB(16,BOT,CBLT,0,1), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "bltl+",   BBOCB(16,BOT,CBLT,0,1), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "bltl",    BBOCB(16,BOT,CBLT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "blta-",   BBOCB(16,BOT,CBLT,1,0), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "blta+",   BBOCB(16,BOT,CBLT,1,0), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "blta",    BBOCB(16,BOT,CBLT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bltla-",  BBOCB(16,BOT,CBLT,1,1), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "bltla+",  BBOCB(16,BOT,CBLT,1,1), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "bltla",   BBOCB(16,BOT,CBLT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bgt-",    BBOCB(16,BOT,CBGT,0,0), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "bgt+",    BBOCB(16,BOT,CBGT,0,0), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "bgt",     BBOCB(16,BOT,CBGT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bgtl-",   BBOCB(16,BOT,CBGT,0,1), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "bgtl+",   BBOCB(16,BOT,CBGT,0,1), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "bgtl",    BBOCB(16,BOT,CBGT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bgta-",   BBOCB(16,BOT,CBGT,1,0), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "bgta+",   BBOCB(16,BOT,CBGT,1,0), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "bgta",    BBOCB(16,BOT,CBGT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bgtla-",  BBOCB(16,BOT,CBGT,1,1), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "bgtla+",  BBOCB(16,BOT,CBGT,1,1), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "bgtla",   BBOCB(16,BOT,CBGT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "beq-",    BBOCB(16,BOT,CBEQ,0,0), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "beq+",    BBOCB(16,BOT,CBEQ,0,0), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "beq",     BBOCB(16,BOT,CBEQ,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "beql-",   BBOCB(16,BOT,CBEQ,0,1), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "beql+",   BBOCB(16,BOT,CBEQ,0,1), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "beql",    BBOCB(16,BOT,CBEQ,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "beqa-",   BBOCB(16,BOT,CBEQ,1,0), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "beqa+",   BBOCB(16,BOT,CBEQ,1,0), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "beqa",    BBOCB(16,BOT,CBEQ,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "beqla-",  BBOCB(16,BOT,CBEQ,1,1), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "beqla+",  BBOCB(16,BOT,CBEQ,1,1), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "beqla",   BBOCB(16,BOT,CBEQ,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bso-",    BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "bso+",    BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "bso",     BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bsol-",   BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "bsol+",   BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "bsol",    BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bsoa-",   BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "bsoa+",   BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "bsoa",    BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bsola-",  BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "bsola+",  BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "bsola",   BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bun-",    BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "bun+",    BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "bun",     BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC,	{ CR, BD } },
+{ "bunl-",   BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "bunl+",   BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "bunl",    BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC,	{ CR, BD } },
+{ "buna-",   BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "buna+",   BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "buna",    BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC,	{ CR, BDA } },
+{ "bunla-",  BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "bunla+",  BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "bunla",   BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC,	{ CR, BDA } },
+{ "bge-",    BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "bge+",    BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "bge",     BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bgel-",   BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "bgel+",   BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "bgel",    BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bgea-",   BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "bgea+",   BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "bgea",    BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bgela-",  BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "bgela+",  BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "bgela",   BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bnl-",    BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "bnl+",    BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "bnl",     BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bnll-",   BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "bnll+",   BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "bnll",    BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bnla-",   BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "bnla+",   BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "bnla",    BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bnlla-",  BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "bnlla+",  BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "bnlla",   BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "ble-",    BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "ble+",    BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "ble",     BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "blel-",   BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "blel+",   BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "blel",    BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "blea-",   BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "blea+",   BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "blea",    BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "blela-",  BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "blela+",  BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "blela",   BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bng-",    BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "bng+",    BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "bng",     BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bngl-",   BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "bngl+",   BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "bngl",    BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bnga-",   BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "bnga+",   BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "bnga",    BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bngla-",  BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "bngla+",  BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "bngla",   BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bne-",    BBOCB(16,BOF,CBEQ,0,0), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "bne+",    BBOCB(16,BOF,CBEQ,0,0), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "bne",     BBOCB(16,BOF,CBEQ,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bnel-",   BBOCB(16,BOF,CBEQ,0,1), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "bnel+",   BBOCB(16,BOF,CBEQ,0,1), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "bnel",    BBOCB(16,BOF,CBEQ,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bnea-",   BBOCB(16,BOF,CBEQ,1,0), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "bnea+",   BBOCB(16,BOF,CBEQ,1,0), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "bnea",    BBOCB(16,BOF,CBEQ,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bnela-",  BBOCB(16,BOF,CBEQ,1,1), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "bnela+",  BBOCB(16,BOF,CBEQ,1,1), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "bnela",   BBOCB(16,BOF,CBEQ,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bns-",    BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "bns+",    BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "bns",     BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bnsl-",   BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "bnsl+",   BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "bnsl",    BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bnsa-",   BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "bnsa+",   BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "bnsa",    BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bnsla-",  BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "bnsla+",  BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "bnsla",   BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bnu-",    BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "bnu+",    BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "bnu",     BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC,	{ CR, BD } },
+{ "bnul-",   BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "bnul+",   BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "bnul",    BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC,	{ CR, BD } },
+{ "bnua-",   BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "bnua+",   BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "bnua",    BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC,	{ CR, BDA } },
+{ "bnula-",  BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "bnula+",  BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "bnula",   BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC,	{ CR, BDA } },
+{ "bdnzt-",  BBO(16,BODNZT,0,0), BBOY_MASK, PPC,	{ BI, BDM } },
+{ "bdnzt+",  BBO(16,BODNZT,0,0), BBOY_MASK, PPC,	{ BI, BDP } },
+{ "bdnzt",   BBO(16,BODNZT,0,0), BBOY_MASK, PPC,	{ BI, BD } },
+{ "bdnztl-", BBO(16,BODNZT,0,1), BBOY_MASK, PPC,	{ BI, BDM } },
+{ "bdnztl+", BBO(16,BODNZT,0,1), BBOY_MASK, PPC,	{ BI, BDP } },
+{ "bdnztl",  BBO(16,BODNZT,0,1), BBOY_MASK, PPC,	{ BI, BD } },
+{ "bdnzta-", BBO(16,BODNZT,1,0), BBOY_MASK, PPC,	{ BI, BDMA } },
+{ "bdnzta+", BBO(16,BODNZT,1,0), BBOY_MASK, PPC,	{ BI, BDPA } },
+{ "bdnzta",  BBO(16,BODNZT,1,0), BBOY_MASK, PPC,	{ BI, BDA } },
+{ "bdnztla-",BBO(16,BODNZT,1,1), BBOY_MASK, PPC,	{ BI, BDMA } },
+{ "bdnztla+",BBO(16,BODNZT,1,1), BBOY_MASK, PPC,	{ BI, BDPA } },
+{ "bdnztla", BBO(16,BODNZT,1,1), BBOY_MASK, PPC,	{ BI, BDA } },
+{ "bdnzf-",  BBO(16,BODNZF,0,0), BBOY_MASK, PPC,	{ BI, BDM } },
+{ "bdnzf+",  BBO(16,BODNZF,0,0), BBOY_MASK, PPC,	{ BI, BDP } },
+{ "bdnzf",   BBO(16,BODNZF,0,0), BBOY_MASK, PPC,	{ BI, BD } },
+{ "bdnzfl-", BBO(16,BODNZF,0,1), BBOY_MASK, PPC,	{ BI, BDM } },
+{ "bdnzfl+", BBO(16,BODNZF,0,1), BBOY_MASK, PPC,	{ BI, BDP } },
+{ "bdnzfl",  BBO(16,BODNZF,0,1), BBOY_MASK, PPC,	{ BI, BD } },
+{ "bdnzfa-", BBO(16,BODNZF,1,0), BBOY_MASK, PPC,	{ BI, BDMA } },
+{ "bdnzfa+", BBO(16,BODNZF,1,0), BBOY_MASK, PPC,	{ BI, BDPA } },
+{ "bdnzfa",  BBO(16,BODNZF,1,0), BBOY_MASK, PPC,	{ BI, BDA } },
+{ "bdnzfla-",BBO(16,BODNZF,1,1), BBOY_MASK, PPC,	{ BI, BDMA } },
+{ "bdnzfla+",BBO(16,BODNZF,1,1), BBOY_MASK, PPC,	{ BI, BDPA } },
+{ "bdnzfla", BBO(16,BODNZF,1,1), BBOY_MASK, PPC,	{ BI, BDA } },
+{ "bt-",     BBO(16,BOT,0,0), BBOY_MASK, PPC,		{ BI, BDM } },
+{ "bt+",     BBO(16,BOT,0,0), BBOY_MASK, PPC,		{ BI, BDP } },
+{ "bt",	     BBO(16,BOT,0,0), BBOY_MASK, PPC,		{ BI, BD } },
+{ "bbt",     BBO(16,BOT,0,0), BBOY_MASK, POWER,		{ BI, BD } },
+{ "btl-",    BBO(16,BOT,0,1), BBOY_MASK, PPC,		{ BI, BDM } },
+{ "btl+",    BBO(16,BOT,0,1), BBOY_MASK, PPC,		{ BI, BDP } },
+{ "btl",     BBO(16,BOT,0,1), BBOY_MASK, PPC,		{ BI, BD } },
+{ "bbtl",    BBO(16,BOT,0,1), BBOY_MASK, POWER,		{ BI, BD } },
+{ "bta-",    BBO(16,BOT,1,0), BBOY_MASK, PPC,		{ BI, BDMA } },
+{ "bta+",    BBO(16,BOT,1,0), BBOY_MASK, PPC,		{ BI, BDPA } },
+{ "bta",     BBO(16,BOT,1,0), BBOY_MASK, PPC,		{ BI, BDA } },
+{ "bbta",    BBO(16,BOT,1,0), BBOY_MASK, POWER,		{ BI, BDA } },
+{ "btla-",   BBO(16,BOT,1,1), BBOY_MASK, PPC,		{ BI, BDMA } },
+{ "btla+",   BBO(16,BOT,1,1), BBOY_MASK, PPC,		{ BI, BDPA } },
+{ "btla",    BBO(16,BOT,1,1), BBOY_MASK, PPC,		{ BI, BDA } },
+{ "bbtla",   BBO(16,BOT,1,1), BBOY_MASK, POWER,		{ BI, BDA } },
+{ "bf-",     BBO(16,BOF,0,0), BBOY_MASK, PPC,		{ BI, BDM } },
+{ "bf+",     BBO(16,BOF,0,0), BBOY_MASK, PPC,		{ BI, BDP } },
+{ "bf",	     BBO(16,BOF,0,0), BBOY_MASK, PPC,		{ BI, BD } },
+{ "bbf",     BBO(16,BOF,0,0), BBOY_MASK, POWER,		{ BI, BD } },
+{ "bfl-",    BBO(16,BOF,0,1), BBOY_MASK, PPC,		{ BI, BDM } },
+{ "bfl+",    BBO(16,BOF,0,1), BBOY_MASK, PPC,		{ BI, BDP } },
+{ "bfl",     BBO(16,BOF,0,1), BBOY_MASK, PPC,		{ BI, BD } },
+{ "bbfl",    BBO(16,BOF,0,1), BBOY_MASK, POWER,		{ BI, BD } },
+{ "bfa-",    BBO(16,BOF,1,0), BBOY_MASK, PPC,		{ BI, BDMA } },
+{ "bfa+",    BBO(16,BOF,1,0), BBOY_MASK, PPC,		{ BI, BDPA } },
+{ "bfa",     BBO(16,BOF,1,0), BBOY_MASK, PPC,		{ BI, BDA } },
+{ "bbfa",    BBO(16,BOF,1,0), BBOY_MASK, POWER,		{ BI, BDA } },
+{ "bfla-",   BBO(16,BOF,1,1), BBOY_MASK, PPC,		{ BI, BDMA } },
+{ "bfla+",   BBO(16,BOF,1,1), BBOY_MASK, PPC,		{ BI, BDPA } },
+{ "bfla",    BBO(16,BOF,1,1), BBOY_MASK, PPC,		{ BI, BDA } },
+{ "bbfla",   BBO(16,BOF,1,1), BBOY_MASK, POWER,		{ BI, BDA } },
+{ "bdzt-",   BBO(16,BODZT,0,0), BBOY_MASK, PPC,		{ BI, BDM } },
+{ "bdzt+",   BBO(16,BODZT,0,0), BBOY_MASK, PPC,		{ BI, BDP } },
+{ "bdzt",    BBO(16,BODZT,0,0), BBOY_MASK, PPC,		{ BI, BD } },
+{ "bdztl-",  BBO(16,BODZT,0,1), BBOY_MASK, PPC,		{ BI, BDM } },
+{ "bdztl+",  BBO(16,BODZT,0,1), BBOY_MASK, PPC,		{ BI, BDP } },
+{ "bdztl",   BBO(16,BODZT,0,1), BBOY_MASK, PPC,		{ BI, BD } },
+{ "bdzta-",  BBO(16,BODZT,1,0), BBOY_MASK, PPC,		{ BI, BDMA } },
+{ "bdzta+",  BBO(16,BODZT,1,0), BBOY_MASK, PPC,		{ BI, BDPA } },
+{ "bdzta",   BBO(16,BODZT,1,0), BBOY_MASK, PPC,		{ BI, BDA } },
+{ "bdztla-", BBO(16,BODZT,1,1), BBOY_MASK, PPC,		{ BI, BDMA } },
+{ "bdztla+", BBO(16,BODZT,1,1), BBOY_MASK, PPC,		{ BI, BDPA } },
+{ "bdztla",  BBO(16,BODZT,1,1), BBOY_MASK, PPC,		{ BI, BDA } },
+{ "bdzf-",   BBO(16,BODZF,0,0), BBOY_MASK, PPC,		{ BI, BDM } },
+{ "bdzf+",   BBO(16,BODZF,0,0), BBOY_MASK, PPC,		{ BI, BDP } },
+{ "bdzf",    BBO(16,BODZF,0,0), BBOY_MASK, PPC,		{ BI, BD } },
+{ "bdzfl-",  BBO(16,BODZF,0,1), BBOY_MASK, PPC,		{ BI, BDM } },
+{ "bdzfl+",  BBO(16,BODZF,0,1), BBOY_MASK, PPC,		{ BI, BDP } },
+{ "bdzfl",   BBO(16,BODZF,0,1), BBOY_MASK, PPC,		{ BI, BD } },
+{ "bdzfa-",  BBO(16,BODZF,1,0), BBOY_MASK, PPC,		{ BI, BDMA } },
+{ "bdzfa+",  BBO(16,BODZF,1,0), BBOY_MASK, PPC,		{ BI, BDPA } },
+{ "bdzfa",   BBO(16,BODZF,1,0), BBOY_MASK, PPC,		{ BI, BDA } },
+{ "bdzfla-", BBO(16,BODZF,1,1), BBOY_MASK, PPC,		{ BI, BDMA } },
+{ "bdzfla+", BBO(16,BODZF,1,1), BBOY_MASK, PPC,		{ BI, BDPA } },
+{ "bdzfla",  BBO(16,BODZF,1,1), BBOY_MASK, PPC,		{ BI, BDA } },
+{ "bc-",     B(16,0,0),	B_MASK,		PPC,		{ BOE, BI, BDM } },
+{ "bc+",     B(16,0,0),	B_MASK,		PPC,		{ BOE, BI, BDP } },
+{ "bc",	     B(16,0,0),	B_MASK,		PPC|POWER,	{ BO, BI, BD } },
+{ "bcl-",    B(16,0,1),	B_MASK,		PPC,		{ BOE, BI, BDM } },
+{ "bcl+",    B(16,0,1),	B_MASK,		PPC,		{ BOE, BI, BDP } },
+{ "bcl",     B(16,0,1),	B_MASK,		PPC|POWER,	{ BO, BI, BD } },
+{ "bca-",    B(16,1,0),	B_MASK,		PPC,		{ BOE, BI, BDMA } },
+{ "bca+",    B(16,1,0),	B_MASK,		PPC,		{ BOE, BI, BDPA } },
+{ "bca",     B(16,1,0),	B_MASK,		PPC|POWER,	{ BO, BI, BDA } },
+{ "bcla-",   B(16,1,1),	B_MASK,		PPC,		{ BOE, BI, BDMA } },
+{ "bcla+",   B(16,1,1),	B_MASK,		PPC,		{ BOE, BI, BDPA } },
+{ "bcla",    B(16,1,1),	B_MASK,		PPC|POWER,	{ BO, BI, BDA } },
+
+{ "sc",      SC(17,1,0), 0xffffffff,	PPC,		{ 0 } },
+{ "svc",     SC(17,0,0), SC_MASK,	POWER,		{ LEV, FL1, FL2 } },
+{ "svcl",    SC(17,0,1), SC_MASK,	POWER,		{ LEV, FL1, FL2 } },
+{ "svca",    SC(17,1,0), SC_MASK,	POWER,		{ SV } },
+{ "svcla",   SC(17,1,1), SC_MASK,	POWER,		{ SV } },
+
+{ "b",	     B(18,0,0),	B_MASK,		PPC|POWER,	{ LI } },
+{ "bl",      B(18,0,1),	B_MASK,		PPC|POWER,	{ LI } },
+{ "ba",      B(18,1,0),	B_MASK,		PPC|POWER,	{ LIA } },
+{ "bla",     B(18,1,1),	B_MASK,		PPC|POWER,	{ LIA } },
+
+{ "mcrf",    XL(19,0),	XLBB_MASK|(3<<21)|(3<<16), PPC|POWER, { BF, BFA } },
+
+{ "blr",     XLO(19,BOU,16,0), XLBOBIBB_MASK, PPC,	{ 0 } },
+{ "br",      XLO(19,BOU,16,0), XLBOBIBB_MASK, POWER,	{ 0 } },
+{ "blrl",    XLO(19,BOU,16,1), XLBOBIBB_MASK, PPC,	{ 0 } },
+{ "brl",     XLO(19,BOU,16,1), XLBOBIBB_MASK, POWER,	{ 0 } },
+{ "bdnzlr",  XLO(19,BODNZ,16,0), XLBOBIBB_MASK, PPC,	{ 0 } },
+{ "bdnzlr-", XLO(19,BODNZ,16,0), XLBOBIBB_MASK, PPC,	{ 0 } },
+{ "bdnzlr+", XLO(19,BODNZP,16,0), XLBOBIBB_MASK, PPC,	{ 0 } },
+{ "bdnzlrl", XLO(19,BODNZ,16,1), XLBOBIBB_MASK, PPC,	{ 0 } },
+{ "bdnzlrl-",XLO(19,BODNZ,16,1), XLBOBIBB_MASK, PPC,	{ 0 } },
+{ "bdnzlrl+",XLO(19,BODNZP,16,1), XLBOBIBB_MASK, PPC,	{ 0 } },
+{ "bdzlr",   XLO(19,BODZ,16,0), XLBOBIBB_MASK, PPC,	{ 0 } },
+{ "bdzlr-",  XLO(19,BODZ,16,0), XLBOBIBB_MASK, PPC,	{ 0 } },
+{ "bdzlr+",  XLO(19,BODZP,16,0), XLBOBIBB_MASK, PPC,	{ 0 } },
+{ "bdzlrl",  XLO(19,BODZ,16,1), XLBOBIBB_MASK, PPC,	{ 0 } },
+{ "bdzlrl-", XLO(19,BODZ,16,1), XLBOBIBB_MASK, PPC,	{ 0 } },
+{ "bdzlrl+", XLO(19,BODZP,16,1), XLBOBIBB_MASK, PPC,	{ 0 } },
+{ "bltlr",   XLOCB(19,BOT,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltlr-",  XLOCB(19,BOT,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltlr+",  XLOCB(19,BOTP,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltr",    XLOCB(19,BOT,CBLT,16,0), XLBOCBBB_MASK, POWER, { CR } },
+{ "bltlrl",  XLOCB(19,BOT,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltlrl-", XLOCB(19,BOT,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltlrl+", XLOCB(19,BOTP,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltrl",   XLOCB(19,BOT,CBLT,16,1), XLBOCBBB_MASK, POWER, { CR } },
+{ "bgtlr",   XLOCB(19,BOT,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtlr-",  XLOCB(19,BOT,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtlr+",  XLOCB(19,BOTP,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtr",    XLOCB(19,BOT,CBGT,16,0), XLBOCBBB_MASK, POWER, { CR } },
+{ "bgtlrl",  XLOCB(19,BOT,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtlrl-", XLOCB(19,BOT,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtlrl+", XLOCB(19,BOTP,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtrl",   XLOCB(19,BOT,CBGT,16,1), XLBOCBBB_MASK, POWER, { CR } },
+{ "beqlr",   XLOCB(19,BOT,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqlr-",  XLOCB(19,BOT,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqlr+",  XLOCB(19,BOTP,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqr",    XLOCB(19,BOT,CBEQ,16,0), XLBOCBBB_MASK, POWER, { CR } },
+{ "beqlrl",  XLOCB(19,BOT,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqlrl-", XLOCB(19,BOT,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqlrl+", XLOCB(19,BOTP,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqrl",   XLOCB(19,BOT,CBEQ,16,1), XLBOCBBB_MASK, POWER, { CR } },
+{ "bsolr",   XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsolr-",  XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsolr+",  XLOCB(19,BOTP,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsor",    XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, POWER, { CR } },
+{ "bsolrl",  XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsolrl-", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsolrl+", XLOCB(19,BOTP,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsorl",   XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, POWER, { CR } },
+{ "bunlr",   XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunlr-",  XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunlr+",  XLOCB(19,BOTP,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunlrl",  XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunlrl-", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunlrl+", XLOCB(19,BOTP,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgelr",   XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgelr-",  XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgelr+",  XLOCB(19,BOFP,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bger",    XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, POWER, { CR } },
+{ "bgelrl",  XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgelrl-", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgelrl+", XLOCB(19,BOFP,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgerl",   XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, POWER, { CR } },
+{ "bnllr",   XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnllr-",  XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnllr+",  XLOCB(19,BOFP,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnlr",    XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, POWER, { CR } },
+{ "bnllrl",  XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnllrl-", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnllrl+", XLOCB(19,BOFP,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnlrl",   XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, POWER, { CR } },
+{ "blelr",   XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "blelr-",  XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "blelr+",  XLOCB(19,BOFP,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bler",    XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, POWER, { CR } },
+{ "blelrl",  XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "blelrl-", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "blelrl+", XLOCB(19,BOFP,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "blerl",   XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, POWER, { CR } },
+{ "bnglr",   XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnglr-",  XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnglr+",  XLOCB(19,BOFP,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bngr",    XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, POWER, { CR } },
+{ "bnglrl",  XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnglrl-", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnglrl+", XLOCB(19,BOFP,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bngrl",   XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, POWER, { CR } },
+{ "bnelr",   XLOCB(19,BOF,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnelr-",  XLOCB(19,BOF,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnelr+",  XLOCB(19,BOFP,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bner",    XLOCB(19,BOF,CBEQ,16,0), XLBOCBBB_MASK, POWER, { CR } },
+{ "bnelrl",  XLOCB(19,BOF,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnelrl-", XLOCB(19,BOF,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnelrl+", XLOCB(19,BOFP,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnerl",   XLOCB(19,BOF,CBEQ,16,1), XLBOCBBB_MASK, POWER, { CR } },
+{ "bnslr",   XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnslr-",  XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnslr+",  XLOCB(19,BOFP,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnsr",    XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, POWER, { CR } },
+{ "bnslrl",  XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnslrl-", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnslrl+", XLOCB(19,BOFP,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnsrl",   XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, POWER, { CR } },
+{ "bnulr",   XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnulr-",  XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnulr+",  XLOCB(19,BOFP,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnulrl",  XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnulrl-", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnulrl+", XLOCB(19,BOFP,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "btlr",    XLO(19,BOT,16,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "btlr-",   XLO(19,BOT,16,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "btlr+",   XLO(19,BOTP,16,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "bbtr",    XLO(19,BOT,16,0), XLBOBB_MASK, POWER,	{ BI } },
+{ "btlrl",   XLO(19,BOT,16,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "btlrl-",  XLO(19,BOT,16,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "btlrl+",  XLO(19,BOTP,16,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "bbtrl",   XLO(19,BOT,16,1), XLBOBB_MASK, POWER,	{ BI } },
+{ "bflr",    XLO(19,BOF,16,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "bflr-",   XLO(19,BOF,16,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "bflr+",   XLO(19,BOFP,16,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "bbfr",    XLO(19,BOF,16,0), XLBOBB_MASK, POWER,	{ BI } },
+{ "bflrl",   XLO(19,BOF,16,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "bflrl-",  XLO(19,BOF,16,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "bflrl+",  XLO(19,BOFP,16,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "bbfrl",   XLO(19,BOF,16,1), XLBOBB_MASK, POWER,	{ BI } },
+{ "bdnztlr", XLO(19,BODNZT,16,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdnztlr-",XLO(19,BODNZT,16,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdnztlr+",XLO(19,BODNZTP,16,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdnztlrl",XLO(19,BODNZT,16,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdnztlrl-",XLO(19,BODNZT,16,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdnztlrl+",XLO(19,BODNZTP,16,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdnzflr", XLO(19,BODNZF,16,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdnzflr-",XLO(19,BODNZF,16,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdnzflr+",XLO(19,BODNZFP,16,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdnzflrl",XLO(19,BODNZF,16,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdnzflrl-",XLO(19,BODNZF,16,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdnzflrl+",XLO(19,BODNZFP,16,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdztlr",  XLO(19,BODZT,16,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdztlr-", XLO(19,BODZT,16,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdztlr+", XLO(19,BODZTP,16,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdztlrl", XLO(19,BODZT,16,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdztlrl-",XLO(19,BODZT,16,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdztlrl+",XLO(19,BODZTP,16,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdzflr",  XLO(19,BODZF,16,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdzflr-", XLO(19,BODZF,16,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdzflr+", XLO(19,BODZFP,16,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdzflrl", XLO(19,BODZF,16,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdzflrl-",XLO(19,BODZF,16,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdzflrl+",XLO(19,BODZFP,16,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "bclr",    XLLK(19,16,0), XLYBB_MASK,	PPC,		{ BO, BI } },
+{ "bclrl",   XLLK(19,16,1), XLYBB_MASK,	PPC,		{ BO, BI } },
+{ "bclr+",   XLYLK(19,16,1,0), XLYBB_MASK, PPC,		{ BOE, BI } },
+{ "bclrl+",  XLYLK(19,16,1,1), XLYBB_MASK, PPC,		{ BOE, BI } },
+{ "bclr-",   XLYLK(19,16,0,0), XLYBB_MASK, PPC,		{ BOE, BI } },
+{ "bclrl-",  XLYLK(19,16,0,1), XLYBB_MASK, PPC,		{ BOE, BI } },
+{ "bcr",     XLLK(19,16,0), XLBB_MASK,	POWER,		{ BO, BI } },
+{ "bcrl",    XLLK(19,16,1), XLBB_MASK,	POWER,		{ BO, BI } },
+
+{ "crnot",   XL(19,33), XL_MASK,	PPC,		{ BT, BA, BBA } },
+{ "crnor",   XL(19,33),	XL_MASK,	PPC|POWER,	{ BT, BA, BB } },
+
+{ "rfi",     XL(19,50),	0xffffffff,	PPC|POWER,	{ 0 } },
+{ "rfci",    XL(19,51),	0xffffffff,	PPC,		{ 0 } },
+
+{ "rfsvc",   XL(19,82),	0xffffffff,	POWER,		{ 0 } },
+
+{ "crandc",  XL(19,129), XL_MASK,	PPC|POWER,	{ BT, BA, BB } },
+
+{ "isync",   XL(19,150), 0xffffffff,	PPC,		{ 0 } },
+{ "ics",     XL(19,150), 0xffffffff,	POWER,		{ 0 } },
+
+{ "crclr",   XL(19,193), XL_MASK,	PPC,		{ BT, BAT, BBA } },
+{ "crxor",   XL(19,193), XL_MASK,	PPC|POWER,	{ BT, BA, BB } },
+
+{ "crnand",  XL(19,225), XL_MASK,	PPC|POWER,	{ BT, BA, BB } },
+
+{ "crand",   XL(19,257), XL_MASK,	PPC|POWER,	{ BT, BA, BB } },
+
+{ "crset",   XL(19,289), XL_MASK,	PPC,		{ BT, BAT, BBA } },
+{ "creqv",   XL(19,289), XL_MASK,	PPC|POWER,	{ BT, BA, BB } },
+
+{ "crorc",   XL(19,417), XL_MASK,	PPC|POWER,	{ BT, BA, BB } },
+
+{ "crmove",  XL(19,449), XL_MASK,	PPC,		{ BT, BA, BBA } },
+{ "cror",    XL(19,449), XL_MASK,	PPC|POWER,	{ BT, BA, BB } },
+
+{ "bctr",    XLO(19,BOU,528,0), XLBOBIBB_MASK, PPC|POWER, { 0 } },
+{ "bctrl",   XLO(19,BOU,528,1), XLBOBIBB_MASK, PPC|POWER, { 0 } },
+{ "bltctr",  XLOCB(19,BOT,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltctr-", XLOCB(19,BOT,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltctr+", XLOCB(19,BOTP,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltctrl", XLOCB(19,BOT,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltctrl-",XLOCB(19,BOT,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltctrl+",XLOCB(19,BOTP,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtctr",  XLOCB(19,BOT,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtctr-", XLOCB(19,BOT,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtctr+", XLOCB(19,BOTP,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtctrl", XLOCB(19,BOT,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtctrl-",XLOCB(19,BOT,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtctrl+",XLOCB(19,BOTP,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqctr",  XLOCB(19,BOT,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqctr-", XLOCB(19,BOT,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqctr+", XLOCB(19,BOTP,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqctrl", XLOCB(19,BOT,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqctrl-",XLOCB(19,BOT,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqctrl+",XLOCB(19,BOTP,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsoctr",  XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsoctr-", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsoctr+", XLOCB(19,BOTP,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsoctrl", XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsoctrl-",XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsoctrl+",XLOCB(19,BOTP,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunctr",  XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunctr-", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunctr+", XLOCB(19,BOTP,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunctrl", XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunctrl-",XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunctrl+",XLOCB(19,BOTP,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgectr",  XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgectr-", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgectr+", XLOCB(19,BOFP,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgectrl", XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgectrl-",XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgectrl+",XLOCB(19,BOFP,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnlctr",  XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnlctr-", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnlctr+", XLOCB(19,BOFP,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnlctrl", XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnlctrl-",XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnlctrl+",XLOCB(19,BOFP,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "blectr",  XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "blectr-", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "blectr+", XLOCB(19,BOFP,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "blectrl", XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "blectrl-",XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "blectrl+",XLOCB(19,BOFP,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bngctr",  XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bngctr-", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bngctr+", XLOCB(19,BOFP,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bngctrl", XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bngctrl-",XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bngctrl+",XLOCB(19,BOFP,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnectr",  XLOCB(19,BOF,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnectr-", XLOCB(19,BOF,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnectr+", XLOCB(19,BOFP,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnectrl", XLOCB(19,BOF,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnectrl-",XLOCB(19,BOF,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnectrl+",XLOCB(19,BOFP,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnsctr",  XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnsctr-", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnsctr+", XLOCB(19,BOFP,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnsctrl", XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnsctrl-",XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnsctrl+",XLOCB(19,BOFP,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnuctr",  XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnuctr-", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnuctr+", XLOCB(19,BOFP,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnuctrl", XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnuctrl-",XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnuctrl+",XLOCB(19,BOFP,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "btctr",   XLO(19,BOT,528,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "btctr-",  XLO(19,BOT,528,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "btctr+",  XLO(19,BOTP,528,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "btctrl",  XLO(19,BOT,528,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "btctrl-", XLO(19,BOT,528,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "btctrl+", XLO(19,BOTP,528,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "bfctr",   XLO(19,BOF,528,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "bfctr-",  XLO(19,BOF,528,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "bfctr+",  XLO(19,BOFP,528,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "bfctrl",  XLO(19,BOF,528,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "bfctrl-", XLO(19,BOF,528,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "bfctrl+", XLO(19,BOFP,528,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "bcctr",   XLLK(19,528,0), XLYBB_MASK, PPC,		{ BO, BI } },
+{ "bcctr-",  XLYLK(19,528,0,0), XLYBB_MASK, PPC,	{ BOE, BI } },
+{ "bcctr+",  XLYLK(19,528,1,0), XLYBB_MASK, PPC,	{ BOE, BI } },
+{ "bcctrl",  XLLK(19,528,1), XLYBB_MASK, PPC,		{ BO, BI } },
+{ "bcctrl-", XLYLK(19,528,0,1), XLYBB_MASK, PPC,	{ BOE, BI } },
+{ "bcctrl+", XLYLK(19,528,1,1), XLYBB_MASK, PPC,	{ BOE, BI } },
+{ "bcc",     XLLK(19,528,0), XLBB_MASK,	POWER,		{ BO, BI } },
+{ "bccl",    XLLK(19,528,1), XLBB_MASK,	POWER,		{ BO, BI } },
+
+{ "rlwimi",  M(20,0),	M_MASK,		PPC,		{ RA,RS,SH,MBE,ME } },
+{ "rlimi",   M(20,0),	M_MASK,		POWER,		{ RA,RS,SH,MBE,ME } },
+
+{ "rlwimi.", M(20,1),	M_MASK,		PPC,		{ RA,RS,SH,MBE,ME } },
+{ "rlimi.",  M(20,1),	M_MASK,		POWER,		{ RA,RS,SH,MBE,ME } },
+
+{ "rotlwi",  MME(21,31,0), MMBME_MASK,	PPC,		{ RA, RS, SH } },
+{ "clrlwi",  MME(21,31,0), MSHME_MASK,	PPC,		{ RA, RS, MB } },
+{ "rlwinm",  M(21,0),	M_MASK,		PPC,		{ RA,RS,SH,MBE,ME } },
+{ "rlinm",   M(21,0),	M_MASK,		POWER,		{ RA,RS,SH,MBE,ME } },
+{ "rotlwi.", MME(21,31,1), MMBME_MASK,	PPC,		{ RA,RS,SH } },
+{ "clrlwi.", MME(21,31,1), MSHME_MASK,	PPC,		{ RA, RS, MB } },
+{ "rlwinm.", M(21,1),	M_MASK,		PPC,		{ RA,RS,SH,MBE,ME } },
+{ "rlinm.",  M(21,1),	M_MASK,		POWER,		{ RA,RS,SH,MBE,ME } },
+
+{ "rlmi",    M(22,0),	M_MASK,		POWER|M601,	{ RA,RS,RB,MBE,ME } },
+{ "rlmi.",   M(22,1),	M_MASK,		POWER|M601,	{ RA,RS,RB,MBE,ME } },
+
+{ "rotlw",   MME(23,31,0), MMBME_MASK,	PPC,		{ RA, RS, RB } },
+{ "rlwnm",   M(23,0),	M_MASK,		PPC,		{ RA,RS,RB,MBE,ME } },
+{ "rlnm",    M(23,0),	M_MASK,		POWER,		{ RA,RS,RB,MBE,ME } },
+{ "rotlw.",  MME(23,31,1), MMBME_MASK,	PPC,		{ RA, RS, RB } },
+{ "rlwnm.",  M(23,1),	M_MASK,		PPC,		{ RA,RS,RB,MBE,ME } },
+{ "rlnm.",   M(23,1),	M_MASK,		POWER,		{ RA,RS,RB,MBE,ME } },
+
+{ "nop",     OP(24),	0xffffffff,	PPC,		{ 0 } },
+{ "ori",     OP(24),	OP_MASK,	PPC,		{ RA, RS, UI } },
+{ "oril",    OP(24),	OP_MASK,	POWER,		{ RA, RS, UI } },
+
+{ "oris",    OP(25),	OP_MASK,	PPC,		{ RA, RS, UI } },
+{ "oriu",    OP(25),	OP_MASK,	POWER,		{ RA, RS, UI } },
+
+{ "xori",    OP(26),	OP_MASK,	PPC,		{ RA, RS, UI } },
+{ "xoril",   OP(26),	OP_MASK,	POWER,		{ RA, RS, UI } },
+
+{ "xoris",   OP(27),	OP_MASK,	PPC,		{ RA, RS, UI } },
+{ "xoriu",   OP(27),	OP_MASK,	POWER,		{ RA, RS, UI } },
+
+{ "andi.",   OP(28),	OP_MASK,	PPC,		{ RA, RS, UI } },
+{ "andil.",  OP(28),	OP_MASK,	POWER,		{ RA, RS, UI } },
+
+{ "andis.",  OP(29),	OP_MASK,	PPC,		{ RA, RS, UI } },
+{ "andiu.",  OP(29),	OP_MASK,	POWER,		{ RA, RS, UI } },
+
+{ "rotldi",  MD(30,0,0), MDMB_MASK,	PPC|B64,	{ RA, RS, SH6 } },
+{ "clrldi",  MD(30,0,0), MDSH_MASK,	PPC|B64,	{ RA, RS, MB6 } },
+{ "rldicl",  MD(30,0,0), MD_MASK,	PPC|B64,	{ RA, RS, SH6, MB6 } },
+{ "rotldi.", MD(30,0,1), MDMB_MASK,	PPC|B64,	{ RA, RS, SH6 } },
+{ "clrldi.", MD(30,0,1), MDSH_MASK,	PPC|B64,	{ RA, RS, MB6 } },
+{ "rldicl.", MD(30,0,1), MD_MASK,	PPC|B64,	{ RA, RS, SH6, MB6 } },
+
+{ "rldicr",  MD(30,1,0), MD_MASK,	PPC|B64,	{ RA, RS, SH6, ME6 } },
+{ "rldicr.", MD(30,1,1), MD_MASK,	PPC|B64,	{ RA, RS, SH6, ME6 } },
+
+{ "rldic",   MD(30,2,0), MD_MASK,	PPC|B64,	{ RA, RS, SH6, MB6 } },
+{ "rldic.",  MD(30,2,1), MD_MASK,	PPC|B64,	{ RA, RS, SH6, MB6 } },
+
+{ "rldimi",  MD(30,3,0), MD_MASK,	PPC|B64,	{ RA, RS, SH6, MB6 } },
+{ "rldimi.", MD(30,3,1), MD_MASK,	PPC|B64,	{ RA, RS, SH6, MB6 } },
+
+{ "rotld",   MDS(30,8,0), MDSMB_MASK,	PPC|B64,	{ RA, RS, RB } },
+{ "rldcl",   MDS(30,8,0), MDS_MASK,	PPC|B64,	{ RA, RS, RB, MB6 } },
+{ "rotld.",  MDS(30,8,1), MDSMB_MASK,	PPC|B64,	{ RA, RS, RB } },
+{ "rldcl.",  MDS(30,8,1), MDS_MASK,	PPC|B64,	{ RA, RS, RB, MB6 } },
+
+{ "rldcr",   MDS(30,9,0), MDS_MASK,	PPC|B64,	{ RA, RS, RB, ME6 } },
+{ "rldcr.",  MDS(30,9,1), MDS_MASK,	PPC|B64,	{ RA, RS, RB, ME6 } },
+
+{ "cmpw",    XCMPL(31,0,0), XCMPL_MASK, PPC,		{ OBF, RA, RB } },
+{ "cmpd",    XCMPL(31,0,1), XCMPL_MASK, PPC|B64,	{ OBF, RA, RB } },
+{ "cmp",     X(31,0),	XCMP_MASK,	PPC,		{ BF, L, RA, RB } },
+{ "cmp",     X(31,0),	XCMPL_MASK,	POWER,		{ BF, RA, RB } },
+
+{ "twlgt",   XTO(31,4,TOLGT), XTO_MASK, PPC,		{ RA, RB } },
+{ "tlgt",    XTO(31,4,TOLGT), XTO_MASK, POWER,		{ RA, RB } },
+{ "twllt",   XTO(31,4,TOLLT), XTO_MASK, PPC,		{ RA, RB } },
+{ "tllt",    XTO(31,4,TOLLT), XTO_MASK, POWER,		{ RA, RB } },
+{ "tweq",    XTO(31,4,TOEQ), XTO_MASK,	PPC,		{ RA, RB } },
+{ "teq",     XTO(31,4,TOEQ), XTO_MASK,	POWER,		{ RA, RB } },
+{ "twlge",   XTO(31,4,TOLGE), XTO_MASK, PPC,		{ RA, RB } },
+{ "tlge",    XTO(31,4,TOLGE), XTO_MASK, POWER,		{ RA, RB } },
+{ "twlnl",   XTO(31,4,TOLNL), XTO_MASK, PPC,		{ RA, RB } },
+{ "tlnl",    XTO(31,4,TOLNL), XTO_MASK, POWER,		{ RA, RB } },
+{ "twlle",   XTO(31,4,TOLLE), XTO_MASK, PPC,		{ RA, RB } },
+{ "tlle",    XTO(31,4,TOLLE), XTO_MASK, POWER,		{ RA, RB } },
+{ "twlng",   XTO(31,4,TOLNG), XTO_MASK, PPC,		{ RA, RB } },
+{ "tlng",    XTO(31,4,TOLNG), XTO_MASK, POWER,		{ RA, RB } },
+{ "twgt",    XTO(31,4,TOGT), XTO_MASK,	PPC,		{ RA, RB } },
+{ "tgt",     XTO(31,4,TOGT), XTO_MASK,	POWER,		{ RA, RB } },
+{ "twge",    XTO(31,4,TOGE), XTO_MASK,	PPC,		{ RA, RB } },
+{ "tge",     XTO(31,4,TOGE), XTO_MASK,	POWER,		{ RA, RB } },
+{ "twnl",    XTO(31,4,TONL), XTO_MASK,	PPC,		{ RA, RB } },
+{ "tnl",     XTO(31,4,TONL), XTO_MASK,	POWER,		{ RA, RB } },
+{ "twlt",    XTO(31,4,TOLT), XTO_MASK,	PPC,		{ RA, RB } },
+{ "tlt",     XTO(31,4,TOLT), XTO_MASK,	POWER,		{ RA, RB } },
+{ "twle",    XTO(31,4,TOLE), XTO_MASK,	PPC,		{ RA, RB } },
+{ "tle",     XTO(31,4,TOLE), XTO_MASK,	POWER,		{ RA, RB } },
+{ "twng",    XTO(31,4,TONG), XTO_MASK,	PPC,		{ RA, RB } },
+{ "tng",     XTO(31,4,TONG), XTO_MASK,	POWER,		{ RA, RB } },
+{ "twne",    XTO(31,4,TONE), XTO_MASK,	PPC,		{ RA, RB } },
+{ "tne",     XTO(31,4,TONE), XTO_MASK,	POWER,		{ RA, RB } },
+{ "trap",    XTO(31,4,TOU), 0xffffffff,	PPC,		{ 0 } },
+{ "tw",      X(31,4),	X_MASK,		PPC,		{ TO, RA, RB } },
+{ "t",       X(31,4),	X_MASK,		POWER,		{ TO, RA, RB } },
+
+{ "subfc",   XO(31,8,0,0), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "sf",      XO(31,8,0,0), XO_MASK,	POWER,		{ RT, RA, RB } },
+{ "subc",    XO(31,8,0,0), XO_MASK,	PPC,		{ RT, RB, RA } },
+{ "subfc.",  XO(31,8,0,1), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "sf.",     XO(31,8,0,1), XO_MASK,	POWER,		{ RT, RA, RB } },
+{ "subc.",   XO(31,8,0,1), XO_MASK,	PPC,		{ RT, RB, RA } },
+{ "subfco",  XO(31,8,1,0), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "sfo",     XO(31,8,1,0), XO_MASK,	POWER,		{ RT, RA, RB } },
+{ "subco",   XO(31,8,1,0), XO_MASK,	PPC,		{ RT, RB, RA } },
+{ "subfco.", XO(31,8,1,1), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "sfo.",    XO(31,8,1,1), XO_MASK,	POWER,		{ RT, RA, RB } },
+{ "subco.",  XO(31,8,1,1), XO_MASK,	PPC,		{ RT, RB, RA } },
+
+{ "mulhdu",  XO(31,9,0,0), XO_MASK,	PPC|B64,	{ RT, RA, RB } },
+{ "mulhdu.", XO(31,9,0,1), XO_MASK,	PPC|B64,	{ RT, RA, RB } },
+
+{ "addc",    XO(31,10,0,0), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "a",       XO(31,10,0,0), XO_MASK,	POWER,		{ RT, RA, RB } },
+{ "addc.",   XO(31,10,0,1), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "a.",      XO(31,10,0,1), XO_MASK,	POWER,		{ RT, RA, RB } },
+{ "addco",   XO(31,10,1,0), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "ao",      XO(31,10,1,0), XO_MASK,	POWER,		{ RT, RA, RB } },
+{ "addco.",  XO(31,10,1,1), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "ao.",     XO(31,10,1,1), XO_MASK,	POWER,		{ RT, RA, RB } },
+
+{ "mulhwu",  XO(31,11,0,0), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "mulhwu.", XO(31,11,0,1), XO_MASK,	PPC,		{ RT, RA, RB } },
+
+{ "mfcr",    X(31,19),	XRARB_MASK,	POWER|PPC,	{ RT } },
+
+{ "lwarx",   X(31,20),	X_MASK,		PPC,		{ RT, RA, RB } },
+
+{ "ldx",     X(31,21),	X_MASK,		PPC|B64,	{ RT, RA, RB } },
+
+{ "lwzx",    X(31,23),	X_MASK,		PPC,		{ RT, RA, RB } },
+{ "lx",      X(31,23),	X_MASK,		POWER,		{ RT, RA, RB } },
+
+{ "slw",     XRC(31,24,0), X_MASK,	PPC,		{ RA, RS, RB } },
+{ "sl",      XRC(31,24,0), X_MASK,	POWER,		{ RA, RS, RB } },
+{ "slw.",    XRC(31,24,1), X_MASK,	PPC,		{ RA, RS, RB } },
+{ "sl.",     XRC(31,24,1), X_MASK,	POWER,		{ RA, RS, RB } },
+
+{ "cntlzw",  XRC(31,26,0), XRB_MASK,	PPC,		{ RA, RS } },
+{ "cntlz",   XRC(31,26,0), XRB_MASK,	POWER,		{ RA, RS } },
+{ "cntlzw.", XRC(31,26,1), XRB_MASK,	PPC,		{ RA, RS } },
+{ "cntlz.",  XRC(31,26,1), XRB_MASK, 	POWER,		{ RA, RS } },
+
+{ "sld",     XRC(31,27,0), X_MASK,	PPC|B64,	{ RA, RS, RB } },
+{ "sld.",    XRC(31,27,1), X_MASK,	PPC|B64,	{ RA, RS, RB } },
+
+{ "and",     XRC(31,28,0), X_MASK,	PPC|POWER,	{ RA, RS, RB } },
+{ "and.",    XRC(31,28,1), X_MASK,	PPC|POWER,	{ RA, RS, RB } },
+
+{ "maskg",   XRC(31,29,0), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+{ "maskg.",  XRC(31,29,1), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+
+{ "cmplw",   XCMPL(31,32,0), XCMPL_MASK, PPC,		{ OBF, RA, RB } },
+{ "cmpld",   XCMPL(31,32,1), XCMPL_MASK, PPC|B64,	{ OBF, RA, RB } },
+{ "cmpl",    X(31,32),	XCMP_MASK,	PPC,		{ BF, L, RA, RB } },
+{ "cmpl",    X(31,32),	XCMPL_MASK,	POWER,		{ BF, RA, RB } },
+
+{ "subf",    XO(31,40,0,0), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "sub",     XO(31,40,0,0), XO_MASK,	PPC,		{ RT, RB, RA } },
+{ "subf.",   XO(31,40,0,1), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "sub.",    XO(31,40,0,1), XO_MASK,	PPC,		{ RT, RB, RA } },
+{ "subfo",   XO(31,40,1,0), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "subo",    XO(31,40,1,0), XO_MASK,	PPC,		{ RT, RB, RA } },
+{ "subfo.",  XO(31,40,1,1), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "subo.",   XO(31,40,1,1), XO_MASK,	PPC,		{ RT, RB, RA } },
+
+{ "ldux",    X(31,53),	X_MASK,		PPC|B64,	{ RT, RAL, RB } },
+
+{ "dcbst",   X(31,54),	XRT_MASK,	PPC,		{ RA, RB } },
+
+{ "lwzux",   X(31,55),	X_MASK,		PPC,		{ RT, RAL, RB } },
+{ "lux",     X(31,55),	X_MASK,		POWER,		{ RT, RA, RB } },
+
+{ "cntlzd",  XRC(31,58,0), XRB_MASK,	PPC|B64,	{ RA, RS } },
+{ "cntlzd.", XRC(31,58,1), XRB_MASK,	PPC|B64,	{ RA, RS } },
+
+{ "andc",    XRC(31,60,0), X_MASK,	PPC|POWER,	{ RA, RS, RB } },
+{ "andc.",   XRC(31,60,1), X_MASK,	PPC|POWER,	{ RA, RS, RB } },
+
+{ "tdlgt",   XTO(31,68,TOLGT), XTO_MASK, PPC|B64,	{ RA, RB } },
+{ "tdllt",   XTO(31,68,TOLLT), XTO_MASK, PPC|B64,	{ RA, RB } },
+{ "tdeq",    XTO(31,68,TOEQ), XTO_MASK, PPC|B64,	{ RA, RB } },
+{ "tdlge",   XTO(31,68,TOLGE), XTO_MASK, PPC|B64,	{ RA, RB } },
+{ "tdlnl",   XTO(31,68,TOLNL), XTO_MASK, PPC|B64,	{ RA, RB } },
+{ "tdlle",   XTO(31,68,TOLLE), XTO_MASK, PPC|B64,	{ RA, RB } },
+{ "tdlng",   XTO(31,68,TOLNG), XTO_MASK, PPC|B64,	{ RA, RB } },
+{ "tdgt",    XTO(31,68,TOGT), XTO_MASK, PPC|B64,	{ RA, RB } },
+{ "tdge",    XTO(31,68,TOGE), XTO_MASK, PPC|B64,	{ RA, RB } },
+{ "tdnl",    XTO(31,68,TONL), XTO_MASK, PPC|B64,	{ RA, RB } },
+{ "tdlt",    XTO(31,68,TOLT), XTO_MASK, PPC|B64,	{ RA, RB } },
+{ "tdle",    XTO(31,68,TOLE), XTO_MASK, PPC|B64,	{ RA, RB } },
+{ "tdng",    XTO(31,68,TONG), XTO_MASK, PPC|B64,	{ RA, RB } },
+{ "tdne",    XTO(31,68,TONE), XTO_MASK, PPC|B64,	{ RA, RB } },
+{ "td",	     X(31,68),	X_MASK,		PPC|B64,	{ TO, RA, RB } },
+
+{ "mulhd",   XO(31,73,0,0), XO_MASK,	PPC|B64,	{ RT, RA, RB } },
+{ "mulhd.",  XO(31,73,0,1), XO_MASK,	PPC|B64,	{ RT, RA, RB } },
+
+{ "mulhw",   XO(31,75,0,0), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "mulhw.",  XO(31,75,0,1), XO_MASK,	PPC,		{ RT, RA, RB } },
+
+{ "mfmsr",   X(31,83),	XRARB_MASK,	PPC|POWER,	{ RT } },
+
+{ "ldarx",   X(31,84),	X_MASK,		PPC|B64,	{ RT, RA, RB } },
+
+{ "dcbf",    X(31,86),	XRT_MASK,	PPC,		{ RA, RB } },
+
+{ "lbzx",    X(31,87),	X_MASK,		PPC|POWER,	{ RT, RA, RB } },
+
+{ "neg",     XO(31,104,0,0), XORB_MASK,	PPC|POWER,	{ RT, RA } },
+{ "neg.",    XO(31,104,0,1), XORB_MASK,	PPC|POWER,	{ RT, RA } },
+{ "nego",    XO(31,104,1,0), XORB_MASK,	PPC|POWER,	{ RT, RA } },
+{ "nego.",   XO(31,104,1,1), XORB_MASK,	PPC|POWER,	{ RT, RA } },
+
+{ "mul",     XO(31,107,0,0), XO_MASK,	POWER|M601,	{ RT, RA, RB } },
+{ "mul.",    XO(31,107,0,1), XO_MASK,	POWER|M601,	{ RT, RA, RB } },
+{ "mulo",    XO(31,107,1,0), XO_MASK,	POWER|M601,	{ RT, RA, RB } },
+{ "mulo.",   XO(31,107,1,1), XO_MASK,	POWER|M601,	{ RT, RA, RB } },
+
+{ "clf",     X(31,118), XRB_MASK,	POWER,		{ RT, RA } },
+
+{ "lbzux",   X(31,119),	X_MASK,		PPC|POWER,	{ RT, RAL, RB } },
+
+{ "not",     XRC(31,124,0), X_MASK,	PPC|POWER,	{ RA, RS, RBS } },
+{ "nor",     XRC(31,124,0), X_MASK,	PPC|POWER,	{ RA, RS, RB } },
+{ "not.",    XRC(31,124,1), X_MASK,	PPC|POWER,	{ RA, RS, RBS } },
+{ "nor.",    XRC(31,124,1), X_MASK,	PPC|POWER,	{ RA, RS, RB } },
+
+{ "subfe",   XO(31,136,0,0), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "sfe",     XO(31,136,0,0), XO_MASK,	POWER,		{ RT, RA, RB } },
+{ "subfe.",  XO(31,136,0,1), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "sfe.",    XO(31,136,0,1), XO_MASK,	POWER,		{ RT, RA, RB } },
+{ "subfeo",  XO(31,136,1,0), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "sfeo",    XO(31,136,1,0), XO_MASK,	POWER,		{ RT, RA, RB } },
+{ "subfeo.", XO(31,136,1,1), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "sfeo.",   XO(31,136,1,1), XO_MASK,	POWER,		{ RT, RA, RB } },
+
+{ "adde",    XO(31,138,0,0), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "ae",      XO(31,138,0,0), XO_MASK,	POWER,		{ RT, RA, RB } },
+{ "adde.",   XO(31,138,0,1), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "ae.",     XO(31,138,0,1), XO_MASK,	POWER,		{ RT, RA, RB } },
+{ "addeo",   XO(31,138,1,0), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "aeo",     XO(31,138,1,0), XO_MASK,	POWER,		{ RT, RA, RB } },
+{ "addeo.",  XO(31,138,1,1), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "aeo.",    XO(31,138,1,1), XO_MASK,	POWER,		{ RT, RA, RB } },
+
+{ "mtcr",    XFXM(31,144,0xff), XFXFXM_MASK|FXM_MASK, PPC|POWER, { RS }},
+{ "mtcrf",   X(31,144),	XFXFXM_MASK,	PPC|POWER,	{ FXM, RS } },
+
+{ "mtmsr",   X(31,146),	XRARB_MASK,	PPC|POWER,	{ RS } },
+
+{ "stdx",    X(31,149), X_MASK,		PPC|B64,	{ RS, RA, RB } },
+
+{ "stwcx.",  XRC(31,150,1), X_MASK,	PPC,		{ RS, RA, RB } },
+
+{ "stwx",    X(31,151), X_MASK,		PPC,		{ RS, RA, RB } },
+{ "stx",     X(31,151), X_MASK,		POWER,		{ RS, RA, RB } },
+
+{ "slq",     XRC(31,152,0), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+{ "slq.",    XRC(31,152,1), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+
+{ "sle",     XRC(31,153,0), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+{ "sle.",    XRC(31,153,1), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+
+{ "stdux",   X(31,181),	X_MASK,		PPC|B64,	{ RS, RAS, RB } },
+
+{ "stwux",   X(31,183),	X_MASK,		PPC,		{ RS, RAS, RB } },
+{ "stux",    X(31,183),	X_MASK,		POWER,		{ RS, RA, RB } },
+
+{ "sliq",    XRC(31,184,0), X_MASK,	POWER|M601,	{ RA, RS, SH } },
+{ "sliq.",   XRC(31,184,1), X_MASK,	POWER|M601,	{ RA, RS, SH } },
+
+{ "subfze",  XO(31,200,0,0), XORB_MASK, PPC,		{ RT, RA } },
+{ "sfze",    XO(31,200,0,0), XORB_MASK, POWER,		{ RT, RA } },
+{ "subfze.", XO(31,200,0,1), XORB_MASK, PPC,		{ RT, RA } },
+{ "sfze.",   XO(31,200,0,1), XORB_MASK, POWER,		{ RT, RA } },
+{ "subfzeo", XO(31,200,1,0), XORB_MASK, PPC,		{ RT, RA } },
+{ "sfzeo",   XO(31,200,1,0), XORB_MASK, POWER,		{ RT, RA } },
+{ "subfzeo.",XO(31,200,1,1), XORB_MASK, PPC,		{ RT, RA } },
+{ "sfzeo.",  XO(31,200,1,1), XORB_MASK, POWER,		{ RT, RA } },
+
+{ "addze",   XO(31,202,0,0), XORB_MASK, PPC,		{ RT, RA } },
+{ "aze",     XO(31,202,0,0), XORB_MASK, POWER,		{ RT, RA } },
+{ "addze.",  XO(31,202,0,1), XORB_MASK, PPC,		{ RT, RA } },
+{ "aze.",    XO(31,202,0,1), XORB_MASK, POWER,		{ RT, RA } },
+{ "addzeo",  XO(31,202,1,0), XORB_MASK, PPC,		{ RT, RA } },
+{ "azeo",    XO(31,202,1,0), XORB_MASK, POWER,		{ RT, RA } },
+{ "addzeo.", XO(31,202,1,1), XORB_MASK, PPC,		{ RT, RA } },
+{ "azeo.",   XO(31,202,1,1), XORB_MASK, POWER,		{ RT, RA } },
+
+{ "mtsr",    X(31,210),	XRB_MASK|(1<<20), PPC|POWER|B32, { SR, RS } },
+
+{ "stdcx.",  XRC(31,214,1), X_MASK,	PPC|B64,	{ RS, RA, RB } },
+
+{ "stbx",    X(31,215),	X_MASK,		PPC|POWER,	{ RS, RA, RB } },
+
+{ "sllq",    XRC(31,216,0), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+{ "sllq.",   XRC(31,216,1), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+
+{ "sleq",    XRC(31,217,0), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+{ "sleq.",   XRC(31,217,1), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+
+{ "subfme",  XO(31,232,0,0), XORB_MASK, PPC,		{ RT, RA } },
+{ "sfme",    XO(31,232,0,0), XORB_MASK, POWER,		{ RT, RA } },
+{ "subfme.", XO(31,232,0,1), XORB_MASK, PPC,		{ RT, RA } },
+{ "sfme.",   XO(31,232,0,1), XORB_MASK, POWER,		{ RT, RA } },
+{ "subfmeo", XO(31,232,1,0), XORB_MASK, PPC,		{ RT, RA } },
+{ "sfmeo",   XO(31,232,1,0), XORB_MASK, POWER,		{ RT, RA } },
+{ "subfmeo.",XO(31,232,1,1), XORB_MASK, PPC,		{ RT, RA } },
+{ "sfmeo.",  XO(31,232,1,1), XORB_MASK, POWER,		{ RT, RA } },
+
+{ "mulld",   XO(31,233,0,0), XO_MASK,	PPC|B64,	{ RT, RA, RB } },
+{ "mulld.",  XO(31,233,0,1), XO_MASK,	PPC|B64,	{ RT, RA, RB } },
+{ "mulldo",  XO(31,233,1,0), XO_MASK,	PPC|B64,	{ RT, RA, RB } },
+{ "mulldo.", XO(31,233,1,1), XO_MASK,	PPC|B64,	{ RT, RA, RB } },
+
+{ "addme",   XO(31,234,0,0), XORB_MASK, PPC,		{ RT, RA } },
+{ "ame",     XO(31,234,0,0), XORB_MASK, POWER,		{ RT, RA } },
+{ "addme.",  XO(31,234,0,1), XORB_MASK, PPC,		{ RT, RA } },
+{ "ame.",    XO(31,234,0,1), XORB_MASK, POWER,		{ RT, RA } },
+{ "addmeo",  XO(31,234,1,0), XORB_MASK, PPC,		{ RT, RA } },
+{ "ameo",    XO(31,234,1,0), XORB_MASK, POWER,		{ RT, RA } },
+{ "addmeo.", XO(31,234,1,1), XORB_MASK, PPC,		{ RT, RA } },
+{ "ameo.",   XO(31,234,1,1), XORB_MASK, POWER,		{ RT, RA } },
+
+{ "mullw",   XO(31,235,0,0), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "muls",    XO(31,235,0,0), XO_MASK,	POWER,		{ RT, RA, RB } },
+{ "mullw.",  XO(31,235,0,1), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "muls.",   XO(31,235,0,1), XO_MASK,	POWER,		{ RT, RA, RB } },
+{ "mullwo",  XO(31,235,1,0), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "mulso",   XO(31,235,1,0), XO_MASK,	POWER,		{ RT, RA, RB } },
+{ "mullwo.", XO(31,235,1,1), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "mulso.",  XO(31,235,1,1), XO_MASK,	POWER,		{ RT, RA, RB } },
+
+{ "mtsrin",  X(31,242),	XRA_MASK,	PPC|B32,	{ RS, RB } },
+{ "mtsri",   X(31,242),	XRA_MASK,	POWER|B32,	{ RS, RB } },
+
+{ "dcbtst",  X(31,246),	XRT_MASK,	PPC,		{ RA, RB } },
+
+{ "stbux",   X(31,247),	X_MASK,		PPC|POWER,	{ RS, RAS, RB } },
+
+{ "slliq",   XRC(31,248,0), X_MASK,	POWER|M601,	{ RA, RS, SH } },
+{ "slliq.",  XRC(31,248,1), X_MASK,	POWER|M601,	{ RA, RS, SH } },
+
+{ "doz",     XO(31,264,0,0), XO_MASK,	POWER|M601,	{ RT, RA, RB } },
+{ "doz.",    XO(31,264,0,1), XO_MASK,	POWER|M601,	{ RT, RA, RB } },
+{ "dozo",    XO(31,264,1,0), XO_MASK,	POWER|M601,	{ RT, RA, RB } },
+{ "dozo.",   XO(31,264,1,1), XO_MASK,	POWER|M601,	{ RT, RA, RB } },
+
+{ "add",     XO(31,266,0,0), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "cax",     XO(31,266,0,0), XO_MASK,	POWER,		{ RT, RA, RB } },
+{ "add.",    XO(31,266,0,1), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "cax.",    XO(31,266,0,1), XO_MASK,	POWER,		{ RT, RA, RB } },
+{ "addo",    XO(31,266,1,0), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "caxo",    XO(31,266,1,0), XO_MASK,	POWER,		{ RT, RA, RB } },
+{ "addo.",   XO(31,266,1,1), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "caxo.",   XO(31,266,1,1), XO_MASK,	POWER,		{ RT, RA, RB } },
+
+{ "lscbx",   XRC(31,277,0), X_MASK,	POWER|M601,	{ RT, RA, RB } },
+{ "lscbx.",  XRC(31,277,1), X_MASK,	POWER|M601,	{ RT, RA, RB } },
+
+{ "dcbt",    X(31,278),	XRT_MASK,	PPC,		{ RA, RB } },
+
+{ "lhzx",    X(31,279),	X_MASK,		PPC|POWER,	{ RT, RA, RB } },
+
+{ "icbt",    X(31,262),	XRT_MASK,	PPC,		{ RA, RB } },
+
+{ "eqv",     XRC(31,284,0), X_MASK,	PPC|POWER,	{ RA, RS, RB } },
+{ "eqv.",    XRC(31,284,1), X_MASK,	PPC|POWER,	{ RA, RS, RB } },
+
+{ "tlbie",   X(31,306),	XRTRA_MASK,	PPC,		{ RB } },
+{ "tlbi",    X(31,306),	XRTRA_MASK,	POWER,		{ RB } },
+
+{ "eciwx",   X(31,310), X_MASK,		PPC,		{ RT, RA, RB } },
+
+{ "lhzux",   X(31,311),	X_MASK,		PPC|POWER,	{ RT, RAL, RB } },
+
+{ "xor",     XRC(31,316,0), X_MASK,	PPC|POWER,	{ RA, RS, RB } },
+{ "xor.",    XRC(31,316,1), X_MASK,	PPC|POWER,	{ RA, RS, RB } },
+
+{ "mfdcr",   X(31,323),	X_MASK,		PPC,		{ RT, SPR } },
+
+{ "div",     XO(31,331,0,0), XO_MASK,	POWER|M601,	{ RT, RA, RB } },
+{ "div.",    XO(31,331,0,1), XO_MASK,	POWER|M601,	{ RT, RA, RB } },
+{ "divo",    XO(31,331,1,0), XO_MASK,	POWER|M601,	{ RT, RA, RB } },
+{ "divo.",   XO(31,331,1,1), XO_MASK,	POWER|M601,	{ RT, RA, RB } },
+
+{ "mfmq",    XSPR(31,339,0), XSPR_MASK,	POWER|M601,	{ RT } },
+{ "mfxer",   XSPR(31,339,1), XSPR_MASK,	PPC|POWER,	{ RT } },
+{ "mfrtcu",  XSPR(31,339,4), XSPR_MASK, PPC|POWER,	{ RT } },
+{ "mfrtcl",  XSPR(31,339,5), XSPR_MASK, PPC|POWER,	{ RT } },
+{ "mfdec",   XSPR(31,339,6), XSPR_MASK, POWER|M601,	{ RT } },
+{ "mflr",    XSPR(31,339,8), XSPR_MASK,	PPC|POWER,	{ RT } },
+{ "mfctr",   XSPR(31,339,9), XSPR_MASK,	PPC|POWER,	{ RT } },
+{ "mftid",   XSPR(31,339,17), XSPR_MASK, POWER,		{ RT } },
+{ "mfdsisr", XSPR(31,339,18), XSPR_MASK, PPC|POWER,	{ RT } },
+{ "mfdar",   XSPR(31,339,19), XSPR_MASK, PPC|POWER,	{ RT } },
+{ "mfdec",   XSPR(31,339,22), XSPR_MASK, PPC,		{ RT } },
+{ "mfsdr0",  XSPR(31,339,24), XSPR_MASK, POWER,		{ RT } },
+{ "mfsdr1",  XSPR(31,339,25), XSPR_MASK, PPC|POWER,	{ RT } },
+{ "mfsrr0",  XSPR(31,339,26), XSPR_MASK, PPC|POWER,	{ RT } },
+{ "mfsrr1",  XSPR(31,339,27), XSPR_MASK, PPC|POWER,	{ RT } },
+{ "mfsprg",  XSPR(31,339,272), XSPRG_MASK, PPC,		{ RT, SPRG } },
+{ "mfasr",   XSPR(31,339,280), XSPR_MASK, PPC|B64,	{ RT } },
+{ "mfear",   XSPR(31,339,282), XSPR_MASK, PPC,		{ RT } },
+{ "mfpvr",   XSPR(31,339,287), XSPR_MASK, PPC,		{ RT } },
+{ "mfibatu", XSPR(31,339,528), XSPRBAT_MASK, PPC,	{ RT, SPRBAT } },
+{ "mfibatl", XSPR(31,339,529), XSPRBAT_MASK, PPC,	{ RT, SPRBAT } },
+{ "mfdbatu", XSPR(31,339,536), XSPRBAT_MASK, PPC,	{ RT, SPRBAT } },
+{ "mfdbatl", XSPR(31,339,537), XSPRBAT_MASK, PPC,	{ RT, SPRBAT } },
+{ "mfspr",   X(31,339),	X_MASK,		PPC|POWER,	{ RT, SPR } },
+
+{ "lwax",    X(31,341),	X_MASK,		PPC|B64,	{ RT, RA, RB } },
+
+{ "lhax",    X(31,343),	X_MASK,		PPC|POWER,	{ RT, RA, RB } },
+
+{ "dccci",   X(31,454),	XRT_MASK,	PPC,		{ RA, RB } },
+
+{ "abs",     XO(31,360,0,0), XORB_MASK, POWER|M601,	{ RT, RA } },
+{ "abs.",    XO(31,360,0,1), XORB_MASK, POWER|M601,	{ RT, RA } },
+{ "abso",    XO(31,360,1,0), XORB_MASK, POWER|M601,	{ RT, RA } },
+{ "abso.",   XO(31,360,1,1), XORB_MASK, POWER|M601,	{ RT, RA } },
+
+{ "divs",    XO(31,363,0,0), XO_MASK,	POWER|M601,	{ RT, RA, RB } },
+{ "divs.",   XO(31,363,0,1), XO_MASK,	POWER|M601,	{ RT, RA, RB } },
+{ "divso",   XO(31,363,1,0), XO_MASK,	POWER|M601,	{ RT, RA, RB } },
+{ "divso.",  XO(31,363,1,1), XO_MASK,	POWER|M601,	{ RT, RA, RB } },
+
+{ "tlbia",   X(31,370),	0xffffffff,	PPC,		{ 0 } },
+
+{ "mftbu",   XSPR(31,371,269), XSPR_MASK, PPC,		{ RT } },
+{ "mftb",    X(31,371),	X_MASK,		PPC,		{ RT, TBR } },
+
+{ "lwaux",   X(31,373),	X_MASK,		PPC|B64,	{ RT, RAL, RB } },
+
+{ "lhaux",   X(31,375),	X_MASK,		PPC|POWER,	{ RT, RAL, RB } },
+
+{ "sthx",    X(31,407),	X_MASK,		PPC|POWER,	{ RS, RA, RB } },
+
+{ "lfqx",    X(31,791),	X_MASK,		POWER2,		{ FRT, RA, RB } },
+
+{ "lfqux",   X(31,823),	X_MASK,		POWER2,		{ FRT, RA, RB } },
+
+{ "stfqx",   X(31,919),	X_MASK,		POWER2,		{ FRS, RA, RB } },
+
+{ "stfqux",  X(31,951),	X_MASK,		POWER2,		{ FRS, RA, RB } },
+
+{ "orc",     XRC(31,412,0), X_MASK,	PPC|POWER,	{ RA, RS, RB } },
+{ "orc.",    XRC(31,412,1), X_MASK,	PPC|POWER,	{ RA, RS, RB } },
+
+{ "sradi",   XS(31,413,0), XS_MASK,	PPC|B64,	{ RA, RS, SH6 } },
+{ "sradi.",  XS(31,413,1), XS_MASK,	PPC|B64,	{ RA, RS, SH6 } },
+
+{ "slbie",   X(31,434),	XRTRA_MASK,	PPC|B64,	{ RB } },
+
+{ "ecowx",   X(31,438),	X_MASK,		PPC,		{ RT, RA, RB } },
+
+{ "sthux",   X(31,439),	X_MASK,		PPC|POWER,	{ RS, RAS, RB } },
+
+{ "mr",	     XRC(31,444,0), X_MASK,	PPC|POWER,	{ RA, RS, RBS } },
+{ "or",      XRC(31,444,0), X_MASK,	PPC|POWER,	{ RA, RS, RB } },
+{ "mr.",     XRC(31,444,1), X_MASK,	PPC|POWER,	{ RA, RS, RBS } },
+{ "or.",     XRC(31,444,1), X_MASK,	PPC|POWER,	{ RA, RS, RB } },
+
+{ "mtdcr",   X(31,451),	X_MASK,		PPC,		{ SPR, RS } },
+
+{ "divdu",   XO(31,457,0,0), XO_MASK,	PPC|B64,	{ RT, RA, RB } },
+{ "divdu.",  XO(31,457,0,1), XO_MASK,	PPC|B64,	{ RT, RA, RB } },
+{ "divduo",  XO(31,457,1,0), XO_MASK,	PPC|B64,	{ RT, RA, RB } },
+{ "divduo.", XO(31,457,1,1), XO_MASK,	PPC|B64,	{ RT, RA, RB } },
+
+{ "divwu",   XO(31,459,0,0), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "divwu.",  XO(31,459,0,1), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "divwuo",  XO(31,459,1,0), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "divwuo.", XO(31,459,1,1), XO_MASK,	PPC,		{ RT, RA, RB } },
+
+{ "mtmq",    XSPR(31,467,0), XSPR_MASK,	POWER|M601,	{ RS } },
+{ "mtxer",   XSPR(31,467,1), XSPR_MASK,	PPC|POWER,	{ RS } },
+{ "mtlr",    XSPR(31,467,8), XSPR_MASK,	PPC|POWER,	{ RS } },
+{ "mtctr",   XSPR(31,467,9), XSPR_MASK,	PPC|POWER,	{ RS } },
+{ "mttid",   XSPR(31,467,17), XSPR_MASK, POWER,		{ RS } },
+{ "mtdsisr", XSPR(31,467,18), XSPR_MASK, PPC|POWER,	{ RS } },
+{ "mtdar",   XSPR(31,467,19), XSPR_MASK, PPC|POWER,	{ RS } },
+{ "mtrtcu",  XSPR(31,467,20), XSPR_MASK, PPC|POWER,	{ RS } },
+{ "mtrtcl",  XSPR(31,467,21), XSPR_MASK, PPC|POWER,	{ RS } },
+{ "mtdec",   XSPR(31,467,22), XSPR_MASK, PPC|POWER,	{ RS } },
+{ "mtsdr0",  XSPR(31,467,24), XSPR_MASK, POWER,		{ RS } },
+{ "mtsdr1",  XSPR(31,467,25), XSPR_MASK, PPC|POWER,	{ RS } },
+{ "mtsrr0",  XSPR(31,467,26), XSPR_MASK, PPC|POWER,	{ RS } },
+{ "mtsrr1",  XSPR(31,467,27), XSPR_MASK, PPC|POWER,	{ RS } },
+{ "mtsprg",  XSPR(31,467,272), XSPRG_MASK, PPC,		{ SPRG, RS } },
+{ "mtasr",   XSPR(31,467,280), XSPR_MASK, PPC|B64,	{ RS } },
+{ "mtear",   XSPR(31,467,282), XSPR_MASK, PPC,		{ RS } },
+{ "mttbl",   XSPR(31,467,284), XSPR_MASK, PPC,		{ RS } },
+{ "mttbu",   XSPR(31,467,285), XSPR_MASK, PPC,		{ RS } },
+{ "mtibatu", XSPR(31,467,528), XSPRBAT_MASK, PPC,	{ SPRBAT, RS } },
+{ "mtibatl", XSPR(31,467,529), XSPRBAT_MASK, PPC,	{ SPRBAT, RS } },
+{ "mtdbatu", XSPR(31,467,536), XSPRBAT_MASK, PPC,	{ SPRBAT, RS } },
+{ "mtdbatl", XSPR(31,467,537), XSPRBAT_MASK, PPC,	{ SPRBAT, RS } },
+{ "mtspr",   X(31,467),	X_MASK,		PPC|POWER,	{ SPR, RS } },
+
+{ "dcbi",    X(31,470),	XRT_MASK,	PPC,		{ RA, RB } },
+
+{ "nand",    XRC(31,476,0), X_MASK,	PPC|POWER,	{ RA, RS, RB } },
+{ "nand.",   XRC(31,476,1), X_MASK,	PPC|POWER,	{ RA, RS, RB } },
+
+{ "nabs",    XO(31,488,0,0), XORB_MASK, POWER|M601,	{ RT, RA } },
+{ "nabs.",   XO(31,488,0,1), XORB_MASK, POWER|M601,	{ RT, RA } },
+{ "nabso",   XO(31,488,1,0), XORB_MASK, POWER|M601,	{ RT, RA } },
+{ "nabso.",  XO(31,488,1,1), XORB_MASK, POWER|M601,	{ RT, RA } },
+
+{ "divd",    XO(31,489,0,0), XO_MASK,	PPC|B64,	{ RT, RA, RB } },
+{ "divd.",   XO(31,489,0,1), XO_MASK,	PPC|B64,	{ RT, RA, RB } },
+{ "divdo",   XO(31,489,1,0), XO_MASK,	PPC|B64,	{ RT, RA, RB } },
+{ "divdo.",  XO(31,489,1,1), XO_MASK,	PPC|B64,	{ RT, RA, RB } },
+
+{ "divw",    XO(31,491,0,0), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "divw.",   XO(31,491,0,1), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "divwo",   XO(31,491,1,0), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "divwo.",  XO(31,491,1,1), XO_MASK,	PPC,		{ RT, RA, RB } },
+
+{ "slbia",   X(31,498),	0xffffffff,	PPC|B64,	{ 0 } },
+
+{ "cli",     X(31,502), XRB_MASK,	POWER,		{ RT, RA } },
+
+{ "mcrxr",   X(31,512),	XRARB_MASK|(3<<21), PPC|POWER,	{ BF } },
+
+{ "clcs",    X(31,531), XRB_MASK,	POWER|M601,	{ RT, RA } },
+
+{ "lswx",    X(31,533),	X_MASK,		PPC,		{ RT, RA, RB } },
+{ "lsx",     X(31,533),	X_MASK,		POWER,		{ RT, RA, RB } },
+
+{ "lwbrx",   X(31,534),	X_MASK,		PPC,		{ RT, RA, RB } },
+{ "lbrx",    X(31,534),	X_MASK,		POWER,		{ RT, RA, RB } },
+
+{ "lfsx",    X(31,535),	X_MASK,		PPC|POWER,	{ FRT, RA, RB } },
+
+{ "srw",     XRC(31,536,0), X_MASK,	PPC,		{ RA, RS, RB } },
+{ "sr",      XRC(31,536,0), X_MASK,	POWER,		{ RA, RS, RB } },
+{ "srw.",    XRC(31,536,1), X_MASK,	PPC,		{ RA, RS, RB } },
+{ "sr.",     XRC(31,536,1), X_MASK,	POWER,		{ RA, RS, RB } },
+
+{ "rrib",    XRC(31,537,0), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+{ "rrib.",   XRC(31,537,1), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+
+{ "srd",     XRC(31,539,0), X_MASK,	PPC|B64,	{ RA, RS, RB } },
+{ "srd.",    XRC(31,539,1), X_MASK,	PPC|B64,	{ RA, RS, RB } },
+
+{ "maskir",  XRC(31,541,0), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+{ "maskir.", XRC(31,541,1), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+
+{ "tlbsync", X(31,566),	0xffffffff,	PPC,		{ 0 } },
+
+{ "lfsux",   X(31,567),	X_MASK,		PPC|POWER,	{ FRT, RAS, RB } },
+
+{ "mfsr",    X(31,595),	XRB_MASK|(1<<20), PPC|POWER|B32, { RT, SR } },
+
+{ "lswi",    X(31,597),	X_MASK,		PPC,		{ RT, RA, NB } },
+{ "lsi",     X(31,597),	X_MASK,		POWER,		{ RT, RA, NB } },
+
+{ "sync",    X(31,598), 0xffffffff,	PPC,		{ 0 } },
+{ "dcs",     X(31,598), 0xffffffff,	POWER,		{ 0 } },
+
+{ "lfdx",    X(31,599), X_MASK,		PPC|POWER,	{ FRT, RA, RB } },
+
+{ "mfsri",   X(31,627), X_MASK,		POWER,		{ RT, RA, RB } },
+
+{ "dclst",   X(31,630), XRB_MASK,	POWER,		{ RS, RA } },
+
+{ "lfdux",   X(31,631), X_MASK,		PPC|POWER,	{ FRT, RAS, RB } },
+
+{ "mfsrin",  X(31,659), XRA_MASK,	PPC|B32,	{ RT, RB } },
+
+{ "stswx",   X(31,661), X_MASK,		PPC,		{ RS, RA, RB } },
+{ "stsx",    X(31,661), X_MASK,		POWER,		{ RS, RA, RB } },
+
+{ "stwbrx",  X(31,662), X_MASK,		PPC,		{ RS, RA, RB } },
+{ "stbrx",   X(31,662), X_MASK,		POWER,		{ RS, RA, RB } },
+
+{ "stfsx",   X(31,663), X_MASK,		PPC|POWER,	{ FRS, RA, RB } },
+
+{ "srq",     XRC(31,664,0), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+{ "srq.",    XRC(31,664,1), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+
+{ "sre",     XRC(31,665,0), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+{ "sre.",    XRC(31,665,1), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+
+{ "stfsux",  X(31,695),	X_MASK,		PPC|POWER,	{ FRS, RAS, RB } },
+
+{ "sriq",    XRC(31,696,0), X_MASK,	POWER|M601,	{ RA, RS, SH } },
+{ "sriq.",   XRC(31,696,1), X_MASK,	POWER|M601,	{ RA, RS, SH } },
+
+{ "stswi",   X(31,725),	X_MASK,		PPC,		{ RS, RA, NB } },
+{ "stsi",    X(31,725),	X_MASK,		POWER,		{ RS, RA, NB } },
+
+{ "stfdx",   X(31,727),	X_MASK,		PPC|POWER,	{ FRS, RA, RB } },
+
+{ "srlq",    XRC(31,728,0), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+{ "srlq.",   XRC(31,728,1), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+
+{ "sreq",    XRC(31,729,0), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+{ "sreq.",   XRC(31,729,1), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+
+{ "stfdux",  X(31,759),	X_MASK,		PPC|POWER,	{ FRS, RAS, RB } },
+
+{ "srliq",   XRC(31,760,0), X_MASK,	POWER|M601,	{ RA, RS, SH } },
+{ "srliq.",  XRC(31,760,1), X_MASK,	POWER|M601,	{ RA, RS, SH } },
+
+{ "lhbrx",   X(31,790),	X_MASK,		PPC|POWER,	{ RT, RA, RB } },
+
+{ "sraw",    XRC(31,792,0), X_MASK,	PPC,		{ RA, RS, RB } },
+{ "sra",     XRC(31,792,0), X_MASK,	POWER,		{ RA, RS, RB } },
+{ "sraw.",   XRC(31,792,1), X_MASK,	PPC,		{ RA, RS, RB } },
+{ "sra.",    XRC(31,792,1), X_MASK,	POWER,		{ RA, RS, RB } },
+
+{ "srad",    XRC(31,794,0), X_MASK,	PPC|B64,	{ RA, RS, RB } },
+{ "srad.",   XRC(31,794,1), X_MASK,	PPC|B64,	{ RA, RS, RB } },
+
+{ "rac",     X(31,818),	X_MASK,		POWER,		{ RT, RA, RB } },
+
+{ "srawi",   XRC(31,824,0), X_MASK,	PPC,		{ RA, RS, SH } },
+{ "srai",    XRC(31,824,0), X_MASK,	POWER,		{ RA, RS, SH } },
+{ "srawi.",  XRC(31,824,1), X_MASK,	PPC,		{ RA, RS, SH } },
+{ "srai.",   XRC(31,824,1), X_MASK,	POWER,		{ RA, RS, SH } },
+
+{ "eieio",   X(31,854),	0xffffffff,	PPC,		{ 0 } },
+
+{ "sthbrx",  X(31,918),	X_MASK,		PPC|POWER,	{ RS, RA, RB } },
+
+{ "sraq",    XRC(31,920,0), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+{ "sraq.",   XRC(31,920,1), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+
+{ "srea",    XRC(31,921,0), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+{ "srea.",   XRC(31,921,1), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+
+{ "extsh",   XRC(31,922,0), XRB_MASK,	PPC,		{ RA, RS } },
+{ "exts",    XRC(31,922,0), XRB_MASK,	POWER,		{ RA, RS } },
+{ "extsh.",  XRC(31,922,1), XRB_MASK,	PPC,		{ RA, RS } },
+{ "exts.",   XRC(31,922,1), XRB_MASK,	POWER,		{ RA, RS } },
+
+{ "sraiq",   XRC(31,952,0), X_MASK,	POWER|M601,	{ RA, RS, SH } },
+{ "sraiq.",  XRC(31,952,1), X_MASK,	POWER|M601,	{ RA, RS, SH } },
+
+{ "extsb",   XRC(31,954,0), XRB_MASK,	PPC,		{ RA, RS} },
+{ "extsb.",  XRC(31,954,1), XRB_MASK,	PPC,		{ RA, RS} },
+
+{ "iccci",   X(31,966),	XRT_MASK,	PPC,		{ RA, RB } },
+
+{ "icbi",    X(31,982),	XRT_MASK,	PPC,		{ RA, RB } },
+
+{ "stfiwx",  X(31,983),	X_MASK,		PPC,		{ FRS, RA, RB } },
+
+{ "extsw",   XRC(31,986,0), XRB_MASK,	PPC,		{ RA, RS } },
+{ "extsw.",  XRC(31,986,1), XRB_MASK,	PPC,		{ RA, RS } },
+
+{ "dcbz",    X(31,1014), XRT_MASK,	PPC,		{ RA, RB } },
+{ "dclz",    X(31,1014), XRT_MASK,	PPC,		{ RA, RB } },
+
+{ "lwz",     OP(32),	OP_MASK,	PPC,		{ RT, D, RA } },
+{ "l",	     OP(32),	OP_MASK,	POWER,		{ RT, D, RA } },
+
+{ "lwzu",    OP(33),	OP_MASK,	PPC,		{ RT, D, RAL } },
+{ "lu",      OP(33),	OP_MASK,	POWER,		{ RT, D, RA } },
+
+{ "lbz",     OP(34),	OP_MASK,	PPC|POWER,	{ RT, D, RA } },
+
+{ "lbzu",    OP(35),	OP_MASK,	PPC|POWER,	{ RT, D, RAL } },
+
+{ "stw",     OP(36),	OP_MASK,	PPC,		{ RS, D, RA } },
+{ "st",      OP(36),	OP_MASK,	POWER,		{ RS, D, RA } },
+
+{ "stwu",    OP(37),	OP_MASK,	PPC,		{ RS, D, RAS } },
+{ "stu",     OP(37),	OP_MASK,	POWER,		{ RS, D, RA } },
+
+{ "stb",     OP(38),	OP_MASK,	PPC|POWER,	{ RS, D, RA } },
+
+{ "stbu",    OP(39),	OP_MASK,	PPC|POWER,	{ RS, D, RAS } },
+
+{ "lhz",     OP(40),	OP_MASK,	PPC|POWER,	{ RT, D, RA } },
+
+{ "lhzu",    OP(41),	OP_MASK,	PPC|POWER,	{ RT, D, RAL } },
+
+{ "lha",     OP(42),	OP_MASK,	PPC|POWER,	{ RT, D, RA } },
+
+{ "lhau",    OP(43),	OP_MASK,	PPC|POWER,	{ RT, D, RAL } },
+
+{ "sth",     OP(44),	OP_MASK,	PPC|POWER,	{ RS, D, RA } },
+
+{ "sthu",    OP(45),	OP_MASK,	PPC|POWER,	{ RS, D, RAS } },
+
+{ "lmw",     OP(46),	OP_MASK,	PPC,		{ RT, D, RAM } },
+{ "lm",      OP(46),	OP_MASK,	POWER,		{ RT, D, RA } },
+
+{ "stmw",    OP(47),	OP_MASK,	PPC,		{ RS, D, RA } },
+{ "stm",     OP(47),	OP_MASK,	POWER,		{ RS, D, RA } },
+
+{ "lfs",     OP(48),	OP_MASK,	PPC|POWER,	{ FRT, D, RA } },
+
+{ "lfsu",    OP(49),	OP_MASK,	PPC|POWER,	{ FRT, D, RAS } },
+
+{ "lfd",     OP(50),	OP_MASK,	PPC|POWER,	{ FRT, D, RA } },
+
+{ "lfdu",    OP(51),	OP_MASK,	PPC|POWER,	{ FRT, D, RAS } },
+
+{ "stfs",    OP(52),	OP_MASK,	PPC|POWER,	{ FRS, D, RA } },
+
+{ "stfsu",   OP(53),	OP_MASK,	PPC|POWER,	{ FRS, D, RAS } },
+
+{ "stfd",    OP(54),	OP_MASK,	PPC|POWER,	{ FRS, D, RA } },
+
+{ "stfdu",   OP(55),	OP_MASK,	PPC|POWER,	{ FRS, D, RAS } },
+
+{ "lfq",     OP(56),	OP_MASK,	POWER2,		{ FRT, D, RA } },
+
+{ "lfqu",    OP(57),	OP_MASK,	POWER2,		{ FRT, D, RA } },
+
+{ "ld",      DSO(58,0),	DS_MASK,	PPC|B64,	{ RT, DS, RA } },
+
+{ "ldu",     DSO(58,1), DS_MASK,	PPC|B64,	{ RT, DS, RAL } },
+
+{ "lwa",     DSO(58,2), DS_MASK,	PPC|B64,	{ RT, DS, RA } },
+
+{ "fdivs",   A(59,18,0), AFRC_MASK,	PPC,		{ FRT, FRA, FRB } },
+{ "fdivs.",  A(59,18,1), AFRC_MASK,	PPC,		{ FRT, FRA, FRB } },
+
+{ "fsubs",   A(59,20,0), AFRC_MASK,	PPC,		{ FRT, FRA, FRB } },
+{ "fsubs.",  A(59,20,1), AFRC_MASK,	PPC,		{ FRT, FRA, FRB } },
+
+{ "fadds",   A(59,21,0), AFRC_MASK,	PPC,		{ FRT, FRA, FRB } },
+{ "fadds.",  A(59,21,1), AFRC_MASK,	PPC,		{ FRT, FRA, FRB } },
+
+{ "fsqrts",  A(59,22,0), AFRAFRC_MASK,	PPC,		{ FRT, FRB } },
+{ "fsqrts.", A(59,22,1), AFRAFRC_MASK,	PPC,		{ FRT, FRB } },
+
+{ "fres",    A(59,24,0), AFRAFRC_MASK,	PPC,		{ FRT, FRB } },
+{ "fres.",   A(59,24,1), AFRAFRC_MASK,	PPC,		{ FRT, FRB } },
+
+{ "fmuls",   A(59,25,0), AFRB_MASK,	PPC,		{ FRT, FRA, FRC } },
+{ "fmuls.",  A(59,25,1), AFRB_MASK,	PPC,		{ FRT, FRA, FRC } },
+
+{ "fmsubs",  A(59,28,0), A_MASK,	PPC,		{ FRT,FRA,FRC,FRB } },
+{ "fmsubs.", A(59,28,1), A_MASK,	PPC,		{ FRT,FRA,FRC,FRB } },
+
+{ "fmadds",  A(59,29,0), A_MASK,	PPC,		{ FRT,FRA,FRC,FRB } },
+{ "fmadds.", A(59,29,1), A_MASK,	PPC,		{ FRT,FRA,FRC,FRB } },
+
+{ "fnmsubs", A(59,30,0), A_MASK,	PPC,		{ FRT,FRA,FRC,FRB } },
+{ "fnmsubs.",A(59,30,1), A_MASK,	PPC,		{ FRT,FRA,FRC,FRB } },
+
+{ "fnmadds", A(59,31,0), A_MASK,	PPC,		{ FRT,FRA,FRC,FRB } },
+{ "fnmadds.",A(59,31,1), A_MASK,	PPC,		{ FRT,FRA,FRC,FRB } },
+
+{ "stfq",    OP(60),	OP_MASK,	POWER2,		{ FRS, D, RA } },
+
+{ "stfqu",   OP(61),	OP_MASK,	POWER2,		{ FRS, D, RA } },
+
+{ "std",     DSO(62,0),	DS_MASK,	PPC|B64,	{ RS, DS, RA } },
+
+{ "stdu",    DSO(62,1),	DS_MASK,	PPC|B64,	{ RS, DS, RAS } },
+
+{ "fcmpu",   X(63,0),	X_MASK|(3<<21),	PPC|POWER,	{ BF, FRA, FRB } },
+
+{ "frsp",    XRC(63,12,0), XRA_MASK,	PPC|POWER,	{ FRT, FRB } },
+{ "frsp.",   XRC(63,12,1), XRA_MASK,	PPC|POWER,	{ FRT, FRB } },
+
+{ "fctiw",   XRC(63,14,0), XRA_MASK,	PPC,		{ FRT, FRB } },
+{ "fcir",    XRC(63,14,0), XRA_MASK,	POWER2,		{ FRT, FRB } },
+{ "fctiw.",  XRC(63,14,1), XRA_MASK,	PPC,		{ FRT, FRB } },
+{ "fcir.",   XRC(63,14,1), XRA_MASK,	POWER2,		{ FRT, FRB } },
+
+{ "fctiwz",  XRC(63,15,0), XRA_MASK,	PPC,		{ FRT, FRB } },
+{ "fcirz",   XRC(63,15,0), XRA_MASK,	POWER2,		{ FRT, FRB } },
+{ "fctiwz.", XRC(63,15,1), XRA_MASK,	PPC,		{ FRT, FRB } },
+{ "fcirz.",  XRC(63,15,1), XRA_MASK,	POWER2,		{ FRT, FRB } },
+
+{ "fdiv",    A(63,18,0), AFRC_MASK,	PPC,		{ FRT, FRA, FRB } },
+{ "fd",      A(63,18,0), AFRC_MASK,	POWER,		{ FRT, FRA, FRB } },
+{ "fdiv.",   A(63,18,1), AFRC_MASK,	PPC,		{ FRT, FRA, FRB } },
+{ "fd.",     A(63,18,1), AFRC_MASK,	POWER,		{ FRT, FRA, FRB } },
+
+{ "fsub",    A(63,20,0), AFRC_MASK,	PPC,		{ FRT, FRA, FRB } },
+{ "fs",      A(63,20,0), AFRC_MASK,	POWER,		{ FRT, FRA, FRB } },
+{ "fsub.",   A(63,20,1), AFRC_MASK,	PPC,		{ FRT, FRA, FRB } },
+{ "fs.",     A(63,20,1), AFRC_MASK,	POWER,		{ FRT, FRA, FRB } },
+
+{ "fadd",    A(63,21,0), AFRC_MASK,	PPC,		{ FRT, FRA, FRB } },
+{ "fa",      A(63,21,0), AFRC_MASK,	POWER,		{ FRT, FRA, FRB } },
+{ "fadd.",   A(63,21,1), AFRC_MASK,	PPC,		{ FRT, FRA, FRB } },
+{ "fa.",     A(63,21,1), AFRC_MASK,	POWER,		{ FRT, FRA, FRB } },
+
+{ "fsqrt",   A(63,22,0), AFRAFRC_MASK,	PPC|POWER2,	{ FRT, FRB } },
+{ "fsqrt.",  A(63,22,1), AFRAFRC_MASK,	PPC|POWER2,	{ FRT, FRB } },
+
+{ "fsel",    A(63,23,0), A_MASK,	PPC,		{ FRT,FRA,FRC,FRB } },
+{ "fsel.",   A(63,23,1), A_MASK,	PPC,		{ FRT,FRA,FRC,FRB } },
+
+{ "fmul",    A(63,25,0), AFRB_MASK,	PPC,		{ FRT, FRA, FRC } },
+{ "fm",      A(63,25,0), AFRB_MASK,	POWER,		{ FRT, FRA, FRC } },
+{ "fmul.",   A(63,25,1), AFRB_MASK,	PPC,		{ FRT, FRA, FRC } },
+{ "fm.",     A(63,25,1), AFRB_MASK,	POWER,		{ FRT, FRA, FRC } },
+
+{ "frsqrte", A(63,26,0), AFRAFRC_MASK,	PPC,		{ FRT, FRB } },
+{ "frsqrte.",A(63,26,1), AFRAFRC_MASK,	PPC,		{ FRT, FRB } },
+
+{ "fmsub",   A(63,28,0), A_MASK,	PPC,		{ FRT,FRA,FRC,FRB } },
+{ "fms",     A(63,28,0), A_MASK,	POWER,		{ FRT,FRA,FRC,FRB } },
+{ "fmsub.",  A(63,28,1), A_MASK,	PPC,		{ FRT,FRA,FRC,FRB } },
+{ "fms.",    A(63,28,1), A_MASK,	POWER,		{ FRT,FRA,FRC,FRB } },
+
+{ "fmadd",   A(63,29,0), A_MASK,	PPC,		{ FRT,FRA,FRC,FRB } },
+{ "fma",     A(63,29,0), A_MASK,	POWER,		{ FRT,FRA,FRC,FRB } },
+{ "fmadd.",  A(63,29,1), A_MASK,	PPC,		{ FRT,FRA,FRC,FRB } },
+{ "fma.",    A(63,29,1), A_MASK,	POWER,		{ FRT,FRA,FRC,FRB } },
+
+{ "fnmsub",  A(63,30,0), A_MASK,	PPC,		{ FRT,FRA,FRC,FRB } },
+{ "fnms",    A(63,30,0), A_MASK,	POWER,		{ FRT,FRA,FRC,FRB } },
+{ "fnmsub.", A(63,30,1), A_MASK,	PPC,		{ FRT,FRA,FRC,FRB } },
+{ "fnms.",   A(63,30,1), A_MASK,	POWER,		{ FRT,FRA,FRC,FRB } },
+
+{ "fnmadd",  A(63,31,0), A_MASK,	PPC,		{ FRT,FRA,FRC,FRB } },
+{ "fnma",    A(63,31,0), A_MASK,	POWER,		{ FRT,FRA,FRC,FRB } },
+{ "fnmadd.", A(63,31,1), A_MASK,	PPC,		{ FRT,FRA,FRC,FRB } },
+{ "fnma.",   A(63,31,1), A_MASK,	POWER,		{ FRT,FRA,FRC,FRB } },
+
+{ "fcmpo",   X(63,30),	X_MASK|(3<<21),	PPC|POWER,	{ BF, FRA, FRB } },
+
+{ "mtfsb1",  XRC(63,38,0), XRARB_MASK,	PPC|POWER,	{ BT } },
+{ "mtfsb1.", XRC(63,38,1), XRARB_MASK,	PPC|POWER,	{ BT } },
+
+{ "fneg",    XRC(63,40,0), XRA_MASK,	PPC|POWER,	{ FRT, FRB } },
+{ "fneg.",   XRC(63,40,1), XRA_MASK,	PPC|POWER,	{ FRT, FRB } },
+
+{ "mcrfs",   X(63,64),	XRB_MASK|(3<<21)|(3<<16), PPC|POWER, { BF, BFA } },
+
+{ "mtfsb0",  XRC(63,70,0), XRARB_MASK,	PPC|POWER,	{ BT } },
+{ "mtfsb0.", XRC(63,70,1), XRARB_MASK,	PPC|POWER,	{ BT } },
+
+{ "fmr",     XRC(63,72,0), XRA_MASK,	PPC|POWER,	{ FRT, FRB } },
+{ "fmr.",    XRC(63,72,1), XRA_MASK,	PPC|POWER,	{ FRT, FRB } },
+
+{ "mtfsfi",  XRC(63,134,0), XRA_MASK|(3<<21)|(1<<11), PPC|POWER, { BF, U } },
+{ "mtfsfi.", XRC(63,134,1), XRA_MASK|(3<<21)|(1<<11), PPC|POWER, { BF, U } },
+
+{ "fnabs",   XRC(63,136,0), XRA_MASK,	PPC|POWER,	{ FRT, FRB } },
+{ "fnabs.",  XRC(63,136,1), XRA_MASK,	PPC|POWER,	{ FRT, FRB } },
+
+{ "fabs",    XRC(63,264,0), XRA_MASK,	PPC|POWER,	{ FRT, FRB } },
+{ "fabs.",   XRC(63,264,1), XRA_MASK,	PPC|POWER,	{ FRT, FRB } },
+
+{ "mffs",    XRC(63,583,0), XRARB_MASK,	PPC|POWER,	{ FRT } },
+{ "mffs.",   XRC(63,583,1), XRARB_MASK,	PPC|POWER,	{ FRT } },
+
+{ "mtfsf",   XFL(63,711,0), XFL_MASK,	PPC|POWER,	{ FLM, FRB } },
+{ "mtfsf.",  XFL(63,711,1), XFL_MASK,	PPC|POWER,	{ FLM, FRB } },
+
+{ "fctid",   XRC(63,814,0), XRA_MASK,	PPC|B64,	{ FRT, FRB } },
+{ "fctid.",  XRC(63,814,1), XRA_MASK,	PPC|B64,	{ FRT, FRB } },
+
+{ "fctidz",  XRC(63,815,0), XRA_MASK,	PPC|B64,	{ FRT, FRB } },
+{ "fctidz.", XRC(63,815,1), XRA_MASK,	PPC|B64,	{ FRT, FRB } },
+
+{ "fcfid",   XRC(63,846,0), XRA_MASK,	PPC|B64,	{ FRT, FRB } },
+{ "fcfid.",  XRC(63,846,1), XRA_MASK,	PPC|B64,	{ FRT, FRB } },
+
+};
+
+const int powerpc_num_opcodes =
+  sizeof (powerpc_opcodes) / sizeof (powerpc_opcodes[0]);
+
+/* The macro table.  This is only used by the assembler.  */
+
+const struct powerpc_macro powerpc_macros[] = {
+{ "extldi",  4,   PPC|B64,	"rldicr %0,%1,%3,(%2)-1" },
+{ "extldi.", 4,   PPC|B64,	"rldicr. %0,%1,%3,(%2)-1" },
+{ "extrdi",  4,   PPC|B64,	"rldicl %0,%1,(%2)+(%3),64-(%2)" },
+{ "extrdi.", 4,   PPC|B64,	"rldicl. %0,%1,(%2)+(%3),64-(%2)" },
+{ "insrdi",  4,   PPC|B64,	"rldimi %0,%1,64-((%2)+(%3)),%3" },
+{ "insrdi.", 4,   PPC|B64,	"rldimi. %0,%1,64-((%2)+(%3)),%3" },
+{ "rotrdi",  3,   PPC|B64,	"rldicl %0,%1,64-(%2),0" },
+{ "rotrdi.", 3,   PPC|B64,	"rldicl. %0,%1,64-(%2),0" },
+{ "sldi",    3,   PPC|B64,	"rldicr %0,%1,%2,63-(%2)" },
+{ "sldi.",   3,   PPC|B64,	"rldicr. %0,%1,%2,63-(%2)" },
+{ "srdi",    3,   PPC|B64,	"rldicl %0,%1,64-(%2),%2" },
+{ "srdi.",   3,   PPC|B64,	"rldicl. %0,%1,64-(%2),%2" },
+{ "clrrdi",  3,   PPC|B64,	"rldicr %0,%1,0,63-(%2)" },
+{ "clrrdi.", 3,   PPC|B64,	"rldicr. %0,%1,0,63-(%2)" },
+{ "clrlsldi",4,   PPC|B64,	"rldic %0,%1,%3,(%2)-(%3)" },
+{ "clrlsldi.",4,  PPC|B64,	"rldic. %0,%1,%3,(%2)-(%3)" },
+
+{ "extlwi",  4,   PPC,		"rlwinm %0,%1,%3,0,(%2)-1" },
+{ "extlwi.", 4,   PPC,		"rlwinm. %0,%1,%3,0,(%2)-1" },
+{ "extrwi",  4,   PPC,		"rlwinm %0,%1,(%2)+(%3),32-(%2),31" },
+{ "extrwi.", 4,   PPC,		"rlwinm. %0,%1,(%2)+(%3),32-(%2),31" },
+{ "inslwi",  4,   PPC,		"rlwimi %0,%1,32-(%3),%3,(%2)+(%3)-1" },
+{ "inslwi.", 4,   PPC,		"rlwimi. %0,%1,32-(%3),%3,(%2)+(%3)-1" },
+{ "insrwi",  4,   PPC,		"rlwimi %0,%1,32-((%2)+(%3)),%3,(%2)+(%3)-1" },
+{ "insrwi.", 4,   PPC,		"rlwimi. %0,%1,32-((%2)+(%3)),%3,(%2)+(%3)-1"},
+{ "rotrwi",  3,   PPC,		"rlwinm %0,%1,32-(%2),0,31" },
+{ "rotrwi.", 3,   PPC,		"rlwinm. %0,%1,32-(%2),0,31" },
+{ "slwi",    3,   PPC,		"rlwinm %0,%1,%2,0,31-(%2)" },
+{ "sli",     3,   POWER,	"rlinm %0,%1,%2,0,31-(%2)" },
+{ "slwi.",   3,   PPC,		"rlwinm. %0,%1,%2,0,31-(%2)" },
+{ "sli.",    3,   POWER,	"rlinm. %0,%1,%2,0,31-(%2)" },
+{ "srwi",    3,   PPC,		"rlwinm %0,%1,32-(%2),%2,31" },
+{ "sri",     3,   POWER,	"rlinm %0,%1,32-(%2),%2,31" },
+{ "srwi.",   3,   PPC,		"rlwinm. %0,%1,32-(%2),%2,31" },
+{ "sri.",    3,   POWER,	"rlinm. %0,%1,32-(%2),%2,31" },
+{ "clrrwi",  3,   PPC,		"rlwinm %0,%1,0,0,31-(%2)" },
+{ "clrrwi.", 3,   PPC,		"rlwinm. %0,%1,0,0,31-(%2)" },
+{ "clrlslwi",4,   PPC,		"rlwinm %0,%1,%3,(%2)-(%3),31-(%3)" },
+{ "clrlslwi.",4,  PPC,		"rlwinm. %0,%1,%3,(%2)-(%3),31-(%3)" },
+
+};
+
+const int powerpc_num_macros =
+  sizeof (powerpc_macros) / sizeof (powerpc_macros[0]);
diff -u --recursive --new-file v2.3.3/linux/arch/ppc/xmon/ppc.h linux/arch/ppc/xmon/ppc.h
--- v2.3.3/linux/arch/ppc/xmon/ppc.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/xmon/ppc.h	Sat May 22 13:03:00 1999
@@ -0,0 +1,240 @@
+/* ppc.h -- Header file for PowerPC opcode table
+   Copyright 1994 Free Software Foundation, Inc.
+   Written by Ian Lance Taylor, Cygnus Support
+
+This file is part of GDB, GAS, and the GNU binutils.
+
+GDB, GAS, and the GNU binutils are free software; you can redistribute
+them and/or modify them under the terms of the GNU General Public
+License as published by the Free Software Foundation; either version
+1, or (at your option) any later version.
+
+GDB, GAS, and the GNU binutils are distributed in the hope that they
+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 file; see the file COPYING.  If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#ifndef PPC_H
+#define PPC_H
+
+/* The opcode table is an array of struct powerpc_opcode.  */
+
+struct powerpc_opcode
+{
+  /* The opcode name.  */
+  const char *name;
+
+  /* The opcode itself.  Those bits which will be filled in with
+     operands are zeroes.  */
+  unsigned long opcode;
+
+  /* The opcode mask.  This is used by the disassembler.  This is a
+     mask containing ones indicating those bits which must match the
+     opcode field, and zeroes indicating those bits which need not
+     match (and are presumably filled in by operands).  */
+  unsigned long mask;
+
+  /* One bit flags for the opcode.  These are used to indicate which
+     specific processors support the instructions.  The defined values
+     are listed below.  */
+  unsigned long flags;
+
+  /* An array of operand codes.  Each code is an index into the
+     operand table.  They appear in the order which the operands must
+     appear in assembly code, and are terminated by a zero.  */
+  unsigned char operands[8];
+};
+
+/* The table itself is sorted by major opcode number, and is otherwise
+   in the order in which the disassembler should consider
+   instructions.  */
+extern const struct powerpc_opcode powerpc_opcodes[];
+extern const int powerpc_num_opcodes;
+
+/* Values defined for the flags field of a struct powerpc_opcode.  */
+
+/* Opcode is defined for the PowerPC architecture.  */
+#define PPC_OPCODE_PPC (01)
+
+/* Opcode is defined for the POWER (RS/6000) architecture.  */
+#define PPC_OPCODE_POWER (02)
+
+/* Opcode is defined for the POWER2 (Rios 2) architecture.  */
+#define PPC_OPCODE_POWER2 (04)
+
+/* Opcode is only defined on 32 bit architectures.  */
+#define PPC_OPCODE_32 (010)
+
+/* Opcode is only defined on 64 bit architectures.  */
+#define PPC_OPCODE_64 (020)
+
+/* Opcode is supported by the Motorola PowerPC 601 processor.  The 601
+   is assumed to support all PowerPC (PPC_OPCODE_PPC) instructions,
+   but it also supports many additional POWER instructions.  */
+#define PPC_OPCODE_601 (040)
+
+/* A macro to extract the major opcode from an instruction.  */
+#define PPC_OP(i) (((i) >> 26) & 0x3f)
+
+/* The operands table is an array of struct powerpc_operand.  */
+
+struct powerpc_operand
+{
+  /* The number of bits in the operand.  */
+  int bits;
+
+  /* How far the operand is left shifted in the instruction.  */
+  int shift;
+
+  /* Insertion function.  This is used by the assembler.  To insert an
+     operand value into an instruction, check this field.
+
+     If it is NULL, execute
+         i |= (op & ((1 << o->bits) - 1)) << o->shift;
+     (i is the instruction which we are filling in, o is a pointer to
+     this structure, and op is the opcode value; this assumes twos
+     complement arithmetic).
+
+     If this field is not NULL, then simply call it with the
+     instruction and the operand value.  It will return the new value
+     of the instruction.  If the ERRMSG argument is not NULL, then if
+     the operand value is illegal, *ERRMSG will be set to a warning
+     string (the operand will be inserted in any case).  If the
+     operand value is legal, *ERRMSG will be unchanged (most operands
+     can accept any value).  */
+  unsigned long (*insert) PARAMS ((unsigned long instruction, long op,
+				   const char **errmsg));
+
+  /* Extraction function.  This is used by the disassembler.  To
+     extract this operand type from an instruction, check this field.
+
+     If it is NULL, compute
+         op = ((i) >> o->shift) & ((1 << o->bits) - 1);
+	 if ((o->flags & PPC_OPERAND_SIGNED) != 0
+	     && (op & (1 << (o->bits - 1))) != 0)
+	   op -= 1 << o->bits;
+     (i is the instruction, o is a pointer to this structure, and op
+     is the result; this assumes twos complement arithmetic).
+
+     If this field is not NULL, then simply call it with the
+     instruction value.  It will return the value of the operand.  If
+     the INVALID argument is not NULL, *INVALID will be set to
+     non-zero if this operand type can not actually be extracted from
+     this operand (i.e., the instruction does not match).  If the
+     operand is valid, *INVALID will not be changed.  */
+  long (*extract) PARAMS ((unsigned long instruction, int *invalid));
+
+  /* One bit syntax flags.  */
+  unsigned long flags;
+};
+
+/* Elements in the table are retrieved by indexing with values from
+   the operands field of the powerpc_opcodes table.  */
+
+extern const struct powerpc_operand powerpc_operands[];
+
+/* Values defined for the flags field of a struct powerpc_operand.  */
+
+/* This operand takes signed values.  */
+#define PPC_OPERAND_SIGNED (01)
+
+/* This operand takes signed values, but also accepts a full positive
+   range of values when running in 32 bit mode.  That is, if bits is
+   16, it takes any value from -0x8000 to 0xffff.  In 64 bit mode,
+   this flag is ignored.  */
+#define PPC_OPERAND_SIGNOPT (02)
+
+/* This operand does not actually exist in the assembler input.  This
+   is used to support extended mnemonics such as mr, for which two
+   operands fields are identical.  The assembler should call the
+   insert function with any op value.  The disassembler should call
+   the extract function, ignore the return value, and check the value
+   placed in the valid argument.  */
+#define PPC_OPERAND_FAKE (04)
+
+/* The next operand should be wrapped in parentheses rather than
+   separated from this one by a comma.  This is used for the load and
+   store instructions which want their operands to look like
+       reg,displacement(reg)
+   */
+#define PPC_OPERAND_PARENS (010)
+
+/* This operand may use the symbolic names for the CR fields, which
+   are
+       lt  0	gt  1	eq  2	so  3	un  3
+       cr0 0	cr1 1	cr2 2	cr3 3
+       cr4 4	cr5 5	cr6 6	cr7 7
+   These may be combined arithmetically, as in cr2*4+gt.  These are
+   only supported on the PowerPC, not the POWER.  */
+#define PPC_OPERAND_CR (020)
+
+/* This operand names a register.  The disassembler uses this to print
+   register names with a leading 'r'.  */
+#define PPC_OPERAND_GPR (040)
+
+/* This operand names a floating point register.  The disassembler
+   prints these with a leading 'f'.  */
+#define PPC_OPERAND_FPR (0100)
+
+/* This operand is a relative branch displacement.  The disassembler
+   prints these symbolically if possible.  */
+#define PPC_OPERAND_RELATIVE (0200)
+
+/* This operand is an absolute branch address.  The disassembler
+   prints these symbolically if possible.  */
+#define PPC_OPERAND_ABSOLUTE (0400)
+
+/* This operand is optional, and is zero if omitted.  This is used for
+   the optional BF and L fields in the comparison instructions.  The
+   assembler must count the number of operands remaining on the line,
+   and the number of operands remaining for the opcode, and decide
+   whether this operand is present or not.  The disassembler should
+   print this operand out only if it is not zero.  */
+#define PPC_OPERAND_OPTIONAL (01000)
+
+/* This flag is only used with PPC_OPERAND_OPTIONAL.  If this operand
+   is omitted, then for the next operand use this operand value plus
+   1, ignoring the next operand field for the opcode.  This wretched
+   hack is needed because the Power rotate instructions can take
+   either 4 or 5 operands.  The disassembler should print this operand
+   out regardless of the PPC_OPERAND_OPTIONAL field.  */
+#define PPC_OPERAND_NEXT (02000)
+
+/* This operand should be regarded as a negative number for the
+   purposes of overflow checking (i.e., the normal most negative
+   number is disallowed and one more than the normal most positive
+   number is allowed).  This flag will only be set for a signed
+   operand.  */
+#define PPC_OPERAND_NEGATIVE (04000)
+
+/* The POWER and PowerPC assemblers use a few macros.  We keep them
+   with the operands table for simplicity.  The macro table is an
+   array of struct powerpc_macro.  */
+
+struct powerpc_macro
+{
+  /* The macro name.  */
+  const char *name;
+
+  /* The number of operands the macro takes.  */
+  unsigned int operands;
+
+  /* One bit flags for the opcode.  These are used to indicate which
+     specific processors support the instructions.  The values are the
+     same as those for the struct powerpc_opcode flags field.  */
+  unsigned long flags;
+
+  /* A format string to turn the macro into a normal instruction.
+     Each %N in the string is replaced with operand number N (zero
+     based).  */
+  const char *format;
+};
+
+extern const struct powerpc_macro powerpc_macros[];
+extern const int powerpc_num_macros;
+
+#endif /* PPC_H */
diff -u --recursive --new-file v2.3.3/linux/arch/ppc/xmon/privinst.h linux/arch/ppc/xmon/privinst.h
--- v2.3.3/linux/arch/ppc/xmon/privinst.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/xmon/privinst.h	Sat May 22 13:03:00 1999
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 1996 Paul Mackerras.
+ */
+
+#define GETREG(reg)		\
+    static inline int get_ ## reg (void)	\
+	{ int ret; asm volatile ("mf" #reg " %0" : "=r" (ret) :); return ret; }
+
+#define SETREG(reg)		\
+    static inline void set_ ## reg (int val)	\
+	{ asm volatile ("mt" #reg " %0" : : "r" (val)); }
+
+GETREG(msr)
+SETREG(msr)
+GETREG(cr)
+
+#define GSETSPR(n, name)	\
+    static inline int get_ ## name (void) \
+	{ int ret; asm volatile ("mfspr %0," #n : "=r" (ret) : ); return ret; } \
+    static inline void set_ ## name (int val) \
+	{ asm volatile ("mtspr " #n ",%0" : : "r" (val)); }
+
+GSETSPR(0, mq)
+GSETSPR(1, xer)
+GSETSPR(4, rtcu)
+GSETSPR(5, rtcl)
+GSETSPR(8, lr)
+GSETSPR(9, ctr)
+GSETSPR(18, dsisr)
+GSETSPR(19, dar)
+GSETSPR(22, dec)
+GSETSPR(25, sdr1)
+GSETSPR(26, srr0)
+GSETSPR(27, srr1)
+GSETSPR(272, sprg0)
+GSETSPR(273, sprg1)
+GSETSPR(274, sprg2)
+GSETSPR(275, sprg3)
+GSETSPR(282, ear)
+GSETSPR(287, pvr)
+GSETSPR(528, bat0u)
+GSETSPR(529, bat0l)
+GSETSPR(530, bat1u)
+GSETSPR(531, bat1l)
+GSETSPR(532, bat2u)
+GSETSPR(533, bat2l)
+GSETSPR(534, bat3u)
+GSETSPR(535, bat3l)
+GSETSPR(1008, hid0)
+GSETSPR(1009, hid1)
+GSETSPR(1010, iabr)
+GSETSPR(1013, dabr)
+GSETSPR(1023, pir)
+
+static inline int get_sr(int n)
+{
+    int ret;
+
+    asm (" mfsrin %0,%1" : "=r" (ret) : "r" (n << 28));
+    return ret;
+}
+
+static inline void set_sr(int n, int val)
+{
+    asm ("mtsrin %0,%1" : : "r" (val), "r" (n << 28));
+}
+
+static inline void store_inst(void *p)
+{
+    asm volatile ("dcbst 0,%0; sync; icbi 0,%0; isync" : : "r" (p));
+}
+
+
diff -u --recursive --new-file v2.3.3/linux/arch/ppc/xmon/setjmp.c linux/arch/ppc/xmon/setjmp.c
--- v2.3.3/linux/arch/ppc/xmon/setjmp.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/xmon/setjmp.c	Sat May 22 13:03:00 1999
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 1996 Paul Mackerras.
+ *
+ * NB this file must be compiled with -O2.
+ */
+
+int
+xmon_setjmp(long *buf)
+{
+    asm ("mflr 0; stw 0,0(%0);"
+	 "stw 1,4(%0); stw 2,8(%0);"
+	 "mfcr 0; stw 0,12(%0);"
+	 "stmw 13,16(%0)"
+	 : : "r" (buf));
+    /* XXX should save fp regs as well */
+    return 0;
+}
+
+void
+xmon_longjmp(long *buf, int val)
+{
+    if (val == 0)
+	val = 1;
+    asm ("lmw 13,16(%0);"
+	 "lwz 0,12(%0); mtcrf 0x38,0;"
+	 "lwz 0,0(%0); lwz 1,4(%0); lwz 2,8(%0);"
+	 "mtlr 0; mr 3,%1"
+	 : : "r" (buf), "r" (val));
+}
diff -u --recursive --new-file v2.3.3/linux/arch/ppc/xmon/start.c linux/arch/ppc/xmon/start.c
--- v2.3.3/linux/arch/ppc/xmon/start.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/xmon/start.c	Wed May 26 16:55:40 1999
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 1996 Paul Mackerras.
+ */
+#include <linux/string.h>
+#include <asm/machdep.h>
+#include <asm/io.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/adb.h>
+#include <asm/prom.h>
+
+static volatile unsigned char *sccc, *sccd;
+unsigned long TXRDY, RXRDY;
+extern void xmon_printf(const char *fmt, ...);
+
+static int console = 0;
+
+void buf_access(void)
+{
+	if ( _machine == _MACH_chrp )
+		sccd[3] &= ~0x80;	/* reset DLAB */
+}
+
+void
+xmon_map_scc(void)
+{
+	volatile unsigned char *base;
+
+	if ( _machine == _MACH_Pmac )
+	{
+		struct device_node *np;
+#ifdef CHRP_ESCC
+		unsigned long addr = 0xc1013020;
+#else
+		unsigned long addr = 0xf3013030;
+#endif
+		TXRDY = 4;
+		RXRDY = 1;
+		
+		np = find_devices("mac-io");
+		if (np && np->n_addrs) {
+			addr = np->addrs[0].address + 0x13000;
+			/* use the B channel on the iMac, A channel on others */
+			if (addr >= 0xf0000000)
+				addr += 0x20; /* use A channel */
+		}
+		base = (volatile unsigned char *) ioremap(addr & PAGE_MASK, PAGE_SIZE);
+		sccc = base + (addr & ~PAGE_MASK);
+#ifdef CHRP_ESCC
+		sccd = sccc + (0xc1013030 - 0xc1013020);
+#else
+		sccd = sccc + (0xf3013030 - 0xf3013020);
+#endif
+	}
+	else
+	{
+		/* should already be mapped by the kernel boot */
+		sccc = (volatile unsigned char *) (isa_io_base + 0x3fd);
+		sccd = (volatile unsigned char *) (isa_io_base + 0x3f8);
+		TXRDY = 0x20;
+		RXRDY = 1;
+	}
+}
+
+static int scc_initialized = 0;
+
+void xmon_init_scc(void);
+extern void pmu_poll(void);
+
+int
+xmon_write(void *handle, void *ptr, int nb)
+{
+    char *p = ptr;
+    int i, ct;
+
+    if (!scc_initialized)
+	xmon_init_scc();
+    for (i = 0; i < nb; ++i) {
+	while ((*sccc & TXRDY) == 0)
+	    if (adb_hardware == ADB_VIAPMU)
+		pmu_poll();
+	buf_access();
+	if ( console && (*p != '\r'))
+		printk("%c", *p);
+	ct = 0;
+	if ( *p == '\n')
+		ct = 1;
+	*sccd = *p++;
+	if ( ct )
+		xmon_write(handle, "\r", 1);
+    }
+    return i;
+}
+
+int
+xmon_read(void *handle, void *ptr, int nb)
+{
+    char *p = ptr;
+    int i;
+
+    if (!scc_initialized)
+	xmon_init_scc();
+    for (i = 0; i < nb; ++i) {
+	while ((*sccc & RXRDY) == 0)
+	    if (adb_hardware == ADB_VIAPMU)
+		pmu_poll();
+	buf_access();
+#if 0	
+	if ( 0/*console*/ )
+		*p++ = ppc_md.kbd_getkeycode();
+	else
+#endif		
+		*p++ = *sccd;
+    }
+    return i;
+}
+
+static unsigned char scc_inittab[] = {
+    13, 0,		/* set baud rate divisor */
+    12, 1,
+    14, 1,		/* baud rate gen enable, src=rtxc */
+    11, 0x50,		/* clocks = br gen */
+    5,  0x6a,		/* tx 8 bits, assert RTS */
+    4,  0x44,		/* x16 clock, 1 stop */
+    3,  0xc1,		/* rx enable, 8 bits */
+};
+
+void
+xmon_init_scc()
+{
+	if ( _machine == _MACH_chrp )
+	{
+		sccd[3] = 0x83; eieio();	/* LCR = 8N1 + DLAB */
+		sccd[0] = 3; eieio();		/* DLL = 38400 baud */
+		sccd[1] = 0; eieio();
+		sccd[2] = 0; eieio();		/* FCR = 0 */
+		sccd[3] = 3; eieio();		/* LCR = 8N1 */
+		sccd[1] = 0; eieio();		/* IER = 0 */
+	}
+	else
+	{
+		int i, x;
+
+		for (i = 20000; i != 0; --i) {
+			x = *sccc; eieio();
+		}
+		*sccc = 9; eieio();		/* reset A or B side */
+		*sccc = ((unsigned long)sccc & 0x20)? 0x80: 0x40; eieio();
+		for (i = 0; i < sizeof(scc_inittab); ++i) {
+			*sccc = scc_inittab[i];
+			eieio();
+		}
+	}
+	scc_initialized = 1;
+}
+
+#if 0
+extern int (*prom_entry)(void *);
+
+int
+xmon_exit(void)
+{
+    struct prom_args {
+	char *service;
+    } args;
+
+    for (;;) {
+	args.service = "exit";
+	(*prom_entry)(&args);
+    }
+}
+#endif
+
+void *xmon_stdin;
+void *xmon_stdout;
+void *xmon_stderr;
+
+void
+xmon_init(void)
+{
+}
+
+int
+xmon_putc(int c, void *f)
+{
+    char ch = c;
+
+    if (c == '\n')
+	xmon_putc('\r', f);
+    return xmon_write(f, &ch, 1) == 1? c: -1;
+}
+
+int
+xmon_putchar(int c)
+{
+    return xmon_putc(c, xmon_stdout);
+}
+
+int
+xmon_fputs(char *str, void *f)
+{
+    int n = strlen(str);
+
+    return xmon_write(f, str, n) == n? 0: -1;
+}
+
+int
+xmon_readchar(void)
+{
+    char ch;
+
+    for (;;) {
+	switch (xmon_read(xmon_stdin, &ch, 1)) {
+	case 1:
+	    return ch;
+	case -1:
+	    xmon_printf("read(stdin) returned -1\r\n", 0, 0);
+	    return -1;
+	}
+    }
+}
+
+static char line[256];
+static char *lineptr;
+static int lineleft;
+
+int
+xmon_getchar(void)
+{
+    int c;
+
+    if (lineleft == 0) {
+	lineptr = line;
+	for (;;) {
+	    c = xmon_readchar();
+	    if (c == -1 || c == 4)
+		break;
+	    if (c == '\r' || c == '\n') {
+		*lineptr++ = '\n';
+		xmon_putchar('\n');
+		break;
+	    }
+	    switch (c) {
+	    case 0177:
+	    case '\b':
+		if (lineptr > line) {
+		    xmon_putchar('\b');
+		    xmon_putchar(' ');
+		    xmon_putchar('\b');
+		    --lineptr;
+		}
+		break;
+	    case 'U' & 0x1F:
+		while (lineptr > line) {
+		    xmon_putchar('\b');
+		    xmon_putchar(' ');
+		    xmon_putchar('\b');
+		    --lineptr;
+		}
+		break;
+	    default:
+		if (lineptr >= &line[sizeof(line) - 1])
+		    xmon_putchar('\a');
+		else {
+		    xmon_putchar(c);
+		    *lineptr++ = c;
+		}
+	    }
+	}
+	lineleft = lineptr - line;
+	lineptr = line;
+    }
+    if (lineleft == 0)
+	return -1;
+    --lineleft;
+    return *lineptr++;
+}
+
+char *
+xmon_fgets(char *str, int nb, void *f)
+{
+    char *p;
+    int c;
+
+    for (p = str; p < str + nb - 1; ) {
+	c = xmon_getchar();
+	if (c == -1) {
+	    if (p == str)
+		return 0;
+	    break;
+	}
+	*p++ = c;
+	if (c == '\n')
+	    break;
+    }
+    *p = 0;
+    return str;
+}
diff -u --recursive --new-file v2.3.3/linux/arch/ppc/xmon/subr_prf.c linux/arch/ppc/xmon/subr_prf.c
--- v2.3.3/linux/arch/ppc/xmon/subr_prf.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/xmon/subr_prf.c	Sat May 22 13:03:00 1999
@@ -0,0 +1,47 @@
+/*
+ * Written by Cort Dougan to replace the version written by
+ * Paul Mackerras that had copyright conflicts with Linux.
+ *
+ * This file makes liberal use of the standard linux utility
+ * routines to reduce the size of the binary.  We assume we can
+ * trust some parts of Linux inside the debugger.
+ *   -- Cort (cort@cs.nmt.edu)
+ *
+ * Copyright (C) 1999 Cort Dougan.
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <stdarg.h>
+#include "nonstdio.h"
+
+extern int xmon_write(void *, void *, int);
+
+void
+xmon_vfprintf(void *f, const char *fmt, va_list ap)
+{
+	char buf[2048];
+	vsprintf( buf, fmt, ap );
+	xmon_write( f, buf, strlen(buf) );
+}
+
+void
+xmon_printf(const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	xmon_vfprintf(stdout, fmt, ap);
+	va_end(ap);
+}
+
+void
+xmon_fprintf(void *f, const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	xmon_vfprintf(f, fmt, ap);
+	va_end(ap);
+}
+
diff -u --recursive --new-file v2.3.3/linux/arch/ppc/xmon/xmon.c linux/arch/ppc/xmon/xmon.c
--- v2.3.3/linux/arch/ppc/xmon/xmon.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/xmon/xmon.c	Sat May 22 13:03:00 1999
@@ -0,0 +1,1323 @@
+/*
+ * Routines providing a simple monitor for use on the PowerMac.
+ *
+ * Copyright (C) 1996 Paul Mackerras.
+ */
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <asm/ptrace.h>
+#include <asm/string.h>
+#include "nonstdio.h"
+#include "privinst.h"
+
+#define scanhex	xmon_scanhex
+#define skipbl	xmon_skipbl
+
+static unsigned adrs;
+static int size = 1;
+static unsigned ndump = 64;
+static unsigned nidump = 16;
+static int termch;
+
+static u_int bus_error_jmp[100];
+#define setjmp xmon_setjmp
+#define longjmp xmon_longjmp
+
+/* Breakpoint stuff */
+struct bpt {
+	unsigned address;
+	unsigned instr;
+	unsigned count;
+	unsigned char enabled;
+};
+
+#define NBPTS	16
+static struct bpt bpts[NBPTS];
+static struct bpt dabr;
+static struct bpt iabr;
+static unsigned bpinstr = 0x7fe00008;	/* trap */
+
+/* Prototypes */
+extern void (*debugger_fault_handler)(struct pt_regs *);
+static int cmds(struct pt_regs *);
+static int mread(unsigned, void *, int);
+static int mwrite(unsigned, void *, int);
+static void handle_fault(struct pt_regs *);
+static void byterev(unsigned char *, int);
+static void memex(void);
+static int bsesc(void);
+static void dump(void);
+static void prdump(unsigned, int);
+#ifdef __MWERKS__
+static void prndump(unsigned, int);
+static int nvreadb(unsigned);
+#endif
+static int ppc_inst_dump(unsigned, int);
+void print_address(unsigned);
+static int getsp(void);
+static void dump_hash_table(void);
+static void backtrace(struct pt_regs *);
+static void excprint(struct pt_regs *);
+static void prregs(struct pt_regs *);
+static void memops(int);
+static void memlocate(void);
+static void memzcan(void);
+static void memdiffs(unsigned char *, unsigned char *, unsigned, unsigned);
+int skipbl(void);
+int scanhex(unsigned *valp);
+static void scannl(void);
+static int hexdigit(int);
+void getstring(char *, int);
+static void flush_input(void);
+static int inchar(void);
+static void take_input(char *);
+/* static void openforth(void); */
+static unsigned read_spr(int);
+static void write_spr(int, unsigned);
+static void super_regs(void);
+static void remove_bpts(void);
+static void insert_bpts(void);
+static struct bpt *at_breakpoint(unsigned pc);
+static void bpt_cmds(void);
+
+extern int print_insn_big_powerpc(FILE *, unsigned long, unsigned);
+extern void printf(const char *fmt, ...);
+extern int putchar(int ch);
+extern int setjmp(u_int *);
+extern void longjmp(u_int *, int);
+
+#define GETWORD(v)	(((v)[0] << 24) + ((v)[1] << 16) + ((v)[2] << 8) + (v)[3])
+
+static char *help_string = "\
+Commands:\n\
+  d	dump bytes\n\
+  dc	dump characters\n\
+  di	dump instructions\n\
+  df	dump float values\n\
+  dd	dump double values\n\
+  e	print exception information\n\
+  h	dump hash table\n\
+  m	examine/change memory\n\
+  mm	move a block of memory\n\
+  ms	set a block of memory\n\
+  md	compare two blocks of memory\n\
+  r	print registers\n\
+  S	print special registers\n\
+  t	print backtrace\n\
+  x	exit monitor\n\
+";
+
+static int xmon_trace;
+#define SSTEP	1		/* stepping because of 's' command */
+#define BRSTEP	2		/* stepping over breakpoint */
+
+void
+xmon(struct pt_regs *excp)
+{
+	struct pt_regs regs;
+	int msr, cmd;
+
+	printk("Entering xmon kernel debugger.\n");
+	
+	if (excp == NULL) {
+		asm volatile ("stw	0,0(%0)\n\
+			lwz	0,0(1)\n\
+			stw	0,4(%0)\n\
+			stmw	2,8(%0)" : : "r" (&regs));
+		regs.nip = regs.link = ((unsigned long *)regs.gpr[1])[1];
+		regs.msr = get_msr();
+		regs.ctr = get_ctr();
+		regs.xer = get_xer();
+		regs.ccr = get_cr();
+		regs.trap = 0;
+		excp = &regs;
+	}
+
+	msr = get_msr();
+	set_msr(msr & ~0x8000);	/* disable interrupts */
+	remove_bpts();
+	excprint(excp);
+	cmd = cmds(excp);
+	if (cmd == 's') {
+		xmon_trace = SSTEP;
+		excp->msr |= 0x400;
+	} else if (at_breakpoint(excp->nip)) {
+		xmon_trace = BRSTEP;
+		excp->msr |= 0x400;
+	} else {
+		xmon_trace = 0;
+		insert_bpts();
+	}
+	set_msr(msr);		/* restore interrupt enable */
+}
+
+void
+xmon_irq(int irq, void *d, struct pt_regs *regs)
+{
+	printf("Keyboard interrupt\n");
+	xmon(regs);
+}
+
+int
+xmon_bpt(struct pt_regs *regs)
+{
+	struct bpt *bp;
+
+	bp = at_breakpoint(regs->nip);
+	if (!bp)
+		return 0;
+	if (bp->count) {
+		--bp->count;
+		remove_bpts();
+		excprint(regs);
+		xmon_trace = BRSTEP;
+		regs->msr |= 0x400;
+	} else {
+		xmon(regs);
+	}
+	return 1;
+}
+
+int
+xmon_sstep(struct pt_regs *regs)
+{
+	if (!xmon_trace)
+		return 0;
+	if (xmon_trace == BRSTEP) {
+		xmon_trace = 0;
+		insert_bpts();
+	} else {
+		xmon(regs);
+	}
+	return 1;
+}
+
+int
+xmon_dabr_match(struct pt_regs *regs)
+{
+	if (dabr.enabled && dabr.count) {
+		--dabr.count;
+		remove_bpts();
+		excprint(regs);
+		xmon_trace = BRSTEP;
+		regs->msr |= 0x400;
+	} else {
+		dabr.instr = regs->nip;
+		xmon(regs);
+	}
+	return 1;
+}
+
+int
+xmon_iabr_match(struct pt_regs *regs)
+{
+	if (iabr.enabled && iabr.count) {
+		--iabr.count;
+		remove_bpts();
+		excprint(regs);
+		xmon_trace = BRSTEP;
+		regs->msr |= 0x400;
+	} else {
+		xmon(regs);
+	}
+	return 1;
+}
+
+static struct bpt *
+at_breakpoint(unsigned pc)
+{
+	int i;
+	struct bpt *bp;
+
+	if (dabr.enabled && pc == dabr.instr)
+		return &dabr;
+	if (iabr.enabled && pc == iabr.address)
+		return &iabr;
+	bp = bpts;
+	for (i = 0; i < NBPTS; ++i, ++bp)
+		if (bp->enabled && pc == bp->address)
+			return bp;
+	return 0;
+}
+
+static void
+insert_bpts()
+{
+	int i;
+	struct bpt *bp;
+
+	bp = bpts;
+	for (i = 0; i < NBPTS; ++i, ++bp) {
+		if (!bp->enabled)
+			continue;
+		if (mread(bp->address, &bp->instr, 4) != 4
+		    || mwrite(bp->address, &bpinstr, 4) != 4) {
+			printf("Couldn't insert breakpoint at %x, disabling\n",
+			       bp->address);
+			bp->enabled = 0;
+		}
+	}
+	if (dabr.enabled)
+		set_dabr(dabr.address);
+	if (iabr.enabled)
+		set_iabr(iabr.address);
+}
+
+static void
+remove_bpts()
+{
+	int i;
+	struct bpt *bp;
+	unsigned instr;
+
+	set_dabr(0);
+	set_iabr(0);
+	bp = bpts;
+	for (i = 0; i < NBPTS; ++i, ++bp) {
+		if (!bp->enabled)
+			continue;
+		if (mread(bp->address, &instr, 4) == 4
+		    && instr == bpinstr
+		    && mwrite(bp->address, &bp->instr, 4) != 4)
+			printf("Couldn't remove breakpoint at %x\n",
+			       bp->address);
+	}
+}
+
+static char *last_cmd;
+
+/* Command interpreting routine */
+static int
+cmds(struct pt_regs *excp)
+{
+	int cmd;
+
+	last_cmd = NULL;
+	for(;;) {
+		printf("mon> ");
+		fflush(stdout);
+		flush_input();
+		termch = 0;
+		cmd = skipbl();
+		if( cmd == '\n' ) {
+			if (last_cmd == NULL)
+				continue;
+			take_input(last_cmd);
+			last_cmd = NULL;
+			cmd = inchar();
+		}
+		switch (cmd) {
+		case 'm':
+			cmd = inchar();
+			switch (cmd) {
+			case 'm':
+			case 's':
+			case 'd':
+				memops(cmd);
+				break;
+			case 'l':
+				memlocate();
+				break;
+			case 'z':
+				memzcan();
+				break;
+			default:
+				termch = cmd;
+				memex();
+			}
+			break;
+		case 'd':
+			dump();
+			break;
+		case 'r':
+			if (excp != NULL)
+				prregs(excp);	/* print regs */
+			break;
+		case 'e':
+			if (excp == NULL)
+				printf("No exception information\n");
+			else
+				excprint(excp);
+			break;
+		case 'S':
+			super_regs();
+			break;
+		case 't':
+			backtrace(excp);
+			break;
+#if 0
+		case 'f':
+			openforth();
+			break;
+#endif
+		case 'h':
+			dump_hash_table();
+			break;
+		case 's':
+		case 'x':
+		case EOF:
+			return cmd;
+		case '?':
+			printf(help_string);
+			break;
+		default:
+			printf("Unrecognized command: ");
+			if( ' ' < cmd && cmd <= '~' )
+				putchar(cmd);
+			else
+				printf("\\x%x", cmd);
+			printf(" (type ? for help)\n");
+			break;
+		case 'b':
+			bpt_cmds();
+			break;
+		}
+	}
+}
+
+static void
+bpt_cmds(void)
+{
+	int cmd;
+	unsigned a;
+	int mode, i;
+	struct bpt *bp;
+
+	cmd = inchar();
+	switch (cmd) {
+	case 'd':
+		mode = 7;
+		cmd = inchar();
+		if (cmd == 'r')
+			mode = 5;
+		else if (cmd == 'w')
+			mode = 6;
+		else
+			termch = cmd;
+		dabr.address = 0;
+		dabr.count = 0;
+		dabr.enabled = scanhex(&dabr.address);
+		scanhex(&dabr.count);
+		if (dabr.enabled)
+			dabr.address = (dabr.address & ~7) | mode;
+		break;
+	case 'i':
+		iabr.address = 0;
+		iabr.count = 0;
+		iabr.enabled = scanhex(&iabr.address);
+		if (iabr.enabled)
+			iabr.address |= 3;
+		scanhex(&iabr.count);
+		break;
+	case 'c':
+		if (!scanhex(&a)) {
+			/* clear all breakpoints */
+			for (i = 0; i < NBPTS; ++i)
+				bpts[i].enabled = 0;
+			iabr.enabled = 0;
+			dabr.enabled = 0;
+			printf("All breakpoints cleared\n");
+		} else {
+			bp = at_breakpoint(a);
+			if (bp == 0) {
+				printf("No breakpoint at %x\n", a);
+			} else {
+				bp->enabled = 0;
+			}
+		}
+		break;
+	default:
+		termch = cmd;
+		if (!scanhex(&a)) {
+			/* print all breakpoints */
+			printf("type  address   count\n");
+			if (dabr.enabled) {
+				printf("data %.8x %8x [", dabr.address & ~7,
+				       dabr.count);
+				if (dabr.address & 1)
+					printf("r");
+				if (dabr.address & 2)
+					printf("w");
+				printf("]\n");
+			}
+			if (iabr.enabled)
+				printf("inst %.8x %8x\n", iabr.address & ~3,
+				       iabr.count);
+			for (bp = bpts; bp < &bpts[NBPTS]; ++bp)
+				if (bp->enabled)
+					printf("trap %.8x %8x\n", bp->address,
+					       bp->count);
+			break;
+		}
+		bp = at_breakpoint(a);
+		if (bp == 0) {
+			for (bp = bpts; bp < &bpts[NBPTS]; ++bp)
+				if (!bp->enabled)
+					break;
+			if (bp >= &bpts[NBPTS]) {
+				printf("Sorry, no free breakpoints\n");
+				break;
+			}
+		}
+		bp->enabled = 1;
+		bp->address = a;
+		bp->count = 0;
+		scanhex(&bp->count);
+		break;
+	}
+}
+
+static void
+backtrace(struct pt_regs *excp)
+{
+	unsigned sp;
+	unsigned stack[2];
+	struct pt_regs regs;
+	extern char int_return, syscall_ret_1, syscall_ret_2;
+	extern char lost_irq_ret, do_bottom_half_ret, do_signal_ret;
+
+	if (excp != NULL)
+		sp = excp->gpr[1];
+	else
+		sp = getsp();
+	scanhex(&sp);
+	scannl();
+	for (; sp != 0; sp = stack[0]) {
+		if (mread(sp, stack, sizeof(stack)) != sizeof(stack))
+			break;
+		printf("%x ", stack[1]);
+		if (stack[1] == (unsigned) &int_return
+		    || stack[1] == (unsigned) &syscall_ret_1
+		    || stack[1] == (unsigned) &syscall_ret_2
+		    || stack[1] == (unsigned) &lost_irq_ret
+		    || stack[1] == (unsigned) &do_bottom_half_ret
+		    || stack[1] == (unsigned) &do_signal_ret) {
+			if (mread(sp+16, &regs, sizeof(regs)) != sizeof(regs))
+				break;
+			printf("\nexception:%x [%x] %x ", regs.trap, sp+16,
+			       regs.nip);
+			sp = regs.gpr[1];
+			if (mread(sp, stack, sizeof(stack)) != sizeof(stack))
+				break;
+		}
+	}
+	printf("\n");
+}
+
+int
+getsp()
+{
+    int x;
+
+    asm("mr %0,1" : "=r" (x) :);
+    return x;
+}
+
+void
+excprint(struct pt_regs *fp)
+{
+	printf("vector: %x at pc = %x, msr = %x, sp = %x [%x]\n",
+	       fp->trap, fp->nip, fp->msr, fp->gpr[1], fp);
+	if ((fp->trap == 0x300) || (fp->trap == 0x600) || (fp->trap == 0x200))
+		printf("dar = %x, dsisr = %x\n", fp->dar, fp->dsisr);
+	if (current)
+		printf("current = %x, pid = %d, comm = %s\n",
+		       current, current->pid, current->comm);
+}
+
+void
+prregs(struct pt_regs *fp)
+{
+	int n;
+	unsigned base;
+
+	if (scanhex(&base))
+		fp = (struct pt_regs *) base;
+	for (n = 0; n < 32; ++n)
+		printf("R%.2d = %.8x%s", n, fp->gpr[n],
+		       (n & 3) == 3? "\n": "   ");
+	printf("pc  = %.8x   msr = %.8x   lr  = %.8x   cr  = %.8x\n",
+	       fp->nip, fp->msr, fp->link, fp->ccr);
+	printf("ctr = %.8x   xer = %.8x   trap = %4x\n",
+	       fp->ctr, fp->xer, fp->trap);
+}
+
+unsigned int
+read_spr(int n)
+{
+    unsigned int instrs[2];
+    int (*code)(void);
+
+    instrs[0] = 0x7c6002a6 + ((n & 0x1F) << 16) + ((n & 0x3e0) << 6);
+    instrs[1] = 0x4e800020;
+    store_inst(instrs);
+    store_inst(instrs+1);
+    code = (int (*)(void)) instrs;
+    return code();
+}
+
+void
+write_spr(int n, unsigned int val)
+{
+    unsigned int instrs[2];
+    int (*code)(unsigned int);
+
+    instrs[0] = 0x7c6003a6 + ((n & 0x1F) << 16) + ((n & 0x3e0) << 6);
+    instrs[1] = 0x4e800020;
+    store_inst(instrs);
+    store_inst(instrs+1);
+    code = (int (*)(unsigned int)) instrs;
+    code(val);
+}
+
+static unsigned int regno;
+extern char exc_prolog;
+extern char dec_exc;
+
+void
+super_regs()
+{
+	int i, cmd;
+	unsigned val;
+
+	cmd = skipbl();
+	if (cmd == '\n') {
+		printf("msr = %x, pvr = %x\n", get_msr(), get_pvr());
+		printf("sprg0-3 = %x %x %x %x\n", get_sprg0(), get_sprg1(),
+		       get_sprg2(), get_sprg3());
+		printf("srr0 = %x, srr1 = %x\n", get_srr0(), get_srr1());
+		printf("sr0-15 =");
+		for (i = 0; i < 16; ++i)
+			printf(" %x", get_sr(i));
+		printf("\n");
+		asm("mr %0,1" : "=r" (i) :);
+		printf("sp = %x ", i);
+		asm("mr %0,2" : "=r" (i) :);
+		printf("toc = %x\n", i);
+		return;
+	}
+
+	scanhex(&regno);
+	switch (cmd) {
+	case 'w':
+		val = read_spr(regno);
+		scanhex(&val);
+		write_spr(regno, val);
+		/* fall through */
+	case 'r':
+		printf("spr %x = %x\n", regno, read_spr(regno));
+		break;
+	case 's':
+		val = get_sr(regno);
+		scanhex(&val);
+		set_sr(regno, val);
+		break;
+	case 'm':
+		val = get_msr();
+		scanhex(&val);
+		set_msr(val);
+		break;
+	}
+	scannl();
+}
+
+#if 0
+static void
+openforth()
+{
+    int c;
+    char *p;
+    char cmd[1024];
+    int args[5];
+    extern int (*prom_entry)(int *);
+
+    p = cmd;
+    c = skipbl();
+    while (c != '\n') {
+	*p++ = c;
+	c = inchar();
+    }
+    *p = 0;
+    args[0] = (int) "interpret";
+    args[1] = 1;
+    args[2] = 1;
+    args[3] = (int) cmd;
+    (*prom_entry)(args);
+    printf("\n");
+    if (args[4] != 0)
+	printf("error %x\n", args[4]);
+}
+#endif
+
+static void
+dump_hash_table_seg(unsigned seg, unsigned start, unsigned end)
+{
+	extern void *Hash;
+	extern unsigned long Hash_size;
+	unsigned *htab = Hash;
+	unsigned hsize = Hash_size;
+	unsigned v, hmask, va, last_va;
+	int found, last_found, i;
+	unsigned *hg, w1, last_w2, last_va0;
+
+	last_found = 0;
+	hmask = hsize / 64 - 1;
+	va = start;
+	start = (start >> 12) & 0xffff;
+	end = (end >> 12) & 0xffff;
+	for (v = start; v < end; ++v) {
+		found = 0;
+		hg = htab + (((v ^ seg) & hmask) * 16);
+		w1 = 0x80000000 | (seg << 7) | (v >> 10);
+		for (i = 0; i < 8; ++i, hg += 2) {
+			if (*hg == w1) {
+				found = 1;
+				break;
+			}
+		}
+		if (!found) {
+			w1 ^= 0x40;
+			hg = htab + ((~(v ^ seg) & hmask) * 16);
+			for (i = 0; i < 8; ++i, hg += 2) {
+				if (*hg == w1) {
+					found = 1;
+					break;
+				}
+			}
+		}
+		if (!(last_found && found && (hg[1] & ~0x180) == last_w2 + 4096)) {
+			if (last_found) {
+				if (last_va != last_va0)
+					printf(" ... %x", last_va);
+				printf("\n");
+			}
+			if (found) {
+				printf("%x to %x", va, hg[1]);
+				last_va0 = va;
+			}
+			last_found = found;
+		}
+		if (found) {
+			last_w2 = hg[1] & ~0x180;
+			last_va = va;
+		}
+		va += 4096;
+	}
+	if (last_found)
+		printf(" ... %x\n", last_va);
+}
+static unsigned hash_ctx;
+static unsigned hash_start;
+static unsigned hash_end;
+
+static void
+dump_hash_table()
+{
+	int seg;
+	unsigned seg_start, seg_end;
+
+	hash_ctx = 0;
+	hash_start = 0;
+	hash_end = 0xfffff000;
+	scanhex(&hash_ctx);
+	scanhex(&hash_start);
+	scanhex(&hash_end);
+	printf("Mappings for context %x\n", hash_ctx);
+	seg_start = hash_start;
+	for (seg = hash_start >> 28; seg <= hash_end >> 28; ++seg) {
+		seg_end = (seg << 28) | 0x0ffff000;
+		if (seg_end > hash_end)
+			seg_end = hash_end;
+		dump_hash_table_seg((hash_ctx << 4) + seg, seg_start, seg_end);
+		seg_start = seg_end + 0x1000;
+	}
+}
+
+/*
+ * Stuff for reading and writing memory safely
+ */
+extern inline void sync(void)
+{
+	asm volatile("sync; isync");
+}
+
+extern inline void __delay(unsigned int loops)
+{
+	if (loops != 0)
+		__asm__ __volatile__("mtctr %0; 1: bdnz 1b" : :
+				     "r" (loops) : "ctr");
+}
+
+int
+mread(unsigned adrs, void *buf, int size)
+{
+	volatile int n;
+	char *p, *q;
+
+	n = 0;
+	if( setjmp(bus_error_jmp) == 0 ){
+		debugger_fault_handler = handle_fault;
+		sync();
+		p = (char *) adrs;
+		q = (char *) buf;
+		switch (size) {
+		case 2: *(short *)q = *(short *)p;	break;
+		case 4: *(int *)q = *(int *)p;		break;
+		default:
+			for( ; n < size; ++n ) {
+				*q++ = *p++;
+				sync();
+			}
+		}
+		sync();
+		/* wait a little while to see if we get a machine check */
+		__delay(200);
+		n = size;
+	}
+	debugger_fault_handler = 0;
+	return n;
+}
+
+int
+mwrite(unsigned adrs, void *buf, int size)
+{
+	volatile int n;
+	char *p, *q;
+
+	n = 0;
+	if( setjmp(bus_error_jmp) == 0 ){
+		debugger_fault_handler = handle_fault;
+		sync();
+		p = (char *) adrs;
+		q = (char *) buf;
+		switch (size) {
+		case 2: *(short *)p = *(short *)q;	break;
+		case 4: *(int *)p = *(int *)q;		break;
+		default:
+			for( ; n < size; ++n ) {
+				*p++ = *q++;
+				sync();
+			}
+		}
+		sync();
+		n = size;
+	} else {
+		printf("*** Error writing address %x\n", adrs + n);
+	}
+	debugger_fault_handler = 0;
+	return n;
+}
+
+static int fault_type;
+static char *fault_chars[] = { "--", "**", "##" };
+
+static void
+handle_fault(struct pt_regs *regs)
+{
+	fault_type = regs->trap == 0x200? 0: regs->trap == 0x300? 1: 2;
+	longjmp(bus_error_jmp, 1);
+}
+
+#define SWAP(a, b, t)	((t) = (a), (a) = (b), (b) = (t))
+
+void
+byterev(unsigned char *val, int size)
+{
+	int t;
+	
+	switch (size) {
+	case 2:
+		SWAP(val[0], val[1], t);
+		break;
+	case 4:
+		SWAP(val[0], val[3], t);
+		SWAP(val[1], val[2], t);
+		break;
+	}
+}
+
+static int brev;
+static int mnoread;
+
+void
+memex()
+{
+    int cmd, inc, i, nslash;
+    unsigned n;
+    unsigned char val[4];
+
+    last_cmd = "m\n";
+    scanhex(&adrs);
+    while ((cmd = skipbl()) != '\n') {
+	switch( cmd ){
+	case 'b':	size = 1;	break;
+	case 'w':	size = 2;	break;
+	case 'l':	size = 4;	break;
+	case 'r': 	brev = !brev;	break;
+	case 'n':	mnoread = 1;	break;
+	case '.':	mnoread = 0;	break;
+	}
+    }
+    if( size <= 0 )
+	size = 1;
+    else if( size > 4 )
+	size = 4;
+    for(;;){
+	if (!mnoread)
+	    n = mread(adrs, val, size);
+	printf("%.8x%c", adrs, brev? 'r': ' ');
+	if (!mnoread) {
+	    if (brev)
+		byterev(val, size);
+	    putchar(' ');
+	    for (i = 0; i < n; ++i)
+		printf("%.2x", val[i]);
+	    for (; i < size; ++i)
+		printf("%s", fault_chars[fault_type]);
+	}
+	putchar(' ');
+	inc = size;
+	nslash = 0;
+	for(;;){
+	    if( scanhex(&n) ){
+		for (i = 0; i < size; ++i)
+		    val[i] = n >> (i * 8);
+		if (!brev)
+		    byterev(val, size);
+		mwrite(adrs, val, size);
+		inc = size;
+	    }
+	    cmd = skipbl();
+	    if (cmd == '\n')
+		break;
+	    inc = 0;
+	    switch (cmd) {
+	    case '\'':
+		for(;;){
+		    n = inchar();
+		    if( n == '\\' )
+			n = bsesc();
+		    else if( n == '\'' )
+			break;
+		    for (i = 0; i < size; ++i)
+			val[i] = n >> (i * 8);
+		    if (!brev)
+			byterev(val, size);
+		    mwrite(adrs, val, size);
+		    adrs += size;
+		}
+		adrs -= size;
+		inc = size;
+		break;
+	    case ',':
+		adrs += size;
+		break;
+	    case '.':
+		mnoread = 0;
+		break;
+	    case ';':
+		break;
+	    case 'x':
+	    case EOF:
+		scannl();
+		return;
+	    case 'b':
+	    case 'v':
+		size = 1;
+		break;
+	    case 'w':
+		size = 2;
+		break;
+	    case 'l':
+		size = 4;
+		break;
+	    case '^':
+		adrs -= size;
+		break;
+		break;
+	    case '/':
+		if (nslash > 0)
+		    adrs -= 1 << nslash;
+		else
+		    nslash = 0;
+		nslash += 4;
+		adrs += 1 << nslash;
+		break;
+	    case '\\':
+		if (nslash < 0)
+		    adrs += 1 << -nslash;
+		else
+		    nslash = 0;
+		nslash -= 4;
+		adrs -= 1 << -nslash;
+		break;
+	    case 'm':
+		scanhex(&adrs);
+		break;
+	    case 'n':
+		mnoread = 1;
+		break;
+	    case 'r':
+		brev = !brev;
+		break;
+	    case '<':
+		n = size;
+		scanhex(&n);
+		adrs -= n;
+		break;
+	    case '>':
+		n = size;
+		scanhex(&n);
+		adrs += n;
+		break;
+	    }
+	}
+	adrs += inc;
+    }
+}
+
+int
+bsesc()
+{
+	int c;
+
+	c = inchar();
+	switch( c ){
+	case 'n':	c = '\n';	break;
+	case 'r':	c = '\r';	break;
+	case 'b':	c = '\b';	break;
+	case 't':	c = '\t';	break;
+	}
+	return c;
+}
+
+#define isxdigit(c)	('0' <= (c) && (c) <= '9' || 'a' <= (c) && (c) <= 'f' \
+			 || 'A' <= (c) && (c) <= 'F')
+void
+dump()
+{
+	int c;
+
+	c = inchar();
+	if ((isxdigit(c) && c != 'f' && c != 'd') || c == '\n')
+		termch = c;
+	scanhex(&adrs);
+	if( termch != '\n')
+		termch = 0;
+	if( c == 'i' ){
+		scanhex(&nidump);
+		if( nidump == 0 )
+			nidump = 16;
+		adrs += ppc_inst_dump(adrs, nidump);
+		last_cmd = "di\n";
+	} else {
+		scanhex(&ndump);
+		if( ndump == 0 )
+			ndump = 64;
+		prdump(adrs, ndump);
+		adrs += ndump;
+		last_cmd = "d\n";
+	}
+}
+
+void
+prdump(unsigned adrs, int ndump)
+{
+	register int n, m, c, r, nr;
+	unsigned char temp[16];
+
+	for( n = ndump; n > 0; ){
+		printf("%.8x", adrs);
+		putchar(' ');
+		r = n < 16? n: 16;
+		nr = mread(adrs, temp, r);
+		adrs += nr;
+		for( m = 0; m < r; ++m ){
+			putchar((m & 3) == 0 && m > 0? '.': ' ');
+			if( m < nr )
+				printf("%.2x", temp[m]);
+			else
+				printf("%s", fault_chars[fault_type]);
+		}
+		for(; m < 16; ++m )
+			printf("   ");
+		printf("  |");
+		for( m = 0; m < r; ++m ){
+			if( m < nr ){
+				c = temp[m];
+				putchar(' ' <= c && c <= '~'? c: '.');
+			} else
+				putchar(' ');
+		}
+		n -= r;
+		for(; m < 16; ++m )
+			putchar(' ');
+		printf("|\n");
+		if( nr < r )
+			break;
+	}
+}
+
+int
+ppc_inst_dump(unsigned adr, int count)
+{
+	int nr, dotted;
+	unsigned first_adr;
+	unsigned long inst, last_inst;
+	unsigned char val[4];
+
+	dotted = 0;
+	for (first_adr = adr; count > 0; --count, adr += 4){
+		nr = mread(adr, val, 4);
+		if( nr == 0 ){
+			const char *x = fault_chars[fault_type];
+			printf("%.8x  %s%s%s%s\n", adr, x, x, x, x);
+			break;
+		}
+		inst = GETWORD(val);
+		if (adr > first_adr && inst == last_inst) {
+			if (!dotted) {
+				printf(" ...\n");
+				dotted = 1;
+			}
+			continue;
+		}
+		dotted = 0;
+		last_inst = inst;
+		printf("%.8x  ", adr);
+		printf("%.8x\t", inst);
+		print_insn_big_powerpc(stdout, inst, adr);	/* always returns 4 */
+		printf("\n");
+	}
+	return adr - first_adr;
+}
+
+void
+print_address(addr)
+unsigned addr;
+{
+	printf("0x%x", addr);
+}
+
+/*
+ * Memory operations - move, set, print differences
+ */
+static unsigned mdest;		/* destination address */
+static unsigned msrc;		/* source address */
+static unsigned mval;		/* byte value to set memory to */
+static unsigned mcount;		/* # bytes to affect */
+static unsigned mdiffs;		/* max # differences to print */
+
+void
+memops(int cmd)
+{
+	scanhex(&mdest);
+	if( termch != '\n' )
+		termch = 0;
+	scanhex(cmd == 's'? &mval: &msrc);
+	if( termch != '\n' )
+		termch = 0;
+	scanhex(&mcount);
+	switch( cmd ){
+	case 'm':
+		memmove((void *)mdest, (void *)msrc, mcount);
+		break;
+	case 's':
+		memset((void *)mdest, mval, mcount);
+		break;
+	case 'd':
+		if( termch != '\n' )
+			termch = 0;
+		scanhex(&mdiffs);
+		memdiffs((unsigned char *)mdest, (unsigned char *)msrc, mcount, mdiffs);
+		break;
+	}
+}
+
+void
+memdiffs(unsigned char *p1, unsigned char *p2, unsigned nb, unsigned maxpr)
+{
+	unsigned n, prt;
+
+	prt = 0;
+	for( n = nb; n > 0; --n )
+		if( *p1++ != *p2++ )
+			if( ++prt <= maxpr )
+				printf("%.8x %.2x # %.8x %.2x\n", (unsigned)p1 - 1,
+					p1[-1], (unsigned)p2 - 1, p2[-1]);
+	if( prt > maxpr )
+		printf("Total of %d differences\n", prt);
+}
+
+static unsigned mend;
+static unsigned mask;
+
+void
+memlocate()
+{
+	unsigned a, n;
+	unsigned char val[4];
+
+	last_cmd = "ml";
+	scanhex(&mdest);
+	if (termch != '\n') {
+		termch = 0;
+		scanhex(&mend);
+		if (termch != '\n') {
+			termch = 0;
+			scanhex(&mval);
+			mask = ~0;
+			if (termch != '\n') termch = 0;
+			scanhex(&mask);
+		}
+	}
+	n = 0;
+	for (a = mdest; a < mend; a += 4) {
+		if (mread(a, val, 4) == 4
+			&& ((GETWORD(val) ^ mval) & mask) == 0) {
+			printf("%.8x:  %.8x\n", a, GETWORD(val));
+			if (++n >= 10)
+				break;
+		}
+	}
+}
+
+static unsigned mskip = 0x1000;
+static unsigned mlim = 0xffffffff;
+
+void
+memzcan()
+{
+	unsigned char v;
+	unsigned a;
+	int ok, ook;
+
+	scanhex(&mdest);
+	if (termch != '\n') termch = 0;
+	scanhex(&mskip);
+	if (termch != '\n') termch = 0;
+	scanhex(&mlim);
+	ook = 0;
+	for (a = mdest; a < mlim; a += mskip) {
+		ok = mread(a, &v, 1);
+		if (ok && !ook) {
+			printf("%.8x .. ", a);
+			fflush(stdout);
+		} else if (!ok && ook)
+			printf("%.8x\n", a - mskip);
+		ook = ok;
+		if (a + mskip < a)
+			break;
+	}
+	if (ook)
+		printf("%.8x\n", a - mskip);
+}
+
+/* Input scanning routines */
+int
+skipbl()
+{
+	int c;
+
+	if( termch != 0 ){
+		c = termch;
+		termch = 0;
+	} else
+		c = inchar();
+	while( c == ' ' || c == '\t' )
+		c = inchar();
+	return c;
+}
+
+int
+scanhex(vp)
+unsigned *vp;
+{
+	int c, d;
+	unsigned v;
+
+	c = skipbl();
+	d = hexdigit(c);
+	if( d == EOF ){
+		termch = c;
+		return 0;
+	}
+	v = 0;
+	do {
+		v = (v << 4) + d;
+		c = inchar();
+		d = hexdigit(c);
+	} while( d != EOF );
+	termch = c;
+	*vp = v;
+	return 1;
+}
+
+void
+scannl()
+{
+	int c;
+
+	c = termch;
+	termch = 0;
+	while( c != '\n' )
+		c = inchar();
+}
+
+int
+hexdigit(c)
+{
+	if( '0' <= c && c <= '9' )
+		return c - '0';
+	if( 'A' <= c && c <= 'F' )
+		return c - ('A' - 10);
+	if( 'a' <= c && c <= 'f' )
+		return c - ('a' - 10);
+	return EOF;
+}
+
+void
+getstring(char *s, int size)
+{
+	int c;
+
+	c = skipbl();
+	do {
+		if( size > 1 ){
+			*s++ = c;
+			--size;
+		}
+		c = inchar();
+	} while( c != ' ' && c != '\t' && c != '\n' );
+	termch = c;
+	*s = 0;
+}
+
+static char line[256];
+static char *lineptr;
+
+void
+flush_input()
+{
+	lineptr = NULL;
+}
+
+int
+inchar()
+{
+	if (lineptr == NULL || *lineptr == 0) {
+		if (fgets(line, sizeof(line), stdin) == NULL) {
+			lineptr = NULL;
+			return EOF;
+		}
+		lineptr = line;
+	}
+	return *lineptr++;
+}
+
+void
+take_input(str)
+char *str;
+{
+	lineptr = str;
+}
diff -u --recursive --new-file v2.3.3/linux/arch/sparc/defconfig linux/arch/sparc/defconfig
--- v2.3.3/linux/arch/sparc/defconfig	Mon May 17 09:55:21 1999
+++ linux/arch/sparc/defconfig	Mon May 31 22:08:10 1999
@@ -62,6 +62,7 @@
 CONFIG_SUN_MOSTEK_RTC=y
 # CONFIG_SUN_BPP is not set
 # CONFIG_SUN_VIDEOPIX is not set
+CONFIG_SUN_AURORA=m
 
 #
 # Linux/SPARC audio subsystem (EXPERIMENTAL)
@@ -128,6 +129,10 @@
 # CONFIG_IPX_INTERN is not set
 # CONFIG_SPX is not set
 CONFIG_ATALK=m
+CONFIG_DECNET=m
+CONFIG_DECNET_SIOCGIFCONF=y
+# CONFIG_DECNET_ROUTER is not set
+CONFIG_DECNET_RAW=y
 # CONFIG_X25 is not set
 # CONFIG_LAPB is not set
 # CONFIG_BRIDGE is not set
@@ -272,6 +277,7 @@
 # CONFIG_MAC_PARTITION is not set
 CONFIG_SMD_DISKLABEL=y
 CONFIG_SOLARIS_X86_PARTITION=y
+# CONFIG_SGI_DISKLABEL is not set
 # CONFIG_UNIXWARE_DISKLABEL is not set
 CONFIG_AMIGA_PARTITION=y
 CONFIG_NLS=y
diff -u --recursive --new-file v2.3.3/linux/arch/sparc/kernel/irq.c linux/arch/sparc/kernel/irq.c
--- v2.3.3/linux/arch/sparc/kernel/irq.c	Thu Apr 22 19:24:51 1999
+++ linux/arch/sparc/kernel/irq.c	Sat May 29 11:09:04 1999
@@ -1,4 +1,4 @@
-/*  $Id: irq.c,v 1.93 1999/04/21 06:15:45 anton Exp $
+/*  $Id: irq.c,v 1.94 1999/05/28 14:59:20 anton Exp $
  *  arch/sparc/kernel/irq.c:  Interrupt request handling routines. On the
  *                            Sparc the IRQ's are basically 'cast in stone'
  *                            and you are supposed to probe the prom's device
@@ -491,15 +491,13 @@
 	extern void smp4m_irq_rotate(int cpu);
 #endif
 
+	irq_enter(cpu, irq);
 	disable_pil_irq(irq);
-#if 0 /* FIXME: rotating IRQs halts the machine during SCSI probe. -ecd */
 #ifdef __SMP__
 	/* Only rotate on lower priority IRQ's (scsi, ethernet, etc.). */
 	if(irq < 10)
 		smp4m_irq_rotate(cpu);
 #endif
-#endif
-	irq_enter(cpu, irq);
 	action = *(irq + irq_action);
 	kstat.irqs[cpu][irq]++;
 	do {
@@ -508,8 +506,8 @@
 		action->handler(irq, action->dev_id, regs);
 		action = action->next;
 	} while (action);
-	irq_exit(cpu, irq);
 	enable_pil_irq(irq);
+	irq_exit(cpu, irq);
 }
 
 #ifdef CONFIG_BLK_DEV_FD
diff -u --recursive --new-file v2.3.3/linux/arch/sparc/kernel/ptrace.c linux/arch/sparc/kernel/ptrace.c
--- v2.3.3/linux/arch/sparc/kernel/ptrace.c	Wed Mar 24 15:10:40 1999
+++ linux/arch/sparc/kernel/ptrace.c	Sat May 22 12:42:04 1999
@@ -540,6 +540,7 @@
 		if((!child->dumpable ||
 		    (current->uid != child->euid) ||
 		    (current->uid != child->uid) ||
+		    (current->uid != child->suid) ||
 		    (current->gid != child->egid) ||
 		    (current->gid != child->sgid) || 
 	 	    (!cap_issubset(child->cap_permitted, current->cap_permitted)) ||
diff -u --recursive --new-file v2.3.3/linux/arch/sparc/kernel/setup.c linux/arch/sparc/kernel/setup.c
--- v2.3.3/linux/arch/sparc/kernel/setup.c	Thu Apr 22 19:24:51 1999
+++ linux/arch/sparc/kernel/setup.c	Sat May 29 11:09:04 1999
@@ -1,4 +1,4 @@
-/*  $Id: setup.c,v 1.105 1999/04/13 14:17:08 jj Exp $
+/*  $Id: setup.c,v 1.106 1999/05/28 16:03:18 anton Exp $
  *  linux/arch/sparc/kernel/setup.c
  *
  *  Copyright (C) 1995  David S. Miller (davem@caip.rutgers.edu)
@@ -456,6 +456,7 @@
 					prom_printf("MrCoffee keyboard\n");
 				} else {
 					prom_printf("Inconsistent or unknown console\n");
+					prom_printf("You cannot mix serial and non serial input/output devices\n");
 					prom_halt();
 				}
 			}
diff -u --recursive --new-file v2.3.3/linux/arch/sparc/kernel/sys_sunos.c linux/arch/sparc/kernel/sys_sunos.c
--- v2.3.3/linux/arch/sparc/kernel/sys_sunos.c	Tue Oct 27 09:52:20 1998
+++ linux/arch/sparc/kernel/sys_sunos.c	Mon May 24 13:03:49 1999
@@ -1,4 +1,4 @@
-/* $Id: sys_sunos.c,v 1.94 1998/10/12 06:15:04 jj Exp $
+/* $Id: sys_sunos.c,v 1.97 1999/05/24 19:40:39 davem Exp $
  * sys_sunos.c: SunOS specific syscall compatibility support.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -1198,7 +1198,7 @@
 
 	lock_kernel();
 	ret = check_nonblock(sys_readv(fd,vector,count),fd);
-	lock_kernel();
+	unlock_kernel();
 	return ret;
 }
 
diff -u --recursive --new-file v2.3.3/linux/arch/sparc/math-emu/Makefile linux/arch/sparc/math-emu/Makefile
--- v2.3.3/linux/arch/sparc/math-emu/Makefile	Tue Apr 14 17:44:19 1998
+++ linux/arch/sparc/math-emu/Makefile	Sat May 29 11:09:04 1999
@@ -14,13 +14,13 @@
 		fdtos.o fitoq.o fmovs.o fmuld.o fmulq.o fmuls.o		\
 		fnegs.o fqtod.o fqtoi.o fqtos.o fsmuld.o fsqrtd.o	\
 		fsqrtq.o fsqrts.o fstod.o fstoi.o fstoq.o fsubd.o 	\
-		fsubq.o	fsubs.o udivmodti4.o
+		fsubq.o	fsubs.o
 
 LINKS	 := double.h faddd.c faddq.c fadds.c fdivd.c fdivq.c fdivs.c	\
 		fdtoi.c fitoq.c fmuld.c fmulq.c fmuls.c fqtoi.c		\
 		fsqrtd.c fsqrtq.c fsqrts.c fstoi.c fsubd.c 		\
-		fsubq.c	fsubs.c op-1.h op-2.h op-4.h op-common.h quad.h	\
-		single.h soft-fp.h udivmodti4.c
+		fsubq.c	fsubs.c op-1.h op-2.h op-4.h op-8.h		\
+		op-common.h quad.h single.h soft-fp.h
 
 .S.s:
 	$(CPP) -D__ASSEMBLY__ -ansi $< -o $*.s
diff -u --recursive --new-file v2.3.3/linux/arch/sparc/math-emu/fabss.c linux/arch/sparc/math-emu/fabss.c
--- v2.3.3/linux/arch/sparc/math-emu/fabss.c	Wed Mar 10 16:53:36 1999
+++ linux/arch/sparc/math-emu/fabss.c	Sat May 29 11:09:04 1999
@@ -1,6 +1,12 @@
+/* $Id: fabss.c,v 1.8 1999/05/28 13:41:33 jj Exp $
+ * arch/sparc/math-emu/fabss.c
+ *
+ * Copyright (C) 1998 Peter Maydell (pmaydell@chiark.greenend.org.uk)
+ *
+ */
+
 int FABSS(unsigned long *rd, unsigned long *rs2)
 {
-	/* Clear the sign bit (high bit of word 0) */
 	rd[0] = rs2[0] & 0x7fffffffUL;
 	return 0;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc/math-emu/fcmpd.c linux/arch/sparc/math-emu/fcmpd.c
--- v2.3.3/linux/arch/sparc/math-emu/fcmpd.c	Wed Mar 10 16:53:36 1999
+++ linux/arch/sparc/math-emu/fcmpd.c	Sat May 29 11:09:04 1999
@@ -1,18 +1,33 @@
+/* $Id: fcmpd.c,v 1.8 1999/05/28 13:41:36 jj Exp $
+ * arch/sparc/math-emu/fcmpd.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1998 Peter Maydell (pmaydell@chiark.greenend.org.uk)
+ *
+ */
+
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "double.h"
 
 int FCMPD(void *rd, void *rs2, void *rs1)
 {
+	FP_DECL_EX;
 	FP_DECL_D(A); FP_DECL_D(B);
 	long ret;
-	unsigned long *fsr = rd;
+	unsigned long fsr;
 	
-	__FP_UNPACK_D(A, rs1);
-	__FP_UNPACK_D(B, rs2);
-	FP_CMP_D(ret, B, A, 2);
-	if (ret == -1)
-		ret = 2;
-
-	*fsr = (*fsr & ~0xc00) | (ret << 10); 
-	return 0;
+	FP_UNPACK_RAW_DP(A, rs1);
+	FP_UNPACK_RAW_DP(B, rs2);
+	FP_CMP_D(ret, B, A, 3);
+	if (ret == 3 && (FP_ISSIGNAN_D(A) || FP_ISSIGNAN_D(B)))
+		FP_SET_EXCEPTION(FP_EX_INVALID);
+	if (!FP_INHIBIT_RESULTS) {
+		if (ret == -1) ret = 2;
+		fsr = *(long *)rd;
+		fsr &= ~0xc00;
+		fsr |= (ret << 10);
+		*(long *)rd = fsr;
+	}
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc/math-emu/fcmped.c linux/arch/sparc/math-emu/fcmped.c
--- v2.3.3/linux/arch/sparc/math-emu/fcmped.c	Wed Mar 10 16:53:36 1999
+++ linux/arch/sparc/math-emu/fcmped.c	Sat May 29 11:09:04 1999
@@ -1,18 +1,33 @@
+/* $Id: fcmped.c,v 1.8 1999/05/28 13:41:38 jj Exp $
+ * arch/sparc/math-emu/fcmped.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1998 Peter Maydell (pmaydell@chiark.greenend.org.uk)
+ *
+ */
+
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "double.h"
 
 int FCMPED(void *rd, void *rs2, void *rs1)
 {
+	FP_DECL_EX;
 	FP_DECL_D(A); FP_DECL_D(B);
 	long ret;
-	unsigned long *fsr = rd;
+	unsigned long fsr;
 	
-	__FP_UNPACK_D(A, rs1);
-	__FP_UNPACK_D(B, rs2);
-	FP_CMP_D(ret, B, A, 2);
-	if (ret == -1)
-		ret = 2;
-
-	*fsr = (*fsr & ~0xc00) | (ret << 10); 
-	return 0;
+	FP_UNPACK_RAW_DP(A, rs1);
+	FP_UNPACK_RAW_DP(B, rs2);
+	FP_CMP_D(ret, B, A, 3);
+	if (ret == 3)
+		FP_SET_EXCEPTION(FP_EX_INVALID);
+	if (!FP_INHIBIT_RESULTS) {
+		if (ret == -1) ret = 2;
+		fsr = *(long *)rd;
+		fsr &= ~0xc00;
+		fsr |= (ret << 10);
+		*(long *)rd = fsr;
+	}
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc/math-emu/fcmpeq.c linux/arch/sparc/math-emu/fcmpeq.c
--- v2.3.3/linux/arch/sparc/math-emu/fcmpeq.c	Wed Mar 10 16:53:36 1999
+++ linux/arch/sparc/math-emu/fcmpeq.c	Sat May 29 11:09:04 1999
@@ -1,18 +1,33 @@
+/* $Id: fcmpeq.c,v 1.8 1999/05/28 13:41:42 jj Exp $
+ * arch/sparc/math-emu/fcmpeq.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1998 Peter Maydell (pmaydell@chiark.greenend.org.uk)
+ *
+ */
+
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "quad.h"
 
 int FCMPEQ(void *rd, void *rs2, void *rs1)
 {
+	FP_DECL_EX;
 	FP_DECL_Q(A); FP_DECL_Q(B);
 	long ret;
 	unsigned long fsr;
 	
-	__FP_UNPACK_Q(A, rs1);
-	__FP_UNPACK_Q(B, rs2);
+	FP_UNPACK_RAW_QP(A, rs1);
+	FP_UNPACK_RAW_QP(B, rs2);
 	FP_CMP_Q(ret, B, A, 3);
-	if (ret == -1) ret = 2;
-	fsr = *(unsigned long *)rd;
-	fsr &= ~0xc00; fsr |= (ret << 10);
-	*(unsigned long *)rd = fsr;
-	return 0;
+	if (ret == 3)
+		FP_SET_EXCEPTION(FP_EX_INVALID);
+	if (!FP_INHIBIT_RESULTS) {
+		if (ret == -1) ret = 2;
+		fsr = *(long *)rd;
+		fsr &= ~0xc00;
+		fsr |= (ret << 10);
+		*(long *)rd = fsr;
+	}
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc/math-emu/fcmpes.c linux/arch/sparc/math-emu/fcmpes.c
--- v2.3.3/linux/arch/sparc/math-emu/fcmpes.c	Wed Mar 10 16:53:36 1999
+++ linux/arch/sparc/math-emu/fcmpes.c	Sat May 29 11:09:04 1999
@@ -1,18 +1,33 @@
+/* $Id: fcmpes.c,v 1.8 1999/05/28 13:41:45 jj Exp $
+ * arch/sparc/math-emu/fcmpes.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1998 Peter Maydell (pmaydell@chiark.greenend.org.uk)
+ *
+ */
+
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "single.h"
 
 int FCMPES(void *rd, void *rs2, void *rs1)
 {
+	FP_DECL_EX;
 	FP_DECL_S(A); FP_DECL_S(B);
 	long ret;
-	unsigned long *fsr = rd;
+	unsigned long fsr;
 	
-	__FP_UNPACK_S(A, rs1);
-	__FP_UNPACK_S(B, rs2);
-	FP_CMP_S(ret, B, A, 1);
-	if (ret == -1)
-		ret = 2;
-
-	*fsr = (*fsr & ~0xc00) | (ret << 10); 
-	return 0;
+	FP_UNPACK_RAW_SP(A, rs1);
+	FP_UNPACK_RAW_SP(B, rs2);
+	FP_CMP_S(ret, B, A, 3);
+	if (ret == 3)
+		FP_SET_EXCEPTION(FP_EX_INVALID);
+	if (!FP_INHIBIT_RESULTS) {
+		if (ret == -1) ret = 2;
+		fsr = *(long *)rd;
+		fsr &= ~0xc00;
+		fsr |= (ret << 10);
+		*(long *)rd = fsr;
+	}
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc/math-emu/fcmpq.c linux/arch/sparc/math-emu/fcmpq.c
--- v2.3.3/linux/arch/sparc/math-emu/fcmpq.c	Wed Mar 10 16:53:36 1999
+++ linux/arch/sparc/math-emu/fcmpq.c	Sat May 29 11:09:04 1999
@@ -1,18 +1,33 @@
+/* $Id: fcmpq.c,v 1.8 1999/05/28 13:41:48 jj Exp $
+ * arch/sparc/math-emu/fcmpq.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1998 Peter Maydell (pmaydell@chiark.greenend.org.uk)
+ *
+ */
+
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "quad.h"
 
 int FCMPQ(void *rd, void *rs2, void *rs1)
 {
+	FP_DECL_EX;
 	FP_DECL_Q(A); FP_DECL_Q(B);
 	long ret;
 	unsigned long fsr;
 	
-	__FP_UNPACK_Q(A, rs1);
-	__FP_UNPACK_Q(B, rs2);
+	FP_UNPACK_RAW_QP(A, rs1);
+	FP_UNPACK_RAW_QP(B, rs2);
 	FP_CMP_Q(ret, B, A, 3);
-	if (ret == -1) ret = 2;
-	fsr = *(unsigned long *)rd;
-	fsr &= ~0xc00; fsr |= (ret << 10);
-	*(unsigned long *)rd = fsr;
-	return 0;
+	if (ret == 3 && (FP_ISSIGNAN_Q(A) || FP_ISSIGNAN_Q(B)))
+		FP_SET_EXCEPTION(FP_EX_INVALID);
+	if (!FP_INHIBIT_RESULTS) {
+		if (ret == -1) ret = 2;
+		fsr = *(long *)rd;
+		fsr &= ~0xc00;
+		fsr |= (ret << 10);
+		*(long *)rd = fsr;
+	}
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc/math-emu/fcmps.c linux/arch/sparc/math-emu/fcmps.c
--- v2.3.3/linux/arch/sparc/math-emu/fcmps.c	Wed Mar 10 16:53:36 1999
+++ linux/arch/sparc/math-emu/fcmps.c	Sat May 29 11:09:04 1999
@@ -1,18 +1,33 @@
+/* $Id: fcmps.c,v 1.8 1999/05/28 13:41:51 jj Exp $
+ * arch/sparc/math-emu/fcmps.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1998 Peter Maydell (pmaydell@chiark.greenend.org.uk)
+ *
+ */
+
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "single.h"
 
 int FCMPS(void *rd, void *rs2, void *rs1)
 {
+	FP_DECL_EX;
 	FP_DECL_S(A); FP_DECL_S(B);
 	long ret;
-	unsigned long *fsr = rd;
+	unsigned long fsr;
 	
-	__FP_UNPACK_S(A, rs1);
-	__FP_UNPACK_S(B, rs2);
-	FP_CMP_S(ret, B, A, 1);
-	if (ret == -1)
-		ret = 2;
-
-	*fsr = (*fsr & ~0xc00) | (ret << 10); 
-	return 0;
+	FP_UNPACK_RAW_SP(A, rs1);
+	FP_UNPACK_RAW_SP(B, rs2);
+	FP_CMP_S(ret, B, A, 3);
+	if (ret == 3 && (FP_ISSIGNAN_S(A) || FP_ISSIGNAN_S(B)))
+		FP_SET_EXCEPTION(FP_EX_INVALID);
+	if (!FP_INHIBIT_RESULTS) {
+		if (ret == -1) ret = 2;
+		fsr = *(long *)rd;
+		fsr &= ~0xc00;
+		fsr |= (ret << 10);
+		*(long *)rd = fsr;
+	}
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc/math-emu/fdmulq.c linux/arch/sparc/math-emu/fdmulq.c
--- v2.3.3/linux/arch/sparc/math-emu/fdmulq.c	Wed Mar 10 16:53:36 1999
+++ linux/arch/sparc/math-emu/fdmulq.c	Sat May 29 11:09:04 1999
@@ -1,15 +1,26 @@
+/* $Id: fdmulq.c,v 1.9 1999/05/28 13:41:56 jj Exp $
+ * arch/sparc/math-emu/fdmulq.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1998 Peter Maydell (pmaydell@chiark.greenend.org.uk)
+ *
+ */
+
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "quad.h"
 #include "double.h"
 
 int FDMULQ(void *rd, void *rs2, void *rs1)
 {
+	FP_DECL_EX;
 	FP_DECL_D(IN); FP_DECL_Q(A); FP_DECL_Q(B); FP_DECL_Q(R);
 
-	__FP_UNPACK_D(IN, rs1);
+	FP_UNPACK_DP(IN, rs1);
 	FP_CONV(Q,D,4,2,A,IN);
-	__FP_UNPACK_D(IN, rs2);
+	FP_UNPACK_DP(IN, rs2);
 	FP_CONV(Q,D,4,2,B,IN);
 	FP_MUL_Q(R, A, B);
-	return __FP_PACK_Q(rd, R);
+	FP_PACK_QP(rd, R);
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc/math-emu/fdtoq.c linux/arch/sparc/math-emu/fdtoq.c
--- v2.3.3/linux/arch/sparc/math-emu/fdtoq.c	Wed Mar 10 16:53:36 1999
+++ linux/arch/sparc/math-emu/fdtoq.c	Sat May 29 11:09:04 1999
@@ -1,12 +1,23 @@
+/* $Id: fdtoq.c,v 1.9 1999/05/28 13:42:01 jj Exp $
+ * arch/sparc/math-emu/fdtoq.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1998 Peter Maydell (pmaydell@chiark.greenend.org.uk)
+ *
+ */
+
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "quad.h"
 #include "double.h"
 
 int FDTOQ(void *rd, void *rs2)
 {
+	FP_DECL_EX;
 	FP_DECL_D(A); FP_DECL_Q(R);
 
-	__FP_UNPACK_D(A, rs2);
+	FP_UNPACK_DP(A, rs2);
 	FP_CONV(Q,D,4,2,R,A);
-	return __FP_PACK_Q(rd, R);
+	FP_PACK_QP(rd, R);
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc/math-emu/fdtos.c linux/arch/sparc/math-emu/fdtos.c
--- v2.3.3/linux/arch/sparc/math-emu/fdtos.c	Wed Mar 10 16:53:36 1999
+++ linux/arch/sparc/math-emu/fdtos.c	Sat May 29 11:09:04 1999
@@ -1,12 +1,23 @@
+/* $Id: fdtos.c,v 1.9 1999/05/28 13:42:03 jj Exp $
+ * arch/sparc/math-emu/fdtos.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1998 Peter Maydell (pmaydell@chiark.greenend.org.uk)
+ *
+ */
+
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "double.h"
 #include "single.h"
 
 int FDTOS(void *rd, void *rs2)
 {
+	FP_DECL_EX;
 	FP_DECL_D(A); FP_DECL_S(R);
 
-	__FP_UNPACK_D(A, rs2);
+	FP_UNPACK_DP(A, rs2);
 	FP_CONV(S,D,1,2,R,A);
-	return __FP_PACK_S(rd, R);
+	FP_PACK_SP(rd, R);
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc/math-emu/fmovs.c linux/arch/sparc/math-emu/fmovs.c
--- v2.3.3/linux/arch/sparc/math-emu/fmovs.c	Tue Apr 14 17:44:19 1998
+++ linux/arch/sparc/math-emu/fmovs.c	Sat May 29 11:09:04 1999
@@ -1,3 +1,10 @@
+/* $Id: fmovs.c,v 1.7 1999/05/28 13:42:05 jj Exp $
+ * arch/sparc/math-emu/fmovs.c
+ *
+ * Copyright (C) 1998 Peter Maydell (pmaydell@chiark.greenend.org.uk)
+ *
+ */
+
 int FMOVS(unsigned long *rd, unsigned long *rs2)
 {
 	rd[0] = rs2[0];
diff -u --recursive --new-file v2.3.3/linux/arch/sparc/math-emu/fnegs.c linux/arch/sparc/math-emu/fnegs.c
--- v2.3.3/linux/arch/sparc/math-emu/fnegs.c	Wed Mar 10 16:53:36 1999
+++ linux/arch/sparc/math-emu/fnegs.c	Sat May 29 11:09:04 1999
@@ -1,3 +1,10 @@
+/* $Id: fnegs.c,v 1.9 1999/05/28 13:42:06 jj Exp $
+ * arch/sparc/math-emu/fnegs.c
+ *
+ * Copyright (C) 1998 Peter Maydell (pmaydell@chiark.greenend.org.uk)
+ *
+ */
+
 int FNEGS(unsigned long *rd, unsigned long *rs2)
 {
  	/* just change the sign bit */
diff -u --recursive --new-file v2.3.3/linux/arch/sparc/math-emu/fqtod.c linux/arch/sparc/math-emu/fqtod.c
--- v2.3.3/linux/arch/sparc/math-emu/fqtod.c	Wed Mar 10 16:53:36 1999
+++ linux/arch/sparc/math-emu/fqtod.c	Sat May 29 11:09:04 1999
@@ -1,12 +1,23 @@
+/* $Id: fqtod.c,v 1.9 1999/05/28 13:42:08 jj Exp $
+ * arch/sparc/math-emu/fqtod.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1998 Peter Maydell (pmaydell@chiark.greenend.org.uk)
+ *
+ */
+
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "quad.h"
 #include "double.h"
 
 int FQTOD(void *rd, void *rs2)
 {
+	FP_DECL_EX;
 	FP_DECL_Q(A); FP_DECL_D(R);
 
-	__FP_UNPACK_Q(A, rs2);
+	FP_UNPACK_QP(A, rs2);
 	FP_CONV(D,Q,2,4,R,A);
-	return __FP_PACK_D(rd, R);
+	FP_PACK_DP(rd, R);
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc/math-emu/fqtos.c linux/arch/sparc/math-emu/fqtos.c
--- v2.3.3/linux/arch/sparc/math-emu/fqtos.c	Wed Mar 10 16:53:36 1999
+++ linux/arch/sparc/math-emu/fqtos.c	Sat May 29 11:09:04 1999
@@ -1,12 +1,23 @@
+/* $Id: fqtos.c,v 1.9 1999/05/28 13:42:10 jj Exp $
+ * arch/sparc/math-emu/fqtos.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1998 Peter Maydell (pmaydell@chiark.greenend.org.uk)
+ *
+ */
+
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "quad.h"
 #include "single.h"
 
 int FQTOS(void *rd, void *rs2)
 {
+	FP_DECL_EX;
 	FP_DECL_Q(A); FP_DECL_S(R);
 
-	__FP_UNPACK_Q(A, rs2);
+	FP_UNPACK_QP(A, rs2);
 	FP_CONV(S,Q,1,4,R,A);
-	return __FP_PACK_S(rd, R);
+	FP_PACK_SP(rd, R);
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc/math-emu/fsmuld.c linux/arch/sparc/math-emu/fsmuld.c
--- v2.3.3/linux/arch/sparc/math-emu/fsmuld.c	Wed Mar 10 16:53:36 1999
+++ linux/arch/sparc/math-emu/fsmuld.c	Sat May 29 11:09:04 1999
@@ -1,15 +1,26 @@
+/* $Id: fsmuld.c,v 1.9 1999/05/28 13:42:12 jj Exp $
+ * arch/sparc/math-emu/fsmuld.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1998 Peter Maydell (pmaydell@chiark.greenend.org.uk)
+ *
+ */
+
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "double.h"
 #include "single.h"
 
 int FSMULD(void *rd, void *rs2, void *rs1)
 {
+	FP_DECL_EX;
 	FP_DECL_S(IN); FP_DECL_D(A); FP_DECL_D(B); FP_DECL_D(R);
 
-	__FP_UNPACK_S(IN, rs1);
+	FP_UNPACK_SP(IN, rs1);
 	FP_CONV(D,S,2,1,A,IN);
-	__FP_UNPACK_S(IN, rs2);
+	FP_UNPACK_SP(IN, rs2);
 	FP_CONV(D,S,2,1,B,IN);
 	FP_MUL_D(R, A, B);
-	return __FP_PACK_D(rd, R);
+	FP_PACK_DP(rd, R);
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc/math-emu/fstod.c linux/arch/sparc/math-emu/fstod.c
--- v2.3.3/linux/arch/sparc/math-emu/fstod.c	Wed Mar 10 16:53:36 1999
+++ linux/arch/sparc/math-emu/fstod.c	Sat May 29 11:09:04 1999
@@ -1,12 +1,23 @@
+/* $Id: fstod.c,v 1.9 1999/05/28 13:42:14 jj Exp $
+ * arch/sparc/math-emu/fstod.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1998 Peter Maydell (pmaydell@chiark.greenend.org.uk)
+ *
+ */
+
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "double.h"
 #include "single.h"
 
 int FSTOD(void *rd, void *rs2)
 {
+	FP_DECL_EX;
 	FP_DECL_S(A); FP_DECL_D(R);
 
-	__FP_UNPACK_S(A, rs2);
+	FP_UNPACK_SP(A, rs2);
 	FP_CONV(D,S,2,1,R,A);
-	return __FP_PACK_D(rd, R);
+	FP_PACK_DP(rd, R);
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc/math-emu/fstoq.c linux/arch/sparc/math-emu/fstoq.c
--- v2.3.3/linux/arch/sparc/math-emu/fstoq.c	Wed Mar 10 16:53:36 1999
+++ linux/arch/sparc/math-emu/fstoq.c	Sat May 29 11:09:04 1999
@@ -1,12 +1,23 @@
+/* $Id: fstoq.c,v 1.9 1999/05/28 13:42:16 jj Exp $
+ * arch/sparc/math-emu/fstoq.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1998 Peter Maydell (pmaydell@chiark.greenend.org.uk)
+ *
+ */
+
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "quad.h"
 #include "single.h"
 
 int FSTOQ(void *rd, void *rs2)
 {
+	FP_DECL_EX;
 	FP_DECL_S(A); FP_DECL_Q(R);
 
-	__FP_UNPACK_S(A, rs2);
+	FP_UNPACK_SP(A, rs2);
 	FP_CONV(Q,S,4,1,R,A);
-	return __FP_PACK_Q(rd, R);
+	FP_PACK_QP(rd, R);
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc/math-emu/math.c linux/arch/sparc/math-emu/math.c
--- v2.3.3/linux/arch/sparc/math-emu/math.c	Wed Mar 10 16:53:36 1999
+++ linux/arch/sparc/math-emu/math.c	Sat May 29 11:09:04 1999
@@ -1,26 +1,19 @@
-/* 
+/*
  * arch/sparc/math-emu/math.c
  *
  * Copyright (C) 1998 Peter Maydell (pmaydell@chiark.greenend.org.uk)
- * Based on the sparc64 code by Jakub Jelinek.
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
  *
  * This is a good place to start if you're trying to understand the
- * emulation code, because it's pretty simple. What we do is 
+ * emulation code, because it's pretty simple. What we do is
  * essentially analyse the instruction to work out what the operation
  * is and which registers are involved. We then execute the appropriate
  * FXXXX function. [The floating point queue introduces a minor wrinkle;
  * see below...]
  * The fxxxxx.c files each emulate a single insn. They look relatively
  * simple because the complexity is hidden away in an unholy tangle
- * of preprocessor macros. 
- *
- * WARNING : don't look at the macro definitions unless you 
- * absolutely have to! They're extremely ugly, rather complicated
- * and a single line in an fxxxx.c file can expand to the equivalent 
- * of  30 lines or more of C. Of course, any error in those 30 lines 
- * is reported by the compiler as an error in the single line with the
- * macro usage...
- * Question: should we replace them with inline functions?
+ * of preprocessor macros.
  *
  * The first layer of macros is single.h, double.h, quad.h. Generally
  * these files define macros for working with floating point numbers
@@ -29,11 +22,11 @@
  * generic macros (in this case _FP_ADD(D,2,R,X,Y) where the number
  * of machine words required to store the given IEEE format is passed
  * as a parameter. [double.h and co check the number of bits in a word
- * and define FP_ADD_D & co appropriately]. 
+ * and define FP_ADD_D & co appropriately].
  * The generic macros are defined in op-common.h. This is where all
  * the grotty stuff like handling NaNs is coded. To handle the possible
  * word sizes macros in op-common.h use macros like _FP_FRAC_SLL_##wc()
- * where wc is the 'number of machine words' parameter (here 2). 
+ * where wc is the 'number of machine words' parameter (here 2).
  * These are defined in the third layer of macros: op-1.h, op-2.h
  * and op-4.h. These handle operations on floating point numbers composed
  * of 1,2 and 4 machine words respectively. [For example, on sparc64
@@ -41,7 +34,7 @@
  * constructs in op-1.h, but on sparc32 they use op-2.h definitions.]
  * soft-fp.h is on the same level as op-common.h, and defines some
  * macros which are independent of both word size and FP format.
- * Finally, sfp-machine.h is the machine dependent part of the 
+ * Finally, sfp-machine.h is the machine dependent part of the
  * code: it defines the word size and what type a word is. It also
  * defines how _FP_MUL_MEAT_t() maps to _FP_MUL_MEAT_n_* : op-n.h
  * provide several possible flavours of multiply algorithm, most
@@ -64,59 +57,11 @@
  * so we follow that practice...
  */
 
-/* WISHLIST:
- *
- * + Replace all the macros with inline functions. These should
- * have the same effect but be much easier to work with.
- *
- * + Emulate the IEEE exception flags. We don't currently do this
- * because a) it would require significant alterations to
- * the emulation macros [see the comments about _FP_NEG()
- * in op-common.c and note that we'd need to invent a convention
- * for passing in the flags to FXXXX fns and returning them] and 
- * b) SPARClinux doesn't let users access the flags anyway 
- * [contrast Solaris, which allows you to examine, clear or set
- * the flags, and request that exceptions cause SIGFPE 
- * [which you then set up a signal handler for, obviously...]].
- * Erm, (b) may quite possibly be garbage. %fsr is user-writable
- * so you don't need a syscall. There may or may not be library
- * support.
- *
- * + Emulation of FMULQ, FDIVQ, FSQRTQ, FDMULQ needs to be 
- * written!
- * 
- * + reindent code to conform to Linux kernel standard :->
- *
- * + work out whether all the compile-time warnings are bogus
- *
- * + check that conversion to/from integers works
- * 
- * + check with the SPARC architecture manual to see if we resolve
- * the implementation-dependent bits of the IEEE spec in the
- * same manner as the hardware.
- *
- * + more test cases for the test script always welcome!
- *
- * + illegal opcodes currently cause SIGFPEs. We should arrange
- * to tell the traps.c code to SIGILL instead. Currently,
- * everywhere that we return 0 should cause SIGILL, I think.
- * SIGFPE should only be caused if we set an IEEE exception bit
- * and the relevant trap bit is also set. (this means that 
- * traps.c should do this; also it should handle the case of
- * IEEE exception generated directly by the hardware.)
- * Should illegal_fp_register (which is a flavour of fp exception)
- * cause SIGFPE or  SIGILL?
- *
- * + the test script needs to be extended to handle the quadword
- * and comparison insns.
- *
- * + _FP_DIV_MEAT_2_udiv_64() appears to work but it should be
- * checked by somebody who understands the algorithm :->
- * 
- * + fpsave() saves the FP queue but fpload() doesn't reload it.
+/* TODO:
+ * fpsave() saves the FP queue but fpload() doesn't reload it.
  * Therefore when we context switch or change FPU ownership
  * we have to check to see if the queue had anything in it and
- * emulate it if it did. This is going to be a pain. 
+ * emulate it if it did. This is going to be a pain.
  */
 
 #include <linux/types.h>
@@ -124,38 +69,21 @@
 #include <linux/mm.h>
 #include <asm/uaccess.h>
 
+#include "sfp-util.h"
 #include "soft-fp.h"
 
 #define FLOATFUNC(x) extern int x(void *,void *,void *)
 
-/* Current status: we don't properly emulate the difficult quadword
- * insns (MUL, DIV, SQRT).
- * There are also some ops involving the FP registers which we don't
- * emulate: the branch on FP condition flags and the load/store to
- * FP regs or FSR. I'm assuming that these will never generate traps
- * (not unreasonable if there's an FPU at all; comments in the NetBSD
- * kernel source agree on this point). If we wanted to allow
- * purely software-emulation of the FPU with FPU totally disabled
- * or non-existent, we'd have to emulate these as well. We'd also
- * need to alter the fp_disabled trap handler to call the math-emu
- * code appropriately. The structure of do_one_mathemu() is also
- * inappropriate for these ops (as it has no way to alter the pc, 
- * for a start) and it might be better to special-case them in do_mathemu().
- * Oh, and you'd need to alter the traps.c code so it didn't try to
- * fpsave() and fpload(). If there's genuinely no FPU then there's 
- * probably bits of kernel stuff that just won't work anyway...
- */
-
 /* The Vn labels indicate what version of the SPARC architecture gas thinks
- * each insn is. This is from the binutils source :-> 
+ * each insn is. This is from the binutils source :->
  */
 /* quadword instructions */
-FLOATFUNC(FSQRTQ);                                /* v8 NYI */
+FLOATFUNC(FSQRTQ);                                /* v8 */
 FLOATFUNC(FADDQ);                                 /* v8 */
 FLOATFUNC(FSUBQ);                                 /* v8 */
-FLOATFUNC(FMULQ);                                 /* v8 NYI */
-FLOATFUNC(FDIVQ);                                 /* v8 NYI */
-FLOATFUNC(FDMULQ);                                /* v8 NYI */
+FLOATFUNC(FMULQ);                                 /* v8 */
+FLOATFUNC(FDIVQ);                                 /* v8 */
+FLOATFUNC(FDMULQ);                                /* v8 */
 FLOATFUNC(FQTOS);                                 /* v8 */
 FLOATFUNC(FQTOD);                                 /* v8 */
 FLOATFUNC(FITOQ);                                 /* v8 */
@@ -197,7 +125,7 @@
 #define FSR_CEXC_SHIFT	0UL
 #define FSR_CEXC_MASK	(0x1fUL << FSR_CEXC_SHIFT)
 
-static int do_one_mathemu(u32 insn, unsigned long *fsr, unsigned long *fregs);   
+static int do_one_mathemu(u32 insn, unsigned long *fsr, unsigned long *fregs);
 
 /* Unlike the Sparc64 version (which has a struct fpustate), we
  * pass the taskstruct corresponding to the task which currently owns the
@@ -210,65 +138,65 @@
  */
 int do_mathemu(struct pt_regs *regs, struct task_struct *fpt)
 {
-   /* regs->pc isn't necessarily the PC at which the offending insn is sitting.
-    * The FPU maintains a queue of FPops which cause traps. 
-    * When it hits an instruction that requires that the trapped op succeeded
-    * (usually because it reads a reg. that the trapped op wrote) then it
-    * causes this exception. We need to emulate all the insns on the queue
-    * and then allow the op to proceed.
-    * This code should also handle the case where the trap was precise,
-    * in which case the queue length is zero and regs->pc points at the 
-    * single FPop to be emulated. (this case is untested, though :->) 
-    * You'll need this case if you want to be able to emulate all FPops
-    * because the FPU either doesn't exist or has been software-disabled.
-    * [The UltraSPARC makes FP a precise trap; this isn't as stupid as it 
-    * might sound because the Ultra does funky things with a superscalar
-    * architecture.]
-    */
-   
-   /* You wouldn't believe how often I typed 'ftp' when I meant 'fpt' :-> */
-
-   int i;
-   int retcode = 0;                               /* assume all succeed */
-   unsigned long insn;
-   
-#ifdef DEBUG_MATHEMU   
-   printk("In do_mathemu()... pc is %08lx\n", regs->pc);
-   printk("fpqdepth is %ld\n",fpt->tss.fpqdepth);
-   for (i = 0; i < fpt->tss.fpqdepth; i++)
-      printk("%d: %08lx at %08lx\n",i,fpt->tss.fpqueue[i].insn, (unsigned long)fpt->tss.fpqueue[i].insn_addr);
-#endif      
-
-   if (fpt->tss.fpqdepth == 0) {                   /* no queue, guilty insn is at regs->pc */
-#ifdef DEBUG_MATHEMU   
-      printk("precise trap at %08lx\n", regs->pc);
+	/* regs->pc isn't necessarily the PC at which the offending insn is sitting.
+	 * The FPU maintains a queue of FPops which cause traps.
+	 * When it hits an instruction that requires that the trapped op succeeded
+	 * (usually because it reads a reg. that the trapped op wrote) then it
+	 * causes this exception. We need to emulate all the insns on the queue
+	 * and then allow the op to proceed.
+	 * This code should also handle the case where the trap was precise,
+	 * in which case the queue length is zero and regs->pc points at the
+	 * single FPop to be emulated. (this case is untested, though :->)
+	 * You'll need this case if you want to be able to emulate all FPops
+	 * because the FPU either doesn't exist or has been software-disabled.
+	 * [The UltraSPARC makes FP a precise trap; this isn't as stupid as it
+	 * might sound because the Ultra does funky things with a superscalar
+	 * architecture.]
+	 */
+
+	/* You wouldn't believe how often I typed 'ftp' when I meant 'fpt' :-> */
+
+	int i;
+	int retcode = 0;                               /* assume all succeed */
+	unsigned long insn;
+
+#ifdef DEBUG_MATHEMU
+	printk("In do_mathemu()... pc is %08lx\n", regs->pc);
+	printk("fpqdepth is %ld\n", fpt->tss.fpqdepth);
+	for (i = 0; i < fpt->tss.fpqdepth; i++)
+		printk("%d: %08lx at %08lx\n", i, fpt->tss.fpqueue[i].insn,
+		       (unsigned long)fpt->tss.fpqueue[i].insn_addr);
 #endif
-      if (!get_user(insn, (u32 *)regs->pc)) {
-         retcode = do_one_mathemu(insn, &fpt->tss.fsr, fpt->tss.float_regs);
-         if (retcode) {
-            /* in this case we need to fix up PC & nPC */
-            regs->pc = regs->npc;
-            regs->npc += 4;
-         }
-      }
-      return retcode;
-   }
-
-   /* Normal case: need to empty the queue... */
-   for (i = 0; i < fpt->tss.fpqdepth; i++)
-   {
-      retcode = do_one_mathemu(fpt->tss.fpqueue[i].insn, &(fpt->tss.fsr), fpt->tss.float_regs);
-      if (!retcode)                               /* insn failed, no point doing any more */
-         break;
-   }
-   /* Now empty the queue and clear the queue_not_empty flag */
-   if(retcode)
-	   fpt->tss.fsr &= ~(0x3000 | FSR_CEXC_MASK);
-   else
-	   fpt->tss.fsr &= ~0x3000;
-   fpt->tss.fpqdepth = 0;
-   
-   return retcode;
+
+	if (fpt->tss.fpqdepth == 0) {                   /* no queue, guilty insn is at regs->pc */
+#ifdef DEBUG_MATHEMU
+		printk("precise trap at %08lx\n", regs->pc);
+#endif
+		if (!get_user(insn, (u32 *)regs->pc)) {
+			retcode = do_one_mathemu(insn, &fpt->tss.fsr, fpt->tss.float_regs);
+			if (retcode) {
+				/* in this case we need to fix up PC & nPC */
+				regs->pc = regs->npc;
+				regs->npc += 4;
+			}
+		}
+		return retcode;
+	}
+
+	/* Normal case: need to empty the queue... */
+	for (i = 0; i < fpt->tss.fpqdepth; i++) {
+		retcode = do_one_mathemu(fpt->tss.fpqueue[i].insn, &(fpt->tss.fsr), fpt->tss.float_regs);
+		if (!retcode)                               /* insn failed, no point doing any more */
+			break;
+	}
+	/* Now empty the queue and clear the queue_not_empty flag */
+	if(retcode)
+		fpt->tss.fsr &= ~(0x3000 | FSR_CEXC_MASK);
+	else
+		fpt->tss.fsr &= ~0x3000;
+	fpt->tss.fpqdepth = 0;
+
+	return retcode;
 }
 
 /* All routines returning an exception to raise should detect
@@ -291,46 +219,36 @@
 	if(would_trap != 0) {
 		eflag &= ((fsr & FSR_TEM_MASK) >> FSR_TEM_SHIFT);
 		if((eflag & (eflag - 1)) != 0) {
-			if(eflag & EFLAG_INVALID)
-				eflag = EFLAG_INVALID;
-			else if(eflag & EFLAG_DIVZERO)
-				eflag = EFLAG_DIVZERO;
-			else if(eflag & EFLAG_INEXACT)
-				eflag = EFLAG_INEXACT;
+			if(eflag & FP_EX_INVALID)
+				eflag = FP_EX_INVALID;
+			else if(eflag & FP_EX_OVERFLOW)
+				eflag = FP_EX_OVERFLOW;
+			else if(eflag & FP_EX_UNDERFLOW)
+				eflag = FP_EX_UNDERFLOW;
+			else if(eflag & FP_EX_DIVZERO)
+				eflag = FP_EX_DIVZERO;
+			else if(eflag & FP_EX_INEXACT)
+				eflag = FP_EX_INEXACT;
 		}
 	}
 
-	/* Set CEXC, here are the rules:
+	/* Set CEXC, here is the rule:
 	 *
-	 * 1) In general all FPU ops will set one and only one
+	 *    In general all FPU ops will set one and only one
 	 *    bit in the CEXC field, this is always the case
 	 *    when the IEEE exception trap is enabled in TEM.
-	 *
-	 * 2) As a special case, if an overflow or underflow
-	 *    is being signalled, AND the trap is not enabled
-	 *    in TEM, then the inexact field shall also be set.
 	 */
 	fsr &= ~(FSR_CEXC_MASK);
-	if(would_trap ||
-	   (eflag & (EFLAG_OVERFLOW | EFLAG_UNDERFLOW)) == 0) {
-		fsr |= ((long)eflag << FSR_CEXC_SHIFT);
-	} else {
-		fsr |= (((long)eflag << FSR_CEXC_SHIFT) |
-			(EFLAG_INEXACT << FSR_CEXC_SHIFT));
-	}
+	fsr |= ((long)eflag << FSR_CEXC_SHIFT);
 
-	/* Set the AEXC field, rules are:
+	/* Set the AEXC field, rule is:
 	 *
-	 * 1) If a trap would not be generated, the
+	 *    If a trap would not be generated, the
 	 *    CEXC just generated is OR'd into the
 	 *    existing value of AEXC.
-	 *
-	 * 2) When a trap is generated, AEXC is cleared.
 	 */
 	if(would_trap == 0)
 		fsr |= ((long)eflag << FSR_AEXC_SHIFT);
-	else
-		fsr &= ~(FSR_AEXC_MASK);
 
 	/* If trapping, indicate fault trap type IEEE. */
 	if(would_trap != 0)
@@ -343,157 +261,150 @@
 
 static int do_one_mathemu(u32 insn, unsigned long *fsr, unsigned long *fregs)
 {
-   /* Emulate the given insn, updating fsr and fregs appropriately. */
-   int type = 0; 
-   /* 01 is single, 10 is double, 11 is quad, 
-    * 000011 is rs1, 001100 is rs2, 110000 is rd (00 in rd is fcc)
-    * 111100000000 tells which ftt that may happen in 
-    * (this field not used on sparc32 code, as we can't 
-    * extract trap type info for ops on the FP queue) 
-    */
-   int freg, eflag;
-   int (*func)(void *,void *,void *) = NULL;
-   void *rs1 = NULL, *rs2 = NULL, *rd = NULL;   
+	/* Emulate the given insn, updating fsr and fregs appropriately. */
+	int type = 0;
+	/* 01 is single, 10 is double, 11 is quad,
+	 * 000011 is rs1, 001100 is rs2, 110000 is rd (00 in rd is fcc)
+	 * 111100000000 tells which ftt that may happen in
+	 * (this field not used on sparc32 code, as we can't
+	 * extract trap type info for ops on the FP queue)
+	 */
+	int freg, eflag;
+	int (*func)(void *,void *,void *) = NULL;
+	void *rs1 = NULL, *rs2 = NULL, *rd = NULL;
+
+#ifdef DEBUG_MATHEMU
+	printk("In do_mathemu(), emulating %08lx\n", insn);
+#endif
+
+	if ((insn & 0xc1f80000) == 0x81a00000)	/* FPOP1 */ {
+		switch ((insn >> 5) & 0x1ff) {
+		/* QUAD - ftt == 3 */
+		case 0x001: type = 0x314; func = FMOVS; break;
+		case 0x005: type = 0x314; func = FNEGS; break;
+		case 0x009: type = 0x314; func = FABSS; break;
+		case 0x02b: type = 0x33c; func = FSQRTQ; break;
+		case 0x043: type = 0x33f; func = FADDQ; break;
+		case 0x047: type = 0x33f; func = FSUBQ; break;
+		case 0x04b: type = 0x33f; func = FMULQ; break;
+		case 0x04f: type = 0x33f; func = FDIVQ; break;
+		case 0x06e: type = 0x33a; func = FDMULQ; break;
+		case 0x0c7: type = 0x31c; func = FQTOS; break;
+		case 0x0cb: type = 0x32c; func = FQTOD; break;
+		case 0x0cc: type = 0x334; func = FITOQ; break;
+		case 0x0cd: type = 0x334; func = FSTOQ; break;
+		case 0x0ce: type = 0x338; func = FDTOQ; break;
+		case 0x0d3: type = 0x31c; func = FQTOI; break;
+		/* SUBNORMAL - ftt == 2 */
+		case 0x029: type = 0x214; func = FSQRTS; break;
+		case 0x02a: type = 0x228; func = FSQRTD; break;
+		case 0x041: type = 0x215; func = FADDS; break;
+		case 0x042: type = 0x22a; func = FADDD; break;
+		case 0x045: type = 0x215; func = FSUBS; break;
+		case 0x046: type = 0x22a; func = FSUBD; break;
+		case 0x049: type = 0x215; func = FMULS; break;
+		case 0x04a: type = 0x22a; func = FMULD; break;
+		case 0x04d: type = 0x215; func = FDIVS; break;
+		case 0x04e: type = 0x22a; func = FDIVD; break;
+		case 0x069: type = 0x225; func = FSMULD; break;
+		case 0x0c6: type = 0x218; func = FDTOS; break;
+		case 0x0c9: type = 0x224; func = FSTOD; break;
+		case 0x0d1: type = 0x214; func = FSTOI; break;
+		case 0x0d2: type = 0x218; func = FDTOI; break;
+		default:
+#ifdef DEBUG_MATHEMU
+			printk("unknown FPop1: %03lx\n",(insn>>5)&0x1ff);
+#endif
+		}
+	} else if ((insn & 0xc1f80000) == 0x81a80000)	/* FPOP2 */ {
+		switch ((insn >> 5) & 0x1ff) {
+		case 0x051: type = 0x305; func = FCMPS; break;
+		case 0x052: type = 0x30a; func = FCMPD; break;
+		case 0x053: type = 0x30f; func = FCMPQ; break;
+		case 0x055: type = 0x305; func = FCMPES; break;
+		case 0x056: type = 0x30a; func = FCMPED; break;
+		case 0x057: type = 0x30f; func = FCMPEQ; break;
+		default:
+#ifdef DEBUG_MATHEMU
+			printk("unknown FPop2: %03lx\n",(insn>>5)&0x1ff);
+#endif
+		}
+	}
+
+	if (!type) {	/* oops, didn't recognise that FPop */
+		printk("attempt to emulate unrecognised FPop!\n");
+		return 0;
+	}
+
+	/* Decode the registers to be used */
+	freg = (*fsr >> 14) & 0xf;
 
+	*fsr &= ~0x1c000;				/* clear the traptype bits */
+
+	freg = ((insn >> 14) & 0x1f);
+	switch (type & 0x3) {				/* is rs1 single, double or quad? */
+	case 3:
+		if (freg & 3) {				/* quadwords must have bits 4&5 of the */
+							/* encoded reg. number set to zero. */
+			*fsr |= (6 << 14);
+			return 0;			/* simulate invalid_fp_register exception */
+		}
+	/* fall through */
+	case 2:
+		if (freg & 1) {				/* doublewords must have bit 5 zeroed */
+			*fsr |= (6 << 14);
+			return 0;
+		}
+	}
+	rs1 = (void *)&fregs[freg];
+	freg = (insn & 0x1f);
+	switch ((type >> 2) & 0x3) {			/* same again for rs2 */
+	case 3:
+		if (freg & 3) {				/* quadwords must have bits 4&5 of the */
+							/* encoded reg. number set to zero. */
+			*fsr |= (6 << 14);
+			return 0;			/* simulate invalid_fp_register exception */
+		}
+	/* fall through */
+	case 2:
+		if (freg & 1) {				/* doublewords must have bit 5 zeroed */
+			*fsr |= (6 << 14);
+			return 0;
+		}
+	}
+	rs2 = (void *)&fregs[freg];
+	freg = ((insn >> 25) & 0x1f);
+	switch ((type >> 4) & 0x3) {			/* and finally rd. This one's a bit different */
+	case 0:						/* dest is fcc. (this must be FCMPQ or FCMPEQ) */
+		if (freg) {				/* V8 has only one set of condition codes, so */
+							/* anything but 0 in the rd field is an error */
+			*fsr |= (6 << 14);		/* (should probably flag as invalid opcode */
+			return 0;			/* but SIGFPE will do :-> ) */
+		}
+		rd = (void *)(fsr);			/* FCMPQ and FCMPEQ are special and only  */
+		break;					/* set bits they're supposed to :-> */
+	case 3:
+		if (freg & 3) {				/* quadwords must have bits 4&5 of the */
+							/* encoded reg. number set to zero. */
+			*fsr |= (6 << 14);
+			return 0;			/* simulate invalid_fp_register exception */
+		}
+	/* fall through */
+	case 2:
+		if (freg & 1) {				/* doublewords must have bit 5 zeroed */
+			*fsr |= (6 << 14);
+			return 0;
+		}
+	/* fall through */
+	case 1:
+		rd = (void *)&fregs[freg];
+		break;
+	}
 #ifdef DEBUG_MATHEMU
-   printk("In do_mathemu(), emulating %08lx\n", insn);
-#endif   
-      
-   if ((insn & 0xc1f80000) == 0x81a00000) /* FPOP1 */ {
-      switch ((insn >> 5) & 0x1ff) {
-         /* QUAD - ftt == 3 */
-         case 0x001: type = 0x314; func = FMOVS; break;
-         case 0x005: type = 0x314; func = FNEGS; break;
-         case 0x009: type = 0x314; func = FABSS; break;
-         case 0x02b: type = 0x33c; func = FSQRTQ; break;
-         case 0x043: type = 0x33f; func = FADDQ; break;
-         case 0x047: type = 0x33f; func = FSUBQ; break;
-         case 0x04b: type = 0x33f; func = FMULQ; break;
-         case 0x04f: type = 0x33f; func = FDIVQ; break;
-         case 0x06e: type = 0x33a; func = FDMULQ; break;
-         case 0x0c7: type = 0x31c; func = FQTOS; break;
-         case 0x0cb: type = 0x32c; func = FQTOD; break;
-         case 0x0cc: type = 0x334; func = FITOQ; break;
-         case 0x0cd: type = 0x334; func = FSTOQ; break;
-         case 0x0ce: type = 0x338; func = FDTOQ; break;
-         case 0x0d3: type = 0x31c; func = FQTOI; break;
-            /* SUBNORMAL - ftt == 2 */
-         case 0x029: type = 0x214; func = FSQRTS; break;
-         case 0x02a: type = 0x228; func = FSQRTD; break;
-         case 0x041: type = 0x215; func = FADDS; break;
-         case 0x042: type = 0x22a; func = FADDD; break;
-         case 0x045: type = 0x215; func = FSUBS; break;
-         case 0x046: type = 0x22a; func = FSUBD; break;
-         case 0x049: type = 0x215; func = FMULS; break;
-         case 0x04a: type = 0x22a; func = FMULD; break;
-         case 0x04d: type = 0x215; func = FDIVS; break;
-         case 0x04e: type = 0x22a; func = FDIVD; break;
-         case 0x069: type = 0x225; func = FSMULD; break;
-         case 0x0c6: type = 0x218; func = FDTOS; break;
-         case 0x0c9: type = 0x224; func = FSTOD; break;
-         case 0x0d1: type = 0x214; func = FSTOI; break;
-         case 0x0d2: type = 0x218; func = FDTOI; break;
-         default: 
-#ifdef DEBUG_MATHEMU         
-         	printk("unknown FPop1: %03lx\n",(insn>>5)&0x1ff);
-#endif         
-      }
-   }
-   else if ((insn & 0xc1f80000) == 0x81a80000) /* FPOP2 */ {
-      switch ((insn >> 5) & 0x1ff) {
-         case 0x051: type = 0x305; func = FCMPS; break;
-         case 0x052: type = 0x30a; func = FCMPD; break;
-         case 0x053: type = 0x30f; func = FCMPQ; break;
-         case 0x055: type = 0x305; func = FCMPES; break;
-         case 0x056: type = 0x30a; func = FCMPED; break;
-         case 0x057: type = 0x30f; func = FCMPEQ; break;
-         default: 
-#ifdef DEBUG_MATHEMU         
-         	printk("unknown FPop2: %03lx\n",(insn>>5)&0x1ff);
-#endif         	
-      }
-   }
-   
-   if (!type) { /* oops, didn't recognise that FPop */
-      printk("attempt to emulate unrecognised FPop!\n");
-      return 0;
-   }
-   
-   /* Decode the registers to be used */
-   freg = (*fsr >> 14) & 0xf;
-
-   *fsr &= ~0x1c000;                              /* clear the traptype bits */
-    
-   freg = ((insn >> 14) & 0x1f);
-   switch (type & 0x3)                            /* is rs1 single, double or quad? */
-   {
-      case 3:
-         if (freg & 3)                            /* quadwords must have bits 4&5 of the */
-         {                                        /* encoded reg. number set to zero. */
-            *fsr |= (6 << 14);                  
-            return 0;                             /* simulate invalid_fp_register exception */
-         }
-         /* fall through */
-      case 2:
-         if (freg & 1)                            /* doublewords must have bit 5 zeroed */
-         {
-            *fsr |= (6 << 14);
-            return 0;
-         }
-   }
-   rs1 = (void *)&fregs[freg];
-   freg = (insn & 0x1f);
-   switch ((type >> 2) & 0x3)
-   {                                              /* same again for rs2 */
-      case 3:
-         if (freg & 3)                            /* quadwords must have bits 4&5 of the */
-         {                                        /* encoded reg. number set to zero. */
-            *fsr |= (6 << 14);                  
-            return 0;                             /* simulate invalid_fp_register exception */
-         }
-         /* fall through */
-      case 2:
-         if (freg & 1)                            /* doublewords must have bit 5 zeroed */
-         {
-            *fsr |= (6 << 14);
-            return 0;
-         }
-   }
-   rs2 = (void *)&fregs[freg];
-   freg = ((insn >> 25) & 0x1f);
-   switch ((type >> 4) & 0x3)                     /* and finally rd. This one's a bit different */
-   {
-      case 0:                                     /* dest is fcc. (this must be FCMPQ or FCMPEQ) */
-         if (freg)                                /* V8 has only one set of condition codes, so */
-         {                                        /* anything but 0 in the rd field is an error */
-            *fsr |= (6 << 14);                    /* (should probably flag as invalid opcode */
-            return 0;                             /* but SIGFPE will do :-> ) */
-         }
-         rd = (void *)(fsr);                      /* FCMPQ and FCMPEQ are special and only  */
-         break;                                   /* set bits they're supposed to :-> */
-      case 3:
-         if (freg & 3)                            /* quadwords must have bits 4&5 of the */
-         {                                        /* encoded reg. number set to zero. */
-            *fsr |= (6 << 14);
-            return 0;                             /* simulate invalid_fp_register exception */
-         }
-         /* fall through */
-      case 2:
-         if (freg & 1)                            /* doublewords must have bit 5 zeroed */
-         {
-            *fsr |= (6 << 14);
-            return 0;
-         }
-         /* fall through */
-      case 1:
-         rd = (void *)&fregs[freg];
-         break;
-   }
-#ifdef DEBUG_MATHEMU   
-   printk("executing insn...\n");
-#endif   
-   eflag = func(rd, rs2, rs1);                   /* do the Right Thing */
-   if(eflag == 0)
-	   return 1;                             /* success! */
-   return record_exception(fsr, eflag);
+	printk("executing insn...\n");
+#endif
+	eflag = func(rd, rs2, rs1);			/* do the Right Thing */
+	if(eflag == 0)
+		return 1;				/* success! */
+	return record_exception(fsr, eflag);
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc/math-emu/sfp-machine.h linux/arch/sparc/math-emu/sfp-machine.h
--- v2.3.3/linux/arch/sparc/math-emu/sfp-machine.h	Wed Mar 10 16:53:36 1999
+++ linux/arch/sparc/math-emu/sfp-machine.h	Sat May 29 11:09:04 1999
@@ -1,6 +1,11 @@
-/* Machine-dependent software floating-point definitions.  Sparc version.
-   Copyright (C) 1997 Free Software Foundation, Inc.
+/* Machine-dependent software floating-point definitions.
+   Sparc userland (_Q_*) version.
+   Copyright (C) 1997,1998,1999 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
+   Contributed by Richard Henderson (rth@cygnus.com),
+		  Jakub Jelinek (jj@ultra.linux.cz),
+		  David S. Miller (davem@redhat.com) and
+		  Peter Maydell (pmaydell@chiark.greenend.org.uk).
 
    The GNU C Library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public License as
@@ -15,382 +20,183 @@
    You should have received a copy of the GNU Library General Public
    License along with the GNU C Library; see the file COPYING.LIB.  If
    not, write to the Free Software Foundation, Inc.,
-   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  
+   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
-   Actually, this is a sparc (32bit) version, written based on the
-   i386 and sparc64 versions, by me, 
-   Peter Maydell (pmaydell@chiark.greenend.org.uk).
-   Comments are by and large also mine, although they may be inaccurate.
-
-   In picking out asm fragments I've gone with the lowest common
-   denominator, which also happens to be the hardware I have :->
-   That is, a SPARC without hardware multiply and divide.
- */
-
-
-/* basic word size definitions */
+#ifndef _SFP_MACHINE_H
+#define _SFP_MACHINE_H
+   
 #define _FP_W_TYPE_SIZE		32
 #define _FP_W_TYPE		unsigned long
 #define _FP_WS_TYPE		signed long
 #define _FP_I_TYPE		long
 
-/* You can optionally code some things like addition in asm. For
- * example, i386 defines __FP_FRAC_ADD_2 as asm. If you don't
- * then you get a fragment of C code [if you change an #ifdef 0
- * in op-2.h] or a call to add_ssaaaa (see below).
- * Good places to look for asm fragments to use are gcc and glibc.
- * gcc's longlong.h is useful.
- */
-
-/* We need to know how to multiply and divide. If the host word size
- * is >= 2*fracbits you can use FP_MUL_MEAT_n_imm(t,R,X,Y) which
- * codes the multiply with whatever gcc does to 'a * b'.
- * _FP_MUL_MEAT_n_wide(t,R,X,Y,f) is used when you have an asm 
- * function that can multiply two 1W values and get a 2W result. 
- * Otherwise you're stuck with _FP_MUL_MEAT_n_hard(t,R,X,Y) which
- * does bitshifting to avoid overflow.
- * For division there is FP_DIV_MEAT_n_imm(t,R,X,Y,f) for word size
- * >= 2*fracbits, where f is either _FP_DIV_HELP_imm or 
- * _FP_DIV_HELP_ldiv (see op-1.h).
- * _FP_DIV_MEAT_udiv() is if you have asm to do 2W/1W => (1W, 1W).
- * [GCC and glibc have longlong.h which has the asm macro udiv_qrnnd
- * to do this.]
- * In general, 'n' is the number of words required to hold the type,
- * and 't' is either S, D or Q for single/double/quad.
- *           -- PMM
- */
-/* Example: SPARC64:
- * #define _FP_MUL_MEAT_S(R,X,Y)	_FP_MUL_MEAT_1_imm(S,R,X,Y)
- * #define _FP_MUL_MEAT_D(R,X,Y)	_FP_MUL_MEAT_1_wide(D,R,X,Y,umul_ppmm)
- * #define _FP_MUL_MEAT_Q(R,X,Y)	_FP_MUL_MEAT_2_wide(Q,R,X,Y,umul_ppmm)
- *
- * #define _FP_DIV_MEAT_S(R,X,Y)	_FP_DIV_MEAT_1_imm(S,R,X,Y,_FP_DIV_HELP_imm)
- * #define _FP_DIV_MEAT_D(R,X,Y)	_FP_DIV_MEAT_1_udiv(D,R,X,Y)
- * #define _FP_DIV_MEAT_Q(R,X,Y)	_FP_DIV_MEAT_2_udiv_64(Q,R,X,Y)
- *
- * Example: i386:
- * #define _FP_MUL_MEAT_S(R,X,Y)   _FP_MUL_MEAT_1_wide(S,R,X,Y,_i386_mul_32_64)
- * #define _FP_MUL_MEAT_D(R,X,Y)   _FP_MUL_MEAT_2_wide(D,R,X,Y,_i386_mul_32_64)
- *
- * #define _FP_DIV_MEAT_S(R,X,Y)   _FP_DIV_MEAT_1_udiv(S,R,X,Y,_i386_div_64_32)
- * #define _FP_DIV_MEAT_D(R,X,Y)   _FP_DIV_MEAT_2_udiv_64(D,R,X,Y)
- */
-#define _FP_MUL_MEAT_S(R,X,Y)   _FP_MUL_MEAT_1_wide(S,R,X,Y,umul_ppmm)
-#define _FP_MUL_MEAT_D(R,X,Y)   _FP_MUL_MEAT_2_wide(D,R,X,Y,umul_ppmm)
-/* FIXME: This is not implemented, but should be soon */
-#define _FP_MUL_MEAT_Q(R,X,Y)   _FP_FRAC_SET_4(R, _FP_ZEROFRAC_4)
-#define _FP_DIV_MEAT_S(R,X,Y)   _FP_DIV_MEAT_1_udiv(S,R,X,Y)
-#define _FP_DIV_MEAT_D(R,X,Y)   _FP_DIV_MEAT_2_udiv_64(D,R,X,Y)
-/* FIXME: This is not implemented, but should be soon */
-#define _FP_DIV_MEAT_Q(R,X,Y)   _FP_FRAC_SET_4(R, _FP_ZEROFRAC_4)
-
-/* These macros define what NaN looks like. They're supposed to expand to 
- * a comma-separated set of 32bit unsigned ints that encode NaN.
- */
-#define _FP_NANFRAC_S		_FP_QNANBIT_S
-#define _FP_NANFRAC_D		_FP_QNANBIT_D, 0
-#define _FP_NANFRAC_Q           _FP_QNANBIT_Q, 0, 0, 0
+#define _FP_MUL_MEAT_S(R,X,Y)	_FP_MUL_MEAT_1_wide(S,R,X,Y,umul_ppmm)
+#define _FP_MUL_MEAT_D(R,X,Y)	_FP_MUL_MEAT_2_wide(D,R,X,Y,umul_ppmm)
+#define _FP_MUL_MEAT_Q(R,X,Y)	_FP_MUL_MEAT_4_wide(Q,R,X,Y,umul_ppmm)
+
+#define _FP_DIV_MEAT_S(R,X,Y)	_FP_DIV_MEAT_1_udiv(S,R,X,Y)
+#define _FP_DIV_MEAT_D(R,X,Y)	_FP_DIV_MEAT_2_udiv(D,R,X,Y)
+#define _FP_DIV_MEAT_Q(R,X,Y)	_FP_DIV_MEAT_4_udiv(Q,R,X,Y)
+
+#define _FP_NANFRAC_S		((_FP_QNANBIT_S << 1) - 1)
+#define _FP_NANFRAC_D		((_FP_QNANBIT_D << 1) - 1), -1
+#define _FP_NANFRAC_Q		((_FP_QNANBIT_Q << 1) - 1), -1, -1, -1
+#define _FP_NANSIGN_S		0
+#define _FP_NANSIGN_D		0
+#define _FP_NANSIGN_Q		0
 
 #define _FP_KEEPNANFRACP 1
 
-/* This macro appears to be called when both X and Y are NaNs, and 
- * has to choose one and copy it to R. i386 goes for the larger of the
- * two, sparc64 just picks Y. I don't understand this at all so I'll
- * go with sparc64 because it's shorter :->   -- PMM 
+/* If one NaN is signaling and the other is not,
+ * we choose that one, otherwise we choose X.
+ */
+/* For _Qp_* and _Q_*, this should prefer X, for
+ * CPU instruction emulation this should prefer Y.
+ * (see SPAMv9 B.2.2 section).
  */
 #define _FP_CHOOSENAN(fs, wc, R, X, Y)				\
   do {								\
-    R##_s = Y##_s;						\
-    _FP_FRAC_COPY_##wc(R,Y);					\
+    if ((_FP_FRAC_HIGH_RAW_##fs(Y) & _FP_QNANBIT_##fs)		\
+	&& !(_FP_FRAC_HIGH_RAW_##fs(X) & _FP_QNANBIT_##fs))	\
+      {								\
+	R##_s = X##_s;						\
+	_FP_FRAC_COPY_##wc(R,X);				\
+      }								\
+    else							\
+      {								\
+	R##_s = Y##_s;						\
+	_FP_FRAC_COPY_##wc(R,Y);				\
+      }								\
     R##_c = FP_CLS_NAN;						\
   } while (0)
-  
-#define __FP_UNPACK_RAW_1(fs, X, val)				\
-  do {								\
-    union _FP_UNION_##fs *_flo =				\
-    	(union _FP_UNION_##fs *)val;				\
-								\
-    X##_f = _flo->bits.frac;					\
-    X##_e = _flo->bits.exp;					\
-    X##_s = _flo->bits.sign;					\
-  } while (0)
-
-#define __FP_UNPACK_RAW_2(fs, X, val)			\
-  do {							\
-    union _FP_UNION_##fs *_flo =			\
-    	(union _FP_UNION_##fs *)val;			\
-							\
-    X##_f0 = _flo->bits.frac0;				\
-    X##_f1 = _flo->bits.frac1;				\
-    X##_e  = _flo->bits.exp;				\
-    X##_s  = _flo->bits.sign;				\
-  } while (0)
-
-#define __FP_UNPACK_RAW_4(fs, X, val)			\
-  do {							\
-    union _FP_UNION_##fs *_flo =			\
-    	(union _FP_UNION_##fs *)val;			\
-							\
-    X##_f[0] = _flo->bits.frac0;			\
-    X##_f[1] = _flo->bits.frac1;			\
-    X##_f[2] = _flo->bits.frac2;			\
-    X##_f[3] = _flo->bits.frac3;			\
-    X##_e  = _flo->bits.exp;				\
-    X##_s  = _flo->bits.sign;				\
-  } while (0)
-
-#define __FP_UNPACK_S(X,val)		\
-  do {					\
-    __FP_UNPACK_RAW_1(S,X,val);		\
-    _FP_UNPACK_CANONICAL(S,1,X);	\
-  } while (0)
-
-#define __FP_UNPACK_D(X,val)		\
-  do {					\
-    __FP_UNPACK_RAW_2(D,X,val);		\
-    _FP_UNPACK_CANONICAL(D,2,X);	\
-  } while (0)
-
-#define __FP_UNPACK_Q(X,val)		\
-  do {					\
-    __FP_UNPACK_RAW_4(Q,X,val);		\
-    _FP_UNPACK_CANONICAL(Q,4,X);	\
-  } while (0)
-
-#define __FP_PACK_RAW_1(fs, val, X)				\
-  do {								\
-    union _FP_UNION_##fs *_flo =				\
-    	(union _FP_UNION_##fs *)val;				\
-								\
-    _flo->bits.frac = X##_f;					\
-    _flo->bits.exp  = X##_e;					\
-    _flo->bits.sign = X##_s;					\
-  } while (0)
-  
-#define __FP_PACK_RAW_2(fs, val, X)			\
-  do {							\
-    union _FP_UNION_##fs *_flo =			\
-    	(union _FP_UNION_##fs *)val;			\
-							\
-    _flo->bits.frac0 = X##_f0;				\
-    _flo->bits.frac1 = X##_f1;				\
-    _flo->bits.exp   = X##_e;				\
-    _flo->bits.sign  = X##_s;				\
-  } while (0)
 
-#define __FP_PACK_RAW_4(fs, val, X)			\
-  do {							\
-    union _FP_UNION_##fs *_flo =			\
-    	(union _FP_UNION_##fs *)val;			\
-							\
-    _flo->bits.frac0 = X##_f[0];			\
-    _flo->bits.frac1 = X##_f[1];			\
-    _flo->bits.frac2 = X##_f[2];			\
-    _flo->bits.frac3 = X##_f[3];			\
-    _flo->bits.exp   = X##_e;				\
-    _flo->bits.sign  = X##_s;				\
-  } while (0)
-
-#include <linux/kernel.h>
-#include <linux/sched.h>
+/* Some assembly to speed things up. */
+#define __FP_FRAC_ADD_3(r2,r1,r0,x2,x1,x0,y2,y1,y0)			\
+  __asm__ ("addcc %r7,%8,%2
+	    addxcc %r5,%6,%1
+	    addx %r3,%4,%0"						\
+	   : "=r" ((USItype)(r2)),					\
+	     "=&r" ((USItype)(r1)),					\
+	     "=&r" ((USItype)(r0))					\
+	   : "%rJ" ((USItype)(x2)),					\
+	     "rI" ((USItype)(y2)),					\
+	     "%rJ" ((USItype)(x1)),					\
+	     "rI" ((USItype)(y1)),					\
+	     "%rJ" ((USItype)(x0)),					\
+	     "rI" ((USItype)(y0))					\
+	   : "cc")
+
+#define __FP_FRAC_SUB_3(r2,r1,r0,x2,x1,x0,y2,y1,y0)			\
+  __asm__ ("subcc %r7,%8,%2
+	    subxcc %r5,%6,%1
+	    subx %r3,%4,%0"						\
+	   : "=r" ((USItype)(r2)),					\
+	     "=&r" ((USItype)(r1)),					\
+	     "=&r" ((USItype)(r0))					\
+	   : "%rJ" ((USItype)(x2)),					\
+	     "rI" ((USItype)(y2)),					\
+	     "%rJ" ((USItype)(x1)),					\
+	     "rI" ((USItype)(y1)),					\
+	     "%rJ" ((USItype)(x0)),					\
+	     "rI" ((USItype)(y0))					\
+	   : "cc")
+
+#define __FP_FRAC_ADD_4(r3,r2,r1,r0,x3,x2,x1,x0,y3,y2,y1,y0)		\
+  do {									\
+    /* We need to fool gcc,  as we need to pass more than 10		\
+       input/outputs.  */						\
+    register USItype _t1 __asm__ ("g1"), _t2 __asm__ ("g2");		\
+    __asm__ __volatile__ ("
+	    addcc %r8,%9,%1
+	    addxcc %r6,%7,%0
+	    addxcc %r4,%5,%%g2
+	    addx %r2,%3,%%g1"						\
+	   : "=&r" ((USItype)(r1)),					\
+	     "=&r" ((USItype)(r0))					\
+	   : "%rJ" ((USItype)(x3)),					\
+	     "rI" ((USItype)(y3)),					\
+	     "%rJ" ((USItype)(x2)),					\
+	     "rI" ((USItype)(y2)),					\
+	     "%rJ" ((USItype)(x1)),					\
+	     "rI" ((USItype)(y1)),					\
+	     "%rJ" ((USItype)(x0)),					\
+	     "rI" ((USItype)(y0))					\
+	   : "cc", "g1", "g2");						\
+    __asm__ __volatile__ ("" : "=r" (_t1), "=r" (_t2));			\
+    r3 = _t1; r2 = _t2;							\
+  } while (0)
+
+#define __FP_FRAC_SUB_4(r3,r2,r1,r0,x3,x2,x1,x0,y3,y2,y1,y0)		\
+  do {									\
+    /* We need to fool gcc,  as we need to pass more than 10		\
+       input/outputs.  */						\
+    register USItype _t1 __asm__ ("g1"), _t2 __asm__ ("g2");		\
+    __asm__ __volatile__ ("
+	    subcc %r8,%9,%1
+	    subxcc %r6,%7,%0
+	    subxcc %r4,%5,%%g2
+	    subx %r2,%3,%%g1"						\
+	   : "=&r" ((USItype)(r1)),					\
+	     "=&r" ((USItype)(r0))					\
+	   : "%rJ" ((USItype)(x3)),					\
+	     "rI" ((USItype)(y3)),					\
+	     "%rJ" ((USItype)(x2)),					\
+	     "rI" ((USItype)(y2)),					\
+	     "%rJ" ((USItype)(x1)),					\
+	     "rI" ((USItype)(y1)),					\
+	     "%rJ" ((USItype)(x0)),					\
+	     "rI" ((USItype)(y0))					\
+	   : "cc", "g1", "g2");						\
+    __asm__ __volatile__ ("" : "=r" (_t1), "=r" (_t2));			\
+    r3 = _t1; r2 = _t2;							\
+  } while (0)
+
+#define __FP_FRAC_DEC_3(x2,x1,x0,y2,y1,y0) __FP_FRAC_SUB_3(x2,x1,x0,x2,x1,x0,y2,y1,y0)
+
+#define __FP_FRAC_DEC_4(x3,x2,x1,x0,y3,y2,y1,y0) __FP_FRAC_SUB_4(x3,x2,x1,x0,x3,x2,x1,x0,y3,y2,y1,y0)
+
+#define __FP_FRAC_ADDI_4(x3,x2,x1,x0,i)					\
+  __asm__ ("addcc %3,%4,%3
+	    addxcc %2,%%g0,%2
+	    addxcc %1,%%g0,%1
+	    addx %0,%%g0,%0"						\
+	   : "=&r" ((USItype)(x3)),					\
+	     "=&r" ((USItype)(x2)),					\
+	     "=&r" ((USItype)(x1)),					\
+	     "=&r" ((USItype)(x0))					\
+	   : "rI" ((USItype)(i)),					\
+	     "0" ((USItype)(x3)),					\
+	     "1" ((USItype)(x2)),					\
+	     "2" ((USItype)(x1)),					\
+	     "3" ((USItype)(x0))					\
+	   : "cc")
 
-/* We only actually write to the destination register
- * if exceptions signalled (if any) will not trap.
- */
-#ifdef __SMP__
-#define __FPU_TEM \
-	(((current->tss.fsr)>>23)&0x1f)
-#else
+#ifndef __SMP__
 extern struct task_struct *last_task_used_math;
-#define __FPU_TEM \
-	(((last_task_used_math->tss.fsr)>>23)&0x1f)
 #endif
-#define __FPU_TRAP_P(bits) \
-	((__FPU_TEM & (bits)) != 0)
-
-#define __FP_PACK_S(val,X)			\
-({  int __exc = _FP_PACK_CANONICAL(S,1,X);	\
-    if(!__exc || !__FPU_TRAP_P(__exc))		\
-        __FP_PACK_RAW_1(S,val,X);		\
-    __exc;					\
-})
-
-#define __FP_PACK_D(val,X)			\
-({  int __exc = _FP_PACK_CANONICAL(D,2,X);	\
-    if(!__exc || !__FPU_TRAP_P(__exc))		\
-        __FP_PACK_RAW_2(D,val,X);		\
-    __exc;					\
-})
-
-#define __FP_PACK_Q(val,X)			\
-({  int __exc = _FP_PACK_CANONICAL(Q,4,X);	\
-    if(!__exc || !__FPU_TRAP_P(__exc))		\
-        __FP_PACK_RAW_4(Q,val,X);		\
-    __exc;					\
-})
 
 /* Obtain the current rounding mode. */
+#ifndef FP_ROUNDMODE
 #ifdef __SMP__
 #define FP_ROUNDMODE	((current->tss.fsr >> 30) & 0x3)
 #else
 #define FP_ROUNDMODE	((last_task_used_math->tss.fsr >> 30) & 0x3)
 #endif
+#endif
 
-/* the asm fragments go here: all these are taken from glibc-2.0.5's stdlib/longlong.h */
-
-#include <linux/types.h>
-#include <asm/byteorder.h>
-
-/* add_ssaaaa is used in op-2.h and should be equivalent to
- * #define add_ssaaaa(sh,sl,ah,al,bh,bl) (sh = ah+bh+ (( sl = al+bl) < al))
- * add_ssaaaa(high_sum, low_sum, high_addend_1, low_addend_1,
- * high_addend_2, low_addend_2) adds two UWtype integers, composed by
- * HIGH_ADDEND_1 and LOW_ADDEND_1, and HIGH_ADDEND_2 and LOW_ADDEND_2
- * respectively.  The result is placed in HIGH_SUM and LOW_SUM.  Overflow
- * (i.e. carry out) is not stored anywhere, and is lost.
- */
-#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
-  __asm__ ("addcc %r4,%5,%1
-        addx %r2,%3,%0"                                                 \
-           : "=r" ((USItype)(sh)),                                      \
-             "=&r" ((USItype)(sl))                                      \
-           : "%rJ" ((USItype)(ah)),                                     \
-             "rI" ((USItype)(bh)),                                      \
-             "%rJ" ((USItype)(al)),                                     \
-             "rI" ((USItype)(bl))                                       \
-           : "cc")
-
-
-/* sub_ddmmss is used in op-2.h and udivmodti4.c and should be equivalent to
- * #define sub_ddmmss(sh, sl, ah, al, bh, bl) (sh = ah-bh - ((sl = al-bl) > al))
- * sub_ddmmss(high_difference, low_difference, high_minuend, low_minuend,
- * high_subtrahend, low_subtrahend) subtracts two two-word UWtype integers,
- * composed by HIGH_MINUEND_1 and LOW_MINUEND_1, and HIGH_SUBTRAHEND_2 and
- * LOW_SUBTRAHEND_2 respectively.  The result is placed in HIGH_DIFFERENCE
- * and LOW_DIFFERENCE.  Overflow (i.e. carry out) is not stored anywhere,
- * and is lost.
- */
-
-#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
-  __asm__ ("subcc %r4,%5,%1
-        subx %r2,%3,%0"                                                 \
-           : "=r" ((USItype)(sh)),                                      \
-             "=&r" ((USItype)(sl))                                      \
-           : "rJ" ((USItype)(ah)),                                      \
-             "rI" ((USItype)(bh)),                                      \
-             "rJ" ((USItype)(al)),                                      \
-             "rI" ((USItype)(bl))                                       \
-           : "cc")
-
-
-/* asm fragments for mul and div */	 
-/* umul_ppmm(high_prod, low_prod, multipler, multiplicand) multiplies two
- * UWtype integers MULTIPLER and MULTIPLICAND, and generates a two UWtype
- * word product in HIGH_PROD and LOW_PROD.
- * These look ugly because the sun4/4c don't have umul/udiv/smul/sdiv in
- * hardware. 
- */
-#define umul_ppmm(w1, w0, u, v) \
-  __asm__ ("! Inlined umul_ppmm
-        wr      %%g0,%2,%%y     ! SPARC has 0-3 delay insn after a wr
-        sra     %3,31,%%g2      ! Don't move this insn
-        and     %2,%%g2,%%g2    ! Don't move this insn
-        andcc   %%g0,0,%%g1     ! Don't move this insn
-        mulscc  %%g1,%3,%%g1
-        mulscc  %%g1,%3,%%g1
-        mulscc  %%g1,%3,%%g1
-        mulscc  %%g1,%3,%%g1
-        mulscc  %%g1,%3,%%g1
-        mulscc  %%g1,%3,%%g1
-        mulscc  %%g1,%3,%%g1
-        mulscc  %%g1,%3,%%g1
-        mulscc  %%g1,%3,%%g1
-        mulscc  %%g1,%3,%%g1
-        mulscc  %%g1,%3,%%g1
-        mulscc  %%g1,%3,%%g1
-        mulscc  %%g1,%3,%%g1
-        mulscc  %%g1,%3,%%g1
-        mulscc  %%g1,%3,%%g1
-        mulscc  %%g1,%3,%%g1
-        mulscc  %%g1,%3,%%g1
-        mulscc  %%g1,%3,%%g1
-        mulscc  %%g1,%3,%%g1
-        mulscc  %%g1,%3,%%g1
-        mulscc  %%g1,%3,%%g1
-        mulscc  %%g1,%3,%%g1
-        mulscc  %%g1,%3,%%g1
-        mulscc  %%g1,%3,%%g1
-        mulscc  %%g1,%3,%%g1
-        mulscc  %%g1,%3,%%g1
-        mulscc  %%g1,%3,%%g1
-        mulscc  %%g1,%3,%%g1
-        mulscc  %%g1,%3,%%g1
-        mulscc  %%g1,%3,%%g1
-        mulscc  %%g1,%3,%%g1
-        mulscc  %%g1,%3,%%g1
-        mulscc  %%g1,0,%%g1
-        add     %%g1,%%g2,%0
-        rd      %%y,%1"                                                 \
-           : "=r" ((USItype)(w1)),                                      \
-             "=r" ((USItype)(w0))                                       \
-           : "%rI" ((USItype)(u)),                                      \
-             "r" ((USItype)(v))                                         \
-           : "%g1", "%g2", "cc")
-
-/* udiv_qrnnd(quotient, remainder, high_numerator, low_numerator,
- * denominator) divides a UDWtype, composed by the UWtype integers
- * HIGH_NUMERATOR and LOW_NUMERATOR, by DENOMINATOR and places the quotient
- * in QUOTIENT and the remainder in REMAINDER.  HIGH_NUMERATOR must be less
- * than DENOMINATOR for correct operation.  If, in addition, the most
- * significant bit of DENOMINATOR must be 1, then the pre-processor symbol
- * UDIV_NEEDS_NORMALIZATION is defined to 1.
- */
-
-#define udiv_qrnnd(q, r, n1, n0, d) \
-  __asm__ ("! Inlined udiv_qrnnd
-        mov     32,%%g1
-        subcc   %1,%2,%%g0
-1:      bcs     5f
-         addxcc %0,%0,%0        ! shift n1n0 and a q-bit in lsb
-        sub     %1,%2,%1        ! this kills msb of n
-        addx    %1,%1,%1        ! so this can't give carry
-        subcc   %%g1,1,%%g1
-2:      bne     1b
-         subcc  %1,%2,%%g0
-        bcs     3f
-         addxcc %0,%0,%0        ! shift n1n0 and a q-bit in lsb
-        b       3f
-         sub    %1,%2,%1        ! this kills msb of n
-4:      sub     %1,%2,%1
-5:      addxcc  %1,%1,%1
-        bcc     2b
-         subcc  %%g1,1,%%g1
-! Got carry from n.  Subtract next step to cancel this carry.
-        bne     4b
-         addcc  %0,%0,%0        ! shift n1n0 and a 0-bit in lsb
-        sub     %1,%2,%1
-3:      xnor    %0,0,%0
-        ! End of inline udiv_qrnnd"                                     \
-           : "=&r" ((USItype) (q)),                                     \
-             "=&r" ((USItype) (r))                                      \
-           : "r" ((USItype) (d)),                                       \
-             "1" ((USItype) (n1)),                                      \
-             "0" ((USItype) (n0)) : "%g1", "cc")
-
-#define UDIV_NEEDS_NORMALIZATION 0
+/* Exception flags. */
+#define FP_EX_INVALID		(1 << 4)
+#define FP_EX_OVERFLOW		(1 << 3)
+#define FP_EX_UNDERFLOW		(1 << 2)
+#define FP_EX_DIVZERO		(1 << 1)
+#define FP_EX_INEXACT		(1 << 0)
 
-#define abort()								\
-	return 0
+#define FP_HANDLE_EXCEPTIONS return _fex
 
-#ifdef __BIG_ENDIAN
-#define __BYTE_ORDER __BIG_ENDIAN
+#ifdef __SMP__
+#define FP_INHIBIT_RESULTS ((current->tss.fsr >> 23) & _fex)
 #else
-#define __BYTE_ORDER __LITTLE_ENDIAN
+#define FP_INHIBIT_RESULTS ((last_task_used_math->tss.fsr >> 23) & _fex)
 #endif
 
-/* Exception flags. */
-#define EFLAG_INVALID		(1 << 4)
-#define EFLAG_OVERFLOW		(1 << 3)
-#define EFLAG_UNDERFLOW		(1 << 2)
-#define EFLAG_DIVZERO		(1 << 1)
-#define EFLAG_INEXACT		(1 << 0)
+#endif
diff -u --recursive --new-file v2.3.3/linux/arch/sparc/math-emu/sfp-util.h linux/arch/sparc/math-emu/sfp-util.h
--- v2.3.3/linux/arch/sparc/math-emu/sfp-util.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc/math-emu/sfp-util.h	Sat May 29 11:09:04 1999
@@ -0,0 +1,115 @@
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <asm/byteorder.h>
+
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) 				\
+  __asm__ ("addcc %r4,%5,%1
+	addx %r2,%3,%0"							\
+	   : "=r" ((USItype)(sh)),					\
+	     "=&r" ((USItype)(sl))					\
+	   : "%rJ" ((USItype)(ah)),					\
+	     "rI" ((USItype)(bh)),					\
+	     "%rJ" ((USItype)(al)),					\
+	     "rI" ((USItype)(bl))					\
+	   : "cc")
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) 				\
+  __asm__ ("subcc %r4,%5,%1
+	subx %r2,%3,%0"							\
+	   : "=r" ((USItype)(sh)),					\
+	     "=&r" ((USItype)(sl))					\
+	   : "rJ" ((USItype)(ah)),					\
+	     "rI" ((USItype)(bh)),					\
+	     "rJ" ((USItype)(al)),					\
+	     "rI" ((USItype)(bl))					\
+	   : "cc")
+
+#define umul_ppmm(w1, w0, u, v) \
+  __asm__ ("! Inlined umul_ppmm
+	wr	%%g0,%2,%%y	! SPARC has 0-3 delay insn after a wr
+	sra	%3,31,%%g2	! Don't move this insn
+	and	%2,%%g2,%%g2	! Don't move this insn
+	andcc	%%g0,0,%%g1	! Don't move this insn
+	mulscc	%%g1,%3,%%g1
+	mulscc	%%g1,%3,%%g1
+	mulscc	%%g1,%3,%%g1
+	mulscc	%%g1,%3,%%g1
+	mulscc	%%g1,%3,%%g1
+	mulscc	%%g1,%3,%%g1
+	mulscc	%%g1,%3,%%g1
+	mulscc	%%g1,%3,%%g1
+	mulscc	%%g1,%3,%%g1
+	mulscc	%%g1,%3,%%g1
+	mulscc	%%g1,%3,%%g1
+	mulscc	%%g1,%3,%%g1
+	mulscc	%%g1,%3,%%g1
+	mulscc	%%g1,%3,%%g1
+	mulscc	%%g1,%3,%%g1
+	mulscc	%%g1,%3,%%g1
+	mulscc	%%g1,%3,%%g1
+	mulscc	%%g1,%3,%%g1
+	mulscc	%%g1,%3,%%g1
+	mulscc	%%g1,%3,%%g1
+	mulscc	%%g1,%3,%%g1
+	mulscc	%%g1,%3,%%g1
+	mulscc	%%g1,%3,%%g1
+	mulscc	%%g1,%3,%%g1
+	mulscc	%%g1,%3,%%g1
+	mulscc	%%g1,%3,%%g1
+	mulscc	%%g1,%3,%%g1
+	mulscc	%%g1,%3,%%g1
+	mulscc	%%g1,%3,%%g1
+	mulscc	%%g1,%3,%%g1
+	mulscc	%%g1,%3,%%g1
+	mulscc	%%g1,%3,%%g1
+	mulscc	%%g1,0,%%g1
+	add	%%g1,%%g2,%0
+	rd	%%y,%1"							\
+	   : "=r" ((USItype)(w1)),					\
+	     "=r" ((USItype)(w0))					\
+	   : "%rI" ((USItype)(u)),					\
+	     "r" ((USItype)(v))						\
+	   : "%g1", "%g2", "cc")
+
+/* It's quite necessary to add this much assembler for the sparc.
+   The default udiv_qrnnd (in C) is more than 10 times slower!  */
+#define udiv_qrnnd(q, r, n1, n0, d) \
+  __asm__ ("! Inlined udiv_qrnnd
+	mov	32,%%g1
+	subcc	%1,%2,%%g0
+1:	bcs	5f
+	 addxcc %0,%0,%0	! shift n1n0 and a q-bit in lsb
+	sub	%1,%2,%1	! this kills msb of n
+	addx	%1,%1,%1	! so this can't give carry
+	subcc	%%g1,1,%%g1
+2:	bne	1b
+	 subcc	%1,%2,%%g0
+	bcs	3f
+	 addxcc %0,%0,%0	! shift n1n0 and a q-bit in lsb
+	b	3f
+	 sub	%1,%2,%1	! this kills msb of n
+4:	sub	%1,%2,%1
+5:	addxcc	%1,%1,%1
+	bcc	2b
+	 subcc	%%g1,1,%%g1
+! Got carry from n.  Subtract next step to cancel this carry.
+	bne	4b
+	 addcc	%0,%0,%0	! shift n1n0 and a 0-bit in lsb
+	sub	%1,%2,%1
+3:	xnor	%0,0,%0
+	! End of inline udiv_qrnnd"					\
+	   : "=&r" ((USItype)(q)),					\
+	     "=&r" ((USItype)(r))					\
+	   : "r" ((USItype)(d)),					\
+	     "1" ((USItype)(n1)),					\
+	     "0" ((USItype)(n0)) : "%g1", "cc")
+#define UDIV_NEEDS_NORMALIZATION 0
+
+#define abort()								\
+	return 0
+
+#ifdef __BIG_ENDIAN
+#define __BYTE_ORDER __BIG_ENDIAN
+#else
+#define __BYTE_ORDER __LITTLE_ENDIAN
+#endif
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/defconfig linux/arch/sparc64/defconfig
--- v2.3.3/linux/arch/sparc64/defconfig	Mon May 17 09:55:21 1999
+++ linux/arch/sparc64/defconfig	Mon May 31 22:08:10 1999
@@ -68,6 +68,7 @@
 CONFIG_OBP_FLASH=m
 # CONFIG_SUN_BPP is not set
 # CONFIG_SUN_VIDEOPIX is not set
+CONFIG_SUN_AURORA=m
 
 #
 # Linux/SPARC audio subsystem (EXPERIMENTAL)
@@ -156,6 +157,10 @@
 # CONFIG_IPX_INTERN is not set
 # CONFIG_SPX is not set
 CONFIG_ATALK=m
+CONFIG_DECNET=m
+CONFIG_DECNET_SIOCGIFCONF=y
+# CONFIG_DECNET_ROUTER is not set
+CONFIG_DECNET_RAW=y
 # CONFIG_X25 is not set
 # CONFIG_LAPB is not set
 # CONFIG_BRIDGE is not set
@@ -310,6 +315,7 @@
 # CONFIG_MAC_PARTITION is not set
 CONFIG_SMD_DISKLABEL=y
 CONFIG_SOLARIS_X86_PARTITION=y
+# CONFIG_SGI_DISKLABEL is not set
 # CONFIG_UNIXWARE_DISKLABEL is not set
 CONFIG_AMIGA_PARTITION=y
 CONFIG_NLS=y
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/kernel/etrap.S linux/arch/sparc64/kernel/etrap.S
--- v2.3.3/linux/arch/sparc64/kernel/etrap.S	Tue Aug  4 16:03:35 1998
+++ linux/arch/sparc64/kernel/etrap.S	Thu May 27 09:55:21 1999
@@ -1,8 +1,8 @@
-/* $Id: etrap.S,v 1.40 1998/06/12 14:54:03 jj Exp $
+/* $Id: etrap.S,v 1.41 1999/05/25 16:53:09 jj Exp $
  * etrap.S: Preparing for entry into the kernel on Sparc V9.
  *
  * Copyright (C) 1996, 1997 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1997, 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997, 1998, 1999 Jakub Jelinek (jj@ultra.linux.cz)
  */
 
 #include <asm/asi.h>
@@ -59,10 +59,9 @@
 		stb			%l5, [%l6 + AOFF_task_tss + AOFF_thread_fpdepth]	! Store
 		wrpr			%g3, 0, %otherwin					! Single	Group+4bubbles
 		wrpr			%g2, 0, %wstate						! Single	Group+4bubbles
-		ldxa			[%l4] ASI_DMMU, %g2					! Load		Group
 		stxa			%g0, [%l4] ASI_DMMU					! Store		Group
-		stxa			%g2, [%l4 + %l4] ASI_DMMU				! Store		Group
 		flush			%l6							! Single	Group+9bubbles
+		wr			%g0, ASI_AIUS, %asi					! Single	Group+4bubbles
 2:		wrpr			%g0, 0x0, %tl						! Single	Group+4bubbles
 		andn			%g1, PSTATE_MM, %l1					! IEU0		Group
 		mov			%g4, %l4						! IEU1
@@ -135,10 +134,9 @@
 		sll			%g2, 3, %g2						! IEU0		Group
 		wrpr			%g3, 0, %otherwin					! Single	Group+4bubbles
 		wrpr			%g2, 0, %wstate						! Single	Group+4bubbles
-		ldxa			[%l4] ASI_DMMU, %g2					! Load		Group
 		stxa			%g0, [%l4] ASI_DMMU					! Store		Group
-		stxa			%g2, [%l4 + %l4] ASI_DMMU				! Store		Group
 		flush			%l6							! Single	Group+9bubbles
+		wr			%g0, ASI_AIUS, %asi					! Single	Group+4bubbles
 2:		wrpr			%g0, 0x0, %tl						! Single	Group+4bubbles
 		andn			%g1, PSTATE_MM, %l1					! IEU0		Group
 		mov			%g4, %l4						! IEU1
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/kernel/head.S linux/arch/sparc64/kernel/head.S
--- v2.3.3/linux/arch/sparc64/kernel/head.S	Thu Apr 22 19:24:51 1999
+++ linux/arch/sparc64/kernel/head.S	Thu May 27 09:55:21 1999
@@ -1,4 +1,4 @@
-/* $Id: head.S,v 1.60 1999/04/12 08:08:21 davem Exp $
+/* $Id: head.S,v 1.61 1999/05/25 16:53:10 jj Exp $
  * head.S: Initial boot code for the Sparc64 port of Linux.
  *
  * Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu)
@@ -286,6 +286,7 @@
 	wr	%g6, 0x0, %pic
 #endif
 
+	wr	%g0, ASI_P, %asi
 	mov	1, %g5
 	sllx	%g5, (PAGE_SHIFT + 1), %g5
 	sub	%g5, (REGWIN_SZ + STACK_BIAS), %g5
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/kernel/process.c linux/arch/sparc64/kernel/process.c
--- v2.3.3/linux/arch/sparc64/kernel/process.c	Tue May 11 08:24:32 1999
+++ linux/arch/sparc64/kernel/process.c	Thu May 27 09:55:21 1999
@@ -1,4 +1,4 @@
-/*  $Id: process.c,v 1.92 1999/05/08 23:04:48 davem Exp $
+/*  $Id: process.c,v 1.94 1999/05/27 04:49:30 davem Exp $
  *  arch/sparc64/kernel/process.c
  *
  *  Copyright (C) 1995, 1996 David S. Miller (davem@caip.rutgers.edu)
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/kernel/ptrace.c linux/arch/sparc64/kernel/ptrace.c
--- v2.3.3/linux/arch/sparc64/kernel/ptrace.c	Wed Mar 24 15:10:28 1999
+++ linux/arch/sparc64/kernel/ptrace.c	Sat May 22 12:42:04 1999
@@ -603,6 +603,7 @@
 		if((!child->dumpable ||
 		    (current->uid != child->euid) ||
 		    (current->uid != child->uid) ||
+		    (current->uid != child->suid) ||
 		    (current->gid != child->egid) ||
 		    (current->gid != child->sgid) ||
 		    (!cap_issubset(child->cap_permitted, current->cap_permitted)) ||
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/kernel/rtrap.S linux/arch/sparc64/kernel/rtrap.S
--- v2.3.3/linux/arch/sparc64/kernel/rtrap.S	Mon Nov 16 10:37:28 1998
+++ linux/arch/sparc64/kernel/rtrap.S	Thu May 27 09:55:21 1999
@@ -1,4 +1,4 @@
-/* $Id: rtrap.S,v 1.45 1998/11/09 15:33:29 davem Exp $
+/* $Id: rtrap.S,v 1.46 1999/05/25 16:53:20 jj Exp $
  * rtrap.S: Preparing for return from trap on Sparc V9.
  *
  * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -106,7 +106,6 @@
 		brnz,pn			%l3, kern_rtt
 		 mov			PRIMARY_CONTEXT, %l7
 		stxa			%l0, [%l7] ASI_DMMU
-		stxa			%l0, [%l7 + %l7] ASI_DMMU
 		flush			%o5
 		rdpr			%wstate, %l1
 
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/kernel/setup.c linux/arch/sparc64/kernel/setup.c
--- v2.3.3/linux/arch/sparc64/kernel/setup.c	Thu Apr 22 19:24:51 1999
+++ linux/arch/sparc64/kernel/setup.c	Sat May 29 11:09:04 1999
@@ -1,4 +1,4 @@
-/*  $Id: setup.c,v 1.43 1999/04/12 08:08:24 davem Exp $
+/*  $Id: setup.c,v 1.44 1999/05/28 02:17:29 davem Exp $
  *  linux/arch/sparc64/kernel/setup.c
  *
  *  Copyright (C) 1995,1996  David S. Miller (davem@caip.rutgers.edu)
@@ -550,7 +550,9 @@
 			ic_servaddr = sv;
 			if (gw)
 				ic_gateway = gw;
+#if defined(CONFIG_IP_PNP_BOOTP) || defined(CONFIG_IP_PNP_RARP)
 			ic_proto_enabled = 0;
+#endif
 		}
 	}
 #endif
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/kernel/smp.c linux/arch/sparc64/kernel/smp.c
--- v2.3.3/linux/arch/sparc64/kernel/smp.c	Tue May 11 08:24:32 1999
+++ linux/arch/sparc64/kernel/smp.c	Thu May 27 09:55:21 1999
@@ -272,25 +272,26 @@
 static inline void xcall_deliver(u64 data0, u64 data1, u64 data2, u64 pstate, unsigned long cpu)
 {
 	u64 result, target = (cpu << 14) | 0x70;
-	int stuck;
+	int stuck, tmp;
 
 #ifdef XCALL_DEBUG
 	printk("CPU[%d]: xcall(data[%016lx:%016lx:%016lx],tgt[%016lx])\n",
 	       smp_processor_id(), data0, data1, data2, target);
 #endif
 again:
+	tmp = 0x40;
 	__asm__ __volatile__("
-	wrpr	%0, %1, %%pstate
-	wr	%%g0, %2, %%asi
-	stxa	%3, [0x40] %%asi
-	stxa	%4, [0x50] %%asi
-	stxa	%5, [0x60] %%asi
+	wrpr	%1, %2, %%pstate
+	stxa	%4, [%0] %3
+	stxa	%5, [%0+%8] %3
+	add	%0, %8, %0
+	stxa	%6, [%0+%8] %3
 	membar	#Sync
-	stxa	%%g0, [%6] %%asi
+	stxa	%%g0, [%7] %3
 	membar	#Sync"
-	: /* No outputs */
+	: "=r" (tmp)
 	: "r" (pstate), "i" (PSTATE_IE), "i" (ASI_UDB_INTR_W),
-	  "r" (data0), "r" (data1), "r" (data2), "r" (target));
+	  "r" (data0), "r" (data1), "r" (data2), "r" (target), "r" (0x10), "0" (tmp));
 
 	/* NOTE: PSTATE_IE is still clear. */
 	stuck = 100000;
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/kernel/sys_sunos32.c linux/arch/sparc64/kernel/sys_sunos32.c
--- v2.3.3/linux/arch/sparc64/kernel/sys_sunos32.c	Tue Oct 27 09:52:20 1998
+++ linux/arch/sparc64/kernel/sys_sunos32.c	Mon May 24 13:03:49 1999
@@ -1,4 +1,4 @@
-/* $Id: sys_sunos32.c,v 1.22 1998/10/26 20:01:13 davem Exp $
+/* $Id: sys_sunos32.c,v 1.25 1999/05/24 19:40:44 davem Exp $
  * sys_sunos32.c: SunOS binary compatability layer on sparc64.
  *
  * Copyright (C) 1995, 1996, 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -1347,7 +1347,7 @@
 
 	lock_kernel();
 	ret = check_nonblock(sys32_readv(fd, vector, count), fd);
-	lock_kernel();
+	unlock_kernel();
 	return ret;
 }
 
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/kernel/trampoline.S linux/arch/sparc64/kernel/trampoline.S
--- v2.3.3/linux/arch/sparc64/kernel/trampoline.S	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/kernel/trampoline.S	Thu May 27 09:55:21 1999
@@ -1,4 +1,4 @@
-/* $Id: trampoline.S,v 1.8 1998/12/09 21:01:15 davem Exp $
+/* $Id: trampoline.S,v 1.9 1999/05/25 16:53:12 jj Exp $
  * trampoline.S: Jump start slave processors on sparc64.
  *
  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -130,6 +130,8 @@
 	 nop
 
 bounce:
+	wr	%g0, ASI_P, %asi
+
 	mov	PRIMARY_CONTEXT, %g7
 	stxa	%g0, [%g7] ASI_DMMU
 	membar	#Sync
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/kernel/traps.c linux/arch/sparc64/kernel/traps.c
--- v2.3.3/linux/arch/sparc64/kernel/traps.c	Thu Apr 22 19:24:51 1999
+++ linux/arch/sparc64/kernel/traps.c	Thu May 27 09:55:21 1999
@@ -1,4 +1,4 @@
-/* $Id: traps.c,v 1.58 1999/03/29 12:38:10 jj Exp $
+/* $Id: traps.c,v 1.59 1999/05/18 16:57:10 jj Exp $
  * arch/sparc64/kernel/traps.c
  *
  * Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu)
@@ -452,15 +452,35 @@
 	if((((unsigned long) pc) & 3))
 		return;
 
+	printk("Instruction DUMP:");
 	for(i = -3; i < 6; i++)
 		printk("%c%08x%c",i?' ':'<',pc[i],i?' ':'>');
 	printk("\n");
 }
 
+void user_instruction_dump (unsigned int *pc)
+{
+	int i;
+	unsigned int buf[9];
+	
+	if((((unsigned long) pc) & 3))
+		return;
+		
+	if(copy_from_user(buf, pc - 3, sizeof(buf)))
+		return;
+
+	printk("Instruction DUMP:");
+	for(i = 0; i < 9; i++)
+		printk("%c%08x%c",i==3?' ':'<',buf[i],i==3?' ':'>');
+	printk("\n");
+}
+
 void die_if_kernel(char *str, struct pt_regs *regs)
 {
 	extern void __show_regs(struct pt_regs * regs);
 	extern void smp_report_regs(void);
+	int count = 0;
+	struct reg_window *lastrw;
 	
 	/* Amuse the user. */
 	printk(
@@ -472,25 +492,28 @@
 	printk("%s(%d): %s\n", current->comm, current->pid, str);
 	__asm__ __volatile__("flushw");
 	__show_regs(regs);
-	{
+	if(regs->tstate & TSTATE_PRIV) {
 		struct reg_window *rw = (struct reg_window *)
 			(regs->u_regs[UREG_FP] + STACK_BIAS);
 
 		/* Stop the back trace when we hit userland or we
 		 * find some badly aligned kernel stack.
 		 */
+		lastrw = (struct reg_window *)current;
 		while(rw					&&
-		      (((unsigned long) rw) >= PAGE_OFFSET)	&&
+		      count++ < 30				&&
+		      rw >= lastrw				&&
+		      (char *) rw < ((char *) current)
+		        + sizeof (union task_union) 		&&
 		      !(((unsigned long) rw) & 0x7)) {
 			printk("Caller[%016lx]\n", rw->ins[7]);
+			lastrw = rw;
 			rw = (struct reg_window *)
 				(rw->ins[6] + STACK_BIAS);
 		}
-	}
-	if(regs->tstate & TSTATE_PRIV) {
-		printk("Instruction DUMP:");
 		instruction_dump ((unsigned int *) regs->tpc);
-	}
+	} else
+		user_instruction_dump ((unsigned int *) regs->tpc);
 #ifdef __SMP__
 	smp_report_regs();
 #endif
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/kernel/unaligned.c linux/arch/sparc64/kernel/unaligned.c
--- v2.3.3/linux/arch/sparc64/kernel/unaligned.c	Thu Apr 22 19:24:51 1999
+++ linux/arch/sparc64/kernel/unaligned.c	Thu May 27 09:55:21 1999
@@ -1,4 +1,4 @@
-/* $Id: unaligned.c,v 1.15 1999/04/03 11:36:21 anton Exp $
+/* $Id: unaligned.c,v 1.16 1999/05/25 16:53:15 jj Exp $
  * unaligned.c: Unaligned load/store trap handling with special
  *              cases for the kernel to do them more quickly.
  *
@@ -228,7 +228,8 @@
 	"sra	%%g7, 0, %%g7\n\t"						\
 	"stx	%%l1, [%0]\n\t"							\
 	"stx	%%g7, [%0 + 8]\n"						\
-"0:\n\n\t"									\
+"0:\n\t"									\
+	"wr	%%g0, %5, %%asi\n\n\t"						\
 	".section __ex_table\n\t"						\
 	".word	4b, " #errh "\n\t"						\
 	".word	5b, " #errh "\n\t"						\
@@ -244,7 +245,8 @@
 	".word	15b, " #errh "\n\t"						\
 	".word	16b, " #errh "\n\n\t"						\
 	".previous\n\t"								\
-	: : "r" (dest_reg), "r" (size), "r" (saddr), "r" (is_signed), "r" (asi)	\
+	: : "r" (dest_reg), "r" (size), "r" (saddr), "r" (is_signed),		\
+	  "r" (asi), "i" (ASI_AIUS)						\
 	: "l1", "l2", "g7", "g1", "cc");					\
 })
 	
@@ -282,7 +284,8 @@
 "2:\t"	"srl	%%l1, 8, %%l2\n"						\
 "16:\t"	"stba	%%l2, [%0] %%asi\n"						\
 "17:\t"	"stba	%%l1, [%0 + 1] %%asi\n"						\
-"0:\n\n\t"									\
+"0:\n\t"									\
+	"wr	%%g0, %4, %%asi\n\n\t"						\
 	".section __ex_table\n\t"						\
 	".word	4b, " #errh "\n\t"						\
 	".word	5b, " #errh "\n\t"						\
@@ -299,7 +302,7 @@
 	".word	16b, " #errh "\n\t"						\
 	".word	17b, " #errh "\n\n\t"						\
 	".previous\n\t"								\
-	: : "r" (dst_addr), "r" (size), "r" (src_val), "r" (asi)		\
+	: : "r" (dst_addr), "r" (size), "r" (src_val), "r" (asi), "i" (ASI_AIUS)\
 	: "l1", "l2", "g7", "g1", "cc");					\
 })
 
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/lib/VIS.h linux/arch/sparc64/lib/VIS.h
--- v2.3.3/linux/arch/sparc64/lib/VIS.h	Mon Jul  7 08:18:55 1997
+++ linux/arch/sparc64/lib/VIS.h	Thu May 27 09:55:21 1999
@@ -1,9 +1,9 @@
-/* $Id: VIS.h,v 1.3 1997/06/27 14:53:18 jj Exp $
+/* $Id: VIS.h,v 1.4 1999/05/25 16:52:50 jj Exp $
  * VIS.h: High speed copy/clear operations utilizing the UltraSparc
  *        Visual Instruction Set.
  *
  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1996, 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1996, 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
  */
 
 	/* VIS code can be used for numerous copy/set operation variants.
@@ -28,6 +28,8 @@
 #include <asm/head.h>
 #include <asm/asi.h>
 #else
+#define ASI_AIUS		0x11 /* Secondary, user				*/
+#define ASI_BLK_AIUS		0x71 /* Secondary, user, blk ld/st		*/
 #define ASI_P			0x80 /* Primary, implicit			*/
 #define ASI_S			0x81 /* Secondary, implicit			*/
 #define ASI_BLK_COMMIT_P	0xe0 /* Primary, blk store commit		*/
@@ -43,15 +45,28 @@
 	 * cell exchange program...
 	 */
 #define ASI_BLK_XOR		(ASI_P ^ ASI_BLK_P)
+	/* Well, things get more hairy if we use ASI_AIUS as
+	 * USER_DS and ASI_P as KERNEL_DS, we'd reach
+	 * commit block stores this way which is not what we want...
+	 */
+	/* ASI_P->ASI_BLK_P && ASI_AIUS->ASI_BLK_AIUS transitions can be done
+	 * as blkasi = asi | ASI_BLK_OR
+	 */
+#define ASI_BLK_OR		(ASI_BLK_P & ~ASI_P)
+	/* Transition back from ASI_BLK_P->ASI_P && ASI_BLK_AIUS->ASI_AIUS is
+	 * more complicated:
+	 * asi = blkasi ^ (blkasi >> 3) ^ ASI_BLK_XOR1
+	 */
+#define ASI_BLK_XOR1		(ASI_BLK_P ^ (ASI_BLK_P >> 3) ^ ASI_P)
 
 #define	asi_src			%o3
 #define asi_dest		%o4
 
 #ifdef __KERNEL__
 #define ASI_SETSRC_BLK		wr	asi_src, 0, %asi;
-#define ASI_SETSRC_NOBLK	wr	asi_src, ASI_BLK_XOR, %asi;
+#define ASI_SETSRC_NOBLK	wr	asi_src, 0, %asi;
 #define ASI_SETDST_BLK		wr	asi_dest, 0, %asi;
-#define ASI_SETDST_NOBLK	wr	asi_dest, ASI_BLK_XOR, %asi;
+#define ASI_SETDST_NOBLK	wr	asi_dest, 0, %asi;
 #define ASIBLK			%asi
 #define ASINORMAL		%asi
 #define LDUB			lduba
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/lib/VISbzero.S linux/arch/sparc64/lib/VISbzero.S
--- v2.3.3/linux/arch/sparc64/lib/VISbzero.S	Tue Aug  4 16:03:35 1998
+++ linux/arch/sparc64/lib/VISbzero.S	Thu May 27 09:55:21 1999
@@ -1,9 +1,9 @@
-/* $Id: VISbzero.S,v 1.9 1998/06/12 14:53:50 jj Exp $
+/* $Id: VISbzero.S,v 1.10 1999/05/25 16:52:56 jj Exp $
  * VISbzero.S: High speed clear operations utilizing the UltraSparc
  *        Visual Instruction Set.
  *
  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1996, 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1996, 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
  */
 
 #include "VIS.h"
@@ -84,9 +84,14 @@
 	.align		32
 #ifdef __KERNEL__
 	.globl		__bzero, __bzero_noasi
+__bzero_noasi:
+	rd		%asi, %g5
+	ba,pt		%xcc, __bzero+12
+	 mov		%g5, %o4
 __bzero:
+	rd		%asi, %g5
 	wr		%g0, ASI_P, %asi		! LSU	Group
-__bzero_noasi:
+	mov		ASI_P, %o4
 #else
 	.globl		bzero
 bzero_private:
@@ -143,8 +148,8 @@
 6:	andncc		%o1, 0x3f, %o3
 7:	be,pn		%xcc, 9f
 #ifdef __KERNEL__
-	 rd		%asi, %o4
-	wr		%o4, ASI_BLK_XOR, %asi
+	 or		%o4, ASI_BLK_OR, %g7 
+	wr		%g7, %g0, %asi
 	VISEntryHalf
 #else
 	 wr		%g0, ASI_BLK_P, %asi
@@ -221,7 +226,11 @@
 	add		%o0, 2, %o0
 1:	bne,a,pn	%xcc, 8f
 	 EX(STB		%g0, [%o0] ASINORMAL, add %g0, 1)
-8:	retl
+8:
+#ifdef __KERNEL__
+	wr		%g5, %g0, %asi
+#endif
+	retl
 	 RETL
 17:	be,pn		%xcc, 13b
 	 orcc		%o1, 0, %g0
@@ -230,7 +239,11 @@
 	subcc		%o1, 1, %o1
 	bne,pt		%xcc, 8b
 	 EX(STB		%g0, [%o0 - 1] ASINORMAL, add %o1, 1)
-0:	retl
+0:
+#ifdef __KERNEL__
+	wr		%g5, %g0, %asi
+#endif
+	retl
 	 RETL
 
 #ifdef __KERNEL__
@@ -239,6 +252,7 @@
 VISbzerofixup_reto1:
 	mov		%o1, %o0
 VISbzerofixup_ret0:
+	wr		%g5, %g0, %asi
 	retl
 	 wr		%g0, 0, %fprs
 VISbzerofixup_ret1:
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/lib/VIScopy.S linux/arch/sparc64/lib/VIScopy.S
--- v2.3.3/linux/arch/sparc64/lib/VIScopy.S	Tue Oct 27 09:52:20 1998
+++ linux/arch/sparc64/lib/VIScopy.S	Thu May 27 09:55:21 1999
@@ -1,9 +1,9 @@
-/* $Id: VIScopy.S,v 1.19 1998/10/19 21:52:19 davem Exp $
+/* $Id: VIScopy.S,v 1.20 1999/05/25 16:52:57 jj Exp $
  * VIScopy.S: High speed copy operations utilizing the UltraSparc
  *            Visual Instruction Set.
  *
  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1996, 1997, 1998 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1996, 1997, 1998, 1999 Jakub Jelinek (jj@ultra.linux.cz)
  */
 
 #include "VIS.h"
@@ -26,18 +26,25 @@
 #ifdef __KERNEL__
 
 #include <asm/visasm.h>
+#include <asm/asm_offsets.h>
 
-#define FPU_CLEAN_RETL				\
-	VISExit					\
-	retl;					\
-	 clr		%o0;
-#define FPU_RETL				\
-	VISExit					\
-	retl;					\
-	 clr		%o0;
-#define NORMAL_RETL				\
-	retl;					\
-	 clr		%o0;
+#define FPU_CLEAN_RETL								\
+	ldub		[%g6 + AOFF_task_tss + AOFF_thread_current_ds], %o1;	\
+	VISExit									\
+	clr		%o0;							\
+	retl;									\
+	 wr		%o1, %g0, %asi;
+#define FPU_RETL								\
+	ldub		[%g6 + AOFF_task_tss + AOFF_thread_current_ds], %o1;	\
+	VISExit									\
+	clr		%o0;							\
+	retl;									\
+	 wr		%o1, %g0, %asi;
+#define NORMAL_RETL								\
+	ldub		[%g6 + AOFF_task_tss + AOFF_thread_current_ds], %o1;	\
+	clr		%o0;							\
+	retl;									\
+	 wr		%o1, %g0, %asi;
 #define EX(x,y,a,b) 				\
 98: 	x,y;					\
 	.section .fixup;			\
@@ -146,10 +153,20 @@
 	EXVIS(STBLK		%fsrc, [%dest] ASIBLK);		\
 	add			%dest, 0x40, %dest;
 
+#ifdef __KERNEL__
 #define STORE_JUMP(dest, fsrc, target)				\
+	srl			asi_dest, 3, %g5;		\
 	EXVIS3(STBLK		%fsrc, [%dest] ASIBLK);		\
+	xor		       asi_dest, ASI_BLK_XOR1, asi_dest;\
 	add			%dest, 0x40, %dest;		\
+	xor			asi_dest, %g5, asi_dest;	\
 	ba,pt			%xcc, target;
+#else
+#define STORE_JUMP(dest, fsrc, target)				\
+	EXVIS3(STBLK		%fsrc, [%dest] ASIBLK);		\
+	add			%dest, 0x40, %dest;		\
+	ba,pt			%xcc, target;
+#endif
 
 #ifndef __KERNEL__
 #define VISLOOP_PAD nop; nop; nop; nop; \
@@ -157,9 +174,7 @@
 		    nop; nop; nop; nop; \
 		    nop; nop; nop;
 #else
-#define VISLOOP_PAD nop; nop; nop; nop; \
-		    nop; nop; nop; nop; \
-		    nop;
+#define VISLOOP_PAD
 #endif
 
 #define FINISH_VISCHUNK(dest, f0, f1, left)			\
@@ -170,11 +185,13 @@
 	EXVIS4(STDF		%f48, [%dest] ASINORMAL);	\
 	add			%dest, 8, %dest;
 
-#define UNEVEN_VISCHUNK(dest, f0, f1, left)			\
+#define UNEVEN_VISCHUNK_LAST(dest, f0, f1, left)		\
 	subcc			%left, 8, %left;		\
 	bl,pn			%xcc, vis_out;			\
-	 fsrc1			%f0, %f1;			\
-	ba,a,pt			%xcc, vis_slk;
+	 fsrc1			%f0, %f1;
+#define UNEVEN_VISCHUNK(dest, f0, f1, left)			\
+	UNEVEN_VISCHUNK_LAST(dest, f0, f1, left)		\
+	ba,a,pt			%xcc, vis_out_slk;
 
 	/* Macros for non-VIS memcpy code. */
 #ifdef REGS_64BIT
@@ -303,32 +320,32 @@
 		.type			__memcpy_entry,@function
 memcpy_private:
 __memcpy:
-memcpy:		mov		ASI_BLK_P, asi_src		! IEU0	Group
+memcpy:		mov		ASI_P, asi_src			! IEU0	Group
 		brnz,pt		%o2, __memcpy_entry		! CTI
-		 mov		ASI_BLK_P, asi_dest		! IEU1
+		 mov		ASI_P, asi_dest			! IEU1
 		retl
 		 clr		%o0
 
 		.align			32
 		.globl			__copy_from_user
 		.type			__copy_from_user,@function
-__copy_from_user:mov		ASI_BLK_S, asi_src		! IEU0	Group
+__copy_from_user:rd		%asi, asi_src			! IEU0	Group
 		brnz,pt		%o2, __memcpy_entry		! CTI
-		 mov		ASI_BLK_P, asi_dest		! IEU1
+		 mov		ASI_P, asi_dest			! IEU1
 
 		.globl			__copy_to_user
 		.type			__copy_to_user,@function
-__copy_to_user:	mov		ASI_BLK_P, asi_src		! IEU0	Group
+__copy_to_user:	mov		ASI_P, asi_src			! IEU0	Group
 		brnz,pt		%o2, __memcpy_entry		! CTI
-		 mov		ASI_BLK_S, asi_dest		! IEU1
+		 rd		%asi, asi_dest			! IEU1
 		retl						! CTI	Group
 		 clr		%o0				! IEU0	Group
 
 		.globl			__copy_in_user
 		.type			__copy_in_user,@function
-__copy_in_user:	mov		ASI_BLK_S, asi_src		! IEU0	Group
+__copy_in_user:	rd		%asi, asi_src			! IEU0	Group
 		brnz,pt		%o2, __memcpy_entry		! CTI
-		 mov		ASI_BLK_S, asi_dest		! IEU1
+		 mov		asi_src, asi_dest		! IEU1
 		retl						! CTI	Group
 		 clr		%o0				! IEU0	Group
 #endif
@@ -446,6 +463,10 @@
 	fmovd			%f0, %f2			! FPU
 	sub			%g3, 0x10, %g3			! IEU0	Group
 	sub			%o2, %g7, %o2			! IEU1
+#ifdef __KERNEL__
+	or			asi_src, ASI_BLK_OR, asi_src	! IEU0	Group
+	or			asi_dest, ASI_BLK_OR, asi_dest	! IEU1
+#endif
 	alignaddr		%g1, %g0, %g0			! GRU	Group
 	add			%g1, %g7, %g1			! IEU0	Group
 	subcc			%o2, %g3, %o2			! IEU1
@@ -584,7 +605,13 @@
 finish_f40:	FINISH_VISCHUNK(o0, f40, f42, g3)
 finish_f42:	FINISH_VISCHUNK(o0, f42, f44, g3)
 finish_f44:	FINISH_VISCHUNK(o0, f44, f46, g3)
-finish_f46:	UNEVEN_VISCHUNK(o0, f46, f0,  g3)
+finish_f46:	UNEVEN_VISCHUNK_LAST(o0, f46, f0,  g3)
+vis_out_slk:
+#ifdef __KERNEL__
+	srl		asi_src, 3, %g5				! IEU0	Group
+	xor		asi_src, ASI_BLK_XOR1, asi_src		! IEU1
+	xor		asi_src, %g5, asi_src			! IEU0	Group
+#endif
 vis_slk:ASI_SETSRC_NOBLK					! LSU	Group
 	EXVIS4(LDDF	[%o1] ASINORMAL, %f2)			! Load	Group
 	add		%o1, 8, %o1				! IEU0
@@ -592,7 +619,7 @@
 	ASI_SETDST_NOBLK					! LSU	Group
 	faligndata	%f0, %f2, %f8				! GRU	Group
 	EXVIS5(STDF	%f8, [%o0] ASINORMAL)			! Store
-	bl,pn		%xcc, vis_out				! CTI
+	bl,pn		%xcc, vis_out_slp			! CTI
 	 add		%o0, 8, %o0				! IEU0	Group
 	ASI_SETSRC_NOBLK					! LSU	Group
 	EXVIS4(LDDF	[%o1] ASINORMAL, %f0)			! Load	Group
@@ -603,8 +630,20 @@
 	EXVIS5(STDF	%f8, [%o0] ASINORMAL)			! Store
 	bge,pt		%xcc, vis_slk				! CTI
 	 add		%o0, 8, %o0				! IEU0	Group
+vis_out_slp:
+#ifdef __KERNEL__
+	brz,pt		%o2, vis_ret				! CTI	Group
+	 mov		%g1, %o1				! IEU0
+	ba,pt		%xcc, vis_slp+4				! CTI	Group
+	 ASI_SETSRC_NOBLK					! LSU	Group
+#endif
 vis_out:brz,pt		%o2, vis_ret				! CTI	Group
 	 mov		%g1, %o1				! IEU0
+#ifdef __KERNEL__
+	srl		asi_src, 3, %g5				! IEU0	Group
+	xor		asi_src, ASI_BLK_XOR1, asi_src		! IEU1
+	xor		asi_src, %g5, asi_src			! IEU0	Group
+#endif
 vis_slp:ASI_SETSRC_NOBLK					! LSU	Group
 	EXO2(LDUB	[%o1] ASINORMAL, %g5)			! LOAD
 	add		%o1, 1, %o1				! IEU0
@@ -970,11 +1009,13 @@
 		/* If this is copy_from_user(), zero out the rest of the
 		 * kernel buffer.
 		 */
+		ldub		[%g6 + AOFF_task_tss + AOFF_thread_current_ds], %o4
 		andcc		asi_src, 0x1, %g0
 		be,pt		%icc, 1f
-		 andcc		asi_dest, 0x1, %g0
-		bne,pn		%icc, 1f
 		 VISExit
+		andcc		asi_dest, 0x1, %g0
+		bne,pn		%icc, 1f
+		 nop
 		save		%sp, -160, %sp
 		mov		%i0, %o0
 		call		__bzero
@@ -982,7 +1023,7 @@
 		restore
 1:		mov		%o1, %o0
 		retl
-		 nop
+		 wr		%o4, %g0, %asi
 VIScopyfixup1:	subcc		%g2, 18, %g2
 		add		%o0, 32, %o0
 		bgeu,a,pt	%icc, VIScopyfixup1
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/lib/VIScsum.S linux/arch/sparc64/lib/VIScsum.S
--- v2.3.3/linux/arch/sparc64/lib/VIScsum.S	Tue Aug  4 16:03:35 1998
+++ linux/arch/sparc64/lib/VIScsum.S	Thu May 27 09:55:21 1999
@@ -1,4 +1,4 @@
-/* $Id: VIScsum.S,v 1.3 1998/06/12 14:53:57 jj Exp $
+/* $Id: VIScsum.S,v 1.4 1999/05/25 16:53:00 jj Exp $
  * VIScsum.S: High bandwidth IP checksumming utilizing the UltraSparc
  *            Visual Instruction Set.
  *
@@ -27,6 +27,7 @@
 #include <asm/head.h>
 #include <asm/asi.h>
 #include <asm/visasm.h>
+#include <asm/asm_offsets.h>
 #else
 #define ASI_BLK_P	0xf0
 #define FRPS_FEF	0x04
@@ -339,9 +340,13 @@
 	membar		#Sync			/*  LSU		Group			*/
 	DO_THE_TRICK(f44,f46,f48,f50,f52,f54,f56,f58,f60,f62,f0,f2,f4,f6,f8,f10,f12,f14)
 	END_THE_TRICK(f60,f62,f0,f2,f4,f6,f8,f10,f12,f14,f16,f18,f20,f22,f24,f26,f28,f30)
+#ifdef __KERNEL__
+	ldub		[%g6 + AOFF_task_tss + AOFF_thread_current_ds], %g7
+#endif
 	and		%o1, 0x3f, %o1		/*  IEU0	Group			*/
 #ifdef __KERNEL__
 	VISExit
+	wr		%g7, %g0, %asi
 #endif
 20:	andcc		%o1, 0xf0, %g1		/*  IEU1	Group			*/
 	be,pn		%icc, 23f		/*  CTI					*/
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/lib/VIScsumcopy.S linux/arch/sparc64/lib/VIScsumcopy.S
--- v2.3.3/linux/arch/sparc64/lib/VIScsumcopy.S	Tue Aug  4 16:03:35 1998
+++ linux/arch/sparc64/lib/VIScsumcopy.S	Thu May 27 09:55:21 1999
@@ -1,8 +1,8 @@
-/* $Id: VIScsumcopy.S,v 1.5 1998/06/12 14:53:48 jj Exp $
+/* $Id: VIScsumcopy.S,v 1.6 1999/05/25 16:53:03 jj Exp $
  * VIScsumcopy.S: High bandwidth IP checksumming with simultaneous
  *            copying utilizing the UltraSparc Visual Instruction Set.
  *
- * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
  *
  * Based on older sparc32/sparc64 checksum.S, which is:
  *
@@ -28,14 +28,17 @@
 #include <asm/asi.h>
 #include <asm/page.h>
 #include <asm/visasm.h>
+#define ASI_BLK_XOR	0
+#define ASI_BLK_XOR1	(ASI_BLK_P ^ (ASI_BLK_P >> 3) ^ ASI_P)
+#define ASI_BLK_OR	(ASI_BLK_P & ~ASI_P)
 #else
 #define ASI_P		0x80
 #define ASI_BLK_P	0xf0
 #define FRPS_FEF	0x04
 #define FPRS_DU		0x02
 #define FPRS_DL		0x01
-#endif
 #define ASI_BLK_XOR	(ASI_BLK_P ^ ASI_P)
+#endif
 
 #define src		o0
 #define dst		o1
@@ -244,7 +247,7 @@
 	.text
 	.globl		csum_partial_copy_vis
 	.align		32
-/* %asi should be either ASI_P or ASI_S for csum_partial_copy resp. csum_partial_copy_from_user */
+/* %asi should be either ASI_P or ASI_AIUS for csum_partial_copy resp. csum_partial_copy_from_user */
 /* This assumes that !((%src^%dst)&3) && !((%src|%dst)&1) && %len >= 256 */
 csum_partial_copy_vis:
 	andcc		%dst, 7, %g0		/*  IEU1	Group			*/
@@ -368,6 +371,7 @@
 3:	rd		%asi, %g2		/*  LSU		Group + 4 bubbles	*/
 #ifdef __KERNEL__
 4:	sethi		%hi(vis0s), %g7		/*  IEU0	Group			*/
+	or		%g2, ASI_BLK_OR, %g2	/*  IEU1				*/
 #else
 4:	rd		%pc, %g7		/*  LSU		Group + 4 bubbles	*/
 #endif
@@ -775,18 +779,24 @@
 e1:	END_THE_TRICK1(	f0,f2,f4,f6,f8,f10,f12,f14,f16,f6)
 e2:	END_THE_TRICK1(	f16,f18,f20,f22,f24,f26,f28,f30,f32,f6)
 e3:	END_THE_TRICK1(	f32,f34,f36,f38,f40,f42,f44,f46,f0,f6)
-ett:	rd		%gsr, %x3		/*  LSU		Group+4bubbles		*/
+ett:	rd		%asi, %x4		/*  LSU		Group+4bubbles		*/
+	rd		%gsr, %x3		/*  LSU		Group+4bubbles		*/
+#ifdef __KERNEL__
+	srl		%x4, 3, %x5		/*  IEU0	Group			*/
+	xor		%x4, ASI_BLK_XOR1, %x4	/*  IEU1				*/
+	wr		%x4, %x5, %asi		/*  LSU		Group+4bubbles		*/
+#else
+	wr		%x4, ASI_BLK_XOR, %asi	/*  LSU		Group+4bubbles		*/
+#endif
 	andcc		%x3, 7, %x3		/*  IEU1	Group			*/
-	add		%dst, 8, %dst		/*  IEU0	Group			*/
+	add		%dst, 8, %dst		/*  IEU0				*/
 	bne,pn		%icc, 1f		/*  CTI					*/
 	 fzero		%f10			/*  FPA					*/
 	brz,a,pn	%len, 2f		/*  CTI+IEU1	Group			*/
 	 std		%f6, [%dst - 8]		/*  Store				*/
-1:	rd		%asi, %x4		/*  LSU		Group+4bubbles		*/
-	sub		%src, 64, %src		/*  IEU0	Group			*/
-	cmp		%len, 8			/*  IEU1				*/
+1:	cmp		%len, 8			/*  IEU1				*/
 	blu,pn		%icc, 3f		/*  CTI					*/
-	 wr		%x4, ASI_BLK_XOR, %asi	/*  LSU		Group+4bubbles		*/
+	 sub		%src, 64, %src		/*  IEU0	Group			*/
 1:	ldda		[%src] %asi, %f2	/*  Load	Group			*/
 	fpadd32		%f10, %f2, %f12		/*  FPA		Group+load stall	*/
 	add		%src, 8, %src		/*  IEU0				*/
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/lib/VISmemset.S linux/arch/sparc64/lib/VISmemset.S
--- v2.3.3/linux/arch/sparc64/lib/VISmemset.S	Tue Aug  4 16:03:35 1998
+++ linux/arch/sparc64/lib/VISmemset.S	Thu May 27 09:55:21 1999
@@ -1,4 +1,4 @@
-/* $Id: VISmemset.S,v 1.8 1998/06/12 14:53:59 jj Exp $
+/* $Id: VISmemset.S,v 1.9 1999/05/25 16:53:01 jj Exp $
  * VISmemset.S: High speed memset operations utilizing the UltraSparc
  *        Visual Instruction Set.
  *
@@ -143,7 +143,8 @@
 	VISEntryHalf
 #endif
 	ldd		[%o0 - 8], %f0
-18:	wr		%g0, ASI_BLK_P, %asi
+18:	rd		%asi, %g2
+	wr		%g0, ASI_BLK_P, %asi
 	membar		#StoreStore | #LoadStore
 	andcc		%o3, 0xc0, %g5
 	and		%o2, 0x3f, %o2
@@ -174,6 +175,7 @@
 	 add		%o0, 256, %o0
 12:
 #ifdef __KERNEL__
+	wr		%g2, %g0, %asi
 	VISExitHalf
 #else
 #ifndef REGS_64BIT
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/lib/blockops.S linux/arch/sparc64/lib/blockops.S
--- v2.3.3/linux/arch/sparc64/lib/blockops.S	Tue Oct 27 09:52:20 1998
+++ linux/arch/sparc64/lib/blockops.S	Thu May 27 09:55:21 1999
@@ -1,4 +1,4 @@
-/* $Id: blockops.S,v 1.16 1998/10/20 03:09:04 jj Exp $
+/* $Id: blockops.S,v 1.17 1999/05/25 16:52:52 jj Exp $
  * blockops.S: UltraSparc block zero optimized routines.
  *
  * Copyright (C) 1996,1998 David S. Miller (davem@caip.rutgers.edu)
@@ -136,6 +136,7 @@
 
 	faddd		%f0, %f2, %f12			! FPA	Group
 	fmuld		%f0, %f2, %f14			! FPM
+	rd		%asi, %g2			! LSU	Group
 	wr		%g0, ASI_BLK_P, %asi		! LSU	Group
 	membar		#StoreLoad | #StoreStore | #LoadStore	! LSU	Group
 1:	stda		%f0, [%o0 + 0x00] %asi		! Store	Group
@@ -147,6 +148,7 @@
 	bne,pt		%icc, 1b			! CTI
 	 add		%o0, 0x100, %o0			! IEU0	Group
 	membar		#Sync				! LSU	Group
+	wr		%g2, %g0, %asi			! LSU	Group
 	VISExitHalf
 
 	stxa		%g5, [%o2] ASI_DMMU
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/lib/checksum.S linux/arch/sparc64/lib/checksum.S
--- v2.3.3/linux/arch/sparc64/lib/checksum.S	Sun Oct  4 10:22:43 1998
+++ linux/arch/sparc64/lib/checksum.S	Thu May 27 09:55:21 1999
@@ -18,6 +18,7 @@
 #include <asm/ptrace.h>
 #include <asm/asi.h>
 #include <asm/page.h>
+#include <asm/asm_offsets.h>
 
 	/* The problem with the "add with carry" instructions on Ultra
 	 * are two fold.  Firstly, they cannot pair with jack shit,
@@ -265,10 +266,12 @@
 	.globl	cpc_handler
 cpc_handler:
 	ldx	[%sp + 0x7ff + 128], %g1
+	ldub	[%g6 + AOFF_task_tss + AOFF_thread_current_ds], %g3
 	sub	%g0, EFAULT, %g2
 	brnz,a,pt %g1, 1f
 	 st	%g2, [%g1]
 1:	sethi	%uhi(PAGE_OFFSET), %g4
+	wr	%g3, %g0, %asi
 	retl
 	 sllx	%g4, 32, %g4
 
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/lib/strlen_user.S linux/arch/sparc64/lib/strlen_user.S
--- v2.3.3/linux/arch/sparc64/lib/strlen_user.S	Thu Sep  4 12:54:48 1997
+++ linux/arch/sparc64/lib/strlen_user.S	Thu May 27 09:55:21 1999
@@ -20,26 +20,26 @@
 	andcc	%o0, 3, %g0
 	be,pt	%icc, 9f
 	 sethi	%hi(HI_MAGIC), %o4
-10:	lduba	[%o0] ASI_S, %o5
+10:	lduba	[%o0] %asi, %o5
 	brz,pn	%o5, 21f
 	 add	%o0, 1, %o0
 	andcc	%o0, 3, %g0
 	be,pn	%icc, 4f
 	 or	%o4, %lo(HI_MAGIC), %o3
-11:	lduba	[%o0] ASI_S, %o5
+11:	lduba	[%o0] %asi, %o5
 	brz,pn	%o5, 22f
 	 add	%o0, 1, %o0
 	andcc	%o0, 3, %g0
 	be,pt	%icc, 13f
 	 srl	%o3, 7, %o2
-12:	lduba	[%o0] ASI_S, %o5
+12:	lduba	[%o0] %asi, %o5
 	brz,pn	%o5, 23f
 	 add	%o0, 1, %o0
 	ba,pt	%icc, 2f
-15:	 lda	[%o0] ASI_S, %o5
+15:	 lda	[%o0] %asi, %o5
 9:	or	%o4, %lo(HI_MAGIC), %o3
 4:	srl	%o3, 7, %o2
-13:	lda	[%o0] ASI_S, %o5
+13:	lda	[%o0] %asi, %o5
 2:	sub	%o5, %o2, %o4
 	andcc	%o4, %o3, %g0
 	be,pt	%icc, 13b
@@ -60,7 +60,7 @@
 	 add	%o4, 1, %o4
 	andcc	%o5, 0xff, %g0
 	bne,a,pt %icc, 2b
-14:	 lda	[%o0] ASI_S, %o5
+14:	 lda	[%o0] %asi, %o5
 	add	%o4, 1, %o4
 1:	retl
 	 sub	%o4, %o1, %o0
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/lib/strncpy_from_user.S linux/arch/sparc64/lib/strncpy_from_user.S
--- v2.3.3/linux/arch/sparc64/lib/strncpy_from_user.S	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/lib/strncpy_from_user.S	Thu May 27 09:55:21 1999
@@ -1,7 +1,7 @@
-/* $Id: strncpy_from_user.S,v 1.5 1997/09/08 11:29:23 jj Exp $
+/* $Id: strncpy_from_user.S,v 1.6 1999/05/25 16:53:05 jj Exp $
  * strncpy_from_user.S: Sparc64 strncpy from userspace.
  *
- *  Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ *  Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
  */
 
 #include <asm/asi.h>
@@ -37,19 +37,20 @@
 	andcc	%o1, 7, %g0		! IEU1
 	bne,pn	%icc, 30f		! CTI
 	 ldx	[%o5 + %lo(0b)], %o4	! Load	Group
-60:	ldxa	[%o1] ASI_S, %g1	! Load	Group
-	add	%o1, %o2, %o1		! IEU0
-	subcc	%g0, %o2, %o3		! IEU1
-	bgeu,pn	%xcc, 10f		! CTI
+	add	%o0, %o2, %g3		! IEU0
+60:	ldxa	[%o1] %asi, %g1		! Load	Group
+	brlez,pn %o2, 10f		! CTI
 	 sllx	%o4, 7, %o5		! IEU0	Group
-	add	%o0, %o2, %o0		! IEU1
+	mov	%o0, %o3		! IEU1
 1:	sub	%g1, %o4, %g2		! IEU0	Group
-	stx	%g1, [%o0 + %o3]	! Store
+	stx	%g1, [%o0]		! Store
+	add	%o0, 8, %o0		! IEU1
 	andcc	%g2, %o5, %g0		! IEU1	Group
 	bne,pn	%xcc, 5f		! CTI
-	 add	%o3, 8, %o3		! IEU0
-	brlz,a,pt %o3, 1b		! CTI(IEU1) Group
-61:	 ldxa	[%o1 + %o3] ASI_S, %g1	! Load
+	 add	%o1, 8, %o1		! IEU0
+	cmp	%o0, %g3		! IEU1	Group
+	bl,a,pt %xcc, 1b		! CTI
+61:	 ldxa	[%o1] %asi, %g1		! Load
 10:	retl				! CTI	Group
 	 mov	%o2, %o0		! IEU0
 5:	srlx	%g2, 32, %g7		! IEU0	Group
@@ -78,44 +79,45 @@
 	be,pn	%icc, 56f		! CTI
 	 andcc	%g1, 0xff, %g0		! IEU1	Group
 	be,a,pn	%icc, 57f		! CTI
-	 add	%o2, %o3, %o0		! IEU0
-2:	brlz,a,pt %o3, 1b		! CTI(IEU1) Group
-62:	 ldxa	[%o1 + %o3] ASI_S, %g1	! Load
+	 sub	%o0, %o3, %o0		! IEU0
+2:	cmp	%o0, %g3		! IEU1	Group
+	bl,a,pt	%xcc, 1b		! CTI
+62:	 ldxa	[%o1] %asi, %g1		! Load
 	retl				! CTI	Group
 	 mov	%o2, %o0		! IEU0
-50:	add	%o2, %o3, %o0
+50:	sub	%o0, %o3, %o0
 	retl
 	 sub	%o0, 8, %o0
-51:	add	%o2, %o3, %o0
+51:	sub	%o0, %o3, %o0
 	retl
 	 sub	%o0, 7, %o0
-52:	add	%o2, %o3, %o0
+52:	sub	%o0, %o3, %o0
 	retl
 	 sub	%o0, 6, %o0
-53:	add	%o2, %o3, %o0
+53:	sub	%o0, %o3, %o0
 	retl
 	 sub	%o0, 5, %o0
-54:	add	%o2, %o3, %o0
+54:	sub	%o0, %o3, %o0
 	retl
 	 sub	%o0, 4, %o0
-55:	add	%o2, %o3, %o0
+55:	sub	%o0, %o3, %o0
 	retl
 	 sub	%o0, 3, %o0
-56:	add	%o2, %o3, %o0
+56:	sub	%o0, %o3, %o0
 	retl
 	 sub	%o0, 2, %o0
 57:	retl
 	 sub	%o0, 1, %o0
 30:	brlez,pn %o2, 3f
-	 add	%o1, %o2, %o1
-	sub	%g0, %o2, %o3
+	 sub	%g0, %o2, %o3
 	add	%o0, %o2, %o0
-63:	lduba	[%o1 + %o3] ASI_S, %o4
-1:	brz,pn	%o4, 2f
+63:	lduba	[%o1] %asi, %o4
+1:	add	%o1, 1, %o1
+	brz,pn	%o4, 2f
 	 stb	%o4, [%o0 + %o3]
 	addcc	%o3, 1, %o3
 	bne,pt	%xcc, 1b
-64:	 lduba	[%o1 + %o3] ASI_S, %o4
+64:	 lduba	[%o1] %asi, %o4
 3:	retl
 	 mov	%o2, %o0
 2:	retl
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/Makefile linux/arch/sparc64/math-emu/Makefile
--- v2.3.3/linux/arch/sparc64/math-emu/Makefile	Tue Oct 27 09:52:20 1998
+++ linux/arch/sparc64/math-emu/Makefile	Sat May 29 11:09:04 1999
@@ -11,7 +11,7 @@
 O_OBJS   := math.o fabsq.o faddq.o fdivq.o fdmulq.o fitoq.o 		\
 		fmovq.o fmulq.o fnegq.o fqtoi.o fqtox.o fsubq.o		\
 		fxtoq.o fdtoq.o fstoq.o fqtos.o fqtod.o fsqrtq.o	\
-		fcmpq.o fcmpeq.o udivmodti4.o				\
+		fcmpq.o fcmpeq.o					\
 		fsqrts.o fsqrtd.o fadds.o faddd.o fsubs.o fsubd.o	\
 		fmuls.o fmuld.o fdivs.o fdivd.o fsmuld.o		\
 		fstoi.o fdtoi.o fstox.o fdtox.o fstod.o fdtos.o
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/double.h linux/arch/sparc64/math-emu/double.h
--- v2.3.3/linux/arch/sparc64/math-emu/double.h	Tue Apr 14 17:44:21 1998
+++ linux/arch/sparc64/math-emu/double.h	Sat May 29 11:09:04 1999
@@ -1,6 +1,26 @@
-/*
- * Definitions for IEEE Double Precision
- */
+/* Software floating-point emulation.
+   Definitions for IEEE Double Precision
+   Copyright (C) 1997,1998,1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Richard Henderson (rth@cygnus.com),
+		  Jakub Jelinek (jj@ultra.linux.cz),
+		  David S. Miller (davem@redhat.com) and
+		  Peter Maydell (pmaydell@chiark.greenend.org.uk).
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If
+   not, write to the Free Software Foundation, Inc.,
+   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 #if _FP_W_TYPE_SIZE < 32
 #error "Here's a nickel kid.  Go buy yourself a real computer."
@@ -49,7 +69,13 @@
 
 #define FP_DECL_D(X)		_FP_DECL(2,X)
 #define FP_UNPACK_RAW_D(X,val)	_FP_UNPACK_RAW_2(D,X,val)
+#define FP_UNPACK_RAW_DP(X,val)	_FP_UNPACK_RAW_2_P(D,X,val)
 #define FP_PACK_RAW_D(val,X)	_FP_PACK_RAW_2(D,val,X)
+#define FP_PACK_RAW_DP(val,X)		\
+  do {					\
+    if (!FP_INHIBIT_RESULTS)		\
+      _FP_PACK_RAW_2_P(D,val,X);	\
+  } while (0)
 
 #define FP_UNPACK_D(X,val)		\
   do {					\
@@ -57,24 +83,42 @@
     _FP_UNPACK_CANONICAL(D,2,X);	\
   } while (0)
 
+#define FP_UNPACK_DP(X,val)		\
+  do {					\
+    _FP_UNPACK_RAW_2_P(D,X,val);	\
+    _FP_UNPACK_CANONICAL(D,2,X);	\
+  } while (0)
+
 #define FP_PACK_D(val,X)		\
   do {					\
     _FP_PACK_CANONICAL(D,2,X);		\
     _FP_PACK_RAW_2(D,val,X);		\
   } while (0)
 
-#define FP_NEG_D(R,X)		_FP_NEG(D,2,R,X)
-#define FP_ADD_D(R,X,Y)		_FP_ADD(D,2,R,X,Y)
-#define FP_SUB_D(R,X,Y)		_FP_SUB(D,2,R,X,Y)
-#define FP_MUL_D(R,X,Y)		_FP_MUL(D,2,R,X,Y)
-#define FP_DIV_D(R,X,Y)		_FP_DIV(D,2,R,X,Y)
-#define FP_SQRT_D(R,X)		_FP_SQRT(D,2,R,X)
+#define FP_PACK_DP(val,X)		\
+  do {					\
+    _FP_PACK_CANONICAL(D,2,X);		\
+    if (!FP_INHIBIT_RESULTS)		\
+      _FP_PACK_RAW_2_P(D,val,X);	\
+  } while (0)
+
+#define FP_ISSIGNAN_D(X)		_FP_ISSIGNAN(D,2,X)
+#define FP_NEG_D(R,X)			_FP_NEG(D,2,R,X)
+#define FP_ADD_D(R,X,Y)			_FP_ADD(D,2,R,X,Y)
+#define FP_SUB_D(R,X,Y)			_FP_SUB(D,2,R,X,Y)
+#define FP_MUL_D(R,X,Y)			_FP_MUL(D,2,R,X,Y)
+#define FP_DIV_D(R,X,Y)			_FP_DIV(D,2,R,X,Y)
+#define FP_SQRT_D(R,X)			_FP_SQRT(D,2,R,X)
+#define _FP_SQRT_MEAT_D(R,S,T,X,Q)	_FP_SQRT_MEAT_2(R,S,T,X,Q)
 
 #define FP_CMP_D(r,X,Y,un)	_FP_CMP(D,2,r,X,Y,un)
 #define FP_CMP_EQ_D(r,X,Y)	_FP_CMP_EQ(D,2,r,X,Y)
 
-#define FP_TO_INT_D(r,X,rsz,rsg)  _FP_TO_INT(D,2,r,X,rsz,rsg)
-#define FP_FROM_INT_D(X,r,rs,rt)  _FP_FROM_INT(D,2,X,r,rs,rt)
+#define FP_TO_INT_D(r,X,rsz,rsg)	_FP_TO_INT(D,2,r,X,rsz,rsg)
+#define FP_FROM_INT_D(X,r,rs,rt)	_FP_FROM_INT(D,2,X,r,rs,rt)
+
+#define _FP_FRAC_HIGH_D(X)	_FP_FRAC_HIGH_2(X)
+#define _FP_FRAC_HIGH_RAW_D(X)	_FP_FRAC_HIGH_2(X)
 
 #else
 
@@ -96,7 +140,13 @@
 
 #define FP_DECL_D(X)		_FP_DECL(1,X)
 #define FP_UNPACK_RAW_D(X,val)	_FP_UNPACK_RAW_1(D,X,val)
+#define FP_UNPACK_RAW_DP(X,val)	_FP_UNPACK_RAW_1_P(D,X,val)
 #define FP_PACK_RAW_D(val,X)	_FP_PACK_RAW_1(D,val,X)
+#define FP_PACK_RAW_DP(val,X)		\
+  do {					\
+    if (!FP_INHIBIT_RESULTS)		\
+      _FP_PACK_RAW_1_P(D,val,X);	\
+  } while (0)
 
 #define FP_UNPACK_D(X,val)		\
   do {					\
@@ -104,18 +154,33 @@
     _FP_UNPACK_CANONICAL(D,1,X);	\
   } while (0)
 
+#define FP_UNPACK_DP(X,val)		\
+  do {					\
+    _FP_UNPACK_RAW_1_P(D,X,val);	\
+    _FP_UNPACK_CANONICAL(D,1,X);	\
+  } while (0)
+
 #define FP_PACK_D(val,X)		\
   do {					\
     _FP_PACK_CANONICAL(D,1,X);		\
     _FP_PACK_RAW_1(D,val,X);		\
   } while (0)
 
-#define FP_NEG_D(R,X)		_FP_NEG(D,1,R,X)
-#define FP_ADD_D(R,X,Y)		_FP_ADD(D,1,R,X,Y)
-#define FP_SUB_D(R,X,Y)		_FP_SUB(D,1,R,X,Y)
-#define FP_MUL_D(R,X,Y)		_FP_MUL(D,1,R,X,Y)
-#define FP_DIV_D(R,X,Y)		_FP_DIV(D,1,R,X,Y)
-#define FP_SQRT_D(R,X)		_FP_SQRT(D,1,R,X)
+#define FP_PACK_DP(val,X)		\
+  do {					\
+    _FP_PACK_CANONICAL(D,1,X);		\
+    if (!FP_INHIBIT_RESULTS)		\
+      _FP_PACK_RAW_1_P(D,val,X);	\
+  } while (0)
+
+#define FP_ISSIGNAN_D(X)		_FP_ISSIGNAN(D,1,X)
+#define FP_NEG_D(R,X)			_FP_NEG(D,1,R,X)
+#define FP_ADD_D(R,X,Y)			_FP_ADD(D,1,R,X,Y)
+#define FP_SUB_D(R,X,Y)			_FP_SUB(D,1,R,X,Y)
+#define FP_MUL_D(R,X,Y)			_FP_MUL(D,1,R,X,Y)
+#define FP_DIV_D(R,X,Y)			_FP_DIV(D,1,R,X,Y)
+#define FP_SQRT_D(R,X)			_FP_SQRT(D,1,R,X)
+#define _FP_SQRT_MEAT_D(R,S,T,X,Q)	_FP_SQRT_MEAT_1(R,S,T,X,Q)
 
 /* The implementation of _FP_MUL_D and _FP_DIV_D should be chosen by
    the target machine.  */
@@ -123,7 +188,10 @@
 #define FP_CMP_D(r,X,Y,un)	_FP_CMP(D,1,r,X,Y,un)
 #define FP_CMP_EQ_D(r,X,Y)	_FP_CMP_EQ(D,1,r,X,Y)
 
-#define FP_TO_INT_D(r,X,rsz,rsg)  _FP_TO_INT(D,1,r,X,rsz,rsg)
-#define FP_FROM_INT_D(X,r,rs,rt)  _FP_FROM_INT(D,1,X,r,rs,rt)
+#define FP_TO_INT_D(r,X,rsz,rsg)	_FP_TO_INT(D,1,r,X,rsz,rsg)
+#define FP_FROM_INT_D(X,r,rs,rt)	_FP_FROM_INT(D,1,X,r,rs,rt)
+
+#define _FP_FRAC_HIGH_D(X)	_FP_FRAC_HIGH_1(X)
+#define _FP_FRAC_HIGH_RAW_D(X)	_FP_FRAC_HIGH_1(X)
 
 #endif /* W_TYPE_SIZE < 64 */
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/extended.h linux/arch/sparc64/math-emu/extended.h
--- v2.3.3/linux/arch/sparc64/math-emu/extended.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/extended.h	Sat May 29 11:09:04 1999
@@ -0,0 +1,388 @@
+/* Software floating-point emulation.
+   Definitions for IEEE Extended Precision.
+   Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Jakub Jelinek (jj@ultra.linux.cz).
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If
+   not, write to the Free Software Foundation, Inc.,
+   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#if _FP_W_TYPE_SIZE < 32
+#error "Here's a nickel, kid. Go buy yourself a real computer."
+#endif
+
+#if _FP_W_TYPE_SIZE < 64
+#define _FP_FRACTBITS_E         (4*_FP_W_TYPE_SIZE)
+#else
+#define _FP_FRACTBITS_E		(2*_FP_W_TYPE_SIZE)
+#endif
+
+#define _FP_FRACBITS_E		64
+#define _FP_FRACXBITS_E		(_FP_FRACTBITS_E - _FP_FRACBITS_E)
+#define _FP_WFRACBITS_E		(_FP_WORKBITS + _FP_FRACBITS_E)
+#define _FP_WFRACXBITS_E	(_FP_FRACTBITS_E - _FP_WFRACBITS_E)
+#define _FP_EXPBITS_E		15
+#define _FP_EXPBIAS_E		16383
+#define _FP_EXPMAX_E		32767
+
+#define _FP_QNANBIT_E		\
+	((_FP_W_TYPE)1 << (_FP_FRACBITS_E-2) % _FP_W_TYPE_SIZE)
+#define _FP_IMPLBIT_E		\
+	((_FP_W_TYPE)1 << (_FP_FRACBITS_E-1) % _FP_W_TYPE_SIZE)
+#define _FP_OVERFLOW_E		\
+	((_FP_W_TYPE)1 << (_FP_WFRACBITS_E % _FP_W_TYPE_SIZE))
+
+#if _FP_W_TYPE_SIZE < 64
+
+union _FP_UNION_E
+{
+   long double flt;
+   struct 
+   {
+#if __BYTE_ORDER == __BIG_ENDIAN
+      unsigned long pad1 : _FP_W_TYPE_SIZE;
+      unsigned long pad2 : (_FP_W_TYPE_SIZE - 1 - _FP_EXPBITS_E);
+      unsigned long sign : 1;
+      unsigned long exp : _FP_EXPBITS_E;
+      unsigned long frac1 : _FP_W_TYPE_SIZE;
+      unsigned long frac0 : _FP_W_TYPE_SIZE;
+#else
+      unsigned long frac0 : _FP_W_TYPE_SIZE;
+      unsigned long frac1 : _FP_W_TYPE_SIZE;
+      unsigned exp : _FP_EXPBITS_E;
+      unsigned sign : 1;
+#endif /* not bigendian */
+   } bits __attribute__((packed));
+};
+
+
+#define FP_DECL_E(X)		_FP_DECL(4,X)
+
+#define FP_UNPACK_RAW_E(X, val)				\
+  do {							\
+    union _FP_UNION_E _flo; _flo.flt = (val);		\
+							\
+    X##_f[2] = 0; X##_f[3] = 0;				\
+    X##_f[0] = _flo.bits.frac0;				\
+    X##_f[1] = _flo.bits.frac1;				\
+    X##_e  = _flo.bits.exp;				\
+    X##_s  = _flo.bits.sign;				\
+    if (!X##_e && (X##_f[1] || X##_f[0])		\
+        && !(X##_f[1] & _FP_IMPLBIT_E))			\
+      {							\
+        X##_e++;					\
+        FP_SET_EXCEPTION(FP_EX_DENORM);			\
+      }							\
+  } while (0)
+
+#define FP_UNPACK_RAW_EP(X, val)			\
+  do {							\
+    union _FP_UNION_E *_flo =				\
+    (union _FP_UNION_E *)(val);				\
+							\
+    X##_f[2] = 0; X##_f[3] = 0;				\
+    X##_f[0] = _flo->bits.frac0;			\
+    X##_f[1] = _flo->bits.frac1;			\
+    X##_e  = _flo->bits.exp;				\
+    X##_s  = _flo->bits.sign;				\
+    if (!X##_e && (X##_f[1] || X##_f[0])		\
+        && !(X##_f[1] & _FP_IMPLBIT_E))			\
+      {							\
+        X##_e++;					\
+        FP_SET_EXCEPTION(FP_EX_DENORM);			\
+      }							\
+  } while (0)
+
+#define FP_PACK_RAW_E(val, X)				\
+  do {							\
+    union _FP_UNION_E _flo;				\
+							\
+    if (X##_e) X##_f[1] |= _FP_IMPLBIT_E;		\
+    else X##_f[1] &= ~(_FP_IMPLBIT_E);			\
+    _flo.bits.frac0 = X##_f[0];				\
+    _flo.bits.frac1 = X##_f[1];				\
+    _flo.bits.exp   = X##_e;				\
+    _flo.bits.sign  = X##_s;				\
+							\
+    (val) = _flo.flt;					\
+  } while (0)
+
+#define FP_PACK_RAW_EP(val, X)				\
+  do {							\
+    if (!FP_INHIBIT_RESULTS)				\
+      {							\
+	union _FP_UNION_E *_flo =			\
+	  (union _FP_UNION_E *)(val);			\
+							\
+	if (X##_e) X##_f[1] |= _FP_IMPLBIT_E;		\
+	else X##_f[1] &= ~(_FP_IMPLBIT_E);		\
+	_flo->bits.frac0 = X##_f[0];			\
+	_flo->bits.frac1 = X##_f[1];			\
+	_flo->bits.exp   = X##_e;			\
+	_flo->bits.sign  = X##_s;			\
+      }							\
+  } while (0)
+
+#define FP_UNPACK_E(X,val)		\
+  do {					\
+    FP_UNPACK_RAW_E(X,val);		\
+    _FP_UNPACK_CANONICAL(E,4,X);	\
+  } while (0)
+
+#define FP_UNPACK_EP(X,val)		\
+  do {					\
+    FP_UNPACK_RAW_2_P(X,val);		\
+    _FP_UNPACK_CANONICAL(E,4,X);	\
+  } while (0)
+
+#define FP_PACK_E(val,X)		\
+  do {					\
+    _FP_PACK_CANONICAL(E,4,X);		\
+    FP_PACK_RAW_E(val,X);		\
+  } while (0)
+
+#define FP_PACK_EP(val,X)		\
+  do {					\
+    _FP_PACK_CANONICAL(E,4,X);		\
+    FP_PACK_RAW_EP(val,X);		\
+  } while (0)
+
+#define FP_ISSIGNAN_E(X)	_FP_ISSIGNAN(E,4,X)
+#define FP_NEG_E(R,X)		_FP_NEG(E,4,R,X)
+#define FP_ADD_E(R,X,Y)		_FP_ADD(E,4,R,X,Y)
+#define FP_SUB_E(R,X,Y)		_FP_SUB(E,4,R,X,Y)
+#define FP_MUL_E(R,X,Y)		_FP_MUL(E,4,R,X,Y)
+#define FP_DIV_E(R,X,Y)		_FP_DIV(E,4,R,X,Y)
+#define FP_SQRT_E(R,X)		_FP_SQRT(E,4,R,X)
+
+/*
+ * Square root algorithms:
+ * We have just one right now, maybe Newton approximation
+ * should be added for those machines where division is fast.
+ * This has special _E version because standard _4 square
+ * root would not work (it has to start normally with the
+ * second word and not the first), but as we have to do it
+ * anyway, we optimize it by doing most of the calculations
+ * in two UWtype registers instead of four.
+ */
+ 
+#define _FP_SQRT_MEAT_E(R, S, T, X, q)			\
+  do {							\
+    q = (_FP_W_TYPE)1 << (_FP_W_TYPE_SIZE - 1);		\
+    _FP_FRAC_SRL_4(X, (_FP_WORKBITS));			\
+    while (q)						\
+      {							\
+	T##_f[1] = S##_f[1] + q;			\
+	if (T##_f[1] <= X##_f[1])			\
+	  {						\
+	    S##_f[1] = T##_f[1] + q;			\
+	    X##_f[1] -= T##_f[1];			\
+	    R##_f[1] += q;				\
+	  }						\
+	_FP_FRAC_SLL_2(X, 1);				\
+	q >>= 1;					\
+      }							\
+    q = (_FP_W_TYPE)1 << (_FP_W_TYPE_SIZE - 1);		\
+    while (q)						\
+      {							\
+	T##_f[0] = S##_f[0] + q;			\
+	T##_f[1] = S##_f[1];				\
+	if (T##_f[1] < X##_f[1] || 			\
+	    (T##_f[1] == X##_f[1] &&			\
+	     T##_f[0] <= X##_f[0]))			\
+	  {						\
+	    S##_f[0] = T##_f[0] + q;			\
+	    S##_f[1] += (T##_f[0] > S##_f[0]);		\
+	    _FP_FRAC_DEC_2(X, T);			\
+	    R##_f[0] += q;				\
+	  }						\
+	_FP_FRAC_SLL_2(X, 1);				\
+	q >>= 1;					\
+      }							\
+    _FP_FRAC_SLL_4(R, (_FP_WORKBITS));			\
+    if (X##_f[0] | X##_f[1])				\
+      {							\
+	if (S##_f[1] < X##_f[1] || 			\
+	    (S##_f[1] == X##_f[1] &&			\
+	     S##_f[0] < X##_f[0]))			\
+	  R##_f[0] |= _FP_WORK_ROUND;			\
+	R##_f[0] |= _FP_WORK_STICKY;			\
+      }							\
+  } while (0)
+
+#define FP_CMP_E(r,X,Y,un)	_FP_CMP(E,4,r,X,Y,un)
+#define FP_CMP_EQ_E(r,X,Y)	_FP_CMP_EQ(E,4,r,X,Y)
+
+#define FP_TO_INT_E(r,X,rsz,rsg)	_FP_TO_INT(E,4,r,X,rsz,rsg)
+#define FP_FROM_INT_E(X,r,rs,rt)	_FP_FROM_INT(E,4,X,r,rs,rt)
+
+#define _FP_FRAC_HIGH_E(X)	(X##_f[2])
+#define _FP_FRAC_HIGH_RAW_E(X)	(X##_f[1])
+
+#else   /* not _FP_W_TYPE_SIZE < 64 */
+union _FP_UNION_E
+{
+  long double flt /* __attribute__((mode(TF))) */ ;
+  struct {
+#if __BYTE_ORDER == __BIG_ENDIAN
+    unsigned long pad : (_FP_W_TYPE_SIZE - 1 - _FP_EXPBITS_E);
+    unsigned sign  : 1;
+    unsigned exp   : _FP_EXPBITS_E;
+    unsigned long frac : _FP_W_TYPE_SIZE;
+#else
+    unsigned long frac : _FP_W_TYPE_SIZE;
+    unsigned exp   : _FP_EXPBITS_E;
+    unsigned sign  : 1;
+#endif
+  } bits;
+};
+
+#define FP_DECL_E(X)		_FP_DECL(2,X)
+
+#define FP_UNPACK_RAW_E(X, val)					\
+  do {								\
+    union _FP_UNION_E _flo; _flo.flt = (val);			\
+								\
+    X##_f0 = _flo.bits.frac;					\
+    X##_f1 = 0;							\
+    X##_e = _flo.bits.exp;					\
+    X##_s = _flo.bits.sign;					\
+    if (!X##_e && X##_f0 && !(X##_f0 & _FP_IMPLBIT_E))		\
+      {								\
+        X##_e++;						\
+        FP_SET_EXCEPTION(FP_EX_DENORM);				\
+      }								\
+  } while (0)
+
+#define FP_UNPACK_RAW_EP(X, val)				\
+  do {								\
+    union _FP_UNION_E *_flo =					\
+      (union _FP_UNION_E *)(val);				\
+								\
+    X##_f0 = _flo->bits.frac;					\
+    X##_f1 = 0;							\
+    X##_e = _flo->bits.exp;					\
+    X##_s = _flo->bits.sign;					\
+    if (!X##_e && X##_f0 && !(X##_f0 & _FP_IMPLBIT_E))		\
+      {								\
+        X##_e++;						\
+        FP_SET_EXCEPTION(FP_EX_DENORM);				\
+      }								\
+  } while (0)
+
+#define FP_PACK_RAW_E(val, X)					\
+  do {								\
+    union _FP_UNION_E _flo;					\
+								\
+    if (X##_e) X##_f0 |= _FP_IMPLBIT_E;				\
+    else X##_f0 &= ~(_FP_IMPLBIT_E);				\
+    _flo.bits.frac = X##_f0;					\
+    _flo.bits.exp  = X##_e;					\
+    _flo.bits.sign = X##_s;					\
+								\
+    (val) = _flo.flt;						\
+  } while (0)
+
+#define FP_PACK_RAW_EP(fs, val, X)				\
+  do {								\
+    if (!FP_INHIBIT_RESULTS)					\
+      {								\
+	union _FP_UNION_E *_flo =				\
+	  (union _FP_UNION_E *)(val);				\
+								\
+	if (X##_e) X##_f0 |= _FP_IMPLBIT_E;			\
+	else X##_f0 &= ~(_FP_IMPLBIT_E);			\
+	_flo->bits.frac = X##_f0;				\
+	_flo->bits.exp  = X##_e;				\
+	_flo->bits.sign = X##_s;				\
+      }								\
+  } while (0)
+
+
+#define FP_UNPACK_E(X,val)		\
+  do {					\
+    FP_UNPACK_RAW_E(X,val);		\
+    _FP_UNPACK_CANONICAL(E,2,X);	\
+  } while (0)
+
+#define FP_UNPACK_EP(X,val)		\
+  do {					\
+    FP_UNPACK_RAW_EP(X,val);		\
+    _FP_UNPACK_CANONICAL(E,2,X);	\
+  } while (0)
+
+#define FP_PACK_E(val,X)		\
+  do {					\
+    _FP_PACK_CANONICAL(E,2,X);		\
+    FP_PACK_RAW_E(val,X);		\
+  } while (0)
+
+#define FP_PACK_EP(val,X)		\
+  do {					\
+    _FP_PACK_CANONICAL(E,2,X);		\
+    FP_PACK_RAW_EP(val,X);		\
+  } while (0)
+
+#define FP_ISSIGNAN_E(X)	_FP_ISSIGNAN(E,2,X)
+#define FP_NEG_E(R,X)		_FP_NEG(E,2,R,X)
+#define FP_ADD_E(R,X,Y)		_FP_ADD(E,2,R,X,Y)
+#define FP_SUB_E(R,X,Y)		_FP_SUB(E,2,R,X,Y)
+#define FP_MUL_E(R,X,Y)		_FP_MUL(E,2,R,X,Y)
+#define FP_DIV_E(R,X,Y)		_FP_DIV(E,2,R,X,Y)
+#define FP_SQRT_E(R,X)		_FP_SQRT(E,2,R,X)
+
+/*
+ * Square root algorithms:
+ * We have just one right now, maybe Newton approximation
+ * should be added for those machines where division is fast.
+ * We optimize it by doing most of the calculations
+ * in one UWtype registers instead of two, although we don't
+ * have to.
+ */
+#define _FP_SQRT_MEAT_E(R, S, T, X, q)			\
+  do {							\
+    q = (_FP_W_TYPE)1 << (_FP_W_TYPE_SIZE - 1);		\
+    _FP_FRAC_SRL_2(X, (_FP_WORKBITS));			\
+    while (q)						\
+      {							\
+        T##_f0 = S##_f0 + q;				\
+        if (T##_f0 <= X##_f0)				\
+          {						\
+            S##_f0 = T##_f0 + q;			\
+            X##_f0 -= T##_f0;				\
+            R##_f0 += q;				\
+          }						\
+        _FP_FRAC_SLL_1(X, 1);				\
+        q >>= 1;					\
+      }							\
+    _FP_FRAC_SLL_2(R, (_FP_WORKBITS));			\
+    if (X##_f0)						\
+      {							\
+	if (S##_f0 < X##_f0)				\
+	  R##_f0 |= _FP_WORK_ROUND;			\
+	R##_f0 |= _FP_WORK_STICKY;			\
+      }							\
+  } while (0)
+ 
+#define FP_CMP_E(r,X,Y,un)	_FP_CMP(E,2,r,X,Y,un)
+#define FP_CMP_EQ_E(r,X,Y)	_FP_CMP_EQ(E,2,r,X,Y)
+
+#define FP_TO_INT_E(r,X,rsz,rsg)	_FP_TO_INT(E,2,r,X,rsz,rsg)
+#define FP_FROM_INT_E(X,r,rs,rt)	_FP_FROM_INT(E,2,X,r,rs,rt)
+
+#define _FP_FRAC_HIGH_E(X)	(X##_f1)
+#define _FP_FRAC_HIGH_RAW_E(X)	(X##_f0)
+
+#endif /* not _FP_W_TYPE_SIZE < 64 */
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/fabsq.c linux/arch/sparc64/math-emu/fabsq.c
--- v2.3.3/linux/arch/sparc64/math-emu/fabsq.c	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/math-emu/fabsq.c	Sat May 29 11:09:04 1999
@@ -1,3 +1,10 @@
+/* $Id: fabsq.c,v 1.5 1999/05/28 13:42:27 jj Exp $
+ * arch/sparc64/math-emu/fabsq.c
+ *
+ * Copyright (C) 1997 Jakub Jelinek (jj@ultra.linux.cz)
+ *
+ */
+
 int FABSQ(unsigned long *rd, unsigned long *rs2)
 {
 	rd[0] = rs2[0] & 0x7fffffffffffffffUL;
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/faddd.c linux/arch/sparc64/math-emu/faddd.c
--- v2.3.3/linux/arch/sparc64/math-emu/faddd.c	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/math-emu/faddd.c	Sat May 29 11:09:04 1999
@@ -1,12 +1,23 @@
+/* $Id: faddd.c,v 1.4 1999/05/28 13:43:17 jj Exp $
+ * arch/sparc64/math-emu/faddd.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ *
+ */
+
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "double.h"
 
 int FADDD(void *rd, void *rs2, void *rs1)
 {
+	FP_DECL_EX;
 	FP_DECL_D(A); FP_DECL_D(B); FP_DECL_D(R);
 
-	__FP_UNPACK_D(A, rs1);
-	__FP_UNPACK_D(B, rs2);
+	FP_UNPACK_DP(A, rs1);
+	FP_UNPACK_DP(B, rs2);
 	FP_ADD_D(R, A, B);
-	return __FP_PACK_D(rd, R);
+	FP_PACK_DP(rd, R);
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/faddq.c linux/arch/sparc64/math-emu/faddq.c
--- v2.3.3/linux/arch/sparc64/math-emu/faddq.c	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/math-emu/faddq.c	Sat May 29 11:09:04 1999
@@ -1,12 +1,23 @@
+/* $Id: faddq.c,v 1.4 1999/05/28 13:43:19 jj Exp $
+ * arch/sparc64/math-emu/faddq.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ *
+ */
+
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "quad.h"
 
 int FADDQ(void *rd, void *rs2, void *rs1)
 {
+	FP_DECL_EX;
 	FP_DECL_Q(A); FP_DECL_Q(B); FP_DECL_Q(R);
 
-	__FP_UNPACK_Q(A, rs1);
-	__FP_UNPACK_Q(B, rs2);
+	FP_UNPACK_QP(A, rs1);
+	FP_UNPACK_QP(B, rs2);
 	FP_ADD_Q(R, A, B);
-	return __FP_PACK_Q(rd, R);
+	FP_PACK_QP(rd, R);
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/fadds.c linux/arch/sparc64/math-emu/fadds.c
--- v2.3.3/linux/arch/sparc64/math-emu/fadds.c	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/math-emu/fadds.c	Sat May 29 11:09:04 1999
@@ -1,12 +1,23 @@
+/* $Id: fadds.c,v 1.4 1999/05/28 13:43:25 jj Exp $
+ * arch/sparc64/math-emu/fadds.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ *
+ */
+
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "single.h"
 
 int FADDS(void *rd, void *rs2, void *rs1)
 {
+	FP_DECL_EX;
 	FP_DECL_S(A); FP_DECL_S(B); FP_DECL_S(R);
 
-	__FP_UNPACK_S(A, rs1);
-	__FP_UNPACK_S(B, rs2);
+	FP_UNPACK_SP(A, rs1);
+	FP_UNPACK_SP(B, rs2);
 	FP_ADD_S(R, A, B);
-	return __FP_PACK_S(rd, R);
+	FP_PACK_SP(rd, R);
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/fcmpeq.c linux/arch/sparc64/math-emu/fcmpeq.c
--- v2.3.3/linux/arch/sparc64/math-emu/fcmpeq.c	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/math-emu/fcmpeq.c	Sat May 29 11:09:04 1999
@@ -1,25 +1,39 @@
+/* $Id: fcmpeq.c,v 1.5 1999/05/28 13:43:29 jj Exp $
+ * arch/sparc64/math-emu/fcmpeq.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ *
+ */
+
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "quad.h"
 
 int FCMPEQ(void *rd, void *rs2, void *rs1)
 {
+	FP_DECL_EX;
 	FP_DECL_Q(A); FP_DECL_Q(B);
 	long ret;
-	int fccno = ((long)rd) & 3;
+	long fccno = (long)rd;
 	unsigned long fsr;
 	
-	rd = (void *)(((long)rd)&~3);
-	__FP_UNPACK_Q(A, rs1);
-	__FP_UNPACK_Q(B, rs2);
+	FP_UNPACK_RAW_QP(A, rs1);
+	FP_UNPACK_RAW_QP(B, rs2);
 	FP_CMP_Q(ret, B, A, 3);
-	if (ret == -1) ret = 2;
-	fsr = *(unsigned long *)rd;
-	switch (fccno) {
-	case 0: fsr &= ~0xc00; fsr |= (ret << 10); break;
-	case 1: fsr &= ~0x300000000UL; fsr |= (ret << 32); break;
-	case 2: fsr &= ~0xc00000000UL; fsr |= (ret << 34); break;
-	case 3: fsr &= ~0x3000000000UL; fsr |= (ret << 36); break;
+	if (ret == 3)
+		FP_SET_EXCEPTION(FP_EX_INVALID);
+	if (!FP_INHIBIT_RESULTS) {
+		rd = (void *)(((long)rd)&~3);
+		if (ret == -1) ret = 2;
+		fsr = current->tss.xfsr[0];
+		switch (fccno) {
+		case 0: fsr &= ~0xc00; fsr |= (ret << 10); break;
+		case 1: fsr &= ~0x300000000UL; fsr |= (ret << 32); break;
+		case 2: fsr &= ~0xc00000000UL; fsr |= (ret << 34); break;
+		case 3: fsr &= ~0x3000000000UL; fsr |= (ret << 36); break;
+		}
+		current->tss.xfsr[0] = fsr;
 	}
-	*(unsigned long *)rd = fsr;
-	return 0;
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/fcmpq.c linux/arch/sparc64/math-emu/fcmpq.c
--- v2.3.3/linux/arch/sparc64/math-emu/fcmpq.c	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/math-emu/fcmpq.c	Sat May 29 11:09:04 1999
@@ -1,25 +1,39 @@
+/* $Id: fcmpq.c,v 1.5 1999/05/28 13:43:33 jj Exp $
+ * arch/sparc64/math-emu/fcmpq.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ *
+ */
+
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "quad.h"
 
 int FCMPQ(void *rd, void *rs2, void *rs1)
 {
+	FP_DECL_EX;
 	FP_DECL_Q(A); FP_DECL_Q(B);
 	long ret;
-	int fccno = ((long)rd) & 3;
+	long fccno = (long)rd;
 	unsigned long fsr;
 	
-	rd = (void *)(((long)rd)&~3);
-	__FP_UNPACK_Q(A, rs1);
-	__FP_UNPACK_Q(B, rs2);
+	FP_UNPACK_RAW_QP(A, rs1);
+	FP_UNPACK_RAW_QP(B, rs2);
 	FP_CMP_Q(ret, B, A, 3);
-	if (ret == -1) ret = 2;
-	fsr = *(unsigned long *)rd;
-	switch (fccno) {
-	case 0: fsr &= ~0xc00; fsr |= (ret << 10); break;
-	case 1: fsr &= ~0x300000000UL; fsr |= (ret << 32); break;
-	case 2: fsr &= ~0xc00000000UL; fsr |= (ret << 34); break;
-	case 3: fsr &= ~0x3000000000UL; fsr |= (ret << 36); break;
+	if (ret == 3 && (FP_ISSIGNAN_Q(A) || FP_ISSIGNAN_Q(B)))
+		FP_SET_EXCEPTION(FP_EX_INVALID);
+	if (!FP_INHIBIT_RESULTS) {
+		rd = (void *)(((long)rd)&~3);
+		if (ret == -1) ret = 2;
+		fsr = current->tss.xfsr[0];
+		switch (fccno) {
+		case 0: fsr &= ~0xc00; fsr |= (ret << 10); break;
+		case 1: fsr &= ~0x300000000UL; fsr |= (ret << 32); break;
+		case 2: fsr &= ~0xc00000000UL; fsr |= (ret << 34); break;
+		case 3: fsr &= ~0x3000000000UL; fsr |= (ret << 36); break;
+		}
+		current->tss.xfsr[0] = fsr;
 	}
-	*(unsigned long *)rd = fsr;
-	return 0;
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/fdivd.c linux/arch/sparc64/math-emu/fdivd.c
--- v2.3.3/linux/arch/sparc64/math-emu/fdivd.c	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/math-emu/fdivd.c	Sat May 29 11:09:04 1999
@@ -1,19 +1,23 @@
+/* $Id: fdivd.c,v 1.4 1999/05/28 13:43:36 jj Exp $
+ * arch/sparc64/math-emu/fdivd.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ *
+ */
+
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "double.h"
 
 int FDIVD(void *rd, void *rs2, void *rs1)
 {
+	FP_DECL_EX;
 	FP_DECL_D(A); FP_DECL_D(B); FP_DECL_D(R);
-	int ret = 0;
 
-	__FP_UNPACK_D(A, rs1);
-	__FP_UNPACK_D(B, rs2);
-	if(B_c == FP_CLS_ZERO &&
-	   A_c != FP_CLS_ZERO) {
-		ret |= EFLAG_DIVZERO;
-		if(__FPU_TRAP_P(EFLAG_DIVZERO))
-			return ret;
-	}
+	FP_UNPACK_DP(A, rs1);
+	FP_UNPACK_DP(B, rs2);
 	FP_DIV_D(R, A, B);
-	return (ret | __FP_PACK_D(rd, R));
+	FP_PACK_DP(rd, R);
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/fdivq.c linux/arch/sparc64/math-emu/fdivq.c
--- v2.3.3/linux/arch/sparc64/math-emu/fdivq.c	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/math-emu/fdivq.c	Sat May 29 11:09:04 1999
@@ -1,19 +1,23 @@
+/* $Id: fdivq.c,v 1.4 1999/05/28 13:43:41 jj Exp $
+ * arch/sparc64/math-emu/fdivq.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ *
+ */
+
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "quad.h"
 
 int FDIVQ(void *rd, void *rs2, void *rs1)
 {
+	FP_DECL_EX;
 	FP_DECL_Q(A); FP_DECL_Q(B); FP_DECL_Q(R);
-	int ret;
 
-	__FP_UNPACK_Q(A, rs1);
-	__FP_UNPACK_Q(B, rs2);
-	if(B_c == FP_CLS_ZERO &&
-	   A_c != FP_CLS_ZERO) {
-		ret |= EFLAG_DIVZERO;
-		if(__FPU_TRAP_P(EFLAG_DIVZERO))
-			return ret;
-	}
+	FP_UNPACK_QP(A, rs1);
+	FP_UNPACK_QP(B, rs2);
 	FP_DIV_Q(R, A, B);
-	return (ret | __FP_PACK_Q(rd, R));
+	FP_PACK_QP(rd, R);
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/fdivs.c linux/arch/sparc64/math-emu/fdivs.c
--- v2.3.3/linux/arch/sparc64/math-emu/fdivs.c	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/math-emu/fdivs.c	Sat May 29 11:09:04 1999
@@ -1,20 +1,24 @@
+/* $Id: fdivs.c,v 1.4 1999/05/28 13:43:45 jj Exp $
+ * arch/sparc64/math-emu/fdivs.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ *
+ */
+
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "single.h"
 
 int FDIVS(void *rd, void *rs2, void *rs1)
 {
+	FP_DECL_EX;
 	FP_DECL_S(A); FP_DECL_S(B); FP_DECL_S(R);
-	int ret = 0;
 
-	__FP_UNPACK_S(A, rs1);
-	__FP_UNPACK_S(B, rs2);
-	if(B_c == FP_CLS_ZERO &&
-	   A_c != FP_CLS_ZERO) {
-		ret |= EFLAG_DIVZERO;
-		if(__FPU_TRAP_P(EFLAG_DIVZERO))
-			return ret;
-	}
+	FP_UNPACK_SP(A, rs1);
+	FP_UNPACK_SP(B, rs2);
 	FP_DIV_S(R, A, B);
-	return (ret | __FP_PACK_S(rd, R));
+	FP_PACK_SP(rd, R);
+	FP_HANDLE_EXCEPTIONS;
 }
 
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/fdmulq.c linux/arch/sparc64/math-emu/fdmulq.c
--- v2.3.3/linux/arch/sparc64/math-emu/fdmulq.c	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/math-emu/fdmulq.c	Sat May 29 11:09:04 1999
@@ -1,15 +1,26 @@
+/* $Id: fdmulq.c,v 1.4 1999/05/28 13:43:48 jj Exp $
+ * arch/sparc64/math-emu/fdmulq.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ *
+ */
+
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "quad.h"
 #include "double.h"
 
 int FDMULQ(void *rd, void *rs2, void *rs1)
 {
+	FP_DECL_EX;
 	FP_DECL_D(IN); FP_DECL_Q(A); FP_DECL_Q(B); FP_DECL_Q(R);
 
-	__FP_UNPACK_D(IN, rs1);
+	FP_UNPACK_DP(IN, rs1);
 	FP_CONV(Q,D,2,1,A,IN);
-	__FP_UNPACK_D(IN, rs2);
+	FP_UNPACK_DP(IN, rs2);
 	FP_CONV(Q,D,2,1,B,IN);
 	FP_MUL_Q(R, A, B);
-	return __FP_PACK_Q(rd, R);
+	FP_PACK_QP(rd, R);
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/fdtoi.c linux/arch/sparc64/math-emu/fdtoi.c
--- v2.3.3/linux/arch/sparc64/math-emu/fdtoi.c	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/math-emu/fdtoi.c	Sat May 29 11:09:04 1999
@@ -1,13 +1,25 @@
+/* $Id: fdtoi.c,v 1.3 1999/05/28 13:43:52 jj Exp $
+ * arch/sparc64/math-emu/fdtoi.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ *
+ */
+
+#define FP_ROUNDMODE FP_RND_ZERO
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "double.h"
 
-int FDTOI(unsigned *rd, void *rs2)
+int FDTOI(int *rd, void *rs2)
 {
+	FP_DECL_EX;
 	FP_DECL_D(A);
-	unsigned r;
+	int r;
 
-	__FP_UNPACK_D(A, rs2);
+	FP_UNPACK_DP(A, rs2);
 	FP_TO_INT_D(r, A, 32, 1);
-	*rd = r;
-	return 0;
+	if (!FP_INHIBIT_RESULTS)
+		*rd = r;
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/fdtoq.c linux/arch/sparc64/math-emu/fdtoq.c
--- v2.3.3/linux/arch/sparc64/math-emu/fdtoq.c	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/math-emu/fdtoq.c	Sat May 29 11:09:04 1999
@@ -1,12 +1,23 @@
+/* $Id: fdtoq.c,v 1.4 1999/05/28 13:43:56 jj Exp $
+ * arch/sparc64/math-emu/fdtoq.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ *
+ */
+
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "quad.h"
 #include "double.h"
 
 int FDTOQ(void *rd, void *rs2)
 {
+	FP_DECL_EX;
 	FP_DECL_D(A); FP_DECL_Q(R);
 
-	__FP_UNPACK_D(A, rs2);
+	FP_UNPACK_DP(A, rs2);
 	FP_CONV(Q,D,2,1,R,A);
-	return __FP_PACK_Q(rd, R);
+	FP_PACK_QP(rd, R);
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/fdtos.c linux/arch/sparc64/math-emu/fdtos.c
--- v2.3.3/linux/arch/sparc64/math-emu/fdtos.c	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/math-emu/fdtos.c	Sat May 29 11:09:04 1999
@@ -1,12 +1,23 @@
+/* $Id: fdtos.c,v 1.4 1999/05/28 13:43:58 jj Exp $
+ * arch/sparc64/math-emu/fdtos.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ *
+ */
+
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "double.h"
 #include "single.h"
 
 int FDTOS(void *rd, void *rs2)
 {
+	FP_DECL_EX;
 	FP_DECL_D(A); FP_DECL_S(R);
 
-	__FP_UNPACK_D(A, rs2);
+	FP_UNPACK_DP(A, rs2);
 	FP_CONV(S,D,1,1,R,A);
-	return __FP_PACK_S(rd, R);
+	FP_PACK_SP(rd, R);
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/fdtox.c linux/arch/sparc64/math-emu/fdtox.c
--- v2.3.3/linux/arch/sparc64/math-emu/fdtox.c	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/math-emu/fdtox.c	Sat May 29 11:09:04 1999
@@ -1,13 +1,25 @@
+/* $Id: fdtox.c,v 1.3 1999/05/28 13:44:02 jj Exp $
+ * arch/sparc64/math-emu/fdtox.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ *
+ */
+
+#define FP_ROUNDMODE FP_RND_ZERO
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "double.h"
 
-int FDTOX(unsigned long *rd, void *rs2)
+int FDTOX(long *rd, void *rs2)
 {
+	FP_DECL_EX;
 	FP_DECL_D(A);
-	unsigned long r;
+	long r;
 
-	__FP_UNPACK_D(A, rs2);
+	FP_UNPACK_DP(A, rs2);
 	FP_TO_INT_D(r, A, 64, 1);
-	*rd = r;
-	return 0;
+	if (!FP_INHIBIT_RESULTS)
+		*rd = r;
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/fitoq.c linux/arch/sparc64/math-emu/fitoq.c
--- v2.3.3/linux/arch/sparc64/math-emu/fitoq.c	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/math-emu/fitoq.c	Sat May 29 11:09:04 1999
@@ -1,11 +1,22 @@
+/* $Id: fitoq.c,v 1.5 1999/05/28 13:44:06 jj Exp $
+ * arch/sparc64/math-emu/fitoq.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ *
+ */
+
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "quad.h"
 
 int FITOQ(void *rd, void *rs2)
 {
+	FP_DECL_EX;
 	FP_DECL_Q(R);
 	int a = *(int *)rs2;
 
 	FP_FROM_INT_Q(R, a, 32, int);
-	return __FP_PACK_Q(rd, R);
+	FP_PACK_QP(rd, R);
+	return 0;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/fmovq.c linux/arch/sparc64/math-emu/fmovq.c
--- v2.3.3/linux/arch/sparc64/math-emu/fmovq.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/fmovq.c	Sat May 29 11:09:04 1999
@@ -1,3 +1,10 @@
+/* $Id: fmovq.c,v 1.2 1999/05/28 13:44:09 jj Exp $
+ * arch/sparc64/math-emu/fmovq.c
+ *
+ * Copyright (C) 1997 Jakub Jelinek (jj@ultra.linux.cz)
+ *
+ */
+
 int FMOVQ(unsigned long *rd, unsigned long *rs2)
 {
 	rd[0] = rs2[0];
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/fmuld.c linux/arch/sparc64/math-emu/fmuld.c
--- v2.3.3/linux/arch/sparc64/math-emu/fmuld.c	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/math-emu/fmuld.c	Sat May 29 11:09:04 1999
@@ -1,12 +1,23 @@
+/* $Id: fmuld.c,v 1.4 1999/05/28 13:44:11 jj Exp $
+ * arch/sparc64/math-emu/fmuld.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ *
+ */
+
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "double.h"
 
 int FMULD(void *rd, void *rs2, void *rs1)
 {
+	FP_DECL_EX;
 	FP_DECL_D(A); FP_DECL_D(B); FP_DECL_D(R);
 
-	__FP_UNPACK_D(A, rs1);
-	__FP_UNPACK_D(B, rs2);
+	FP_UNPACK_DP(A, rs1);
+	FP_UNPACK_DP(B, rs2);
 	FP_MUL_D(R, A, B);
-	return __FP_PACK_D(rd, R);
+	FP_PACK_DP(rd, R);
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/fmulq.c linux/arch/sparc64/math-emu/fmulq.c
--- v2.3.3/linux/arch/sparc64/math-emu/fmulq.c	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/math-emu/fmulq.c	Sat May 29 11:09:04 1999
@@ -1,12 +1,23 @@
+/* $Id: fmulq.c,v 1.4 1999/05/28 13:44:14 jj Exp $
+ * arch/sparc64/math-emu/fmulq.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ *
+ */
+
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "quad.h"
 
 int FMULQ(void *rd, void *rs2, void *rs1)
 {
+	FP_DECL_EX;
 	FP_DECL_Q(A); FP_DECL_Q(B); FP_DECL_Q(R);
 
-	__FP_UNPACK_Q(A, rs1);
-	__FP_UNPACK_Q(B, rs2);
+	FP_UNPACK_QP(A, rs1);
+	FP_UNPACK_QP(B, rs2);
 	FP_MUL_Q(R, A, B);
-	return __FP_PACK_Q(rd, R);
+	FP_PACK_QP(rd, R);
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/fmuls.c linux/arch/sparc64/math-emu/fmuls.c
--- v2.3.3/linux/arch/sparc64/math-emu/fmuls.c	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/math-emu/fmuls.c	Sat May 29 11:09:04 1999
@@ -1,12 +1,23 @@
+/* $Id: fmuls.c,v 1.4 1999/05/28 13:44:18 jj Exp $
+ * arch/sparc64/math-emu/fmuls.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ *
+ */
+
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "single.h"
 
 int FMULS(void *rd, void *rs2, void *rs1)
 {
+	FP_DECL_EX;
 	FP_DECL_S(A); FP_DECL_S(B); FP_DECL_S(R);
 
-	__FP_UNPACK_S(A, rs1);
-	__FP_UNPACK_S(B, rs2);
+	FP_UNPACK_SP(A, rs1);
+	FP_UNPACK_SP(B, rs2);
 	FP_MUL_S(R, A, B);
-	return __FP_PACK_S(rd, R);
+	FP_PACK_SP(rd, R);
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/fnegq.c linux/arch/sparc64/math-emu/fnegq.c
--- v2.3.3/linux/arch/sparc64/math-emu/fnegq.c	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/math-emu/fnegq.c	Sat May 29 11:09:04 1999
@@ -1,7 +1,13 @@
+/* $Id: fnegq.c,v 1.6 1999/05/28 13:44:21 jj Exp $
+ * arch/sparc64/math-emu/fnegq.c
+ *
+ * Copyright (C) 1997 Jakub Jelinek (jj@ultra.linux.cz)
+ *
+ */
+
 int FNEGQ(unsigned long *rd, unsigned long *rs2)
 {
 	rd[0] = rs2[0] ^ 0x8000000000000000UL;
 	rd[1] = rs2[1];
 	return 0;
 }
-
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/fqtod.c linux/arch/sparc64/math-emu/fqtod.c
--- v2.3.3/linux/arch/sparc64/math-emu/fqtod.c	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/math-emu/fqtod.c	Sat May 29 11:09:04 1999
@@ -1,12 +1,23 @@
+/* $Id: fqtod.c,v 1.4 1999/05/28 13:44:24 jj Exp $
+ * arch/sparc64/math-emu/fqtod.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ *
+ */
+
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "quad.h"
 #include "double.h"
 
 int FQTOD(void *rd, void *rs2)
 {
+	FP_DECL_EX;
 	FP_DECL_Q(A); FP_DECL_D(R);
 
-	__FP_UNPACK_Q(A, rs2);
+	FP_UNPACK_QP(A, rs2);
 	FP_CONV(D,Q,1,2,R,A);
-	return __FP_PACK_D(rd, R);
+	FP_PACK_DP(rd, R);
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/fqtoi.c linux/arch/sparc64/math-emu/fqtoi.c
--- v2.3.3/linux/arch/sparc64/math-emu/fqtoi.c	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/math-emu/fqtoi.c	Sat May 29 11:09:04 1999
@@ -1,13 +1,25 @@
+/* $Id: fqtoi.c,v 1.4 1999/05/28 13:44:26 jj Exp $
+ * arch/sparc64/math-emu/fqtoi.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ *
+ */
+
+#define FP_ROUNDMODE FP_RND_ZERO
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "quad.h"
 
-int FQTOI(unsigned *rd, void *rs2)
+int FQTOI(int *rd, void *rs2)
 {
+	FP_DECL_EX;
 	FP_DECL_Q(A);
-	unsigned r;
+	int r;
 
-	__FP_UNPACK_Q(A, rs2);
+	FP_UNPACK_QP(A, rs2);
 	FP_TO_INT_Q(r, A, 32, 1);
-	*rd = r;
-	return 0;
+	if (!FP_INHIBIT_RESULTS)
+		*rd = r;
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/fqtos.c linux/arch/sparc64/math-emu/fqtos.c
--- v2.3.3/linux/arch/sparc64/math-emu/fqtos.c	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/math-emu/fqtos.c	Sat May 29 11:09:04 1999
@@ -1,12 +1,23 @@
+/* $Id: fqtos.c,v 1.4 1999/05/28 13:44:30 jj Exp $
+ * arch/sparc64/math-emu/fqtos.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ *
+ */
+
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "quad.h"
 #include "single.h"
 
 int FQTOS(void *rd, void *rs2)
 {
+	FP_DECL_EX;
 	FP_DECL_Q(A); FP_DECL_S(R);
 
-	__FP_UNPACK_Q(A, rs2);
+	FP_UNPACK_QP(A, rs2);
 	FP_CONV(S,Q,1,2,R,A);
-	return __FP_PACK_S(rd, R);
+	FP_PACK_SP(rd, R);
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/fqtox.c linux/arch/sparc64/math-emu/fqtox.c
--- v2.3.3/linux/arch/sparc64/math-emu/fqtox.c	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/math-emu/fqtox.c	Sat May 29 11:09:04 1999
@@ -1,13 +1,25 @@
+/* $Id: fqtox.c,v 1.4 1999/05/28 13:44:34 jj Exp $
+ * arch/sparc64/math-emu/fqtox.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ *
+ */
+
+#define FP_ROUNDMODE FP_RND_ZERO
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "quad.h"
 
-int FQTOX(unsigned long *rd, void *rs2)
+int FQTOX(long *rd, void *rs2)
 {
+	FP_DECL_EX;
 	FP_DECL_Q(A);
-	unsigned long r;
+	long r;
 
-	__FP_UNPACK_Q(A, rs2);
+	FP_UNPACK_QP(A, rs2);
 	FP_TO_INT_Q(r, A, 64, 1);
-	*rd = r;
-	return 0;
+	if (!FP_INHIBIT_RESULTS)
+		*rd = r;
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/fsmuld.c linux/arch/sparc64/math-emu/fsmuld.c
--- v2.3.3/linux/arch/sparc64/math-emu/fsmuld.c	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/math-emu/fsmuld.c	Sat May 29 11:09:04 1999
@@ -1,15 +1,26 @@
+/* $Id: fsmuld.c,v 1.4 1999/05/28 13:44:37 jj Exp $
+ * arch/sparc64/math-emu/fsmuld.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ *
+ */
+
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "double.h"
 #include "single.h"
 
 int FSMULD(void *rd, void *rs2, void *rs1)
 {
+	FP_DECL_EX;
 	FP_DECL_S(IN); FP_DECL_D(A); FP_DECL_D(B); FP_DECL_D(R);
 
-	__FP_UNPACK_S(IN, rs1);
+	FP_UNPACK_SP(IN, rs1);
 	FP_CONV(D,S,1,1,A,IN);
-	__FP_UNPACK_S(IN, rs2);
+	FP_UNPACK_SP(IN, rs2);
 	FP_CONV(D,S,1,1,B,IN);
 	FP_MUL_D(R, A, B);
-	return __FP_PACK_D(rd, R);
+	FP_PACK_DP(rd, R);
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/fsqrtd.c linux/arch/sparc64/math-emu/fsqrtd.c
--- v2.3.3/linux/arch/sparc64/math-emu/fsqrtd.c	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/math-emu/fsqrtd.c	Sat May 29 11:09:04 1999
@@ -1,11 +1,22 @@
+/* $Id: fsqrtd.c,v 1.4 1999/05/28 13:44:39 jj Exp $
+ * arch/sparc64/math-emu/fsqrtd.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ *
+ */
+
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "double.h"
 
 int FSQRTD(void *rd, void *rs2)
 {
+	FP_DECL_EX;
 	FP_DECL_D(A); FP_DECL_D(R);
         
-	__FP_UNPACK_D(A, rs2);
+	FP_UNPACK_DP(A, rs2);
 	FP_SQRT_D(R, A);
-	return __FP_PACK_D(rd, R);
+	FP_PACK_DP(rd, R);
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/fsqrtq.c linux/arch/sparc64/math-emu/fsqrtq.c
--- v2.3.3/linux/arch/sparc64/math-emu/fsqrtq.c	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/math-emu/fsqrtq.c	Sat May 29 11:09:04 1999
@@ -1,11 +1,22 @@
+/* $Id: fsqrtq.c,v 1.5 1999/05/28 13:44:44 jj Exp $
+ * arch/sparc64/math-emu/fsqrtq.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ *
+ */
+
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "quad.h"
 
 int FSQRTQ(void *rd, void *rs2)
 {
+	FP_DECL_EX;
 	FP_DECL_Q(A); FP_DECL_Q(R);
         
-	__FP_UNPACK_Q(A, rs2);
+	FP_UNPACK_QP(A, rs2);
 	FP_SQRT_Q(R, A);
-	return __FP_PACK_Q(rd, R);
+	FP_PACK_QP(rd, R);
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/fsqrts.c linux/arch/sparc64/math-emu/fsqrts.c
--- v2.3.3/linux/arch/sparc64/math-emu/fsqrts.c	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/math-emu/fsqrts.c	Sat May 29 11:09:04 1999
@@ -1,11 +1,22 @@
+/* $Id: fsqrts.c,v 1.4 1999/05/28 13:44:48 jj Exp $
+ * arch/sparc64/math-emu/fsqrts.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ *
+ */
+
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "single.h"
 
 int FSQRTS(void *rd, void *rs2)
 {
+	FP_DECL_EX;
 	FP_DECL_S(A); FP_DECL_S(R);
         
-	__FP_UNPACK_S(A, rs2);
+	FP_UNPACK_SP(A, rs2);
 	FP_SQRT_S(R, A);
-	return __FP_PACK_S(rd, R);
+	FP_PACK_SP(rd, R);
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/fstod.c linux/arch/sparc64/math-emu/fstod.c
--- v2.3.3/linux/arch/sparc64/math-emu/fstod.c	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/math-emu/fstod.c	Sat May 29 11:09:04 1999
@@ -1,12 +1,23 @@
+/* $Id: fstod.c,v 1.4 1999/05/28 13:44:51 jj Exp $
+ * arch/sparc64/math-emu/fstod.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ *
+ */
+
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "double.h"
 #include "single.h"
 
 int FSTOD(void *rd, void *rs2)
 {
+	FP_DECL_EX;
 	FP_DECL_S(A); FP_DECL_D(R);
 
-	__FP_UNPACK_S(A, rs2);
+	FP_UNPACK_SP(A, rs2);
 	FP_CONV(D,S,1,1,R,A);
-	return __FP_PACK_D(rd, R);
+	FP_PACK_DP(rd, R);
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/fstoi.c linux/arch/sparc64/math-emu/fstoi.c
--- v2.3.3/linux/arch/sparc64/math-emu/fstoi.c	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/math-emu/fstoi.c	Sat May 29 11:09:04 1999
@@ -1,13 +1,25 @@
+/* $Id: fstoi.c,v 1.3 1999/05/28 13:44:54 jj Exp $
+ * arch/sparc64/math-emu/fstoi.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ *
+ */
+
+#define FP_ROUNDMODE FP_RND_ZERO
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "single.h"
 
-int FSTOI(unsigned *rd, void *rs2)
+int FSTOI(int *rd, void *rs2)
 {
+	FP_DECL_EX;
 	FP_DECL_S(A);
-	unsigned r;
+	int r;
 
-	__FP_UNPACK_S(A, rs2);
+	FP_UNPACK_SP(A, rs2);
 	FP_TO_INT_S(r, A, 32, 1);
-	*rd = r;
-	return 0;
+	if (!FP_INHIBIT_RESULTS)
+		*rd = r;
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/fstoq.c linux/arch/sparc64/math-emu/fstoq.c
--- v2.3.3/linux/arch/sparc64/math-emu/fstoq.c	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/math-emu/fstoq.c	Sat May 29 11:09:04 1999
@@ -1,12 +1,23 @@
+/* $Id: fstoq.c,v 1.4 1999/05/28 13:44:58 jj Exp $
+ * arch/sparc64/math-emu/fstoq.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ *
+ */
+
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "quad.h"
 #include "single.h"
 
 int FSTOQ(void *rd, void *rs2)
 {
+	FP_DECL_EX;
 	FP_DECL_S(A); FP_DECL_Q(R);
 
-	__FP_UNPACK_S(A, rs2);
+	FP_UNPACK_SP(A, rs2);
 	FP_CONV(Q,S,2,1,R,A);
-	return __FP_PACK_Q(rd, R);
+	FP_PACK_QP(rd, R);
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/fstox.c linux/arch/sparc64/math-emu/fstox.c
--- v2.3.3/linux/arch/sparc64/math-emu/fstox.c	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/math-emu/fstox.c	Sat May 29 11:09:04 1999
@@ -1,13 +1,25 @@
+/* $Id: fstox.c,v 1.3 1999/05/28 13:45:01 jj Exp $
+ * arch/sparc64/math-emu/fstox.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ *
+ */
+
+#define FP_ROUNDMODE FP_RND_ZERO
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "single.h"
 
-int FSTOX(unsigned long *rd, void *rs2)
+int FSTOX(long *rd, void *rs2)
 {
+	FP_DECL_EX;
 	FP_DECL_S(A);
-	unsigned long r;
+	long r;
 
-	__FP_UNPACK_S(A, rs2);
+	FP_UNPACK_SP(A, rs2);
 	FP_TO_INT_S(r, A, 64, 1);
-	*rd = r;
-	return 0;
+	if (!FP_INHIBIT_RESULTS)
+		*rd = r;
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/fsubd.c linux/arch/sparc64/math-emu/fsubd.c
--- v2.3.3/linux/arch/sparc64/math-emu/fsubd.c	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/math-emu/fsubd.c	Sat May 29 11:09:04 1999
@@ -1,14 +1,25 @@
+/* $Id: fsubd.c,v 1.4 1999/05/28 13:45:04 jj Exp $
+ * arch/sparc64/math-emu/fsubd.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ *
+ */
+
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "double.h"
 
 int FSUBD(void *rd, void *rs2, void *rs1)
 {
+	FP_DECL_EX;
 	FP_DECL_D(A); FP_DECL_D(B); FP_DECL_D(R);
 
-	__FP_UNPACK_D(A, rs1);
-	__FP_UNPACK_D(B, rs2);
+	FP_UNPACK_DP(A, rs1);
+	FP_UNPACK_DP(B, rs2);
 	if (B_c != FP_CLS_NAN)
 		B_s ^= 1;
 	FP_ADD_D(R, A, B);
-	return __FP_PACK_D(rd, R);
+	FP_PACK_DP(rd, R);
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/fsubq.c linux/arch/sparc64/math-emu/fsubq.c
--- v2.3.3/linux/arch/sparc64/math-emu/fsubq.c	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/math-emu/fsubq.c	Sat May 29 11:09:04 1999
@@ -1,14 +1,25 @@
+/* $Id: fsubq.c,v 1.4 1999/05/28 13:45:09 jj Exp $
+ * arch/sparc64/math-emu/fsubq.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ *
+ */
+
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "quad.h"
 
 int FSUBQ(void *rd, void *rs2, void *rs1)
 {
+	FP_DECL_EX;
 	FP_DECL_Q(A); FP_DECL_Q(B); FP_DECL_Q(R);
 
-	__FP_UNPACK_Q(A, rs1);
-	__FP_UNPACK_Q(B, rs2);
+	FP_UNPACK_QP(A, rs1);
+	FP_UNPACK_QP(B, rs2);
 	if (B_c != FP_CLS_NAN)
 		B_s ^= 1;
 	FP_ADD_Q(R, A, B);
-	return __FP_PACK_Q(rd, R);
+	FP_PACK_QP(rd, R);
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/fsubs.c linux/arch/sparc64/math-emu/fsubs.c
--- v2.3.3/linux/arch/sparc64/math-emu/fsubs.c	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/math-emu/fsubs.c	Sat May 29 11:09:04 1999
@@ -1,14 +1,25 @@
+/* $Id: fsubs.c,v 1.4 1999/05/28 13:45:12 jj Exp $
+ * arch/sparc64/math-emu/fsubs.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ *
+ */
+
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "single.h"
 
 int FSUBS(void *rd, void *rs2, void *rs1)
 {
+	FP_DECL_EX;
 	FP_DECL_S(A); FP_DECL_S(B); FP_DECL_S(R);
 
-	__FP_UNPACK_S(A, rs1);
-	__FP_UNPACK_S(B, rs2);
+	FP_UNPACK_SP(A, rs1);
+	FP_UNPACK_SP(B, rs2);
 	if (B_c != FP_CLS_NAN)
 		B_s ^= 1;
 	FP_ADD_S(R, A, B);
-	return __FP_PACK_S(rd, R);
+	FP_PACK_SP(rd, R);
+	FP_HANDLE_EXCEPTIONS;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/fxtoq.c linux/arch/sparc64/math-emu/fxtoq.c
--- v2.3.3/linux/arch/sparc64/math-emu/fxtoq.c	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/math-emu/fxtoq.c	Sat May 29 11:09:04 1999
@@ -1,11 +1,22 @@
+/* $Id: fxtoq.c,v 1.5 1999/05/28 13:45:15 jj Exp $
+ * arch/sparc64/math-emu/fxtoq.c
+ *
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ *
+ */
+
+#include "sfp-util.h"
 #include "soft-fp.h"
 #include "quad.h"
 
 int FXTOQ(void *rd, void *rs2)
 {
+	FP_DECL_EX;
 	FP_DECL_Q(R);
 	long a = *(long *)rs2;
 
 	FP_FROM_INT_Q(R, a, 64, long);
-	return __FP_PACK_Q(rd, R);
+	FP_PACK_QP(rd, R);
+	return 0;
 }
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/math.c linux/arch/sparc64/math-emu/math.c
--- v2.3.3/linux/arch/sparc64/math-emu/math.c	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/math-emu/math.c	Sat May 29 11:09:04 1999
@@ -1,7 +1,7 @@
-/* $Id: math.c,v 1.7 1999/02/10 14:16:26 davem Exp $
+/* $Id: math.c,v 1.8 1999/05/28 13:43:11 jj Exp $
  * arch/sparc64/math-emu/math.c
  *
- * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997,1999 Jakub Jelinek (jj@ultra.linux.cz)
  * Copyright (C) 1999 David S. Miller (davem@redhat.com)
  *
  * Emulation routines originate from soft-fp package, which is part
@@ -15,6 +15,7 @@
 #include <asm/ptrace.h>
 #include <asm/uaccess.h>
 
+#include "sfp-util.h"
 #include "soft-fp.h"
 
 #define FLOATFUNC(x) extern int x(void *,void *,void *);
@@ -84,46 +85,36 @@
 	if(would_trap != 0) {
 		eflag &= ((fsr & FSR_TEM_MASK) >> FSR_TEM_SHIFT);
 		if((eflag & (eflag - 1)) != 0) {
-			if(eflag & EFLAG_INVALID)
-				eflag = EFLAG_INVALID;
-			else if(eflag & EFLAG_DIVZERO)
-				eflag = EFLAG_DIVZERO;
-			else if(eflag & EFLAG_INEXACT)
-				eflag = EFLAG_INEXACT;
+			if(eflag & FP_EX_INVALID)
+				eflag = FP_EX_INVALID;
+			else if(eflag & FP_EX_OVERFLOW)
+				eflag = FP_EX_OVERFLOW;
+			else if(eflag & FP_EX_UNDERFLOW)
+				eflag = FP_EX_UNDERFLOW;
+			else if(eflag & FP_EX_DIVZERO)
+				eflag = FP_EX_DIVZERO;
+			else if(eflag & FP_EX_INEXACT)
+				eflag = FP_EX_INEXACT;
 		}
 	}
 
-	/* Set CEXC, here are the rules:
+	/* Set CEXC, here is the rule:
 	 *
-	 * 1) In general all FPU ops will set one and only one
+	 *    In general all FPU ops will set one and only one
 	 *    bit in the CEXC field, this is always the case
 	 *    when the IEEE exception trap is enabled in TEM.
-	 *
-	 * 2) As a special case, if an overflow or underflow
-	 *    is being signalled, AND the trap is not enabled
-	 *    in TEM, then the inexact field shall also be set.
 	 */
 	fsr &= ~(FSR_CEXC_MASK);
-	if(would_trap ||
-	   (eflag & (EFLAG_OVERFLOW | EFLAG_UNDERFLOW)) == 0) {
-		fsr |= ((long)eflag << FSR_CEXC_SHIFT);
-	} else {
-		fsr |= (((long)eflag << FSR_CEXC_SHIFT) |
-			(EFLAG_INEXACT << FSR_CEXC_SHIFT));
-	}
+	fsr |= ((long)eflag << FSR_CEXC_SHIFT);
 
-	/* Set the AEXC field, rules are:
+	/* Set the AEXC field, rule is:
 	 *
-	 * 1) If a trap would not be generated, the
+	 *    If a trap would not be generated, the
 	 *    CEXC just generated is OR'd into the
 	 *    existing value of AEXC.
-	 *
-	 * 2) When a trap is generated, AEXC is cleared.
 	 */
 	if(would_trap == 0)
 		fsr |= ((long)eflag << FSR_AEXC_SHIFT);
-	else
-		fsr &= ~(FSR_AEXC_MASK);
 
 	/* If trapping, indicate fault trap type IEEE. */
 	if(would_trap != 0)
@@ -242,7 +233,7 @@
 		}
 		freg = ((insn >> 25) & 0x1f);
 		switch ((type >> 4) & 0x3) {
-		case 0: rd = (void *)(((long)&current->tss.xfsr[0]) | (freg & 3)); break;
+		case 0: rd = (void *)(long)(freg & 3); break;
 		case 3: if (freg & 2) {
 				current->tss.xfsr[0] |= (6 << 14) /* invalid_fp_register */;
 				goto err;
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/op-1.h linux/arch/sparc64/math-emu/op-1.h
--- v2.3.3/linux/arch/sparc64/math-emu/op-1.h	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/op-1.h	Sat May 29 11:09:04 1999
@@ -1,6 +1,26 @@
-/*
- * Basic one-word fraction declaration and manipulation.
- */
+/* Software floating-point emulation.
+   Basic one-word fraction declaration and manipulation.
+   Copyright (C) 1997,1998,1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Richard Henderson (rth@cygnus.com),
+		  Jakub Jelinek (jj@ultra.linux.cz),
+		  David S. Miller (davem@redhat.com) and
+		  Peter Maydell (pmaydell@chiark.greenend.org.uk).
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If
+   not, write to the Free Software Foundation, Inc.,
+   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 #define _FP_FRAC_DECL_1(X)	_FP_W_TYPE X##_f
 #define _FP_FRAC_COPY_1(D,S)	(D##_f = S##_f)
@@ -28,6 +48,7 @@
 
 #define _FP_FRAC_ADD_1(R,X,Y)	(R##_f = X##_f + Y##_f)
 #define _FP_FRAC_SUB_1(R,X,Y)	(R##_f = X##_f - Y##_f)
+#define _FP_FRAC_DEC_1(X,Y)	(X##_f -= Y##_f)
 #define _FP_FRAC_CLZ_1(z, X)	__FP_CLZ(z, X##_f)
 
 /* Predicates */
@@ -40,6 +61,7 @@
 
 #define _FP_ZEROFRAC_1		0
 #define _FP_MINFRAC_1		1
+#define _FP_MAXFRAC_1		(~(_FP_WS_TYPE)0)
 
 /*
  * Unpack the raw bits of a native fp value.  Do not classify or
@@ -55,6 +77,15 @@
     X##_s = _flo.bits.sign;					\
   } while (0)
 
+#define _FP_UNPACK_RAW_1_P(fs, X, val)				\
+  do {								\
+    union _FP_UNION_##fs *_flo =				\
+      (union _FP_UNION_##fs *)(val);				\
+								\
+    X##_f = _flo->bits.frac;					\
+    X##_e = _flo->bits.exp;					\
+    X##_s = _flo->bits.sign;					\
+  } while (0)
 
 /*
  * Repack the raw bits of a native fp value.
@@ -71,6 +102,16 @@
     (val) = _flo.flt;						\
   } while (0)
 
+#define _FP_PACK_RAW_1_P(fs, val, X)				\
+  do {								\
+    union _FP_UNION_##fs *_flo =				\
+      (union _FP_UNION_##fs *)(val);				\
+								\
+    _flo->bits.frac = X##_f;					\
+    _flo->bits.exp  = X##_e;					\
+    _flo->bits.sign = X##_s;					\
+  } while (0)
+
 
 /*
  * Multiplication algorithms:
@@ -207,7 +248,7 @@
  
 #define _FP_SQRT_MEAT_1(R, S, T, X, q)			\
   do {							\
-    while (q)						\
+    while (q != _FP_WORK_ROUND)				\
       {							\
         T##_f = S##_f + q;				\
         if (T##_f <= X##_f)				\
@@ -219,6 +260,12 @@
         _FP_FRAC_SLL_1(X, 1);				\
         q >>= 1;					\
       }							\
+    if (X##_f)						\
+      {							\
+	if (S##_f < X##_f)				\
+	  R##_f |= _FP_WORK_ROUND;			\
+	R##_f |= _FP_WORK_STICKY;			\
+      }							\
   } while (0)
 
 /*
@@ -238,8 +285,13 @@
   do {									\
     D##_f = S##_f;							\
     if (_FP_WFRACBITS_##sfs > _FP_WFRACBITS_##dfs)			\
-      _FP_FRAC_SRS_1(D, (_FP_WFRACBITS_##sfs-_FP_WFRACBITS_##dfs),	\
-		     _FP_WFRACBITS_##sfs);				\
+      {									\
+	if (S##_c != FP_CLS_NAN)					\
+	  _FP_FRAC_SRS_1(D, (_FP_WFRACBITS_##sfs-_FP_WFRACBITS_##dfs),	\
+			 _FP_WFRACBITS_##sfs);				\
+	else								\
+	  _FP_FRAC_SRL_1(D, (_FP_WFRACBITS_##sfs-_FP_WFRACBITS_##dfs));	\
+      }									\
     else								\
       D##_f <<= _FP_WFRACBITS_##dfs - _FP_WFRACBITS_##sfs;		\
   } while (0)
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/op-2.h linux/arch/sparc64/math-emu/op-2.h
--- v2.3.3/linux/arch/sparc64/math-emu/op-2.h	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/math-emu/op-2.h	Sat May 29 11:09:04 1999
@@ -1,6 +1,26 @@
-/*
- * Basic two-word fraction declaration and manipulation.
- */
+/* Software floating-point emulation.
+   Basic two-word fraction declaration and manipulation.
+   Copyright (C) 1997,1998,1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Richard Henderson (rth@cygnus.com),
+		  Jakub Jelinek (jj@ultra.linux.cz),
+		  David S. Miller (davem@redhat.com) and
+		  Peter Maydell (pmaydell@chiark.greenend.org.uk).
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If
+   not, write to the Free Software Foundation, Inc.,
+   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 #define _FP_FRAC_DECL_2(X)	_FP_W_TYPE X##_f0, X##_f1
 #define _FP_FRAC_COPY_2(D,S)	(D##_f0 = S##_f0, D##_f1 = S##_f1)
@@ -13,13 +33,13 @@
   do {									\
     if ((N) < _FP_W_TYPE_SIZE)						\
       {									\
-        if (__builtin_constant_p(N) && (N) == 1) 			\
-          {								\
-            X##_f1 = X##_f1 + X##_f1 + (((_FP_WS_TYPE)(X##_f0)) < 0);	\
-            X##_f0 += X##_f0;						\
-          }								\
-        else								\
-          {								\
+	if (__builtin_constant_p(N) && (N) == 1) 			\
+	  {								\
+	    X##_f1 = X##_f1 + X##_f1 + (((_FP_WS_TYPE)(X##_f0)) < 0);	\
+	    X##_f0 += X##_f0;						\
+	  }								\
+	else								\
+	  {								\
 	    X##_f1 = X##_f1 << (N) | X##_f0 >> (_FP_W_TYPE_SIZE - (N));	\
 	    X##_f0 <<= (N);						\
 	  }								\
@@ -59,20 +79,23 @@
     else								\
       {									\
 	X##_f0 = (X##_f1 >> ((N) - _FP_W_TYPE_SIZE) |			\
-	          (((X##_f1 << (sz - (N))) | X##_f0) != 0));		\
+		  (((X##_f1 << (sz - (N))) | X##_f0) != 0));		\
 	X##_f1 = 0;							\
       }									\
   } while (0)
 
-#define _FP_FRAC_ADDI_2(X,I) \
+#define _FP_FRAC_ADDI_2(X,I)	\
   __FP_FRAC_ADDI_2(X##_f1, X##_f0, I)
 
-#define _FP_FRAC_ADD_2(R,X,Y) \
+#define _FP_FRAC_ADD_2(R,X,Y)	\
   __FP_FRAC_ADD_2(R##_f1, R##_f0, X##_f1, X##_f0, Y##_f1, Y##_f0)
 
-#define _FP_FRAC_SUB_2(R,X,Y) \
+#define _FP_FRAC_SUB_2(R,X,Y)	\
   __FP_FRAC_SUB_2(R##_f1, R##_f0, X##_f1, X##_f0, Y##_f1, Y##_f0)
 
+#define _FP_FRAC_DEC_2(X,Y)	\
+  __FP_FRAC_DEC_2(X##_f1, X##_f0, Y##_f1, Y##_f0)
+
 #define _FP_FRAC_CLZ_2(R,X)	\
   do {				\
     if (X##_f1)			\
@@ -87,7 +110,7 @@
 /* Predicates */
 #define _FP_FRAC_NEGP_2(X)	((_FP_WS_TYPE)X##_f1 < 0)
 #define _FP_FRAC_ZEROP_2(X)	((X##_f1 | X##_f0) == 0)
-#define _FP_FRAC_OVERP_2(fs,X)	(X##_f1 & _FP_OVERFLOW_##fs)
+#define _FP_FRAC_OVERP_2(fs,X)	(_FP_FRAC_HIGH_##fs(X) & _FP_OVERFLOW_##fs)
 #define _FP_FRAC_EQ_2(X, Y)	(X##_f1 == Y##_f1 && X##_f0 == Y##_f0)
 #define _FP_FRAC_GT_2(X, Y)	\
   (X##_f1 > Y##_f1 || X##_f1 == Y##_f1 && X##_f0 > Y##_f0)
@@ -96,6 +119,7 @@
 
 #define _FP_ZEROFRAC_2		0, 0
 #define _FP_MINFRAC_2		0, 1
+#define _FP_MAXFRAC_2		(~(_FP_WS_TYPE)0), (~(_FP_WS_TYPE)0)
 
 /*
  * Internals 
@@ -106,7 +130,7 @@
 #define __FP_CLZ_2(R, xh, xl)	\
   do {				\
     if (xh)			\
-      __FP_CLZ(R,xl);		\
+      __FP_CLZ(R,xh);		\
     else 			\
     {				\
       __FP_CLZ(R,xl);		\
@@ -117,17 +141,24 @@
 #if 0
 
 #ifndef __FP_FRAC_ADDI_2
-#define __FP_FRAC_ADDI_2(xh, xl, i) \
+#define __FP_FRAC_ADDI_2(xh, xl, i)	\
   (xh += ((xl += i) < i))
 #endif
 #ifndef __FP_FRAC_ADD_2
-#define __FP_FRAC_ADD_2(rh, rl, xh, xl, yh, yl) \
+#define __FP_FRAC_ADD_2(rh, rl, xh, xl, yh, yl)	\
   (rh = xh + yh + ((rl = xl + yl) < xl))
 #endif
 #ifndef __FP_FRAC_SUB_2
-#define __FP_FRAC_SUB_2(rh, rl, xh, xl, yh, yl) \
+#define __FP_FRAC_SUB_2(rh, rl, xh, xl, yh, yl)	\
   (rh = xh - yh - ((rl = xl - yl) > xl))
 #endif
+#ifndef __FP_FRAC_DEC_2
+#define __FP_FRAC_DEC_2(xh, xl, yh, yl)	\
+  do {					\
+    UWtype _t = xl;			\
+    xh -= yh + ((xl -= yl) > _t);	\
+  } while (0)
+#endif
 
 #else
 
@@ -137,6 +168,8 @@
 #define __FP_FRAC_ADD_2			add_ssaaaa
 #undef __FP_FRAC_SUB_2
 #define __FP_FRAC_SUB_2			sub_ddmmss
+#undef __FP_FRAC_DEC_2
+#define __FP_FRAC_DEC_2(xh, xl, yh, yl)	sub_ddmmss(xh, xl, xh, xl, yh, yl)
 
 #endif
 
@@ -155,6 +188,17 @@
     X##_s  = _flo.bits.sign;				\
   } while (0)
 
+#define _FP_UNPACK_RAW_2_P(fs, X, val)			\
+  do {							\
+    union _FP_UNION_##fs *_flo =			\
+      (union _FP_UNION_##fs *)(val);			\
+							\
+    X##_f0 = _flo->bits.frac0;				\
+    X##_f1 = _flo->bits.frac1;				\
+    X##_e  = _flo->bits.exp;				\
+    X##_s  = _flo->bits.sign;				\
+  } while (0)
+
 
 /*
  * Repack the raw bits of a native fp value.
@@ -172,6 +216,17 @@
     (val) = _flo.flt;					\
   } while (0)
 
+#define _FP_PACK_RAW_2_P(fs, val, X)			\
+  do {							\
+    union _FP_UNION_##fs *_flo =			\
+      (union _FP_UNION_##fs *)(val);			\
+							\
+    _flo->bits.frac0 = X##_f0;				\
+    _flo->bits.frac1 = X##_f1;				\
+    _flo->bits.exp   = X##_e;				\
+    _flo->bits.sign  = X##_s;				\
+  } while (0)
+
 
 /*
  * Multiplication algorithms:
@@ -183,21 +238,63 @@
   do {									\
     _FP_FRAC_DECL_4(_z); _FP_FRAC_DECL_2(_b); _FP_FRAC_DECL_2(_c);	\
 									\
-    doit(_FP_FRAC_WORD_4(_z,1), _FP_FRAC_WORD_4(_z,0), X##_f0, Y##_f0); \
+    doit(_FP_FRAC_WORD_4(_z,1), _FP_FRAC_WORD_4(_z,0), X##_f0, Y##_f0);	\
     doit(_b_f1, _b_f0, X##_f0, Y##_f1);					\
     doit(_c_f1, _c_f0, X##_f1, Y##_f0);					\
-    doit(_FP_FRAC_WORD_4(_z,3), _FP_FRAC_WORD_4(_z,2), X##_f1, Y##_f1); \
+    doit(_FP_FRAC_WORD_4(_z,3), _FP_FRAC_WORD_4(_z,2), X##_f1, Y##_f1);	\
 									\
-    __FP_FRAC_ADD_4(_FP_FRAC_WORD_4(_z,3),_FP_FRAC_WORD_4(_z,2),	\
-		    _FP_FRAC_WORD_4(_z,1),_FP_FRAC_WORD_4(_z,0),	\
-		    0, _b_f1, _b_f0, 0,					\
+    __FP_FRAC_ADD_3(_FP_FRAC_WORD_4(_z,3),_FP_FRAC_WORD_4(_z,2),	\
+		    _FP_FRAC_WORD_4(_z,1), 0, _b_f1, _b_f0,		\
 		    _FP_FRAC_WORD_4(_z,3),_FP_FRAC_WORD_4(_z,2),	\
-		    _FP_FRAC_WORD_4(_z,1),_FP_FRAC_WORD_4(_z,0));	\
-    __FP_FRAC_ADD_4(_FP_FRAC_WORD_4(_z,3),_FP_FRAC_WORD_4(_z,2),	\
-		    _FP_FRAC_WORD_4(_z,1),_FP_FRAC_WORD_4(_z,0),	\
-		    0, _c_f1, _c_f0, 0,					\
+		    _FP_FRAC_WORD_4(_z,1));				\
+    __FP_FRAC_ADD_3(_FP_FRAC_WORD_4(_z,3),_FP_FRAC_WORD_4(_z,2),	\
+		    _FP_FRAC_WORD_4(_z,1), 0, _c_f1, _c_f0,		\
 		    _FP_FRAC_WORD_4(_z,3),_FP_FRAC_WORD_4(_z,2),	\
-		    _FP_FRAC_WORD_4(_z,1),_FP_FRAC_WORD_4(_z,0));	\
+		    _FP_FRAC_WORD_4(_z,1));				\
+									\
+    /* Normalize since we know where the msb of the multiplicands	\
+       were (bit B), we know that the msb of the of the product is	\
+       at either 2B or 2B-1.  */					\
+    _FP_FRAC_SRS_4(_z, _FP_WFRACBITS_##fs-1, 2*_FP_WFRACBITS_##fs);	\
+    R##_f0 = _FP_FRAC_WORD_4(_z,0);					\
+    R##_f1 = _FP_FRAC_WORD_4(_z,1);					\
+  } while (0)
+
+/* Given a 1W * 1W => 2W primitive, do the extended multiplication.
+   Do only 3 multiplications instead of four. This one is for machines
+   where multiplication is much more expensive than subtraction.  */
+
+#define _FP_MUL_MEAT_2_wide_3mul(fs, R, X, Y, doit)			\
+  do {									\
+    _FP_FRAC_DECL_4(_z); _FP_FRAC_DECL_2(_b); _FP_FRAC_DECL_2(_c);	\
+    _FP_W_TYPE _d;							\
+    int _c1, _c2;							\
+									\
+    _b_f0 = X##_f0 + X##_f1;						\
+    _c1 = _b_f0 < X##_f0;						\
+    _b_f1 = Y##_f0 + Y##_f1;						\
+    _c2 = _b_f1 < Y##_f0;						\
+    doit(_d, _FP_FRAC_WORD_4(_z,0), X##_f0, Y##_f0);			\
+    doit(_FP_FRAC_WORD_4(_z,2), _FP_FRAC_WORD_4(_z,1), _b_f0, _b_f1);	\
+    doit(_c_f1, _c_f0, X##_f1, Y##_f1);					\
+									\
+    _b_f0 &= -_c2;							\
+    _b_f1 &= -_c1;							\
+    __FP_FRAC_ADD_3(_FP_FRAC_WORD_4(_z,3),_FP_FRAC_WORD_4(_z,2),	\
+		    _FP_FRAC_WORD_4(_z,1), (_c1 & _c2), 0, _d,		\
+		    0, _FP_FRAC_WORD_4(_z,2), _FP_FRAC_WORD_4(_z,1));	\
+    __FP_FRAC_ADDI_2(_FP_FRAC_WORD_4(_z,3),_FP_FRAC_WORD_4(_z,2),	\
+		     _b_f0);						\
+    __FP_FRAC_ADDI_2(_FP_FRAC_WORD_4(_z,3),_FP_FRAC_WORD_4(_z,2),	\
+		     _b_f1);						\
+    __FP_FRAC_DEC_3(_FP_FRAC_WORD_4(_z,3),_FP_FRAC_WORD_4(_z,2),	\
+		    _FP_FRAC_WORD_4(_z,1),				\
+		    0, _d, _FP_FRAC_WORD_4(_z,0));			\
+    __FP_FRAC_DEC_3(_FP_FRAC_WORD_4(_z,3),_FP_FRAC_WORD_4(_z,2),	\
+		    _FP_FRAC_WORD_4(_z,1), 0, _c_f1, _c_f0);		\
+    __FP_FRAC_ADD_2(_FP_FRAC_WORD_4(_z,3), _FP_FRAC_WORD_4(_z,2),	\
+		    _c_f1, _c_f0,					\
+		    _FP_FRAC_WORD_4(_z,3), _FP_FRAC_WORD_4(_z,2));	\
 									\
     /* Normalize since we know where the msb of the multiplicands	\
        were (bit B), we know that the msb of the of the product is	\
@@ -207,118 +304,93 @@
     R##_f1 = _FP_FRAC_WORD_4(_z,1);					\
   } while (0)
 
-/* This next macro appears to be totally broken. Fortunately nowhere
- * seems to use it :-> The problem is that we define _z[4] but
- * then use it in _FP_FRAC_SRS_4, which will attempt to access
- * _z_f[n] which will cause an error. The fix probably involves 
- * declaring it with _FP_FRAC_DECL_4, see previous macro. -- PMM 02/1998 
- */
 #define _FP_MUL_MEAT_2_gmp(fs, R, X, Y)					\
   do {									\
-    _FP_W_TYPE _x[2], _y[2], _z[4];					\
+    _FP_FRAC_DECL_4(_z);						\
+    _FP_W_TYPE _x[2], _y[2];						\
     _x[0] = X##_f0; _x[1] = X##_f1;					\
     _y[0] = Y##_f0; _y[1] = Y##_f1;					\
 									\
-    mpn_mul_n(_z, _x, _y, 2);						\
+    mpn_mul_n(_z_f, _x, _y, 2);						\
 									\
     /* Normalize since we know where the msb of the multiplicands	\
        were (bit B), we know that the msb of the of the product is	\
        at either 2B or 2B-1.  */					\
     _FP_FRAC_SRS_4(_z, _FP_WFRACBITS##_fs-1, 2*_FP_WFRACBITS_##fs);	\
-    R##_f0 = _z[0];							\
-    R##_f1 = _z[1];							\
+    R##_f0 = _z_f[0];							\
+    R##_f1 = _z_f[1];							\
   } while (0)
 
 
 /*
  * Division algorithms:
- * This seems to be giving me difficulties -- PMM 
- * Look, NetBSD seems to be able to comment algorithms. Can't you?
- * I've thrown printks at the problem.
- * This now appears to work, but I still don't really know why.
- * Also, I don't think the result is properly normalised...
  */
 
-#define _FP_DIV_MEAT_2_udiv_64(fs, R, X, Y)				\
+#define _FP_DIV_MEAT_2_udiv(fs, R, X, Y)				\
   do {									\
-    extern void _fp_udivmodti4(_FP_W_TYPE q[2], _FP_W_TYPE r[2],	\
-			       _FP_W_TYPE n1, _FP_W_TYPE n0,		\
-			       _FP_W_TYPE d1, _FP_W_TYPE d0);		\
-    _FP_W_TYPE _n_f3, _n_f2, _n_f1, _n_f0, _r_f1, _r_f0;		\
-    _FP_W_TYPE _q_f1, _q_f0, _m_f1, _m_f0;				\
-    _FP_W_TYPE _rmem[2], _qmem[2];					\
-    /* I think this check is to ensure that the result is normalised.   \
-     * Assuming X,Y normalised (ie in [1.0,2.0)) X/Y will be in         \
-     * [0.5,2.0). Furthermore, it will be less than 1.0 iff X < Y.      \
-     * In this case we tweak things. (this is based on comments in      \
-     * the NetBSD FPU emulation code. )                                 \
-     * We know X,Y are normalised because we ensure this as part of     \
-     * the unpacking process. -- PMM                                    \
-     */									\
+    _FP_W_TYPE _n_f2, _n_f1, _n_f0, _r_f1, _r_f0, _m_f1, _m_f0;		\
     if (_FP_FRAC_GT_2(X, Y))						\
       {									\
-/*	R##_e++; */							\
-	_n_f3 = X##_f1 >> 1;						\
-	_n_f2 = X##_f1 << (_FP_W_TYPE_SIZE - 1) | X##_f0 >> 1;		\
-	_n_f1 = X##_f0 << (_FP_W_TYPE_SIZE - 1);			\
-	_n_f0 = 0;							\
+	_n_f2 = X##_f1 >> 1;						\
+	_n_f1 = X##_f1 << (_FP_W_TYPE_SIZE - 1) | X##_f0 >> 1;		\
+	_n_f0 = X##_f0 << (_FP_W_TYPE_SIZE - 1);			\
       }									\
     else								\
       {									\
 	R##_e--;							\
-	_n_f3 = X##_f1;							\
-	_n_f2 = X##_f0;							\
-	_n_f1 = _n_f0 = 0;						\
+	_n_f2 = X##_f1;							\
+	_n_f1 = X##_f0;							\
+	_n_f0 = 0;							\
       }									\
 									\
     /* Normalize, i.e. make the most significant bit of the 		\
-       denominator set.  CHANGED: - 1 to nothing -- PMM */		\
-    _FP_FRAC_SLL_2(Y, _FP_WFRACXBITS_##fs /* -1 */);			\
-									\
-    /* Do the 256/128 bit division given the 128-bit _fp_udivmodtf4 	\
-       primitive snagged from libgcc2.c.  */				\
+       denominator set. */						\
+    _FP_FRAC_SLL_2(Y, _FP_WFRACXBITS_##fs);				\
 									\
-    _fp_udivmodti4(_qmem, _rmem, _n_f3, _n_f2, 0, Y##_f1);		\
-    _q_f1 = _qmem[0];							\
-    umul_ppmm(_m_f1, _m_f0, _q_f1, Y##_f0);				\
-    _r_f1 = _rmem[0];							\
-    _r_f0 = _n_f1;							\
+    udiv_qrnnd(R##_f1, _r_f1, _n_f2, _n_f1, Y##_f1);			\
+    umul_ppmm(_m_f1, _m_f0, R##_f1, Y##_f0);				\
+    _r_f0 = _n_f0;							\
     if (_FP_FRAC_GT_2(_m, _r))						\
       {									\
-	_q_f1--;							\
-	_FP_FRAC_ADD_2(_r, _r, Y);					\
+	R##_f1--;							\
+	_FP_FRAC_ADD_2(_r, Y, _r);					\
 	if (_FP_FRAC_GE_2(_r, Y) && _FP_FRAC_GT_2(_m, _r))		\
 	  {								\
-	    _q_f1--;							\
-	    _FP_FRAC_ADD_2(_r, _r, Y);					\
+	    R##_f1--;							\
+	    _FP_FRAC_ADD_2(_r, Y, _r);					\
 	  }								\
       }									\
-    _FP_FRAC_SUB_2(_r, _r, _m);						\
+    _FP_FRAC_DEC_2(_r, _m);						\
 									\
-    _fp_udivmodti4(_qmem, _rmem, _r_f1, _r_f0, 0, Y##_f1);		\
-    _q_f0 = _qmem[0];							\
-    umul_ppmm(_m_f1, _m_f0, _q_f0, Y##_f0);				\
-    _r_f1 = _rmem[0];							\
-    _r_f0 = _n_f0;							\
-    if (_FP_FRAC_GT_2(_m, _r))						\
+    if (_r_f1 == Y##_f1)						\
       {									\
-	_q_f0--;							\
-	_FP_FRAC_ADD_2(_r, _r, Y);					\
-	if (_FP_FRAC_GE_2(_r, Y) && _FP_FRAC_GT_2(_m, _r))		\
+	/* This is a special case, not an optimization			\
+	   (_r/Y##_f1 would not fit into UWtype).			\
+	   As _r is guaranteed to be < Y,  R##_f0 can be either		\
+	   (UWtype)-1 or (UWtype)-2.  But as we know what kind		\
+	   of bits it is (sticky, guard, round),  we don't care.	\
+	   We also don't care what the reminder is,  because the	\
+	   guard bit will be set anyway.  -jj */			\
+	R##_f0 = -1;							\
+      }									\
+    else								\
+      {									\
+	udiv_qrnnd(R##_f0, _r_f1, _r_f1, _r_f0, Y##_f1);		\
+	umul_ppmm(_m_f1, _m_f0, R##_f0, Y##_f0);			\
+	_r_f0 = 0;							\
+	if (_FP_FRAC_GT_2(_m, _r))					\
 	  {								\
-	    _q_f0--;							\
-	    _FP_FRAC_ADD_2(_r, _r, Y);					\
+	    R##_f0--;							\
+	    _FP_FRAC_ADD_2(_r, Y, _r);					\
+	    if (_FP_FRAC_GE_2(_r, Y) && _FP_FRAC_GT_2(_m, _r))		\
+	      {								\
+		R##_f0--;						\
+		_FP_FRAC_ADD_2(_r, Y, _r);				\
+	      }								\
 	  }								\
+	if (!_FP_FRAC_EQ_2(_r, _m))					\
+	  R##_f0 |= _FP_WORK_STICKY;					\
       }									\
-    _FP_FRAC_SUB_2(_r, _r, _m);						\
-									\
-    R##_f1 = _q_f1;							\
-    R##_f0 = _q_f0 | ((_r_f1 | _r_f0) != 0);				\
-    /* adjust so answer is normalized again. I'm not sure what the 	\
-     * final sz param should be. In practice it's never used since      \
-     * N is 1 which is always going to be < _FP_W_TYPE_SIZE...		\
-     */									\
-    /* _FP_FRAC_SRS_2(R,1,_FP_WFRACBITS_##fs);	*/			\
   } while (0)
 
 
@@ -330,17 +402,17 @@
     if (_FP_FRAC_GT_2(X, Y))						\
       {									\
 	R##_e++;							\
-	_x[1] = (X##_f0 << (_FP_WFRACBITS-1 - _FP_W_TYPE_SIZE) |	\
+	_x[1] = (X##_f0 << (_FP_WFRACBITS_##fs-1 - _FP_W_TYPE_SIZE) |	\
 		 X##_f1 >> (_FP_W_TYPE_SIZE -				\
-			    (_FP_WFRACBITS-1 - _FP_W_TYPE_SIZE)));	\
-	_x[2] = X##_f1 << (_FP_WFRACBITS-1 - _FP_W_TYPE_SIZE);		\
+			    (_FP_WFRACBITS_##fs-1 - _FP_W_TYPE_SIZE)));	\
+	_x[2] = X##_f1 << (_FP_WFRACBITS_##fs-1 - _FP_W_TYPE_SIZE);	\
       }									\
     else								\
       {									\
-	_x[1] = (X##_f0 << (_FP_WFRACBITS - _FP_W_TYPE_SIZE) |		\
+	_x[1] = (X##_f0 << (_FP_WFRACBITS_##fs - _FP_W_TYPE_SIZE) |	\
 		 X##_f1 >> (_FP_W_TYPE_SIZE -				\
-			    (_FP_WFRACBITS - _FP_W_TYPE_SIZE)));	\
-	_x[2] = X##_f1 << (_FP_WFRACBITS - _FP_W_TYPE_SIZE);		\
+			    (_FP_WFRACBITS_##fs - _FP_W_TYPE_SIZE)));	\
+	_x[2] = X##_f1 << (_FP_WFRACBITS_##fs - _FP_W_TYPE_SIZE);	\
       }									\
 									\
     (void) mpn_divrem (_z, 0, _x, 4, _y, 2);				\
@@ -359,33 +431,38 @@
   do {							\
     while (q)						\
       {							\
-        T##_f1 = S##_f1 + q;				\
-        if (T##_f1 <= X##_f1)				\
-          {						\
-            S##_f1 = T##_f1 + q;			\
-            X##_f1 -= T##_f1;				\
-            R##_f1 += q;				\
-          }						\
-        _FP_FRAC_SLL_2(X, 1);				\
-        q >>= 1;					\
+	T##_f1 = S##_f1 + q;				\
+	if (T##_f1 <= X##_f1)				\
+	  {						\
+	    S##_f1 = T##_f1 + q;			\
+	    X##_f1 -= T##_f1;				\
+	    R##_f1 += q;				\
+	  }						\
+	_FP_FRAC_SLL_2(X, 1);				\
+	q >>= 1;					\
       }							\
     q = (_FP_W_TYPE)1 << (_FP_W_TYPE_SIZE - 1);		\
-    while (q)						\
+    while (q != _FP_WORK_ROUND)				\
       {							\
-        T##_f0 = S##_f0 + q;				\
-        T##_f1 = S##_f1;				\
-        if (T##_f1 < X##_f1 || 				\
-            (T##_f1 == X##_f1 && T##_f0 < X##_f0))	\
-          {						\
-            S##_f0 = T##_f0 + q;			\
-            if (((_FP_WS_TYPE)T##_f0) < 0 &&		\
-                ((_FP_WS_TYPE)S##_f0) >= 0)		\
-              S##_f1++;					\
-            _FP_FRAC_SUB_2(X, X, T);			\
-            R##_f0 += q;				\
-          }						\
-        _FP_FRAC_SLL_2(X, 1);				\
-        q >>= 1;					\
+	T##_f0 = S##_f0 + q;				\
+	T##_f1 = S##_f1;				\
+	if (T##_f1 < X##_f1 || 				\
+	    (T##_f1 == X##_f1 && T##_f0 <= X##_f0))	\
+	  {						\
+	    S##_f0 = T##_f0 + q;			\
+	    S##_f1 += (T##_f0 > S##_f0);		\
+	    _FP_FRAC_DEC_2(X, T);			\
+	    R##_f0 += q;				\
+	  }						\
+	_FP_FRAC_SLL_2(X, 1);				\
+	q >>= 1;					\
+      }							\
+    if (X##_f0 | X##_f1)				\
+      {							\
+	if (S##_f1 < X##_f1 || 				\
+	    (S##_f1 == X##_f1 && S##_f0 < X##_f0))	\
+	  R##_f0 |= _FP_WORK_ROUND;			\
+	R##_f0 |= _FP_WORK_STICKY;			\
       }							\
   } while (0)
 
@@ -419,8 +496,11 @@
 
 #define _FP_FRAC_CONV_1_2(dfs, sfs, D, S)				\
   do {									\
-    _FP_FRAC_SRS_2(S, (_FP_WFRACBITS_##sfs - _FP_WFRACBITS_##dfs),	\
-		   _FP_WFRACBITS_##sfs);				\
+    if (S##_c != FP_CLS_NAN)						\
+      _FP_FRAC_SRS_2(S, (_FP_WFRACBITS_##sfs - _FP_WFRACBITS_##dfs),	\
+		     _FP_WFRACBITS_##sfs);				\
+    else								\
+      _FP_FRAC_SRL_2(S, (_FP_WFRACBITS_##sfs - _FP_WFRACBITS_##dfs));	\
     D##_f = S##_f0;							\
   } while (0)
 
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/op-4.h linux/arch/sparc64/math-emu/op-4.h
--- v2.3.3/linux/arch/sparc64/math-emu/op-4.h	Tue Apr 14 17:44:21 1998
+++ linux/arch/sparc64/math-emu/op-4.h	Sat May 29 11:09:04 1999
@@ -1,41 +1,31 @@
-/*
- * Basic four-word fraction declaration and manipulation.
- *
- * When adding quadword support for 32 bit machines, we need
- * to be a little careful as double multiply uses some of these
- * macros: (in op-2.h)
- * _FP_MUL_MEAT_2_wide() uses _FP_FRAC_DECL_4, _FP_FRAC_WORD_4,
- * _FP_FRAC_ADD_4, _FP_FRAC_SRS_4
- * _FP_MUL_MEAT_2_gmp() uses _FP_FRAC_SRS_4 (and should use
- * _FP_FRAC_DECL_4: it appears to be broken and is not used 
- * anywhere anyway. )
- *
- * I've now fixed all the macros that were here from the sparc64 code.
- * [*none* of the shift macros were correct!] -- PMM 02/1998
- * 
- * The only quadword stuff that remains to be coded is: 
- * 1) the conversion to/from ints, which requires 
- * that we check (in op-common.h) that the following do the right thing
- * for quadwords: _FP_TO_INT(Q,4,r,X,rsz,rsg), _FP_FROM_INT(Q,4,X,r,rs,rt)
- * 2) multiply, divide and sqrt, which require:
- * _FP_MUL_MEAT_4_*(R,X,Y), _FP_DIV_MEAT_4_*(R,X,Y), _FP_SQRT_MEAT_4(R,S,T,X,q),
- * This also needs _FP_MUL_MEAT_Q and _FP_DIV_MEAT_Q to be defined to
- * some suitable _FP_MUL_MEAT_4_* macros in sfp-machine.h.
- * [we're free to choose whatever FP_MUL_MEAT_4_* macros we need for
- * these; they are used nowhere else. ]
- */
+/* Software floating-point emulation.
+   Basic four-word fraction declaration and manipulation.
+   Copyright (C) 1997,1998,1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Richard Henderson (rth@cygnus.com),
+		  Jakub Jelinek (jj@ultra.linux.cz),
+		  David S. Miller (davem@redhat.com) and
+		  Peter Maydell (pmaydell@chiark.greenend.org.uk).
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If
+   not, write to the Free Software Foundation, Inc.,
+   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 #define _FP_FRAC_DECL_4(X)	_FP_W_TYPE X##_f[4]
 #define _FP_FRAC_COPY_4(D,S)			\
   (D##_f[0] = S##_f[0], D##_f[1] = S##_f[1],	\
    D##_f[2] = S##_f[2], D##_f[3] = S##_f[3])
-/* The _FP_FRAC_SET_n(X,I) macro is intended for use with another
- * macro such as _FP_ZEROFRAC_n which returns n comma separated values.
- * The result is that we get an expansion of __FP_FRAC_SET_n(X,I0,I1,I2,I3)
- * which just assigns the In values to the array X##_f[]. 
- * This is why the number of parameters doesn't appear to match
- * at first glance...      -- PMM 
- */
 #define _FP_FRAC_SET_4(X,I)	__FP_FRAC_SET_4(X, I)
 #define _FP_FRAC_HIGH_4(X)	(X##_f[3])
 #define _FP_FRAC_LOW_4(X)	(X##_f[0])
@@ -47,11 +37,17 @@
     _skip = (N) / _FP_W_TYPE_SIZE;					\
     _up = (N) % _FP_W_TYPE_SIZE;					\
     _down = _FP_W_TYPE_SIZE - _up;					\
-    for (_i = 3; _i > _skip; --_i)					\
-      X##_f[_i] = X##_f[_i-_skip] << _up | X##_f[_i-_skip-1] >> _down;	\
-/* bugfixed: was X##_f[_i] <<= _up;  -- PMM 02/1998 */                  \
-    X##_f[_i] = X##_f[0] << _up; 	                                \
-    for (--_i; _i >= 0; --_i)						\
+    if (!_up)								\
+      for (_i = 3; _i >= _skip; --_i)					\
+	X##_f[_i] = X##_f[_i-_skip];					\
+    else								\
+      {									\
+	for (_i = 3; _i > _skip; --_i)					\
+	  X##_f[_i] = X##_f[_i-_skip] << _up				\
+		      | X##_f[_i-_skip-1] >> _down;			\
+	X##_f[_i--] = X##_f[0] << _up; 					\
+      }									\
+    for (; _i >= 0; --_i)						\
       X##_f[_i] = 0;							\
   } while (0)
 
@@ -62,10 +58,17 @@
     _skip = (N) / _FP_W_TYPE_SIZE;					\
     _down = (N) % _FP_W_TYPE_SIZE;					\
     _up = _FP_W_TYPE_SIZE - _down;					\
-    for (_i = 0; _i < 3-_skip; ++_i)					\
-      X##_f[_i] = X##_f[_i+_skip] >> _down | X##_f[_i+_skip+1] << _up;	\
-    X##_f[_i] = X##_f[3] >> _down;			         	\
-    for (++_i; _i < 4; ++_i)						\
+    if (!_down)								\
+      for (_i = 0; _i <= 3-_skip; ++_i)					\
+	X##_f[_i] = X##_f[_i+_skip];					\
+    else								\
+      {									\
+	for (_i = 0; _i < 3-_skip; ++_i)				\
+	  X##_f[_i] = X##_f[_i+_skip] >> _down				\
+		      | X##_f[_i+_skip+1] << _up;			\
+	X##_f[_i++] = X##_f[3] >> _down;				\
+      }									\
+    for (; _i < 4; ++_i)						\
       X##_f[_i] = 0;							\
   } while (0)
 
@@ -85,14 +88,21 @@
     for (_s = _i = 0; _i < _skip; ++_i)					\
       _s |= X##_f[_i];							\
     _s |= X##_f[_i] << _up;						\
-/* s is now != 0 if we want to set the LSbit */                         \
-    for (_i = 0; _i < 3-_skip; ++_i)					\
-      X##_f[_i] = X##_f[_i+_skip] >> _down | X##_f[_i+_skip+1] << _up;	\
-    X##_f[_i] = X##_f[3] >> _down;					\
-    for (++_i; _i < 4; ++_i)						\
+/* s is now != 0 if we want to set the LSbit */				\
+    if (!_down)								\
+      for (_i = 0; _i <= 3-_skip; ++_i)					\
+	X##_f[_i] = X##_f[_i+_skip];					\
+    else								\
+      {									\
+	for (_i = 0; _i < 3-_skip; ++_i)				\
+	  X##_f[_i] = X##_f[_i+_skip] >> _down				\
+		      | X##_f[_i+_skip+1] << _up;			\
+	X##_f[_i++] = X##_f[3] >> _down;				\
+      }									\
+    for (; _i < 4; ++_i)						\
       X##_f[_i] = 0;							\
-    /* don't fix the LSB until the very end when we're sure f[0] is stable */ \
-    X##_f[0] |= (_s != 0);                                              \
+    /* don't fix the LSB until the very end when we're sure f[0] is stable */	\
+    X##_f[0] |= (_s != 0);						\
   } while (0)
 
 #define _FP_FRAC_ADD_4(R,X,Y)						\
@@ -100,89 +110,394 @@
 		  X##_f[3], X##_f[2], X##_f[1], X##_f[0],		\
 		  Y##_f[3], Y##_f[2], Y##_f[1], Y##_f[0])
 
-#define _FP_FRAC_SUB_4(R,X,Y)                                           \
+#define _FP_FRAC_SUB_4(R,X,Y)						\
   __FP_FRAC_SUB_4(R##_f[3], R##_f[2], R##_f[1], R##_f[0],		\
 		  X##_f[3], X##_f[2], X##_f[1], X##_f[0],		\
 		  Y##_f[3], Y##_f[2], Y##_f[1], Y##_f[0])
 
-#define _FP_FRAC_ADDI_4(X,I)                                            \
+#define _FP_FRAC_DEC_4(X,Y)						\
+  __FP_FRAC_DEC_4(X##_f[3], X##_f[2], X##_f[1], X##_f[0],		\
+		  Y##_f[3], Y##_f[2], Y##_f[1], Y##_f[0])
+
+#define _FP_FRAC_ADDI_4(X,I)						\
   __FP_FRAC_ADDI_4(X##_f[3], X##_f[2], X##_f[1], X##_f[0], I)
 
 #define _FP_ZEROFRAC_4  0,0,0,0
 #define _FP_MINFRAC_4   0,0,0,1
+#define _FP_MAXFRAC_4	(~(_FP_WS_TYPE)0), (~(_FP_WS_TYPE)0), (~(_FP_WS_TYPE)0), (~(_FP_WS_TYPE)0)
 
 #define _FP_FRAC_ZEROP_4(X)     ((X##_f[0] | X##_f[1] | X##_f[2] | X##_f[3]) == 0)
 #define _FP_FRAC_NEGP_4(X)      ((_FP_WS_TYPE)X##_f[3] < 0)
-#define _FP_FRAC_OVERP_4(fs,X)  (X##_f[0] & _FP_OVERFLOW_##fs)
+#define _FP_FRAC_OVERP_4(fs,X)  (_FP_FRAC_HIGH_##fs(X) & _FP_OVERFLOW_##fs)
 
-#define _FP_FRAC_EQ_4(X,Y)                              \
- (X##_f[0] == Y##_f[0] && X##_f[1] == Y##_f[1]          \
+#define _FP_FRAC_EQ_4(X,Y)				\
+ (X##_f[0] == Y##_f[0] && X##_f[1] == Y##_f[1]		\
   && X##_f[2] == Y##_f[2] && X##_f[3] == Y##_f[3])
 
-#define _FP_FRAC_GT_4(X,Y)                              \
- (X##_f[3] > Y##_f[3] ||                                \
-  (X##_f[3] == Y##_f[3] && (X##_f[2] > Y##_f[2] ||      \
-   (X##_f[2] == Y##_f[2] && (X##_f[1] > Y##_f[1] ||     \
-    (X##_f[1] == Y##_f[1] && X##_f[0] > Y##_f[0])       \
-   ))                                                   \
-  ))                                                    \
+#define _FP_FRAC_GT_4(X,Y)				\
+ (X##_f[3] > Y##_f[3] ||				\
+  (X##_f[3] == Y##_f[3] && (X##_f[2] > Y##_f[2] ||	\
+   (X##_f[2] == Y##_f[2] && (X##_f[1] > Y##_f[1] ||	\
+    (X##_f[1] == Y##_f[1] && X##_f[0] > Y##_f[0])	\
+   ))							\
+  ))							\
  )
 
-#define _FP_FRAC_GE_4(X,Y)                              \
- (X##_f[3] > Y##_f[3] ||                                \
-  (X##_f[3] == Y##_f[3] && (X##_f[2] > Y##_f[2] ||      \
-   (X##_f[2] == Y##_f[2] && (X##_f[1] > Y##_f[1] ||     \
-    (X##_f[1] == Y##_f[1] && X##_f[0] >= Y##_f[0])      \
-   ))                                                   \
-  ))                                                    \
+#define _FP_FRAC_GE_4(X,Y)				\
+ (X##_f[3] > Y##_f[3] ||				\
+  (X##_f[3] == Y##_f[3] && (X##_f[2] > Y##_f[2] ||	\
+   (X##_f[2] == Y##_f[2] && (X##_f[1] > Y##_f[1] ||	\
+    (X##_f[1] == Y##_f[1] && X##_f[0] >= Y##_f[0])	\
+   ))							\
+  ))							\
  )
 
 
-#define _FP_FRAC_CLZ_4(R,X)             \
-  do {                                  \
-    if (X##_f[3])                       \
-    {                                   \
-        __FP_CLZ(R,X##_f[3]);           \
-    }                                   \
-    else if (X##_f[2])                  \
-    {                                   \
-        __FP_CLZ(R,X##_f[2]);           \
-        R += _FP_W_TYPE_SIZE;           \
-    }                                   \
-    else if (X##_f[1])                  \
-    {                                   \
-        __FP_CLZ(R,X##_f[2]);           \
-        R += _FP_W_TYPE_SIZE*2;         \
-    }                                   \
-    else                                \
-    {                                   \
-        __FP_CLZ(R,X##_f[0]);           \
-        R += _FP_W_TYPE_SIZE*3;         \
-    }                                   \
+#define _FP_FRAC_CLZ_4(R,X)		\
+  do {					\
+    if (X##_f[3])			\
+    {					\
+	__FP_CLZ(R,X##_f[3]);		\
+    }					\
+    else if (X##_f[2])			\
+    {					\
+	__FP_CLZ(R,X##_f[2]);		\
+	R += _FP_W_TYPE_SIZE;		\
+    }					\
+    else if (X##_f[1])			\
+    {					\
+	__FP_CLZ(R,X##_f[2]);		\
+	R += _FP_W_TYPE_SIZE*2;		\
+    }					\
+    else				\
+    {					\
+	__FP_CLZ(R,X##_f[0]);		\
+	R += _FP_W_TYPE_SIZE*3;		\
+    }					\
   } while(0)
 
 
-#define _FP_UNPACK_RAW_4(fs, X, val)                            \
-  do {                                                          \
-    union _FP_UNION_##fs _flo; _flo.flt = (val);        	\
-    X##_f[0] = _flo.bits.frac0;                                 \
-    X##_f[1] = _flo.bits.frac1;                                 \
-    X##_f[2] = _flo.bits.frac2;                                 \
-    X##_f[3] = _flo.bits.frac3;                                 \
-    X##_e  = _flo.bits.exp;                                     \
-    X##_s  = _flo.bits.sign;                                    \
+#define _FP_UNPACK_RAW_4(fs, X, val)				\
+  do {								\
+    union _FP_UNION_##fs _flo; _flo.flt = (val);		\
+    X##_f[0] = _flo.bits.frac0;					\
+    X##_f[1] = _flo.bits.frac1;					\
+    X##_f[2] = _flo.bits.frac2;					\
+    X##_f[3] = _flo.bits.frac3;					\
+    X##_e  = _flo.bits.exp;					\
+    X##_s  = _flo.bits.sign;					\
+  } while (0)
+
+#define _FP_UNPACK_RAW_4_P(fs, X, val)				\
+  do {								\
+    union _FP_UNION_##fs *_flo =				\
+      (union _FP_UNION_##fs *)(val);				\
+								\
+    X##_f[0] = _flo->bits.frac0;				\
+    X##_f[1] = _flo->bits.frac1;				\
+    X##_f[2] = _flo->bits.frac2;				\
+    X##_f[3] = _flo->bits.frac3;				\
+    X##_e  = _flo->bits.exp;					\
+    X##_s  = _flo->bits.sign;					\
   } while (0)
 
-#define _FP_PACK_RAW_4(fs, val, X)                              \
-  do {                                                          \
+#define _FP_PACK_RAW_4(fs, val, X)				\
+  do {								\
     union _FP_UNION_##fs _flo;					\
-    _flo.bits.frac0 = X##_f[0];                                 \
-    _flo.bits.frac1 = X##_f[1];                                 \
-    _flo.bits.frac2 = X##_f[2];                                 \
-    _flo.bits.frac3 = X##_f[3];                                 \
-    _flo.bits.exp   = X##_e;                                    \
-    _flo.bits.sign  = X##_s;                                    \
-    (val) = _flo.flt;                                   	\
+    _flo.bits.frac0 = X##_f[0];					\
+    _flo.bits.frac1 = X##_f[1];					\
+    _flo.bits.frac2 = X##_f[2];					\
+    _flo.bits.frac3 = X##_f[3];					\
+    _flo.bits.exp   = X##_e;					\
+    _flo.bits.sign  = X##_s;					\
+    (val) = _flo.flt;				   		\
+  } while (0)
+
+#define _FP_PACK_RAW_4_P(fs, val, X)				\
+  do {								\
+    union _FP_UNION_##fs *_flo =				\
+      (union _FP_UNION_##fs *)(val);				\
+								\
+    _flo->bits.frac0 = X##_f[0];				\
+    _flo->bits.frac1 = X##_f[1];				\
+    _flo->bits.frac2 = X##_f[2];				\
+    _flo->bits.frac3 = X##_f[3];				\
+    _flo->bits.exp   = X##_e;					\
+    _flo->bits.sign  = X##_s;					\
+  } while (0)
+
+/*
+ * Multiplication algorithms:
+ */
+
+/* Given a 1W * 1W => 2W primitive, do the extended multiplication.  */
+
+#define _FP_MUL_MEAT_4_wide(fs, R, X, Y, doit)				    \
+  do {									    \
+    _FP_FRAC_DECL_8(_z); _FP_FRAC_DECL_2(_b); _FP_FRAC_DECL_2(_c);	    \
+    _FP_FRAC_DECL_2(_d); _FP_FRAC_DECL_2(_e); _FP_FRAC_DECL_2(_f);	    \
+									    \
+    doit(_FP_FRAC_WORD_8(_z,1), _FP_FRAC_WORD_8(_z,0), X##_f[0], Y##_f[0]); \
+    doit(_b_f1, _b_f0, X##_f[0], Y##_f[1]);				    \
+    doit(_c_f1, _c_f0, X##_f[1], Y##_f[0]);				    \
+    doit(_d_f1, _d_f0, X##_f[1], Y##_f[1]);				    \
+    doit(_e_f1, _e_f0, X##_f[0], Y##_f[2]);				    \
+    doit(_f_f1, _f_f0, X##_f[2], Y##_f[0]);				    \
+    __FP_FRAC_ADD_3(_FP_FRAC_WORD_8(_z,3),_FP_FRAC_WORD_8(_z,2),	    \
+		    _FP_FRAC_WORD_8(_z,1), 0,_b_f1,_b_f0,		    \
+		    0,0,_FP_FRAC_WORD_8(_z,1));				    \
+    __FP_FRAC_ADD_3(_FP_FRAC_WORD_8(_z,3),_FP_FRAC_WORD_8(_z,2),	    \
+		    _FP_FRAC_WORD_8(_z,1), 0,_c_f1,_c_f0,		    \
+		    _FP_FRAC_WORD_8(_z,3),_FP_FRAC_WORD_8(_z,2),	    \
+		    _FP_FRAC_WORD_8(_z,1));				    \
+    __FP_FRAC_ADD_3(_FP_FRAC_WORD_8(_z,4),_FP_FRAC_WORD_8(_z,3),	    \
+		    _FP_FRAC_WORD_8(_z,2), 0,_d_f1,_d_f0,		    \
+		    0,_FP_FRAC_WORD_8(_z,3),_FP_FRAC_WORD_8(_z,2));	    \
+    __FP_FRAC_ADD_3(_FP_FRAC_WORD_8(_z,4),_FP_FRAC_WORD_8(_z,3),	    \
+		    _FP_FRAC_WORD_8(_z,2), 0,_e_f1,_e_f0,		    \
+		    _FP_FRAC_WORD_8(_z,4),_FP_FRAC_WORD_8(_z,3),	    \
+		    _FP_FRAC_WORD_8(_z,2));				    \
+    __FP_FRAC_ADD_3(_FP_FRAC_WORD_8(_z,4),_FP_FRAC_WORD_8(_z,3),	    \
+		    _FP_FRAC_WORD_8(_z,2), 0,_f_f1,_f_f0,		    \
+		    _FP_FRAC_WORD_8(_z,4),_FP_FRAC_WORD_8(_z,3),	    \
+		    _FP_FRAC_WORD_8(_z,2));				    \
+    doit(_b_f1, _b_f0, X##_f[0], Y##_f[3]);				    \
+    doit(_c_f1, _c_f0, X##_f[3], Y##_f[0]);				    \
+    doit(_d_f1, _d_f0, X##_f[1], Y##_f[2]);				    \
+    doit(_e_f1, _e_f0, X##_f[2], Y##_f[1]);				    \
+    __FP_FRAC_ADD_3(_FP_FRAC_WORD_8(_z,5),_FP_FRAC_WORD_8(_z,4),	    \
+		    _FP_FRAC_WORD_8(_z,3), 0,_b_f1,_b_f0,		    \
+		    0,_FP_FRAC_WORD_8(_z,4),_FP_FRAC_WORD_8(_z,3));	    \
+    __FP_FRAC_ADD_3(_FP_FRAC_WORD_8(_z,5),_FP_FRAC_WORD_8(_z,4),	    \
+		    _FP_FRAC_WORD_8(_z,3), 0,_c_f1,_c_f0,		    \
+		    _FP_FRAC_WORD_8(_z,5),_FP_FRAC_WORD_8(_z,4),	    \
+		    _FP_FRAC_WORD_8(_z,3));				    \
+    __FP_FRAC_ADD_3(_FP_FRAC_WORD_8(_z,5),_FP_FRAC_WORD_8(_z,4),	    \
+		    _FP_FRAC_WORD_8(_z,3), 0,_d_f1,_d_f0,		    \
+		    _FP_FRAC_WORD_8(_z,5),_FP_FRAC_WORD_8(_z,4),	    \
+		    _FP_FRAC_WORD_8(_z,3));				    \
+    __FP_FRAC_ADD_3(_FP_FRAC_WORD_8(_z,5),_FP_FRAC_WORD_8(_z,4),	    \
+		    _FP_FRAC_WORD_8(_z,3), 0,_e_f1,_e_f0,		    \
+		    _FP_FRAC_WORD_8(_z,5),_FP_FRAC_WORD_8(_z,4),	    \
+		    _FP_FRAC_WORD_8(_z,3));				    \
+    doit(_b_f1, _b_f0, X##_f[2], Y##_f[2]);				    \
+    doit(_c_f1, _c_f0, X##_f[1], Y##_f[3]);				    \
+    doit(_d_f1, _d_f0, X##_f[3], Y##_f[1]);				    \
+    doit(_e_f1, _e_f0, X##_f[2], Y##_f[3]);				    \
+    doit(_f_f1, _f_f0, X##_f[3], Y##_f[2]);				    \
+    __FP_FRAC_ADD_3(_FP_FRAC_WORD_8(_z,6),_FP_FRAC_WORD_8(_z,5),	    \
+		    _FP_FRAC_WORD_8(_z,4), 0,_b_f1,_b_f0,		    \
+		    0,_FP_FRAC_WORD_8(_z,5),_FP_FRAC_WORD_8(_z,4));	    \
+    __FP_FRAC_ADD_3(_FP_FRAC_WORD_8(_z,6),_FP_FRAC_WORD_8(_z,5),	    \
+		    _FP_FRAC_WORD_8(_z,4), 0,_c_f1,_c_f0,		    \
+		    _FP_FRAC_WORD_8(_z,6),_FP_FRAC_WORD_8(_z,5),	    \
+		    _FP_FRAC_WORD_8(_z,4));				    \
+    __FP_FRAC_ADD_3(_FP_FRAC_WORD_8(_z,6),_FP_FRAC_WORD_8(_z,5),	    \
+		    _FP_FRAC_WORD_8(_z,4), 0,_d_f1,_d_f0,		    \
+		    _FP_FRAC_WORD_8(_z,6),_FP_FRAC_WORD_8(_z,5),	    \
+		    _FP_FRAC_WORD_8(_z,4));				    \
+    __FP_FRAC_ADD_3(_FP_FRAC_WORD_8(_z,7),_FP_FRAC_WORD_8(_z,6),	    \
+		    _FP_FRAC_WORD_8(_z,5), 0,_e_f1,_e_f0,		    \
+		    0,_FP_FRAC_WORD_8(_z,6),_FP_FRAC_WORD_8(_z,5));	    \
+    __FP_FRAC_ADD_3(_FP_FRAC_WORD_8(_z,7),_FP_FRAC_WORD_8(_z,6),	    \
+		    _FP_FRAC_WORD_8(_z,5), 0,_f_f1,_f_f0,		    \
+		    _FP_FRAC_WORD_8(_z,7),_FP_FRAC_WORD_8(_z,6),	    \
+		    _FP_FRAC_WORD_8(_z,5));				    \
+    doit(_b_f1, _b_f0, X##_f[3], Y##_f[3]);				    \
+    __FP_FRAC_ADD_2(_FP_FRAC_WORD_8(_z,7),_FP_FRAC_WORD_8(_z,6),	    \
+		    _b_f1,_b_f0,					    \
+		    _FP_FRAC_WORD_8(_z,7),_FP_FRAC_WORD_8(_z,6));	    \
+									    \
+    /* Normalize since we know where the msb of the multiplicands	    \
+       were (bit B), we know that the msb of the of the product is	    \
+       at either 2B or 2B-1.  */					    \
+    _FP_FRAC_SRS_8(_z, _FP_WFRACBITS_##fs-1, 2*_FP_WFRACBITS_##fs);	    \
+    __FP_FRAC_SET_4(R, _FP_FRAC_WORD_8(_z,3), _FP_FRAC_WORD_8(_z,2),	    \
+		    _FP_FRAC_WORD_8(_z,1), _FP_FRAC_WORD_8(_z,0));	    \
+  } while (0)
+
+#define _FP_MUL_MEAT_4_gmp(fs, R, X, Y)					    \
+  do {									    \
+    _FP_FRAC_DECL_8(_z);						    \
+									    \
+    mpn_mul_n(_z_f, _x_f, _y_f, 4);					    \
+									    \
+    /* Normalize since we know where the msb of the multiplicands	    \
+       were (bit B), we know that the msb of the of the product is	    \
+       at either 2B or 2B-1.  */					    \
+    _FP_FRAC_SRS_8(_z, _FP_WFRACBITS_##fs-1, 2*_FP_WFRACBITS_##fs);	    \
+    __FP_FRAC_SET_4(R, _FP_FRAC_WORD_8(_z,3), _FP_FRAC_WORD_8(_z,2),	    \
+		    _FP_FRAC_WORD_8(_z,1), _FP_FRAC_WORD_8(_z,0));	    \
+  } while (0)
+
+/*
+ * Helper utility for _FP_DIV_MEAT_4_udiv:
+ * pppp = m * nnn
+ */
+#define umul_ppppmnnn(p3,p2,p1,p0,m,n2,n1,n0)				    \
+  do {									    \
+    UWtype _t;								    \
+    umul_ppmm(p1,p0,m,n0);						    \
+    umul_ppmm(p2,_t,m,n1);						    \
+    __FP_FRAC_ADDI_2(p2,p1,_t);						    \
+    umul_ppmm(p3,_t,m,n2);						    \
+    __FP_FRAC_ADDI_2(p3,p2,_t);						    \
+  } while (0)
+
+/*
+ * Division algorithms:
+ */
+
+#define _FP_DIV_MEAT_4_udiv(fs, R, X, Y)				    \
+  do {									    \
+    int _i;								    \
+    _FP_FRAC_DECL_4(_n); _FP_FRAC_DECL_4(_m);				    \
+    _FP_FRAC_SET_4(_n, _FP_ZEROFRAC_4);					    \
+    if (_FP_FRAC_GT_4(X, Y))						    \
+      {									    \
+	_n_f[3] = X##_f[0] << (_FP_W_TYPE_SIZE - 1);			    \
+	_FP_FRAC_SRL_4(X, 1);						    \
+      }									    \
+    else								    \
+      R##_e--;								    \
+									    \
+    /* Normalize, i.e. make the most significant bit of the 		    \
+       denominator set. */						    \
+    _FP_FRAC_SLL_4(Y, _FP_WFRACXBITS_##fs);				    \
+									    \
+    for (_i = 3; ; _i--)						    \
+      {									    \
+        if (X##_f[3] == Y##_f[3])					    \
+          {								    \
+            /* This is a special case, not an optimization		    \
+               (X##_f[3]/Y##_f[3] would not fit into UWtype).		    \
+               As X## is guaranteed to be < Y,  R##_f[_i] can be either	    \
+               (UWtype)-1 or (UWtype)-2.  */				    \
+            R##_f[_i] = -1;						    \
+            if (!_i)							    \
+	      break;							    \
+            __FP_FRAC_SUB_4(X##_f[3], X##_f[2], X##_f[1], X##_f[0],	    \
+			    Y##_f[2], Y##_f[1], Y##_f[0], 0,		    \
+			    X##_f[2], X##_f[1], X##_f[0], _n_f[_i]);	    \
+            _FP_FRAC_SUB_4(X, Y, X);					    \
+            if (X##_f[3] > Y##_f[3])					    \
+              {								    \
+                R##_f[_i] = -2;						    \
+                _FP_FRAC_ADD_4(X, Y, X);				    \
+              }								    \
+          }								    \
+        else								    \
+          {								    \
+            udiv_qrnnd(R##_f[_i], X##_f[3], X##_f[3], X##_f[2], Y##_f[3]);  \
+            umul_ppppmnnn(_m_f[3], _m_f[2], _m_f[1], _m_f[0],		    \
+			  R##_f[_i], Y##_f[2], Y##_f[1], Y##_f[0]);	    \
+            X##_f[2] = X##_f[1];					    \
+            X##_f[1] = X##_f[0];					    \
+            X##_f[0] = _n_f[_i];					    \
+            if (_FP_FRAC_GT_4(_m, X))					    \
+              {								    \
+                R##_f[_i]--;						    \
+                _FP_FRAC_ADD_4(X, Y, X);				    \
+                if (_FP_FRAC_GE_4(X, Y) && _FP_FRAC_GT_4(_m, X))	    \
+                  {							    \
+		    R##_f[_i]--;					    \
+		    _FP_FRAC_ADD_4(X, Y, X);				    \
+                  }							    \
+              }								    \
+            _FP_FRAC_DEC_4(X, _m);					    \
+            if (!_i)							    \
+	      {								    \
+		if (!_FP_FRAC_EQ_4(X, _m))				    \
+		  R##_f[0] |= _FP_WORK_STICKY;				    \
+		break;							    \
+	      }								    \
+          }								    \
+      }									    \
+  } while (0)
+
+
+/*
+ * Square root algorithms:
+ * We have just one right now, maybe Newton approximation
+ * should be added for those machines where division is fast.
+ */
+ 
+#define _FP_SQRT_MEAT_4(R, S, T, X, q)				\
+  do {								\
+    while (q)							\
+      {								\
+	T##_f[3] = S##_f[3] + q;				\
+	if (T##_f[3] <= X##_f[3])				\
+	  {							\
+	    S##_f[3] = T##_f[3] + q;				\
+	    X##_f[3] -= T##_f[3];				\
+	    R##_f[3] += q;					\
+	  }							\
+	_FP_FRAC_SLL_4(X, 1);					\
+	q >>= 1;						\
+      }								\
+    q = (_FP_W_TYPE)1 << (_FP_W_TYPE_SIZE - 1);			\
+    while (q)							\
+      {								\
+	T##_f[2] = S##_f[2] + q;				\
+	T##_f[3] = S##_f[3];					\
+	if (T##_f[3] < X##_f[3] || 				\
+	    (T##_f[3] == X##_f[3] && T##_f[2] <= X##_f[2]))	\
+	  {							\
+	    S##_f[2] = T##_f[2] + q;				\
+	    S##_f[3] += (T##_f[2] > S##_f[2]);			\
+	    __FP_FRAC_DEC_2(X##_f[3], X##_f[2],			\
+			    T##_f[3], T##_f[2]);		\
+	    R##_f[2] += q;					\
+	  }							\
+	_FP_FRAC_SLL_4(X, 1);					\
+	q >>= 1;						\
+      }								\
+    q = (_FP_W_TYPE)1 << (_FP_W_TYPE_SIZE - 1);			\
+    while (q)							\
+      {								\
+	T##_f[1] = S##_f[1] + q;				\
+	T##_f[2] = S##_f[2];					\
+	T##_f[3] = S##_f[3];					\
+	if (T##_f[3] < X##_f[3] || 				\
+	    (T##_f[3] == X##_f[3] && (T##_f[2] < X##_f[2] ||	\
+	     (T##_f[2] == X##_f[2] && T##_f[1] <= X##_f[1]))))	\
+	  {							\
+	    S##_f[1] = T##_f[1] + q;				\
+	    S##_f[2] += (T##_f[1] > S##_f[1]);			\
+	    S##_f[3] += (T##_f[2] > S##_f[2]);			\
+	    __FP_FRAC_DEC_3(X##_f[3], X##_f[2], X##_f[1],	\
+	    		    T##_f[3], T##_f[2], T##_f[1]);	\
+	    R##_f[1] += q;					\
+	  }							\
+	_FP_FRAC_SLL_4(X, 1);					\
+	q >>= 1;						\
+      }								\
+    q = (_FP_W_TYPE)1 << (_FP_W_TYPE_SIZE - 1);			\
+    while (q != _FP_WORK_ROUND)					\
+      {								\
+	T##_f[0] = S##_f[0] + q;				\
+	T##_f[1] = S##_f[1];					\
+	T##_f[2] = S##_f[2];					\
+	T##_f[3] = S##_f[3];					\
+	if (_FP_FRAC_GE_4(X,T))					\
+	  {							\
+	    S##_f[0] = T##_f[0] + q;				\
+	    S##_f[1] += (T##_f[0] > S##_f[0]);			\
+	    S##_f[2] += (T##_f[1] > S##_f[1]);			\
+	    S##_f[3] += (T##_f[2] > S##_f[2]);			\
+	    _FP_FRAC_DEC_4(X, T);				\
+	    R##_f[0] += q;					\
+	  }							\
+	_FP_FRAC_SLL_4(X, 1);					\
+	q >>= 1;						\
+      }								\
+    if (!_FP_FRAC_ZEROP_4(X))					\
+      {								\
+	if (_FP_FRAC_GT_4(X,S))					\
+	  R##_f[0] |= _FP_WORK_ROUND;				\
+	R##_f[0] |= _FP_WORK_STICKY;				\
+      }								\
   } while (0)
 
 
@@ -193,6 +508,13 @@
 #define __FP_FRAC_SET_4(X,I3,I2,I1,I0)					\
   (X##_f[3] = I3, X##_f[2] = I2, X##_f[1] = I1, X##_f[0] = I0)
 
+#ifndef __FP_FRAC_ADD_3
+#define __FP_FRAC_ADD_3(r2,r1,r0,x2,x1,x0,y2,y1,y0)			\
+  (r0 = x0 + y0,							\
+   r1 = x1 + y1 + (r0 < x0),						\
+   r2 = x2 + y2 + (r1 < x1))
+#endif
+
 #ifndef __FP_FRAC_ADD_4
 #define __FP_FRAC_ADD_4(r3,r2,r1,r0,x3,x2,x1,x0,y3,y2,y1,y0)		\
   (r0 = x0 + y0,							\
@@ -201,18 +523,56 @@
    r3 = x3 + y3 + (r2 < x2))
 #endif
 
+#ifndef __FP_FRAC_SUB_3
+#define __FP_FRAC_SUB_3(r2,r1,r0,x2,x1,x0,y2,y1,y0)			\
+  (r0 = x0 - y0,							\
+   r1 = x1 - y1 - (r0 > x0),						\
+   r2 = x2 - y2 - (r1 > x1))
+#endif
+
 #ifndef __FP_FRAC_SUB_4
 #define __FP_FRAC_SUB_4(r3,r2,r1,r0,x3,x2,x1,x0,y3,y2,y1,y0)		\
-  (r0 = x0 - y0,                                                        \
-   r1 = x1 - y1 - (r0 > x0),                                            \
-   r2 = x2 - y2 - (r1 > x1),                                            \
+  (r0 = x0 - y0,							\
+   r1 = x1 - y1 - (r0 > x0),						\
+   r2 = x2 - y2 - (r1 > x1),						\
    r3 = x3 - y3 - (r2 > x2))
 #endif
 
+#ifndef __FP_FRAC_DEC_3
+#define __FP_FRAC_DEC_3(x2,x1,x0,y2,y1,y0)				\
+  do {									\
+    UWtype _t0, _t1;							\
+    _t0 = x0;								\
+    x0 -= y0;								\
+    _t1 = x1;								\
+    x1 -= y1 + (x0 > _t0);						\
+    x2 -= y2 + (x1 > _t1);						\
+  } while (0)
+#endif
+
+#ifndef __FP_FRAC_DEC_4
+#define __FP_FRAC_DEC_4(x3,x2,x1,x0,y3,y2,y1,y0)			\
+  do {									\
+    UWtype _t0, _t1;							\
+    _t0 = x0;								\
+    x0 -= y0;								\
+    _t1 = x1;								\
+    x1 -= y1 + (x0 > _t0);						\
+    _t0 = x2;								\
+    x2 -= y2 + (x1 > _t1);						\
+    x3 -= y3 + (x2 > _t0);						\
+  } while (0)
+#endif
+
 #ifndef __FP_FRAC_ADDI_4
-/* I always wanted to be a lisp programmer :-> */
-#define __FP_FRAC_ADDI_4(x3,x2,x1,x0,i)                                 \
-  (x3 += ((x2 += ((x1 += ((x0 += i) < x0)) < x1) < x2)))
+#define __FP_FRAC_ADDI_4(x3,x2,x1,x0,i)					\
+  do {									\
+    UWtype _t;								\
+    _t = ((x0 += i) < i);						\
+    x1 += _t; _t = (x1 < _t);						\
+    x2 += _t; _t = (x2 < _t);						\
+    x3 += _t;								\
+  } while (0)
 #endif
 
 /* Convert FP values between word sizes. This appears to be more
@@ -222,47 +582,53 @@
  * internally [eg, that 2 word vars are X_f0 and x_f1]. But so do
  * the ones in op-2.h and op-1.h. 
  */
-#define _FP_FRAC_CONV_1_4(dfs, sfs, D, S)                               \
-   do {                                                                 \
-     _FP_FRAC_SRS_4(S, (_FP_WFRACBITS_##sfs - _FP_WFRACBITS_##dfs),     \
-                        _FP_WFRACBITS_##sfs);                           \
-     D##_f = S##_f[0];                                                   \
+#define _FP_FRAC_CONV_1_4(dfs, sfs, D, S)				\
+   do {									\
+     if (S##_c != FP_CLS_NAN)						\
+       _FP_FRAC_SRS_4(S, (_FP_WFRACBITS_##sfs - _FP_WFRACBITS_##dfs),	\
+			  _FP_WFRACBITS_##sfs);				\
+     else								\
+       _FP_FRAC_SRL_4(S, (_FP_WFRACBITS_##sfs - _FP_WFRACBITS_##dfs));	\
+     D##_f = S##_f[0];							\
   } while (0)
 
-#define _FP_FRAC_CONV_2_4(dfs, sfs, D, S)                               \
-   do {                                                                 \
-     _FP_FRAC_SRS_4(S, (_FP_WFRACBITS_##sfs - _FP_WFRACBITS_##dfs),     \
-                        _FP_WFRACBITS_##sfs);                           \
-     D##_f0 = S##_f[0];                                                  \
-     D##_f1 = S##_f[1];                                                  \
+#define _FP_FRAC_CONV_2_4(dfs, sfs, D, S)				\
+   do {									\
+     if (S##_c != FP_CLS_NAN)						\
+       _FP_FRAC_SRS_4(S, (_FP_WFRACBITS_##sfs - _FP_WFRACBITS_##dfs),	\
+		      _FP_WFRACBITS_##sfs);				\
+     else								\
+       _FP_FRAC_SRL_4(S, (_FP_WFRACBITS_##sfs - _FP_WFRACBITS_##dfs));	\
+     D##_f0 = S##_f[0];							\
+     D##_f1 = S##_f[1];							\
   } while (0)
 
 /* Assembly/disassembly for converting to/from integral types.  
  * No shifting or overflow handled here.
  */
 /* Put the FP value X into r, which is an integer of size rsize. */
-#define _FP_FRAC_ASSEMBLE_4(r, X, rsize)                                \
-  do {                                                                  \
-    if (rsize <= _FP_W_TYPE_SIZE)                                       \
-      r = X##_f[0];                                                     \
-    else if (rsize <= 2*_FP_W_TYPE_SIZE)                                \
-    {                                                                   \
-      r = X##_f[1];                                                     \
-      r <<= _FP_W_TYPE_SIZE;                                            \
-      r += X##_f[0];                                                    \
-    }                                                                   \
-    else                                                                \
-    {                                                                   \
-      /* I'm feeling lazy so we deal with int == 3words (implausible)*/ \
-      /* and int == 4words as a single case.                         */ \
-      r = X##_f[3];                                                     \
-      r <<= _FP_W_TYPE_SIZE;                                            \
-      r += X##_f[2];                                                    \
-      r <<= _FP_W_TYPE_SIZE;                                            \
-      r += X##_f[1];                                                    \
-      r <<= _FP_W_TYPE_SIZE;                                            \
-      r += X##_f[0];                                                    \
-    }                                                                   \
+#define _FP_FRAC_ASSEMBLE_4(r, X, rsize)				\
+  do {									\
+    if (rsize <= _FP_W_TYPE_SIZE)					\
+      r = X##_f[0];							\
+    else if (rsize <= 2*_FP_W_TYPE_SIZE)				\
+    {									\
+      r = X##_f[1];							\
+      r <<= _FP_W_TYPE_SIZE;						\
+      r += X##_f[0];							\
+    }									\
+    else								\
+    {									\
+      /* I'm feeling lazy so we deal with int == 3words (implausible)*/	\
+      /* and int == 4words as a single case.			 */	\
+      r = X##_f[3];							\
+      r <<= _FP_W_TYPE_SIZE;						\
+      r += X##_f[2];							\
+      r <<= _FP_W_TYPE_SIZE;						\
+      r += X##_f[1];							\
+      r <<= _FP_W_TYPE_SIZE;						\
+      r += X##_f[0];							\
+    }									\
   } while (0)
 
 /* "No disassemble Number Five!" */
@@ -270,28 +636,26 @@
  * the _f[] array consisting of words of size _FP_W_TYPE_SIZE to avoid
  * having to mask the values we store into it.
  */
-#define _FP_FRAC_DISASSEMBLE_4(X, r, rsize)                             \
-  do {                                                                  \
-    X##_f[0] = r;                                                       \
-    X##_f[1] = (rsize <= _FP_W_TYPE_SIZE ? 0 : r >> _FP_W_TYPE_SIZE);   \
+#define _FP_FRAC_DISASSEMBLE_4(X, r, rsize)				\
+  do {									\
+    X##_f[0] = r;							\
+    X##_f[1] = (rsize <= _FP_W_TYPE_SIZE ? 0 : r >> _FP_W_TYPE_SIZE);	\
     X##_f[2] = (rsize <= 2*_FP_W_TYPE_SIZE ? 0 : r >> 2*_FP_W_TYPE_SIZE); \
     X##_f[3] = (rsize <= 3*_FP_W_TYPE_SIZE ? 0 : r >> 3*_FP_W_TYPE_SIZE); \
   } while (0);
 
-#define _FP_FRAC_CONV_4_1(dfs, sfs, D, S)                               \
-   do {                                                                 \
-     D##_f[0] = S##_f;                                                  \
-     D##_f[1] = D##_f[2] = D##_f[3] = 0;                                \
-     _FP_FRAC_SLL_4(D, (_FP_WFRACBITS_##dfs - _FP_WFRACBITS_##sfs));    \
+#define _FP_FRAC_CONV_4_1(dfs, sfs, D, S)				\
+   do {									\
+     D##_f[0] = S##_f;							\
+     D##_f[1] = D##_f[2] = D##_f[3] = 0;				\
+     _FP_FRAC_SLL_4(D, (_FP_WFRACBITS_##dfs - _FP_WFRACBITS_##sfs));	\
    } while (0)
 
-#define _FP_FRAC_CONV_4_2(dfs, sfs, D, S)                               \
-   do {                                                                 \
-     D##_f[0] = S##_f0;                                                 \
-     D##_f[1] = S##_f1;                                                 \
-     D##_f[2] = D##_f[3] = 0;                                           \
-     _FP_FRAC_SLL_4(D, (_FP_WFRACBITS_##dfs - _FP_WFRACBITS_##sfs));    \
+#define _FP_FRAC_CONV_4_2(dfs, sfs, D, S)				\
+   do {									\
+     D##_f[0] = S##_f0;							\
+     D##_f[1] = S##_f1;							\
+     D##_f[2] = D##_f[3] = 0;						\
+     _FP_FRAC_SLL_4(D, (_FP_WFRACBITS_##dfs - _FP_WFRACBITS_##sfs));	\
    } while (0)
 
-/* FIXME! This has to be written */
-#define _FP_SQRT_MEAT_4(R, S, T, X, q)
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/op-8.h linux/arch/sparc64/math-emu/op-8.h
--- v2.3.3/linux/arch/sparc64/math-emu/op-8.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/op-8.h	Sat May 29 11:09:04 1999
@@ -0,0 +1,103 @@
+/* Software floating-point emulation.
+   Basic eight-word fraction declaration and manipulation.
+   Copyright (C) 1997,1998,1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Richard Henderson (rth@cygnus.com),
+		  Jakub Jelinek (jj@ultra.linux.cz) and
+		  Peter Maydell (pmaydell@chiark.greenend.org.uk).
+                                                         
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If
+   not, write to the Free Software Foundation, Inc.,
+   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+/* We need just a few things from here for op-4, if we ever need some
+   other macros, they can be added. */
+#define _FP_FRAC_DECL_8(X)	_FP_W_TYPE X##_f[8]
+#define _FP_FRAC_HIGH_8(X)	(X##_f[7])
+#define _FP_FRAC_LOW_8(X)	(X##_f[0])
+#define _FP_FRAC_WORD_8(X,w)	(X##_f[w])
+
+#define _FP_FRAC_SLL_8(X,N)						\
+  do {									\
+    _FP_I_TYPE _up, _down, _skip, _i;					\
+    _skip = (N) / _FP_W_TYPE_SIZE;					\
+    _up = (N) % _FP_W_TYPE_SIZE;					\
+    _down = _FP_W_TYPE_SIZE - _up;					\
+    if (!_up)								\
+      for (_i = 7; _i >= _skip; --_i)					\
+	X##_f[_i] = X##_f[_i-_skip];					\
+    else								\
+      {									\
+	for (_i = 7; _i > _skip; --_i)					\
+	  X##_f[_i] = X##_f[_i-_skip] << _up				\
+		      | X##_f[_i-_skip-1] >> _down;			\
+	X##_f[_i--] = X##_f[0] << _up; 					\
+      }									\
+    for (; _i >= 0; --_i)						\
+      X##_f[_i] = 0;							\
+  } while (0)
+
+#define _FP_FRAC_SRL_8(X,N)						\
+  do {									\
+    _FP_I_TYPE _up, _down, _skip, _i;					\
+    _skip = (N) / _FP_W_TYPE_SIZE;					\
+    _down = (N) % _FP_W_TYPE_SIZE;					\
+    _up = _FP_W_TYPE_SIZE - _down;					\
+    if (!_down)								\
+      for (_i = 0; _i <= 7-_skip; ++_i)					\
+	X##_f[_i] = X##_f[_i+_skip];					\
+    else								\
+      {									\
+	for (_i = 0; _i < 7-_skip; ++_i)				\
+	  X##_f[_i] = X##_f[_i+_skip] >> _down				\
+		      | X##_f[_i+_skip+1] << _up;			\
+	X##_f[_i++] = X##_f[7] >> _down;				\
+      }									\
+    for (; _i < 8; ++_i)						\
+      X##_f[_i] = 0;							\
+  } while (0)
+
+
+/* Right shift with sticky-lsb. 
+ * What this actually means is that we do a standard right-shift,
+ * but that if any of the bits that fall off the right hand side
+ * were one then we always set the LSbit.
+ */
+#define _FP_FRAC_SRS_8(X,N,size)					\
+  do {									\
+    _FP_I_TYPE _up, _down, _skip, _i;					\
+    _FP_W_TYPE _s;							\
+    _skip = (N) / _FP_W_TYPE_SIZE;					\
+    _down = (N) % _FP_W_TYPE_SIZE;					\
+    _up = _FP_W_TYPE_SIZE - _down;					\
+    for (_s = _i = 0; _i < _skip; ++_i)					\
+      _s |= X##_f[_i];							\
+    _s |= X##_f[_i] << _up;						\
+/* s is now != 0 if we want to set the LSbit */				\
+    if (!_down)								\
+      for (_i = 0; _i <= 7-_skip; ++_i)					\
+	X##_f[_i] = X##_f[_i+_skip];					\
+    else								\
+      {									\
+	for (_i = 0; _i < 7-_skip; ++_i)				\
+	  X##_f[_i] = X##_f[_i+_skip] >> _down				\
+		      | X##_f[_i+_skip+1] << _up;			\
+	X##_f[_i++] = X##_f[7] >> _down;				\
+      }									\
+    for (; _i < 8; ++_i)						\
+      X##_f[_i] = 0;							\
+    /* don't fix the LSB until the very end when we're sure f[0] is stable */	\
+    X##_f[0] |= (_s != 0);						\
+  } while (0)
+
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/op-common.h linux/arch/sparc64/math-emu/op-common.h
--- v2.3.3/linux/arch/sparc64/math-emu/op-common.h	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/math-emu/op-common.h	Sat May 29 11:09:04 1999
@@ -1,3 +1,25 @@
+/* Software floating-point emulation. Common operations.
+   Copyright (C) 1997,1998,1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Richard Henderson (rth@cygnus.com),
+		  Jakub Jelinek (jj@ultra.linux.cz),
+		  David S. Miller (davem@redhat.com) and
+		  Peter Maydell (pmaydell@chiark.greenend.org.uk).
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If
+   not, write to the Free Software Foundation, Inc.,
+   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 #define _FP_DECL(wc, X)			\
   _FP_I_TYPE X##_c, X##_s, X##_e;	\
@@ -13,7 +35,7 @@
   switch (X##_e)							\
   {									\
   default:								\
-    _FP_FRAC_HIGH_##wc(X) |= _FP_IMPLBIT_##fs;				\
+    _FP_FRAC_HIGH_RAW_##fs(X) |= _FP_IMPLBIT_##fs;			\
     _FP_FRAC_SLL_##wc(X, _FP_WORKBITS);					\
     X##_e -= _FP_EXPBIAS_##fs;						\
     X##_c = FP_CLS_NORMAL;						\
@@ -31,6 +53,7 @@
 	_FP_FRAC_SLL_##wc(X, (_shift+_FP_WORKBITS));			\
 	X##_e -= _FP_EXPBIAS_##fs - 1 + _shift;				\
 	X##_c = FP_CLS_NORMAL;						\
+	FP_SET_EXCEPTION(FP_EX_DENORM);					\
       }									\
     break;								\
 									\
@@ -38,13 +61,16 @@
     if (_FP_FRAC_ZEROP_##wc(X))						\
       X##_c = FP_CLS_INF;						\
     else								\
-      /* we don't differentiate between signaling and quiet nans */	\
-      X##_c = FP_CLS_NAN;						\
+      {									\
+	X##_c = FP_CLS_NAN;						\
+	/* Check for signaling NaN */					\
+	if (!(_FP_FRAC_HIGH_RAW_##fs(X) & _FP_QNANBIT_##fs))		\
+	  FP_SET_EXCEPTION(FP_EX_INVALID);				\
+      }									\
     break;								\
   }									\
 } while (0)
 
-
 /*
  * Before packing the bits back into the native fp result, take care
  * of such mundane things as rounding and overflow.  Also, for some
@@ -53,14 +79,14 @@
  */
 
 #define _FP_PACK_CANONICAL(fs, wc, X)				\
-({int __ret = 0;						\
+do {								\
   switch (X##_c)						\
   {								\
   case FP_CLS_NORMAL:						\
     X##_e += _FP_EXPBIAS_##fs;					\
     if (X##_e > 0)						\
       {								\
-	__ret |= _FP_ROUND(wc, X);				\
+	_FP_ROUND(wc, X);					\
 	if (_FP_FRAC_OVERP_##wc(fs, X))				\
 	  {							\
 	    _FP_FRAC_SRL_##wc(X, (_FP_WORKBITS+1));		\
@@ -70,10 +96,33 @@
 	  _FP_FRAC_SRL_##wc(X, _FP_WORKBITS);			\
 	if (X##_e >= _FP_EXPMAX_##fs)				\
 	  {							\
-	    /* overflow to infinity */				\
-	    X##_e = _FP_EXPMAX_##fs;				\
-	    _FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc);		\
-            __ret |= EFLAG_OVERFLOW;				\
+	    /* overflow */					\
+	    switch (FP_ROUNDMODE)				\
+	      {							\
+	      case FP_RND_NEAREST:				\
+		X##_c = FP_CLS_INF;				\
+		break;						\
+	      case FP_RND_PINF:					\
+		if (!X##_s) X##_c = FP_CLS_INF;			\
+		break;						\
+	      case FP_RND_MINF:					\
+		if (X##_s) X##_c = FP_CLS_INF;			\
+		break;						\
+	      }							\
+	    if (X##_c == FP_CLS_INF)				\
+	      {							\
+		/* Overflow to infinity */			\
+		X##_e = _FP_EXPMAX_##fs;			\
+		_FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc);	\
+	      }							\
+	    else						\
+	      {							\
+		/* Overflow to maximum normal */		\
+		X##_e = _FP_EXPMAX_##fs - 1;			\
+		_FP_FRAC_SET_##wc(X, _FP_MAXFRAC_##wc);		\
+	      }							\
+	    FP_SET_EXCEPTION(FP_EX_OVERFLOW);			\
+            FP_SET_EXCEPTION(FP_EX_INEXACT);			\
 	  }							\
       }								\
     else							\
@@ -83,9 +132,9 @@
 	if (X##_e <= _FP_WFRACBITS_##fs)			\
 	  {							\
 	    _FP_FRAC_SRS_##wc(X, X##_e, _FP_WFRACBITS_##fs);	\
-	    __ret |= _FP_ROUND(wc, X);				\
-	    _FP_FRAC_SLL_##wc(X, 1);				\
-	    if (_FP_FRAC_OVERP_##wc(fs, X))			\
+	    _FP_ROUND(wc, X);					\
+	    if (_FP_FRAC_HIGH_##fs(X)				\
+		& (_FP_OVERFLOW_##fs >> 1))			\
 	      {							\
 	        X##_e = 1;					\
 	        _FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc);	\
@@ -93,16 +142,21 @@
 	    else						\
 	      {							\
 		X##_e = 0;					\
-		_FP_FRAC_SRL_##wc(X, _FP_WORKBITS+1);		\
-                __ret |= EFLAG_UNDERFLOW;			\
+		_FP_FRAC_SRL_##wc(X, _FP_WORKBITS);		\
+		FP_SET_EXCEPTION(FP_EX_UNDERFLOW);		\
 	      }							\
 	  }							\
 	else							\
 	  {							\
 	    /* underflow to zero */				\
 	    X##_e = 0;						\
-	    _FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc);		\
-            __ret |= EFLAG_UNDERFLOW;				\
+	    if (!_FP_FRAC_ZEROP_##wc(X))			\
+	      {							\
+	        _FP_FRAC_SET_##wc(X, _FP_MINFRAC_##wc);		\
+	        _FP_ROUND(wc, X);				\
+	        _FP_FRAC_LOW_##wc(X) >>= (_FP_WORKBITS);	\
+	      }							\
+	    FP_SET_EXCEPTION(FP_EX_UNDERFLOW);			\
 	  }							\
       }								\
     break;							\
@@ -122,16 +176,33 @@
     if (!_FP_KEEPNANFRACP)					\
       {								\
 	_FP_FRAC_SET_##wc(X, _FP_NANFRAC_##fs);			\
-	X##_s = 0;						\
+	X##_s = _FP_NANSIGN_##fs;				\
       }								\
     else							\
-      _FP_FRAC_HIGH_##wc(X) |= _FP_QNANBIT_##fs;		\
+      _FP_FRAC_HIGH_RAW_##fs(X) |= _FP_QNANBIT_##fs;		\
     break;							\
   }								\
+} while (0)
+
+/* This one accepts raw argument and not cooked,  returns
+ * 1 if X is a signaling NaN.
+ */
+#define _FP_ISSIGNAN(fs, wc, X)					\
+({								\
+  int __ret = 0;						\
+  if (X##_e == _FP_EXPMAX_##fs)					\
+    {								\
+      if (!_FP_FRAC_ZEROP_##wc(X)				\
+	  && !(_FP_FRAC_HIGH_RAW_##fs(X) & _FP_QNANBIT_##fs))	\
+	__ret = 1;						\
+    }								\
   __ret;							\
 })
 
 
+
+
+
 /*
  * Main addition routine.  The input values should be cooked.
  */
@@ -152,8 +223,6 @@
 	    _FP_FRAC_SRS_##wc(X, diff, _FP_WFRACBITS_##fs);		     \
 	  else if (!_FP_FRAC_ZEROP_##wc(X))				     \
 	    _FP_FRAC_SET_##wc(X, _FP_MINFRAC_##wc);			     \
-	  else								     \
-	    _FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc);			     \
 	  R##_e = Y##_e;						     \
 	}								     \
       else								     \
@@ -164,8 +233,6 @@
 	        _FP_FRAC_SRS_##wc(Y, diff, _FP_WFRACBITS_##fs);		     \
 	      else if (!_FP_FRAC_ZEROP_##wc(Y))				     \
 	        _FP_FRAC_SET_##wc(Y, _FP_MINFRAC_##wc);			     \
-	      else							     \
-	        _FP_FRAC_SET_##wc(Y, _FP_ZEROFRAC_##wc);		     \
 	    }								     \
 	  R##_e = X##_e;						     \
 	}								     \
@@ -245,8 +312,9 @@
       {									     \
 	/* +INF + -INF => NAN */					     \
 	_FP_FRAC_SET_##wc(R, _FP_NANFRAC_##fs);				     \
-	R##_s = X##_s ^ Y##_s;						     \
+	R##_s = _FP_NANSIGN_##fs;					     \
 	R##_c = FP_CLS_NAN;						     \
+	FP_SET_EXCEPTION(FP_EX_INVALID);				     \
 	break;								     \
       }									     \
     /* FALLTHRU */							     \
@@ -343,8 +411,10 @@
 							\
   case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_ZERO):		\
   case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_INF):		\
+    R##_s = _FP_NANSIGN_##fs;				\
     R##_c = FP_CLS_NAN;					\
     _FP_FRAC_SET_##wc(R, _FP_NANFRAC_##fs);		\
+    FP_SET_EXCEPTION(FP_EX_INVALID);			\
     break;						\
 							\
   default:						\
@@ -396,6 +466,7 @@
     break;						\
 							\
   case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_ZERO):	\
+    FP_SET_EXCEPTION(FP_EX_DIVZERO);			\
   case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_ZERO):		\
   case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_NORMAL):	\
     R##_c = FP_CLS_INF;					\
@@ -403,8 +474,10 @@
 							\
   case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_INF):		\
   case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_ZERO):	\
+    R##_s = _FP_NANSIGN_##fs;				\
     R##_c = FP_CLS_NAN;					\
     _FP_FRAC_SET_##wc(R, _FP_NANFRAC_##fs);		\
+    FP_SET_EXCEPTION(FP_EX_INVALID);			\
     break;						\
 							\
   default:						\
@@ -485,15 +558,17 @@
     switch (X##_c)							\
     {									\
     case FP_CLS_NAN:							\
-    	R##_s = 0;							\
+	_FP_FRAC_COPY_##wc(R, X);					\
+	R##_s = X##_s;							\
     	R##_c = FP_CLS_NAN;						\
-    	_FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc);			\
     	break;								\
     case FP_CLS_INF:							\
     	if (X##_s)							\
     	  {								\
-    	    R##_s = 0;							\
-	    R##_c = FP_CLS_NAN; /* sNAN */				\
+    	    R##_s = _FP_NANSIGN_##fs;					\
+	    R##_c = FP_CLS_NAN; /* NAN */				\
+	    _FP_FRAC_SET_##wc(R, _FP_NANFRAC_##fs);			\
+	    FP_SET_EXCEPTION(FP_EX_INVALID);				\
     	  }								\
     	else								\
     	  {								\
@@ -503,13 +578,16 @@
     	break;								\
     case FP_CLS_ZERO:							\
 	R##_s = X##_s;							\
-    	R##_c = FP_CLS_ZERO; /* sqrt(+-0) = +-0 */			\
+	R##_c = FP_CLS_ZERO; /* sqrt(+-0) = +-0 */			\
 	break;								\
     case FP_CLS_NORMAL:							\
     	R##_s = 0;							\
         if (X##_s)							\
           {								\
 	    R##_c = FP_CLS_NAN; /* sNAN */				\
+	    R##_s = _FP_NANSIGN_##fs;					\
+	    _FP_FRAC_SET_##wc(R, _FP_NANFRAC_##fs);			\
+	    FP_SET_EXCEPTION(FP_EX_INVALID);				\
 	    break;							\
           }								\
     	R##_c = FP_CLS_NORMAL;						\
@@ -518,10 +596,8 @@
         R##_e = X##_e >> 1;						\
         _FP_FRAC_SET_##wc(S, _FP_ZEROFRAC_##wc);			\
         _FP_FRAC_SET_##wc(R, _FP_ZEROFRAC_##wc);			\
-        q = _FP_OVERFLOW_##fs;						\
-        _FP_FRAC_SLL_##wc(X, 1);					\
+        q = _FP_OVERFLOW_##fs >> 1;					\
         _FP_SQRT_MEAT_##wc(R, S, T, X, q);				\
-        _FP_FRAC_SRL_##wc(R, 1);					\
     }									\
   } while (0)
 
@@ -529,28 +605,15 @@
  * Convert from FP to integer
  */
 
-/* "When a NaN, infinity, large positive argument >= 2147483648.0, or 
- * large negative argument <= -2147483649.0 is converted to an integer,
- * the invalid_current bit...should be set and fp_exception_IEEE_754 should
- * be raised. If the floating point invalid trap is disabled, no trap occurs
- * and a numerical result is generated: if the sign bit of the operand
- * is 0, the result is 2147483647; if the sign bit of the operand is 1,
- * the result is -2147483648."
- * Similarly for conversion to extended ints, except that the boundaries
- * are >= 2^63, <= -(2^63 + 1), and the results are 2^63 + 1 for s=0 and
- * -2^63 for s=1.
- * -- SPARC Architecture Manual V9, Appendix B, which specifies how
- * SPARCs resolve implementation dependencies in the IEEE-754 spec.
- * I don't believe that the code below follows this. I'm not even sure
- * it's right! 
- * It doesn't cope with needing to convert to an n bit integer when there
- * is no n bit integer type. Fortunately gcc provides long long so this
- * isn't a problem for sparc32.
- * I have, however, fixed its NaN handling to conform as above.
- *         -- PMM 02/1998
- * NB: rsigned is not 'is r declared signed?' but 'should the value stored
- * in r be signed or unsigned?'. r is always(?) declared unsigned.
- * Comments below are mine, BTW -- PMM 
+/* RSIGNED can have following values:
+ * 0:  the number is required to be 0..(2^rsize)-1, if not, NV is set plus
+ *     the result is either 0 or (2^rsize)-1 depending on the sign in such case.
+ * 1:  the number is required to be -(2^(rsize-1))..(2^(rsize-1))-1, if not, NV is
+ *     set plus the result is either -(2^(rsize-1)) or (2^(rsize-1))-1 depending
+ *     on the sign in such case.
+ * -1: the number is required to be -(2^(rsize-1))..(2^rsize)-1, if not, NV is
+ *     set plus the result is either -(2^(rsize-1)) or (2^(rsize-1))-1 depending
+ *     on the sign in such case.
  */
 #define _FP_TO_INT(fs, wc, r, X, rsize, rsigned)				\
   do {										\
@@ -559,26 +622,26 @@
       case FP_CLS_NORMAL:							\
 	if (X##_e < 0)								\
 	  {									\
-	  /* case FP_CLS_NAN: see above! */					\
+	    FP_SET_EXCEPTION(FP_EX_INEXACT);					\
 	  case FP_CLS_ZERO:							\
 	    r = 0;								\
 	  }									\
-	else if (X##_e >= rsize - (rsigned != 0))				\
+	else if (X##_e >= rsize - (rsigned > 0 || X##_s)			\
+		 || (!rsigned && X##_s))					\
 	  {	/* overflow */							\
 	  case FP_CLS_NAN:                                                      \
-          case FP_CLS_INF:							\
+	  case FP_CLS_INF:							\
 	    if (rsigned)							\
 	      {									\
 		r = 1;								\
 		r <<= rsize - 1;						\
 		r -= 1 - X##_s;							\
-	      }									\
-	    else								\
-	      {									\
+	      } else {								\
 		r = 0;								\
-		if (!X##_s)							\
+		if (X##_s)							\
 		  r = ~r;							\
 	      }									\
+	    FP_SET_EXCEPTION(FP_EX_INVALID);					\
 	  }									\
 	else									\
 	  {									\
@@ -591,8 +654,14 @@
 	      {									\
 		if (X##_e >= _FP_WFRACBITS_##fs)				\
 		  _FP_FRAC_SLL_##wc(X, (X##_e - _FP_WFRACBITS_##fs + 1));	\
-		else								\
-		  _FP_FRAC_SRL_##wc(X, (_FP_WFRACBITS_##fs - X##_e - 1));	\
+		else if (X##_e < _FP_WFRACBITS_##fs - 1)			\
+		  {								\
+		    _FP_FRAC_SRS_##wc(X, (_FP_WFRACBITS_##fs - X##_e - 2),	\
+				      _FP_WFRACBITS_##fs);			\
+		    if (_FP_FRAC_LOW_##wc(X) & 1)				\
+		      FP_SET_EXCEPTION(FP_EX_INEXACT);				\
+		    _FP_FRAC_SRL_##wc(X, 1);					\
+		  }								\
 		_FP_FRAC_ASSEMBLE_##wc(r, X, rsize);				\
 	      }									\
 	    if (rsigned && X##_s)						\
@@ -610,8 +679,6 @@
 									\
 	if ((X##_s = (r < 0)))						\
 	  r = -r;							\
-	/* Note that `r' is now considered unsigned, so we don't have	\
-	   to worry about the single signed overflow case.  */		\
 									\
 	if (rsize <= _FP_W_TYPE_SIZE)					\
 	  __FP_CLZ(X##_e, r);						\
@@ -624,7 +691,7 @@
 									\
 	if (_FP_FRACBITS_##fs < rsize && _FP_WFRACBITS_##fs < X##_e)	\
 	  __FP_FRAC_SRS_1(r, (X##_e - _FP_WFRACBITS_##fs), rsize);	\
-	r &= ~((_FP_W_TYPE)1 << X##_e);					\
+	r &= ~((rtype)1 << X##_e);					\
 	_FP_FRAC_DISASSEMBLE_##wc(X, ((unsigned rtype)r), rsize);	\
 	_FP_FRAC_SLL_##wc(X, (_FP_WFRACBITS_##fs - X##_e - 1));		\
       }									\
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/quad.h linux/arch/sparc64/math-emu/quad.h
--- v2.3.3/linux/arch/sparc64/math-emu/quad.h	Tue Apr 14 17:44:21 1998
+++ linux/arch/sparc64/math-emu/quad.h	Sat May 29 11:09:04 1999
@@ -1,13 +1,32 @@
-/*
- * Definitions for IEEE Quad Precision
- */
+/* Software floating-point emulation.
+   Definitions for IEEE Quad Precision.
+   Copyright (C) 1997,1998,1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Richard Henderson (rth@cygnus.com),
+		  Jakub Jelinek (jj@ultra.linux.cz),
+		  David S. Miller (davem@redhat.com) and
+		  Peter Maydell (pmaydell@chiark.greenend.org.uk).
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If
+   not, write to the Free Software Foundation, Inc.,
+   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
 #if _FP_W_TYPE_SIZE < 32
-/* It appears to be traditional to abuse 16bitters in these header files... */
 #error "Here's a nickel, kid. Go buy yourself a real computer."
 #endif
 
 #if _FP_W_TYPE_SIZE < 64
-/* This is all terribly experimental and I don't know if it'll work properly -- PMM 02/1998 */
 #define _FP_FRACTBITS_Q         (4*_FP_W_TYPE_SIZE)
 #else
 #define _FP_FRACTBITS_Q		(2*_FP_W_TYPE_SIZE)
@@ -56,7 +75,13 @@
 
 #define FP_DECL_Q(X)		_FP_DECL(4,X)
 #define FP_UNPACK_RAW_Q(X,val)	_FP_UNPACK_RAW_4(Q,X,val)
+#define FP_UNPACK_RAW_QP(X,val)	_FP_UNPACK_RAW_4_P(Q,X,val)
 #define FP_PACK_RAW_Q(val,X)	_FP_PACK_RAW_4(Q,val,X)
+#define FP_PACK_RAW_QP(val,X)		\
+  do {					\
+    if (!FP_INHIBIT_RESULTS)		\
+      _FP_PACK_RAW_4_P(Q,val,X);	\
+  } while (0)
 
 #define FP_UNPACK_Q(X,val)		\
   do {					\
@@ -64,28 +89,46 @@
     _FP_UNPACK_CANONICAL(Q,4,X);	\
   } while (0)
 
+#define FP_UNPACK_QP(X,val)		\
+  do {					\
+    _FP_UNPACK_RAW_4_P(Q,X,val);	\
+    _FP_UNPACK_CANONICAL(Q,4,X);	\
+  } while (0)
+
 #define FP_PACK_Q(val,X)		\
   do {					\
     _FP_PACK_CANONICAL(Q,4,X);		\
     _FP_PACK_RAW_4(Q,val,X);		\
   } while (0)
 
+#define FP_PACK_QP(val,X)		\
+  do {					\
+    _FP_PACK_CANONICAL(Q,4,X);		\
+    if (!FP_INHIBIT_RESULTS)		\
+      _FP_PACK_RAW_4_P(Q,val,X);	\
+  } while (0)
+
+#define FP_ISSIGNAN_Q(X)	_FP_ISSIGNAN(Q,4,X)
 #define FP_NEG_Q(R,X)		_FP_NEG(Q,4,R,X)
 #define FP_ADD_Q(R,X,Y)		_FP_ADD(Q,4,R,X,Y)
 /* single.h and double.h define FP_SUB_t this way too. However, _FP_SUB is
  * never defined in op-common.h! Fortunately nobody seems to use the FP_SUB_t 
  * macros: I suggest a combination of FP_NEG and FP_ADD :-> -- PMM 02/1998
  */
-#define FP_SUB_Q(R,X,Y)		_FP_SUB(Q,4,R,X,Y)
-#define FP_MUL_Q(R,X,Y)		_FP_MUL(Q,4,R,X,Y)
-#define FP_DIV_Q(R,X,Y)		_FP_DIV(Q,4,R,X,Y)
-#define FP_SQRT_Q(R,X)		_FP_SQRT(Q,4,R,X)
+#define FP_SUB_Q(R,X,Y)			_FP_SUB(Q,4,R,X,Y)
+#define FP_MUL_Q(R,X,Y)			_FP_MUL(Q,4,R,X,Y)
+#define FP_DIV_Q(R,X,Y)			_FP_DIV(Q,4,R,X,Y)
+#define FP_SQRT_Q(R,X)			_FP_SQRT(Q,4,R,X)
+#define _FP_SQRT_MEAT_Q(R,S,T,X,Q)	_FP_SQRT_MEAT_4(R,S,T,X,Q)
 
 #define FP_CMP_Q(r,X,Y,un)	_FP_CMP(Q,4,r,X,Y,un)
 #define FP_CMP_EQ_Q(r,X,Y)	_FP_CMP_EQ(Q,4,r,X,Y)
 
-#define FP_TO_INT_Q(r,X,rsz,rsg)  _FP_TO_INT(Q,4,r,X,rsz,rsg)
-#define FP_FROM_INT_Q(X,r,rs,rt)  _FP_FROM_INT(Q,4,X,r,rs,rt)
+#define FP_TO_INT_Q(r,X,rsz,rsg)	_FP_TO_INT(Q,4,r,X,rsz,rsg)
+#define FP_FROM_INT_Q(X,r,rs,rt)	_FP_FROM_INT(Q,4,X,r,rs,rt)
+
+#define _FP_FRAC_HIGH_Q(X)	_FP_FRAC_HIGH_4(X)
+#define _FP_FRAC_HIGH_RAW_Q(X)	_FP_FRAC_HIGH_4(X)
 
 #else   /* not _FP_W_TYPE_SIZE < 64 */
 union _FP_UNION_Q
@@ -108,7 +151,13 @@
 
 #define FP_DECL_Q(X)		_FP_DECL(2,X)
 #define FP_UNPACK_RAW_Q(X,val)	_FP_UNPACK_RAW_2(Q,X,val)
+#define FP_UNPACK_RAW_QP(X,val)	_FP_UNPACK_RAW_2_P(Q,X,val)
 #define FP_PACK_RAW_Q(val,X)	_FP_PACK_RAW_2(Q,val,X)
+#define FP_PACK_RAW_QP(val,X)		\
+  do {					\
+    if (!FP_INHIBIT_RESULTS)		\
+      _FP_PACK_RAW_2_P(Q,val,X);	\
+  } while (0)
 
 #define FP_UNPACK_Q(X,val)		\
   do {					\
@@ -116,23 +165,41 @@
     _FP_UNPACK_CANONICAL(Q,2,X);	\
   } while (0)
 
+#define FP_UNPACK_QP(X,val)		\
+  do {					\
+    _FP_UNPACK_RAW_2_P(Q,X,val);	\
+    _FP_UNPACK_CANONICAL(Q,2,X);	\
+  } while (0)
+
 #define FP_PACK_Q(val,X)		\
   do {					\
     _FP_PACK_CANONICAL(Q,2,X);		\
     _FP_PACK_RAW_2(Q,val,X);		\
   } while (0)
 
-#define FP_NEG_Q(R,X)		_FP_NEG(Q,2,R,X)
-#define FP_ADD_Q(R,X,Y)		_FP_ADD(Q,2,R,X,Y)
-#define FP_SUB_Q(R,X,Y)		_FP_SUB(Q,2,R,X,Y)
-#define FP_MUL_Q(R,X,Y)		_FP_MUL(Q,2,R,X,Y)
-#define FP_DIV_Q(R,X,Y)		_FP_DIV(Q,2,R,X,Y)
-#define FP_SQRT_Q(R,X)		_FP_SQRT(Q,2,R,X)
+#define FP_PACK_QP(val,X)		\
+  do {					\
+    _FP_PACK_CANONICAL(Q,2,X);		\
+    if (!FP_INHIBIT_RESULTS)		\
+      _FP_PACK_RAW_2_P(Q,val,X);	\
+  } while (0)
+
+#define FP_ISSIGNAN_Q(X)		_FP_ISSIGNAN(Q,2,X)
+#define FP_NEG_Q(R,X)			_FP_NEG(Q,2,R,X)
+#define FP_ADD_Q(R,X,Y)			_FP_ADD(Q,2,R,X,Y)
+#define FP_SUB_Q(R,X,Y)			_FP_SUB(Q,2,R,X,Y)
+#define FP_MUL_Q(R,X,Y)			_FP_MUL(Q,2,R,X,Y)
+#define FP_DIV_Q(R,X,Y)			_FP_DIV(Q,2,R,X,Y)
+#define FP_SQRT_Q(R,X)			_FP_SQRT(Q,2,R,X)
+#define _FP_SQRT_MEAT_Q(R,S,T,X,Q)	_FP_SQRT_MEAT_2(R,S,T,X,Q)
 
 #define FP_CMP_Q(r,X,Y,un)	_FP_CMP(Q,2,r,X,Y,un)
 #define FP_CMP_EQ_Q(r,X,Y)	_FP_CMP_EQ(Q,2,r,X,Y)
 
-#define FP_TO_INT_Q(r,X,rsz,rsg)  _FP_TO_INT(Q,2,r,X,rsz,rsg)
-#define FP_FROM_INT_Q(X,r,rs,rt)  _FP_FROM_INT(Q,2,X,r,rs,rt)
+#define FP_TO_INT_Q(r,X,rsz,rsg)	_FP_TO_INT(Q,2,r,X,rsz,rsg)
+#define FP_FROM_INT_Q(X,r,rs,rt)	_FP_FROM_INT(Q,2,X,r,rs,rt)
+
+#define _FP_FRAC_HIGH_Q(X)	_FP_FRAC_HIGH_2(X)
+#define _FP_FRAC_HIGH_RAW_Q(X)	_FP_FRAC_HIGH_2(X)
 
 #endif /* not _FP_W_TYPE_SIZE < 64 */
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/sfp-machine.h linux/arch/sparc64/math-emu/sfp-machine.h
--- v2.3.3/linux/arch/sparc64/math-emu/sfp-machine.h	Tue Mar 16 21:52:06 1999
+++ linux/arch/sparc64/math-emu/sfp-machine.h	Sat May 29 11:09:04 1999
@@ -1,6 +1,10 @@
-/* Machine-dependent software floating-point definitions.  Sparc64 version.
-   Copyright (C) 1997 Free Software Foundation, Inc.
+/* Machine-dependent software floating-point definitions.
+   Sparc64 kernel version.
+   Copyright (C) 1997,1998,1999 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
+   Contributed by Richard Henderson (rth@cygnus.com),
+		  Jakub Jelinek (jj@ultra.linux.cz) and
+		  David S. Miller (davem@redhat.com).
 
    The GNU C Library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public License as
@@ -17,6 +21,9 @@
    not, write to the Free Software Foundation, Inc.,
    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
+#ifndef _SFP_MACHINE_H
+#define _SFP_MACHINE_H
+   
 #define _FP_W_TYPE_SIZE		64
 #define _FP_W_TYPE		unsigned long
 #define _FP_WS_TYPE		signed long
@@ -24,222 +31,58 @@
 
 #define _FP_MUL_MEAT_S(R,X,Y)	_FP_MUL_MEAT_1_imm(S,R,X,Y)
 #define _FP_MUL_MEAT_D(R,X,Y)	_FP_MUL_MEAT_1_wide(D,R,X,Y,umul_ppmm)
-#define _FP_MUL_MEAT_Q(R,X,Y)	_FP_MUL_MEAT_2_wide(Q,R,X,Y,umul_ppmm)
+#define _FP_MUL_MEAT_Q(R,X,Y)	_FP_MUL_MEAT_2_wide_3mul(Q,R,X,Y,umul_ppmm)
 
 #define _FP_DIV_MEAT_S(R,X,Y)	_FP_DIV_MEAT_1_imm(S,R,X,Y,_FP_DIV_HELP_imm)
 #define _FP_DIV_MEAT_D(R,X,Y)	_FP_DIV_MEAT_1_udiv(D,R,X,Y)
-#define _FP_DIV_MEAT_Q(R,X,Y)	_FP_DIV_MEAT_2_udiv_64(Q,R,X,Y)
+#define _FP_DIV_MEAT_Q(R,X,Y)	_FP_DIV_MEAT_2_udiv(Q,R,X,Y)
 
-#define _FP_NANFRAC_S		_FP_QNANBIT_S
-#define _FP_NANFRAC_D		_FP_QNANBIT_D
-#define _FP_NANFRAC_Q		_FP_QNANBIT_Q, 0
+#define _FP_NANFRAC_S		((_FP_QNANBIT_S << 1) - 1)
+#define _FP_NANFRAC_D		((_FP_QNANBIT_D << 1) - 1)
+#define _FP_NANFRAC_Q		((_FP_QNANBIT_Q << 1) - 1), -1
+#define _FP_NANSIGN_S		0
+#define _FP_NANSIGN_D		0
+#define _FP_NANSIGN_Q		0
 
 #define _FP_KEEPNANFRACP 1
+
+/* If one NaN is signaling and the other is not,
+ * we choose that one, otherwise we choose X.
+ */
+/* For _Qp_* and _Q_*, this should prefer X, for
+ * CPU instruction emulation this should prefer Y.
+ * (see SPAMv9 B.2.2 section).
+ */
 #define _FP_CHOOSENAN(fs, wc, R, X, Y)				\
   do {								\
-    R##_s = Y##_s;						\
-    _FP_FRAC_COPY_##wc(R,Y);					\
+    if ((_FP_FRAC_HIGH_RAW_##fs(Y) & _FP_QNANBIT_##fs)		\
+	&& !(_FP_FRAC_HIGH_RAW_##fs(X) & _FP_QNANBIT_##fs))	\
+      {								\
+	R##_s = X##_s;						\
+	_FP_FRAC_COPY_##wc(R,X);				\
+      }								\
+    else							\
+      {								\
+	R##_s = Y##_s;						\
+	_FP_FRAC_COPY_##wc(R,Y);				\
+      }								\
     R##_c = FP_CLS_NAN;						\
   } while (0)
 
-#define __FP_UNPACK_RAW_1(fs, X, val)				\
-  do {								\
-    union _FP_UNION_##fs *_flo =				\
-    	(union _FP_UNION_##fs *)val;				\
-								\
-    X##_f = _flo->bits.frac;					\
-    X##_e = _flo->bits.exp;					\
-    X##_s = _flo->bits.sign;					\
-  } while (0)
-
-#define __FP_UNPACK_RAW_2(fs, X, val)			\
-  do {							\
-    union _FP_UNION_##fs *_flo =			\
-    	(union _FP_UNION_##fs *)val;			\
-							\
-    X##_f0 = _flo->bits.frac0;				\
-    X##_f1 = _flo->bits.frac1;				\
-    X##_e  = _flo->bits.exp;				\
-    X##_s  = _flo->bits.sign;				\
-  } while (0)
-
-#define __FP_UNPACK_S(X,val)		\
-  do {					\
-    __FP_UNPACK_RAW_1(S,X,val);		\
-    _FP_UNPACK_CANONICAL(S,1,X);	\
-  } while (0)
-
-#define __FP_UNPACK_D(X,val)		\
-  do {					\
-    __FP_UNPACK_RAW_1(D,X,val);		\
-    _FP_UNPACK_CANONICAL(D,1,X);	\
-  } while (0)
-
-#define __FP_UNPACK_Q(X,val)		\
-  do {					\
-    __FP_UNPACK_RAW_2(Q,X,val);		\
-    _FP_UNPACK_CANONICAL(Q,2,X);	\
-  } while (0)
-
-#define __FP_PACK_RAW_1(fs, val, X)				\
-  do {								\
-    union _FP_UNION_##fs *_flo =				\
-    	(union _FP_UNION_##fs *)val;				\
-								\
-    _flo->bits.frac = X##_f;					\
-    _flo->bits.exp  = X##_e;					\
-    _flo->bits.sign = X##_s;					\
-  } while (0)
-  
-#define __FP_PACK_RAW_2(fs, val, X)			\
-  do {							\
-    union _FP_UNION_##fs *_flo =			\
-    	(union _FP_UNION_##fs *)val;			\
-							\
-    _flo->bits.frac0 = X##_f0;				\
-    _flo->bits.frac1 = X##_f1;				\
-    _flo->bits.exp   = X##_e;				\
-    _flo->bits.sign  = X##_s;				\
-  } while (0)
-
-#include <linux/kernel.h>
-#include <linux/sched.h>
-
-/* We only actually write to the destination register
- * if exceptions signalled (if any) will not trap.
- */
-#define __FPU_TEM \
-	(((current->tss.xfsr[0])>>23)&0x1f)
-#define __FPU_TRAP_P(bits) \
-	((__FPU_TEM & (bits)) != 0)
-
-#define __FP_PACK_S(val,X)			\
-({  int __exc = _FP_PACK_CANONICAL(S,1,X);	\
-    if(!__exc || !__FPU_TRAP_P(__exc))		\
-        __FP_PACK_RAW_1(S,val,X);		\
-    __exc;					\
-})
-
-#define __FP_PACK_D(val,X)			\
-({  int __exc = _FP_PACK_CANONICAL(D,1,X);	\
-    if(!__exc || !__FPU_TRAP_P(__exc))		\
-        __FP_PACK_RAW_1(D,val,X);		\
-    __exc;					\
-})
-
-#define __FP_PACK_Q(val,X)			\
-({  int __exc = _FP_PACK_CANONICAL(Q,2,X);	\
-    if(!__exc || !__FPU_TRAP_P(__exc))		\
-        __FP_PACK_RAW_2(Q,val,X);		\
-    __exc;					\
-})
-
 /* Obtain the current rounding mode. */
+#ifndef FP_ROUNDMODE
 #define FP_ROUNDMODE	((current->tss.xfsr[0] >> 30) & 0x3)
+#endif
 
-#include <linux/types.h>
-#include <asm/byteorder.h>
-
-#define add_ssaaaa(sh, sl, ah, al, bh, bl) 						\
-  __asm__ ("addcc %4,%5,%1
-  	    add %2,%3,%0
-  	    bcs,a,pn %%xcc, 1f
-  	    add %0, 1, %0
-  	    1:"										\
-	   : "=r" ((UDItype)(sh)),				      			\
-	     "=&r" ((UDItype)(sl))				      			\
-	   : "r" ((UDItype)(ah)),				     			\
-	     "r" ((UDItype)(bh)),				      			\
-	     "r" ((UDItype)(al)),				     			\
-	     "r" ((UDItype)(bl))				       			\
-	   : "cc")
-	   
-#define sub_ddmmss(sh, sl, ah, al, bh, bl) 						\
-  __asm__ ("subcc %4,%5,%1
-  	    sub %2,%3,%0
-  	    bcs,a,pn %%xcc, 1f
-  	    sub %0, 1, %0
-  	    1:"										\
-	   : "=r" ((UDItype)(sh)),				      			\
-	     "=&r" ((UDItype)(sl))				      			\
-	   : "r" ((UDItype)(ah)),				     			\
-	     "r" ((UDItype)(bh)),				      			\
-	     "r" ((UDItype)(al)),				     			\
-	     "r" ((UDItype)(bl))				       			\
-	   : "cc")
-	   
-#define umul_ppmm(wh, wl, u, v) 							\
-  do {											\
-	  __asm__ ("mulx %2,%3,%1
-  		    srlx %2,32,%%g1
-	  	    srl %3,0,%%g2
-  		    mulx %%g1,%%g2,%%g3
-  		    srlx %3,32,%%g1
-  		    srl %2,0,%%g2
-  		    mulx %%g1,%%g2,%%g2
-  		    srlx %2,32,%%g1
-  		    add %%g2,%%g3,%%g3
-  		    srlx %3,32,%%g2
-  		    mulx %%g1,%%g2,%%g1
-  		    srlx %%g3,32,%%g2
-  		    add %%g1,%%g2,%0"							\
-	   : "=r" ((UDItype)(wh)),				      			\
-	     "=&r" ((UDItype)(wl))				      			\
-	   : "r" ((UDItype)(u)),				     			\
-	     "r" ((UDItype)(v))				      				\
-	   : "g1", "g2", "g3", "cc");							\
-  } while (0)
-  
-#define udiv_qrnnd(q, r, n1, n0, d) 							\
-  do {                                                                  		\
-    UWtype __d1, __d0, __q1, __q0, __r1, __r0, __m;                     		\
-    __d1 = (d >> 32);                                           			\
-    __d0 = (USItype)d;                                            			\
-                                                                        		\
-    __r1 = (n1) % __d1;                                                 		\
-    __q1 = (n1) / __d1;                                                 		\
-    __m = (UWtype) __q1 * __d0;                                         		\
-    __r1 = (__r1 << 32) | (n0 >> 32);                          				\
-    if (__r1 < __m)                                                     		\
-      {                                                                 		\
-        __q1--, __r1 += (d);                                            		\
-        if (__r1 >= (d)) /* i.e. we didn't get carry when adding to __r1 */		\
-          if (__r1 < __m)                                               		\
-            __q1--, __r1 += (d);                                        		\
-      }                                                                 		\
-    __r1 -= __m;                                                        		\
-                                                                        		\
-    __r0 = __r1 % __d1;                                                 		\
-    __q0 = __r1 / __d1;                                                 		\
-    __m = (UWtype) __q0 * __d0;                                         		\
-    __r0 = (__r0 << 32) | ((USItype)n0);                           			\
-    if (__r0 < __m)                                                     		\
-      {                                                                 		\
-        __q0--, __r0 += (d);                                            		\
-        if (__r0 >= (d))                                                		\
-          if (__r0 < __m)                                               		\
-            __q0--, __r0 += (d);                                        		\
-      }                                                                 		\
-    __r0 -= __m;                                                        		\
-                                                                        		\
-    (q) = (UWtype) (__q1 << 32)  | __q0;                                		\
-    (r) = __r0;                                                         		\
-  } while (0)
+/* Exception flags. */
+#define FP_EX_INVALID		(1 << 4)
+#define FP_EX_OVERFLOW		(1 << 3)
+#define FP_EX_UNDERFLOW		(1 << 2)
+#define FP_EX_DIVZERO		(1 << 1)
+#define FP_EX_INEXACT		(1 << 0)
 
-#define UDIV_NEEDS_NORMALIZATION 1  
+#define FP_HANDLE_EXCEPTIONS return _fex
 
-#define abort()										\
-	return 0
+#define FP_INHIBIT_RESULTS ((current->tss.xfsr[0] >> 23) & _fex)
 
-#ifdef __BIG_ENDIAN
-#define __BYTE_ORDER __BIG_ENDIAN
-#else
-#define __BYTE_ORDER __LITTLE_ENDIAN
 #endif
-
-/* Exception flags. */
-#define EFLAG_INVALID		(1 << 4)
-#define EFLAG_OVERFLOW		(1 << 3)
-#define EFLAG_UNDERFLOW		(1 << 2)
-#define EFLAG_DIVZERO		(1 << 1)
-#define EFLAG_INEXACT		(1 << 0)
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/sfp-util.h linux/arch/sparc64/math-emu/sfp-util.h
--- v2.3.3/linux/arch/sparc64/math-emu/sfp-util.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/math-emu/sfp-util.h	Sat May 29 11:09:04 1999
@@ -0,0 +1,120 @@
+/* $Id: sfp-util.h,v 1.1 1999/05/28 13:43:07 jj Exp $
+ * arch/sparc64/math-emu/sfp-util.h
+ *
+ * Copyright (C) 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <asm/byteorder.h>
+
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) 						\
+  __asm__ ("addcc %4,%5,%1
+  	    add %2,%3,%0
+  	    bcs,a,pn %%xcc, 1f
+  	    add %0, 1, %0
+  	    1:"										\
+	   : "=r" ((UDItype)(sh)),				      			\
+	     "=&r" ((UDItype)(sl))				      			\
+	   : "r" ((UDItype)(ah)),				     			\
+	     "r" ((UDItype)(bh)),				      			\
+	     "r" ((UDItype)(al)),				     			\
+	     "r" ((UDItype)(bl))				       			\
+	   : "cc")
+	   
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) 						\
+  __asm__ ("subcc %4,%5,%1
+  	    sub %2,%3,%0
+  	    bcs,a,pn %%xcc, 1f
+  	    sub %0, 1, %0
+  	    1:"										\
+	   : "=r" ((UDItype)(sh)),				      			\
+	     "=&r" ((UDItype)(sl))				      			\
+	   : "r" ((UDItype)(ah)),				     			\
+	     "r" ((UDItype)(bh)),				      			\
+	     "r" ((UDItype)(al)),				     			\
+	     "r" ((UDItype)(bl))				       			\
+	   : "cc")
+
+#define umul_ppmm(wh, wl, u, v)								\
+  do {											\
+	  UDItype tmp1, tmp2, tmp3, tmp4;						\
+	  __asm__ __volatile__ (							\
+		   "srl %7,0,%3
+		    mulx %3,%6,%1
+		    srlx %6,32,%2
+		    mulx %2,%3,%4
+		    sllx %4,32,%5
+		    srl %6,0,%3
+		    sub %1,%5,%5
+		    srlx %5,32,%5
+		    addcc %4,%5,%4
+		    srlx %7,32,%5
+		    mulx %3,%5,%3
+		    mulx %2,%5,%5
+		    sethi 0x80000000,%2
+		    addcc %4,%3,%4
+		    srlx %4,32,%4
+		    add %2,%2,%2
+		    movcc %%xcc,%%g0,%2
+		    addcc %5,%4,%5
+		    sllx %3,32,%3
+		    add %1,%3,%1
+		    add %5,%2,%0"							\
+	   : "=r" ((UDItype)(wh)),							\
+	     "=&r" ((UDItype)(wl)),							\
+	     "=&r" (tmp1), "=&r" (tmp2), "=&r" (tmp3), "=&r" (tmp4)			\
+	   : "r" ((UDItype)(u)),							\
+	     "r" ((UDItype)(v))								\
+	   : "cc");									\
+  } while (0)
+  
+#define udiv_qrnnd(q, r, n1, n0, d) 							\
+  do {                                                                  		\
+    UWtype __d1, __d0, __q1, __q0, __r1, __r0, __m;                     		\
+    __d1 = (d >> 32);                                           			\
+    __d0 = (USItype)d;                                            			\
+                                                                        		\
+    __r1 = (n1) % __d1;                                                 		\
+    __q1 = (n1) / __d1;                                                 		\
+    __m = (UWtype) __q1 * __d0;                                         		\
+    __r1 = (__r1 << 32) | (n0 >> 32);                          				\
+    if (__r1 < __m)                                                     		\
+      {                                                                 		\
+        __q1--, __r1 += (d);                                            		\
+        if (__r1 >= (d)) /* i.e. we didn't get carry when adding to __r1 */		\
+          if (__r1 < __m)                                               		\
+            __q1--, __r1 += (d);                                        		\
+      }                                                                 		\
+    __r1 -= __m;                                                        		\
+                                                                        		\
+    __r0 = __r1 % __d1;                                                 		\
+    __q0 = __r1 / __d1;                                                 		\
+    __m = (UWtype) __q0 * __d0;                                         		\
+    __r0 = (__r0 << 32) | ((USItype)n0);                           			\
+    if (__r0 < __m)                                                     		\
+      {                                                                 		\
+        __q0--, __r0 += (d);                                            		\
+        if (__r0 >= (d))                                                		\
+          if (__r0 < __m)                                               		\
+            __q0--, __r0 += (d);                                        		\
+      }                                                                 		\
+    __r0 -= __m;                                                        		\
+                                                                        		\
+    (q) = (UWtype) (__q1 << 32)  | __q0;                                		\
+    (r) = __r0;                                                         		\
+  } while (0)
+
+#define UDIV_NEEDS_NORMALIZATION 1  
+
+#define abort()										\
+	return 0
+
+#ifdef __BIG_ENDIAN
+#define __BYTE_ORDER __BIG_ENDIAN
+#else
+#define __BYTE_ORDER __LITTLE_ENDIAN
+#endif
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/single.h linux/arch/sparc64/math-emu/single.h
--- v2.3.3/linux/arch/sparc64/math-emu/single.h	Tue Apr 14 17:44:21 1998
+++ linux/arch/sparc64/math-emu/single.h	Sat May 29 11:09:04 1999
@@ -1,6 +1,26 @@
-/*
- * Definitions for IEEE Single Precision
- */
+/* Software floating-point emulation.
+   Definitions for IEEE Single Precision.
+   Copyright (C) 1997,1998,1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Richard Henderson (rth@cygnus.com),
+		  Jakub Jelinek (jj@ultra.linux.cz),
+		  David S. Miller (davem@redhat.com) and
+		  Peter Maydell (pmaydell@chiark.greenend.org.uk).
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If
+   not, write to the Free Software Foundation, Inc.,
+   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 #if _FP_W_TYPE_SIZE < 32
 #error "Here's a nickel kid.  Go buy yourself a real computer."
@@ -38,7 +58,13 @@
 
 #define FP_DECL_S(X)		_FP_DECL(1,X)
 #define FP_UNPACK_RAW_S(X,val)	_FP_UNPACK_RAW_1(S,X,val)
+#define FP_UNPACK_RAW_SP(X,val)	_FP_UNPACK_RAW_1_P(S,X,val)
 #define FP_PACK_RAW_S(val,X)	_FP_PACK_RAW_1(S,val,X)
+#define FP_PACK_RAW_SP(val,X)		\
+  do {					\
+    if (!FP_INHIBIT_RESULTS)		\
+      _FP_PACK_RAW_1_P(S,val,X);	\
+  } while (0)
 
 #define FP_UNPACK_S(X,val)		\
   do {					\
@@ -46,21 +72,39 @@
     _FP_UNPACK_CANONICAL(S,1,X);	\
   } while (0)
 
+#define FP_UNPACK_SP(X,val)		\
+  do {					\
+    _FP_UNPACK_RAW_1_P(S,X,val);	\
+    _FP_UNPACK_CANONICAL(S,1,X);	\
+  } while (0)
+
 #define FP_PACK_S(val,X)		\
   do {					\
     _FP_PACK_CANONICAL(S,1,X);		\
     _FP_PACK_RAW_1(S,val,X);		\
   } while (0)
 
-#define FP_NEG_S(R,X)		_FP_NEG(S,1,R,X)
-#define FP_ADD_S(R,X,Y)		_FP_ADD(S,1,R,X,Y)
-#define FP_SUB_S(R,X,Y)		_FP_SUB(S,1,R,X,Y)
-#define FP_MUL_S(R,X,Y)		_FP_MUL(S,1,R,X,Y)
-#define FP_DIV_S(R,X,Y)		_FP_DIV(S,1,R,X,Y)
-#define FP_SQRT_S(R,X)		_FP_SQRT(S,1,R,X)
+#define FP_PACK_SP(val,X)		\
+  do {					\
+    _FP_PACK_CANONICAL(S,1,X);		\
+    if (!FP_INHIBIT_RESULTS)		\
+      _FP_PACK_RAW_1_P(S,val,X);	\
+  } while (0)
+
+#define FP_ISSIGNAN_S(X)		_FP_ISSIGNAN(S,1,X)
+#define FP_NEG_S(R,X)			_FP_NEG(S,1,R,X)
+#define FP_ADD_S(R,X,Y)			_FP_ADD(S,1,R,X,Y)
+#define FP_SUB_S(R,X,Y)			_FP_SUB(S,1,R,X,Y)
+#define FP_MUL_S(R,X,Y)			_FP_MUL(S,1,R,X,Y)
+#define FP_DIV_S(R,X,Y)			_FP_DIV(S,1,R,X,Y)
+#define FP_SQRT_S(R,X)			_FP_SQRT(S,1,R,X)
+#define _FP_SQRT_MEAT_S(R,S,T,X,Q)	_FP_SQRT_MEAT_1(R,S,T,X,Q)
 
 #define FP_CMP_S(r,X,Y,un)	_FP_CMP(S,1,r,X,Y,un)
 #define FP_CMP_EQ_S(r,X,Y)	_FP_CMP_EQ(S,1,r,X,Y)
 
-#define FP_TO_INT_S(r,X,rsz,rsg)  _FP_TO_INT(S,1,r,X,rsz,rsg)
-#define FP_FROM_INT_S(X,r,rs,rt)  _FP_FROM_INT(S,1,X,r,rs,rt)
+#define FP_TO_INT_S(r,X,rsz,rsg)	_FP_TO_INT(S,1,r,X,rsz,rsg)
+#define FP_FROM_INT_S(X,r,rs,rt)	_FP_FROM_INT(S,1,X,r,rs,rt)
+
+#define _FP_FRAC_HIGH_S(X)	_FP_FRAC_HIGH_1(X)
+#define _FP_FRAC_HIGH_RAW_S(X)	_FP_FRAC_HIGH_1(X)
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/soft-fp.h linux/arch/sparc64/math-emu/soft-fp.h
--- v2.3.3/linux/arch/sparc64/math-emu/soft-fp.h	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/math-emu/soft-fp.h	Sat May 29 11:09:04 1999
@@ -1,8 +1,36 @@
+/* Software floating-point emulation.
+   Copyright (C) 1997,1998,1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Richard Henderson (rth@cygnus.com),
+		  Jakub Jelinek (jj@ultra.linux.cz),
+		  David S. Miller (davem@redhat.com) and
+		  Peter Maydell (pmaydell@chiark.greenend.org.uk).
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If
+   not, write to the Free Software Foundation, Inc.,
+   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
 #ifndef SOFT_FP_H
 #define SOFT_FP_H
 
 #include "sfp-machine.h"
 
+/* Allow sfp-machine to have its own byte order definitions. */
+#ifndef __BYTE_ORDER
+#include <endian.h>
+#endif
+
 #define _FP_WORKBITS		3
 #define _FP_WORK_LSB		((_FP_W_TYPE)1 << 3)
 #define _FP_WORK_ROUND		((_FP_W_TYPE)1 << 2)
@@ -19,51 +47,100 @@
 #endif
 #endif
 
+/* By default don't care about exceptions. */
+#ifndef FP_EX_INVALID
+#define FP_EX_INVALID		0
+#endif
+#ifndef FP_EX_OVERFLOW
+#define FP_EX_OVERFLOW		0
+#endif
+#ifndef FP_EX_UNDERFLOW
+#define FP_EX_UNDERFLOW		
+#endif
+#ifndef FP_EX_DIVZERO
+#define FP_EX_DIVZERO		0
+#endif
+#ifndef FP_EX_INEXACT
+#define FP_EX_INEXACT		0
+#endif
+#ifndef FP_EX_DENORM
+#define FP_EX_DENORM		0
+#endif
+
+#ifdef _FP_DECL_EX
+#define FP_DECL_EX					\
+  int _fex = 0;						\
+  _FP_DECL_EX
+#else
+#define FP_DECL_EX int _fex = 0
+#endif
+  
+#ifndef FP_INIT_ROUNDMODE
+#define FP_INIT_ROUNDMODE do {} while (0)
+#endif
+
+#ifndef FP_HANDLE_EXCEPTIONS
+#define FP_HANDLE_EXCEPTIONS do {} while (0)
+#endif
+
+#ifndef FP_INHIBIT_RESULTS
+/* By default we write the results always.
+ * sfp-machine may override this and e.g.
+ * check if some exceptions are unmasked
+ * and inhibit it in such a case.
+ */
+#define FP_INHIBIT_RESULTS 0
+#endif
+
+#define FP_SET_EXCEPTION(ex)				\
+  _fex |= (ex)
+  
+#define FP_UNSET_EXCEPTION(ex)				\
+  _fex &= ~(ex)
+
+#define FP_CLEAR_EXCEPTIONS				\
+  _fex = 0
+
 #define _FP_ROUND_NEAREST(wc, X)			\
-({  int __ret = EFLAG_INEXACT;				\
+do {							\
     if ((_FP_FRAC_LOW_##wc(X) & 15) != _FP_WORK_ROUND)	\
       _FP_FRAC_ADDI_##wc(X, _FP_WORK_ROUND);		\
-    else __ret = 0;					\
-    __ret;						\
-})
+} while (0)
 
-#define _FP_ROUND_ZERO(wc, X)		0 /* XXX */
+#define _FP_ROUND_ZERO(wc, X)		0
 
 #define _FP_ROUND_PINF(wc, X)				\
-({  int __ret = EFLAG_INEXACT;				\
+do {							\
     if (!X##_s && (_FP_FRAC_LOW_##wc(X) & 7))		\
       _FP_FRAC_ADDI_##wc(X, _FP_WORK_LSB);		\
-    else __ret = 0;					\
-    __ret;						\
-})
+} while (0)
 
 #define _FP_ROUND_MINF(wc, X)				\
-({  int __ret = EFLAG_INEXACT;				\
+do {							\
     if (X##_s && (_FP_FRAC_LOW_##wc(X) & 7))		\
       _FP_FRAC_ADDI_##wc(X, _FP_WORK_LSB);		\
-    else __ret = 0;					\
-    __ret;						\
-})
+} while (0)
 
 #define _FP_ROUND(wc, X)			\
-({	int __ret = 0;				\
+do {						\
+	if (_FP_FRAC_LOW_##wc(X) & 7)		\
+	  FP_SET_EXCEPTION(FP_EX_INEXACT);	\
 	switch (FP_ROUNDMODE)			\
 	{					\
 	  case FP_RND_NEAREST:			\
-	    __ret |= _FP_ROUND_NEAREST(wc,X);	\
+	    _FP_ROUND_NEAREST(wc,X);		\
 	    break;				\
 	  case FP_RND_ZERO:			\
-	    __ret |= _FP_ROUND_ZERO(wc,X);	\
+	    _FP_ROUND_ZERO(wc,X);		\
 	    break;				\
 	  case FP_RND_PINF:			\
-	    __ret |= _FP_ROUND_PINF(wc,X);	\
+	    _FP_ROUND_PINF(wc,X);		\
 	    break;				\
 	  case FP_RND_MINF:			\
-	    __ret |= _FP_ROUND_MINF(wc,X);	\
+	    _FP_ROUND_MINF(wc,X);		\
 	    break;				\
-	};					\
-	__ret;					\
-})
+	}					\
+} while (0)
 
 #define FP_CLS_NORMAL		0
 #define FP_CLS_ZERO		1
@@ -75,6 +152,7 @@
 #include "op-1.h"
 #include "op-2.h"
 #include "op-4.h"
+#include "op-8.h"
 #include "op-common.h"
 
 /* Sigh.  Silly things longlong.h needs.  */
@@ -89,6 +167,10 @@
 typedef unsigned int UHWtype __attribute__((mode(HI)));
 #elif _FP_W_TYPE_SIZE == 64
 typedef USItype UHWtype;
+#endif
+
+#ifndef umul_ppmm
+#include <stdlib/longlong.h>
 #endif
 
 #endif
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/math-emu/udivmodti4.c linux/arch/sparc64/math-emu/udivmodti4.c
--- v2.3.3/linux/arch/sparc64/math-emu/udivmodti4.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/math-emu/udivmodti4.c	Wed Dec 31 16:00:00 1969
@@ -1,191 +0,0 @@
-/* This has so very few changes over libgcc2's __udivmoddi4 it isn't funny.  */
-
-#include "soft-fp.h"
-
-#undef count_leading_zeros
-#define count_leading_zeros  __FP_CLZ
-
-void
-_fp_udivmodti4(_FP_W_TYPE q[2], _FP_W_TYPE r[2],
-	       _FP_W_TYPE n1, _FP_W_TYPE n0,
-	       _FP_W_TYPE d1, _FP_W_TYPE d0)
-{
-  _FP_W_TYPE q0, q1, r0, r1;
-  _FP_I_TYPE b, bm;
-
-  if (d1 == 0)
-    {
-#if !UDIV_NEEDS_NORMALIZATION
-      if (d0 > n1)
-	{
-	  /* 0q = nn / 0D */
-
-	  udiv_qrnnd (q0, n0, n1, n0, d0);
-	  q1 = 0;
-
-	  /* Remainder in n0.  */
-	}
-      else
-	{
-	  /* qq = NN / 0d */
-
-	  if (d0 == 0)
-	    d0 = 1 / d0;	/* Divide intentionally by zero.  */
-
-	  udiv_qrnnd (q1, n1, 0, n1, d0);
-	  udiv_qrnnd (q0, n0, n1, n0, d0);
-
-	  /* Remainder in n0.  */
-	}
-
-      r0 = n0;
-      r1 = 0;
-
-#else /* UDIV_NEEDS_NORMALIZATION */
-
-      if (d0 > n1)
-	{
-	  /* 0q = nn / 0D */
-
-	  count_leading_zeros (bm, d0);
-
-	  if (bm != 0)
-	    {
-	      /* Normalize, i.e. make the most significant bit of the
-		 denominator set.  */
-
-	      d0 = d0 << bm;
-	      n1 = (n1 << bm) | (n0 >> (_FP_W_TYPE_SIZE - bm));
-	      n0 = n0 << bm;
-	    }
-
-	  udiv_qrnnd (q0, n0, n1, n0, d0);
-	  q1 = 0;
-
-	  /* Remainder in n0 >> bm.  */
-	}
-      else
-	{
-	  /* qq = NN / 0d */
-
-	  if (d0 == 0)
-	    d0 = 1 / d0;	/* Divide intentionally by zero.  */
-
-	  count_leading_zeros (bm, d0);
-
-	  if (bm == 0)
-	    {
-	      /* From (n1 >= d0) /\ (the most significant bit of d0 is set),
-		 conclude (the most significant bit of n1 is set) /\ (the
-		 leading quotient digit q1 = 1).
-
-		 This special case is necessary, not an optimization.
-		 (Shifts counts of SI_TYPE_SIZE are undefined.)  */
-
-	      n1 -= d0;
-	      q1 = 1;
-	    }
-	  else
-	    {
-	      _FP_W_TYPE n2;
-
-	      /* Normalize.  */
-
-	      b = _FP_W_TYPE_SIZE - bm;
-
-	      d0 = d0 << bm;
-	      n2 = n1 >> b;
-	      n1 = (n1 << bm) | (n0 >> b);
-	      n0 = n0 << bm;
-
-	      udiv_qrnnd (q1, n1, n2, n1, d0);
-	    }
-
-	  /* n1 != d0...  */
-
-	  udiv_qrnnd (q0, n0, n1, n0, d0);
-
-	  /* Remainder in n0 >> bm.  */
-	}
-
-      r0 = n0 >> bm;
-      r1 = 0;
-#endif /* UDIV_NEEDS_NORMALIZATION */
-    }
-  else
-    {
-      if (d1 > n1)
-	{
-	  /* 00 = nn / DD */
-
-	  q0 = 0;
-	  q1 = 0;
-
-	  /* Remainder in n1n0.  */
-	  r0 = n0;
-	  r1 = n1;
-	}
-      else
-	{
-	  /* 0q = NN / dd */
-
-	  count_leading_zeros (bm, d1);
-	  if (bm == 0)
-	    {
-	      /* From (n1 >= d1) /\ (the most significant bit of d1 is set),
-		 conclude (the most significant bit of n1 is set) /\ (the
-		 quotient digit q0 = 0 or 1).
-
-		 This special case is necessary, not an optimization.  */
-
-	      /* The condition on the next line takes advantage of that
-		 n1 >= d1 (true due to program flow).  */
-	      if (n1 > d1 || n0 >= d0)
-		{
-		  q0 = 1;
-		  sub_ddmmss (n1, n0, n1, n0, d1, d0);
-		}
-	      else
-		q0 = 0;
-
-	      q1 = 0;
-
-	      r0 = n0;
-	      r1 = n1;
-	    }
-	  else
-	    {
-	      _FP_W_TYPE m1, m0, n2;
-
-	      /* Normalize.  */
-
-	      b = _FP_W_TYPE_SIZE - bm;
-
-	      d1 = (d1 << bm) | (d0 >> b);
-	      d0 = d0 << bm;
-	      n2 = n1 >> b;
-	      n1 = (n1 << bm) | (n0 >> b);
-	      n0 = n0 << bm;
-
-	      udiv_qrnnd (q0, n1, n2, n1, d1);
-	      umul_ppmm (m1, m0, q0, d0);
-
-	      if (m1 > n1 || (m1 == n1 && m0 > n0))
-		{
-		  q0--;
-		  sub_ddmmss (m1, m0, m1, m0, d1, d0);
-		}
-
-	      q1 = 0;
-
-	      /* Remainder in (n1n0 - m1m0) >> bm.  */
-	      sub_ddmmss (n1, n0, n1, n0, m1, m0);
-	      r0 = (n1 << b) | (n0 >> bm);
-	      r1 = n1 >> bm;
-	    }
-	}
-    }
-
-  q[0] = q0; q[1] = q1;
-  r[0] = r0, r[1] = r1;
-}
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/mm/init.c linux/arch/sparc64/mm/init.c
--- v2.3.3/linux/arch/sparc64/mm/init.c	Tue May 11 08:24:32 1999
+++ linux/arch/sparc64/mm/init.c	Thu May 27 09:55:21 1999
@@ -1,4 +1,4 @@
-/*  $Id: init.c,v 1.127 1999/05/08 03:00:38 davem Exp $
+/*  $Id: init.c,v 1.128 1999/05/25 16:53:24 jj Exp $
  *  arch/sparc64/mm/init.c
  *
  *  Copyright (C) 1996-1999 David S. Miller (davem@caip.rutgers.edu)
@@ -21,6 +21,7 @@
 #include <asm/oplib.h>
 #include <asm/iommu.h>
 #include <asm/io.h>
+#include <asm/uaccess.h>
 #include <asm/mmu_context.h>
 #include <asm/vaddrs.h>
 #include <asm/dma.h>
@@ -729,6 +730,9 @@
 {
 	unsigned long pstate;
 	int i;
+
+	if (!enter)
+		set_fs(current->tss.current_ds);
 
 	if (!prom_ditlb_set)
 		return;
diff -u --recursive --new-file v2.3.3/linux/arch/sparc64/solaris/ioctl.c linux/arch/sparc64/solaris/ioctl.c
--- v2.3.3/linux/arch/sparc64/solaris/ioctl.c	Tue Apr 14 17:44:21 1998
+++ linux/arch/sparc64/solaris/ioctl.c	Wed May 26 18:14:37 1999
@@ -1,4 +1,4 @@
-/* $Id: ioctl.c,v 1.10 1998/03/29 10:11:00 davem Exp $
+/* $Id: ioctl.c,v 1.11 1999/05/27 00:36:25 davem Exp $
  * ioctl.c: Solaris ioctl emulation.
  *
  * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -677,7 +677,10 @@
 			struct device *d;
 			int i = 0;
 			
+			read_lock_bh(&dev_base_lock);
 			for (d = dev_base; d; d = d->next) i++;
+			read_unlock_bh(&dev_base_lock);
+
 			if (put_user (i, (int *)A(arg)))
 				return -EFAULT;
 			return 0;
diff -u --recursive --new-file v2.3.3/linux/drivers/block/Makefile linux/drivers/block/Makefile
--- v2.3.3/linux/drivers/block/Makefile	Mon May 17 09:55:21 1999
+++ linux/drivers/block/Makefile	Wed May 26 09:30:31 1999
@@ -20,10 +20,10 @@
 
 
 L_TARGET := block.a
-L_OBJS   := genhd.o blkpg.o
+L_OBJS   := genhd.o
 M_OBJS   :=
 MOD_LIST_NAME := BLOCK_MODULES
-LX_OBJS := ll_rw_blk.o
+LX_OBJS := ll_rw_blk.o blkpg.o
 MX_OBJS :=
 
 ifeq ($(CONFIG_MAC_FLOPPY),y)
diff -u --recursive --new-file v2.3.3/linux/drivers/block/aec6210.c linux/drivers/block/aec6210.c
--- v2.3.3/linux/drivers/block/aec6210.c	Fri May 14 18:55:13 1999
+++ linux/drivers/block/aec6210.c	Wed May 26 16:55:40 1999
@@ -34,7 +34,6 @@
 
  */
 
-#include <linux/config.h>	/* for CONFIG_BLK_DEV_IDEPCI */
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/delay.h>
diff -u --recursive --new-file v2.3.3/linux/drivers/block/alim15x3.c linux/drivers/block/alim15x3.c
--- v2.3.3/linux/drivers/block/alim15x3.c	Fri May 14 18:55:13 1999
+++ linux/drivers/block/alim15x3.c	Wed May 26 16:55:40 1999
@@ -12,6 +12,7 @@
  *
  */
 
+#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/pci.h>
diff -u --recursive --new-file v2.3.3/linux/drivers/block/blkpg.c linux/drivers/block/blkpg.c
--- v2.3.3/linux/drivers/block/blkpg.c	Mon May 17 09:55:21 1999
+++ linux/drivers/block/blkpg.c	Wed May 26 09:30:31 1999
@@ -35,6 +35,7 @@
 #include <linux/blkpg.h>
 #include <linux/genhd.h>
 #include <linux/swap.h>			/* for is_swap_partition() */
+#include <linux/module.h>               /* for EXPORT_SYMBOL */
 
 #include <asm/uaccess.h>
 
@@ -286,3 +287,4 @@
 	}
 }
 
+EXPORT_SYMBOL(blk_ioctl);
diff -u --recursive --new-file v2.3.3/linux/drivers/block/cmd646.c linux/drivers/block/cmd646.c
--- v2.3.3/linux/drivers/block/cmd646.c	Mon May 17 09:55:21 1999
+++ linux/drivers/block/cmd646.c	Thu May 27 09:55:21 1999
@@ -1,4 +1,4 @@
-/* $Id: cmd646.c,v 1.12 1999/05/14 07:21:01 davem Exp $
+/* $Id: cmd646.c,v 1.13 1999/05/27 04:49:38 davem Exp $
  * cmd646.c: Enable interrupts at initialization time on Ultra/PCI machines.
  *           Note, this driver is not used at all on other systems because
  *           there the "BIOS" has done all of the following already.
@@ -275,9 +275,11 @@
 	(void) pci_write_config_byte(dev, 0x58, 0x3f);
 	(void) pci_write_config_byte(dev, 0x5b, 0x3f);
 
-	if (class_rev == 0x01) {
-		hwif->dmaproc = &cmd646_1_dmaproc;
-	} else {
-		hwif->dmaproc = &cmd646_dmaproc;
+	if (hwif->dma_base) {
+		if (class_rev == 0x01) {
+			hwif->dmaproc = &cmd646_1_dmaproc;
+		} else {
+			hwif->dmaproc = &cmd646_dmaproc;
+		}
 	}
 }
diff -u --recursive --new-file v2.3.3/linux/drivers/block/cy82c693.c linux/drivers/block/cy82c693.c
--- v2.3.3/linux/drivers/block/cy82c693.c	Fri May 14 18:55:13 1999
+++ linux/drivers/block/cy82c693.c	Fri May 28 09:34:41 1999
@@ -19,7 +19,7 @@
  *   driver is working fine :-)
  *   hdparm -t reports 8.17 MB/sec at about 6% CPU usage for the DTTA 
  * - this is my first linux driver, so there's probably a lot  of room 
- *   for optimizations and bug fixing, so fell free to do it.
+ *   for optimizations and bug fixing, so feel free to do it.
  * - use idebus=xx parameter to set PCI bus speed - needed to calc
  *   timings for PIO modes (default will be 40)
  * - if using PIO mode it's a good idea to set the PIO mode and 
diff -u --recursive --new-file v2.3.3/linux/drivers/block/hpt343.c linux/drivers/block/hpt343.c
--- v2.3.3/linux/drivers/block/hpt343.c	Sat May 15 23:46:03 1999
+++ linux/drivers/block/hpt343.c	Wed May 26 16:55:40 1999
@@ -20,7 +20,6 @@
  *	= ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
  */
 
-#include <linux/config.h>	/* for CONFIG_BLK_DEV_IDEPCI */
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/delay.h>
diff -u --recursive --new-file v2.3.3/linux/drivers/block/ide-cd.c linux/drivers/block/ide-cd.c
--- v2.3.3/linux/drivers/block/ide-cd.c	Fri May 14 18:55:13 1999
+++ linux/drivers/block/ide-cd.c	Fri May 28 09:34:41 1999
@@ -249,6 +249,7 @@
 
 #define IDECD_VERSION "4.53"
 
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
@@ -3014,6 +3015,7 @@
 				break;
 		}
 	}
+	(void) ide_cdrom_select_disc(devinfo, 0);
 }
 #endif /* CONFIG_IDECD_SLOTS */
 
diff -u --recursive --new-file v2.3.3/linux/drivers/block/ide-disk.c linux/drivers/block/ide-disk.c
--- v2.3.3/linux/drivers/block/ide-disk.c	Fri May 14 18:55:13 1999
+++ linux/drivers/block/ide-disk.c	Fri May 28 09:34:41 1999
@@ -55,6 +55,12 @@
 #include <asm/uaccess.h>
 #include <asm/io.h>
 
+#ifdef CONFIG_BLK_DEV_PDC4030
+#define IS_PDC4030_DRIVE (HWIF(drive)->chipset == ide_pdc4030)
+#else
+#define IS_PDC4030_DRIVE (0)	/* auto-NULLs out pdc4030 code */
+#endif
+
 static void idedisk_bswap_data (void *buffer, int wcount)
 {
 	u16 *p = buffer;
@@ -336,27 +342,11 @@
  */
 static void do_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block)
 {
-#ifdef CONFIG_BLK_DEV_PDC4030
-	ide_hwif_t *hwif = HWIF(drive);
-	int use_pdc4030_io = 0;
-#endif /* CONFIG_BLK_DEV_PDC4030 */
-
 	if (IDE_CONTROL_REG)
 		OUT_BYTE(drive->ctl,IDE_CONTROL_REG);
 	OUT_BYTE(rq->nr_sectors,IDE_NSECTOR_REG);
 #ifdef CONFIG_BLK_DEV_PDC4030
-#ifdef CONFIG_BLK_DEV_PDC4030_TESTING
-	if (IS_PDC4030_DRIVE) {
-		use_pdc4030_io = 1;
-	}
-#else
-	if (IS_PDC4030_DRIVE) {
-		if (hwif->channel != 0 || rq->cmd == READ) {
-			use_pdc4030_io = 1;
-		}
-	}
-#endif /* CONFIG_BLK_DEV_PDC4030_TESTING */
-	if (drive->select.b.lba || use_pdc4030_io) {
+	if (drive->select.b.lba || IS_PDC4030_DRIVE) {
 #else /* !CONFIG_BLK_DEV_PDC4030 */
 	if (drive->select.b.lba) {
 #endif /* CONFIG_BLK_DEV_PDC4030 */
@@ -386,7 +376,7 @@
 #endif
 	}
 #ifdef CONFIG_BLK_DEV_PDC4030
-	if (use_pdc4030_io) {
+	if (IS_PDC4030_DRIVE) {
 		extern void do_pdc4030_io(ide_drive_t *, struct request *);
 		do_pdc4030_io (drive, rq);
 		return;
diff -u --recursive --new-file v2.3.3/linux/drivers/block/ide-probe.c linux/drivers/block/ide-probe.c
--- v2.3.3/linux/drivers/block/ide-probe.c	Fri May 14 18:55:14 1999
+++ linux/drivers/block/ide-probe.c	Fri May 28 09:34:41 1999
@@ -189,9 +189,10 @@
 	}
 
 #if CONFIG_BLK_DEV_PDC4030
-	if (IS_PDC4030_DRIVE) {
-		extern int pdc4030_cmd(ide_drive_t *, byte);
-		if (pdc4030_cmd(drive,PROMISE_IDENTIFY)) {
+	if (HWIF(drive)->chipset == ide_pdc4030) {
+		/* DC4030 hosted drives need their own identify... */
+		extern int pdc4030_identify(ide_drive_t *);
+		if (pdc4030_identify(drive)) {
 			if (irqs)
 				(void) probe_irq_off(irqs);
 			return 1;
diff -u --recursive --new-file v2.3.3/linux/drivers/block/ide.c linux/drivers/block/ide.c
--- v2.3.3/linux/drivers/block/ide.c	Mon May 17 09:55:21 1999
+++ linux/drivers/block/ide.c	Fri May 28 09:34:41 1999
@@ -230,7 +230,6 @@
 		drive->media			= ide_disk;
 		drive->select.all		= (unit<<4)|0xa0;
 		drive->hwif			= hwif;
-		init_waitqueue_head(&drive->wqueue);
 		drive->ctl			= 0x08;
 		drive->ready_stat		= READY_STAT;
 		drive->bad_wstat		= BAD_W_STAT;
@@ -296,8 +295,9 @@
 
 	if (drive->removable && id != NULL) {
 		if (!strncmp(id->model, "KODAK ATA_FLASH", 15)	/* Kodak */
-		 || !strncmp(id->model, "Hitachi CV", 10)		/* Hitachi */
-		 || !strncmp(id->model, "SunDisk SDCFB", 13))	/* SunDisk */
+		 || !strncmp(id->model, "Hitachi CV", 10)	/* Hitachi */
+		 || !strncmp(id->model, "SunDisk SDCFB", 13)	/* SunDisk */
+		 || !strncmp(id->model, "HAGIWARA HPC", 12))	/* Hagiwara */
 		{
 			return 1;	/* yes, it is a flash memory card */
 		}
@@ -956,6 +956,7 @@
 int ide_wait_stat (ide_drive_t *drive, byte good, byte bad, unsigned long timeout)
 {
 	byte stat;
+	int i;
 	unsigned long flags;
 
 	udelay(1);	/* spec allows drive 400ns to assert "BUSY" */
@@ -972,9 +973,18 @@
 		}
 		__restore_flags(flags);	/* local CPU only */
 	}
-	udelay(1);	/* allow status to settle, then read it again */
-	if (OK_STAT((stat = GET_STAT()), good, bad))
-		return 0;
+	/*
+	 * Allow status to settle, then read it again.
+	 * A few rare drives vastly violate the 400ns spec here,
+	 * so we'll wait up to 10usec for a "good" status
+	 * rather than expensively fail things immediately.
+	 * This fix courtesy of Matthew Faupel & Niccolo Rigacci.
+	 */
+	for (i = 0; i < 10; i++) {
+		udelay(1);
+		if (OK_STAT((stat = GET_STAT()), good, bad))
+			return 0;
+	}
 	ide_error(drive, "status error", stat);
 	return 1;
 }
@@ -1540,8 +1550,10 @@
 	struct request *cur_rq;
 	DECLARE_MUTEX_LOCKED(sem);
 
-	if (IS_PDC4030_DRIVE && rq->buffer != NULL)
+#ifdef CONFIG_BLK_DEV_PDC4030
+	if (HWIF(drive)->chipset == ide_pdc4030 && rq->buffer != NULL)
 		return -ENOSYS;  /* special drive cmds not supported */
+#endif
 	rq->errors = 0;
 	rq->rq_status = RQ_ACTIVE;
 	rq->rq_dev = MKDEV(major,(drive->select.b.unit)<<PARTN_BITS);
@@ -1770,6 +1782,8 @@
 	sti();
 	for (unit = 0; unit < MAX_DRIVES; ++unit) {
 		drive = &hwif->drives[unit];
+		if (!drive->present)
+			continue;
 		minor = drive->select.b.unit << PARTN_BITS;
 		for (p = 0; p < (1<<PARTN_BITS); ++p) {
 			if (drive->part[p].nr_sects > 0) {
@@ -2777,8 +2791,8 @@
 #ifdef CONFIG_BLK_DEV_PDC4030
 			case -14: /* "dc4030" */
 			{
-				extern void setup_pdc4030(ide_hwif_t *);
-				setup_pdc4030(hwif);
+				extern void init_pdc4030(void);
+				init_pdc4030();
 				goto done;
 			}
 #endif /* CONFIG_BLK_DEV_PDC4030 */
@@ -3031,8 +3045,8 @@
 #endif /* CONFIG_BLK_DEV_CMD640 */
 #ifdef CONFIG_BLK_DEV_PDC4030
 	{
-		extern int init_pdc4030(void);
-		(void) init_pdc4030();
+		extern int ide_probe_for_pdc4030(void);
+		(void) ide_probe_for_pdc4030();
 	}
 #endif /* CONFIG_BLK_DEV_PDC4030 */
 #ifdef CONFIG_BLK_DEV_IDE_PMAC
diff -u --recursive --new-file v2.3.3/linux/drivers/block/loop.c linux/drivers/block/loop.c
--- v2.3.3/linux/drivers/block/loop.c	Thu Jan 14 10:33:36 1999
+++ linux/drivers/block/loop.c	Sun May 30 10:17:43 1999
@@ -504,6 +504,8 @@
 	if ((unsigned int) info.lo_encrypt_key_size > LO_KEY_SIZE)
 		return -EINVAL;
 	type = info.lo_encrypt_type; 
+	if (info.lo_encrypt_key_size == 0 && type == LO_CRYPT_XOR)
+		return -EINVAL;
 	if (type >= MAX_LO_CRYPT || xfer_funcs[type] == NULL)
 		return -EINVAL;
 	err = loop_release_xfer(lo);
diff -u --recursive --new-file v2.3.3/linux/drivers/block/ns87415.c linux/drivers/block/ns87415.c
--- v2.3.3/linux/drivers/block/ns87415.c	Fri May 14 18:55:14 1999
+++ linux/drivers/block/ns87415.c	Sat May 29 11:09:04 1999
@@ -49,8 +49,25 @@
 	new = use_dma ? ((new & ~other) | bit) : (new & ~bit);
 
 	if (new != *old) {
+		unsigned char stat;
+
+		/*
+		 * Don't change DMA engine settings while Write Buffers
+		 * are busy.
+		 */
+		(void) pci_read_config_byte(dev, 0x43, &stat);
+		while (stat & 0x03) {
+			udelay(1);
+			(void) pci_read_config_byte(dev, 0x43, &stat);
+		}
+
 		*old = new;
 		(void) pci_write_config_dword(dev, 0x40, new);
+
+		/*
+		 * And let things settle...
+		 */
+		udelay(10);
 	}
 
 	__restore_flags(flags);	/* local CPU only */
diff -u --recursive --new-file v2.3.3/linux/drivers/block/paride/paride.c linux/drivers/block/paride/paride.c
--- v2.3.3/linux/drivers/block/paride/paride.c	Fri May 14 18:55:14 1999
+++ linux/drivers/block/paride/paride.c	Wed May 26 09:30:31 1999
@@ -247,7 +247,7 @@
 	pi->pardev = (void *) parport_register_device(
 	      pp,pi->device,NULL,pi_wake_up,NULL,0,(void *)pi);
 
-	init_wait_queue_head(&pi->parq);
+	init_waitqueue_head(&pi->parq);
 
 	if (verbose) printk("%s: 0x%x is %s\n",pi->device,pi->port,pp->name);
 	
@@ -357,7 +357,7 @@
 
 		pi->parname = NULL;
 		pi->pardev = NULL;
-		init_wait_queue_head(&pi->parq);
+		init_waitqueue_head(&pi->parq);
 		pi->claimed = 0;
 		pi->claim_cont = NULL;
 	        
diff -u --recursive --new-file v2.3.3/linux/drivers/block/pdc202xx.c linux/drivers/block/pdc202xx.c
--- v2.3.3/linux/drivers/block/pdc202xx.c	Fri May 14 18:55:14 1999
+++ linux/drivers/block/pdc202xx.c	Wed May 26 16:55:40 1999
@@ -72,7 +72,6 @@
  *      = ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
  */
 
-#include <linux/config.h>	/* for CONFIG_BLK_DEV_IDEPCI */
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/delay.h>
diff -u --recursive --new-file v2.3.3/linux/drivers/block/pdc4030.c linux/drivers/block/pdc4030.c
--- v2.3.3/linux/drivers/block/pdc4030.c	Fri May 14 18:55:14 1999
+++ linux/drivers/block/pdc4030.c	Fri May 28 09:34:41 1999
@@ -1,5 +1,5 @@
 /*  -*- linux-c -*-
- *  linux/drivers/block/pdc4030.c	Version 0.10  Jan 25, 1999
+ *  linux/drivers/block/pdc4030.c	Version 0.11  May 17, 1999
  *
  *  Copyright (C) 1995-1999  Linus Torvalds & authors (see below)
  */
@@ -33,27 +33,43 @@
  *  Version 0.09	Obsolete - never released - did manual write request
  *			splitting before max_sectors[major][minor] available.
  *  Version 0.10	Updated for 2.1 series of kernels
+ *  Version 0.11	Updated for 2.3 series of kernels
+ *			Autodetection code added.
  */
 
 /*
  * Once you've compiled it in, you'll have to also enable the interface
  * setup routine from the kernel command line, as in 
  *
- *	'linux ide0=dc4030'
+ *	'linux ide0=dc4030' or 'linux ide1=dc4030'
  *
  * It should now work as a second controller also ('ide1=dc4030') but only
- * if you DON'T have BIOS V4.44, which has a bug. If you have this and EPROM
- * programming facilities, I can tell you what to fix...
+ * if you DON'T have BIOS V4.44, which has a bug. If you have this version
+ * and EPROM programming facilities, you need to fix 4 bytes:
+ * 	2496:	81	81
+ *	2497:	3E	3E
+ *	2498:	22	98	*
+ *	2499:	06	05	*
+ *	249A:	F0	F0
+ *	249B:	01	01
+ *	...
+ *	24A7:	81	81
+ *	24A8:	3E	3E
+ *	24A9:	22	98	*
+ *	24AA:	06	05	*
+ *	24AB:	70	70
+ *	24AC:	01	01
  *
  * As of January 1999, Promise Technology Inc. have finally supplied me with
  * some technical information which has shed a glimmer of light on some of the
  * problems I was having, especially with writes. 
+ *
+ * There are still problems with the robustness and efficiency of this driver
+ * because I still don't understand what the card is doing with interrupts.
  */
 
-#define DEBUG_READ
-#define DEBUG_WRITE
-
-#undef REALLY_SLOW_IO		/* most systems can safely undef this */
+#undef DEBUG_READ
+#undef DEBUG_WRITE
 
 #include <linux/types.h>
 #include <linux/kernel.h>
@@ -70,11 +86,6 @@
 
 #include "pdc4030.h"
 
-/* This is needed as the controller may not interrupt if the required data is
-available in the cache. We have to simulate an interrupt. Ugh! */
-
-extern void ide_intr(int, void *dev_id, struct pt_regs*);
-
 /*
  * promise_selectproc() is invoked by ide.c
  * in preparation for access to the specified drive.
@@ -119,21 +130,27 @@
 		return 1; /* device returned failure */
 }
 
-ide_hwif_t *hwif_required = NULL;
+/*
+ * pdc4030_identify sends a vendor-specific IDENTIFY command to the drive
+ */
+int pdc4030_identify(ide_drive_t *drive)
+{
+	return pdc4030_cmd(drive, PROMISE_IDENTIFY);
+}
+
+int enable_promise_support = 0;
 
-void setup_pdc4030 (ide_hwif_t *hwif)
+void __init init_pdc4030 (void)
 {
-    hwif_required = hwif;
+	enable_promise_support = 1;
 }
 
 /*
-init_pdc4030: Test for presence of a Promise caching controller card.
-Returns: 0 if no Promise card present at this io_base
-	 1 if Promise card found
-*/
-int init_pdc4030 (void)
+ * setup_pdc4030()
+ * Completes the setup of a Promise DC4030 controller card, once found.
+ */
+int __init setup_pdc4030 (ide_hwif_t *hwif)
 {
-	ide_hwif_t *hwif = hwif_required;
         ide_drive_t *drive;
 	ide_hwif_t *hwif2;
 	struct dc_ident ident;
@@ -186,11 +203,25 @@
             default:   hwif->irq = 15; break;
 	}
 	printk("on IRQ %d\n",hwif->irq);
+
+	/*
+	 * Once found and identified, we set up the next hwif in the array
+	 * (hwif2 = ide_hwifs[hwif->index+1]) with the same io ports, irq
+	 * and other settings as the main hwif. This gives us two "mated"
+	 * hwifs pointing to the Promise card.
+	 *
+	 * We also have to shift the default values for the remaining
+	 * interfaces "up by one" to make room for the second interface on the
+	 * same set of values.
+	 */
+
 	hwif->chipset	= hwif2->chipset = ide_pdc4030;
 	hwif->mate	= hwif2;
 	hwif2->mate	= hwif;
 	hwif2->channel	= 1;
 	hwif->selectproc = hwif2->selectproc = &promise_selectproc;
+	hwif->serialized = hwif2->serialized = 1;
+
 /* Shift the remaining interfaces down by one */
 	for (i=MAX_HWIFS-1 ; i > hwif->index+1 ; i--) {
 		ide_hwif_t *h = &ide_hwifs[i];
@@ -220,6 +251,50 @@
 }
 
 /*
+ * detect_pdc4030()
+ * Tests for the presence of a DC4030 Promise card on this interface
+ * Returns: 1 if found, 0 if not found
+ */
+int __init detect_pdc4030(ide_hwif_t *hwif)
+{
+	ide_drive_t *drive = &hwif->drives[0];
+
+	if (IDE_DATA_REG == 0) { /* Skip test for non-existent interface */
+		return 0;
+	}
+	OUT_BYTE(0xF3, IDE_SECTOR_REG);
+	OUT_BYTE(0x14, IDE_SELECT_REG);
+	OUT_BYTE(PROMISE_EXTENDED_COMMAND, IDE_COMMAND_REG);
+	
+	ide_delay_50ms();
+
+	if (IN_BYTE(IDE_ERROR_REG) == 'P' &&
+	    IN_BYTE(IDE_NSECTOR_REG) == 'T' &&
+	    IN_BYTE(IDE_SECTOR_REG) == 'I') {
+		return 1;
+	} else {
+		return 0;
+	}
+}
+
+void __init ide_probe_for_pdc4030(void)
+{
+	unsigned int	index;
+	ide_hwif_t	*hwif;
+
+	if (enable_promise_support == 0)
+		return;
+	for (index = 0; index < MAX_HWIFS; index++) {
+		hwif = &ide_hwifs[index];
+		if (hwif->chipset == ide_unknown && detect_pdc4030(hwif)) {
+			setup_pdc4030(hwif);
+		}
+	}
+}
+
+
+
+/*
  * promise_read_intr() is the handler for disk read/multread interrupts
  */
 static void promise_read_intr (ide_drive_t *drive)
@@ -297,6 +372,21 @@
 }
 
 /*
+ * promise_finish_write()
+ * called at the end of all writes
+ */
+static void promise_finish_write(ide_drive_t *drive)
+{
+	struct request *rq = HWGROUP(drive)->rq;
+	int i;
+
+	for (i = rq->nr_sectors; i > 0; ) {
+		i -= rq->current_nr_sectors;
+		ide_end_request(1, HWGROUP(drive));
+	}
+}
+
+/*
  * promise_write_intr()
  * This interrupt is called after the particularly odd polling for completion
  * of the write request, once all the data has been sent.
@@ -304,8 +394,6 @@
 static void promise_write_intr(ide_drive_t *drive)
 {
 	byte stat;
-	int i;
-	struct request *rq;
 
 	if (!OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->bad_wstat)) {
 		ide_error(drive, "promise_write_intr", stat);
@@ -314,11 +402,7 @@
 #ifdef DEBUG_WRITE
 	printk(KERN_DEBUG "%s: Write complete - end_request\n", drive->name);
 #endif
-	rq = HWGROUP(drive)->rq;
-	for (i = rq->nr_sectors; i > 0;) {
-		i -= rq->current_nr_sectors;
-		ide_end_request(1, HWGROUP(drive));
-	}
+	promise_finish_write(drive);
 }
 
 /*
@@ -336,10 +420,11 @@
 		return;
 	}
 
+	ide_multwrite(drive, 4);
 #ifdef DEBUG_WRITE
-	printk(KERN_DEBUG "%s: Doing last 4 sectors\n", drive->name);
+	printk(KERN_DEBUG "%s: Done last 4 sectors - status = %02x\n",
+		drive->name, GET_STAT());
 #endif
-	ide_multwrite(drive, 4);
 	ide_set_handler(drive, &promise_write_intr, WAIT_CMD);
 	return;
 }
@@ -358,8 +443,8 @@
 
 #ifdef DEBUG_WRITE
 	printk(KERN_DEBUG "%s: promise_write: sectors(%ld-%ld), "
-	       "buffer=0x%08lx\n", drive->name, rq->sector,
-	       rq->sector + rq->nr_sectors - 1, rq->buffer);
+	       "buffer=0x%08x\n", drive->name, rq->sector,
+	       rq->sector + rq->nr_sectors - 1, (unsigned int)rq->buffer);
 #endif
 	if (rq->nr_sectors > 4) {
 		ide_multwrite(drive, rq->nr_sectors - 4);
@@ -368,7 +453,11 @@
 		return;
 	} else {
 		ide_multwrite(drive, rq->nr_sectors);
-		ide_set_handler(drive, &promise_write_intr, WAIT_CMD);
+#ifdef DEBUG_WRITE
+		printk(KERN_DEBUG "%s: promise_write: <= 4 sectors, "
+			"status = %02x\n", drive->name, GET_STAT());
+#endif
+		promise_finish_write(drive);
 	}
 }
 
diff -u --recursive --new-file v2.3.3/linux/drivers/block/via82c586.c linux/drivers/block/via82c586.c
--- v2.3.3/linux/drivers/block/via82c586.c	Fri May 14 18:55:15 1999
+++ linux/drivers/block/via82c586.c	Wed May 26 16:55:40 1999
@@ -41,6 +41,7 @@
  *  and its threshold is set to 3/4.
  */
 
+#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/timer.h>
diff -u --recursive --new-file v2.3.3/linux/drivers/cdrom/sonycd535.c linux/drivers/cdrom/sonycd535.c
--- v2.3.3/linux/drivers/cdrom/sonycd535.c	Fri May 14 18:55:15 1999
+++ linux/drivers/cdrom/sonycd535.c	Wed May 26 09:31:44 1999
@@ -318,7 +318,7 @@
 cdu535_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
 	disable_interrupts();
-	if (wait_queue_active(&cdu535_irq_wait))
+	if (waitqueue_active(&cdu535_irq_wait))
 		wake_up(&cdu535_irq_wait);
 	else
 		printk(CDU535_MESSAGE_NAME
diff -u --recursive --new-file v2.3.3/linux/drivers/char/Config.in linux/drivers/char/Config.in
--- v2.3.3/linux/drivers/char/Config.in	Fri May  7 11:05:30 1999
+++ linux/drivers/char/Config.in	Sat May 22 15:02:48 1999
@@ -159,6 +159,7 @@
   if [ "$CONFIG_RADIO_ZOLTRIX" = "y" ]; then
     hex '  ZOLTRIX I/O port (0x20c or 0x30c)' CONFIG_RADIO_ZOLTRIX_PORT 20c
   fi
+  dep_tristate 'IIC on parallel port' CONFIG_I2C_PARPORT $CONFIG_PARPORT
 fi
 
 endmenu
diff -u --recursive --new-file v2.3.3/linux/drivers/char/Makefile linux/drivers/char/Makefile
--- v2.3.3/linux/drivers/char/Makefile	Fri May  7 11:05:30 1999
+++ linux/drivers/char/Makefile	Sat May 22 15:02:48 1999
@@ -330,6 +330,16 @@
   endif
 endif
 
+ifeq ($(CONFIG_I2C_PARPORT),y)
+L_OBJS += i2c-parport.o
+L_I2C = y
+else
+  ifeq ($(CONFIG_I2C_PARPORT),m)
+    M_OBJS += i2c-parport.o
+    M_I2C = y
+  endif
+endif
+
 ifeq ($(CONFIG_VIDEO_BWQCAM),y)
 L_OBJS += bw-qcam.o
 else
diff -u --recursive --new-file v2.3.3/linux/drivers/char/bttv.c linux/drivers/char/bttv.c
--- v2.3.3/linux/drivers/char/bttv.c	Fri May 14 18:55:15 1999
+++ linux/drivers/char/bttv.c	Sun May 30 10:18:03 1999
@@ -543,6 +543,8 @@
         { 3, 1, 0, 2,15, { 2, 3, 1, 1}, { 0, 0, 0, 0, 0}},
         /* Pixelview PlayTV (bt878) */
         { 3, 4, 0, 2, 0x01e000, { 2, 0, 1, 1}, {0x01c000, 0, 0x018000, 0x014000, 0x002000, 0 }},
+        /* "Leadtek WinView 601", */
+        { 3, 1, 0, 2, 0x8300f8, { 2, 3, 1, 1,0}, {0x4fa007,0xcfa007,0xcfa007,0xcfa007,0xcfa007,0xcfa007}},
 };
 #define TVCARDS (sizeof(tvcards)/sizeof(tvcard))
 
@@ -2036,6 +2038,41 @@
 				I2CWrite(&(btv->i2c), I2C_TDA9850,
 					TDA9850_CON3, con3, 1);
 			}
+		   
+		       /* PT2254A programming Jon Tombs, jon@gte.esi.us.es */
+		        if (btv->type == BTTV_WINVIEW_601) { 
+			   int bits_out, loops, vol, data;
+
+			   /* 32 levels logarithmic */
+			   vol = 32 - ((v.volume>>11));
+			   /* units */
+                           bits_out = (PT2254_DBS_IN_2>>(vol%5));
+			   /* tens */
+                           bits_out |= (PT2254_DBS_IN_10>>(vol/5));
+			   bits_out |= PT2254_L_CHANEL | PT2254_R_CHANEL;
+			   data = btread(BT848_GPIO_DATA);
+			   data &= ~(WINVIEW_PT2254_CLK| WINVIEW_PT2254_DATA|
+				      WINVIEW_PT2254_STROBE);
+			   for (loops = 17; loops >= 0 ; loops--) {
+				if (bits_out & (1<<loops))
+				   data |=  WINVIEW_PT2254_DATA;
+				else
+				   data &= ~WINVIEW_PT2254_DATA;
+			       btwrite(data, BT848_GPIO_DATA);
+			       udelay(5);
+			       data |= WINVIEW_PT2254_CLK;
+			       btwrite(data, BT848_GPIO_DATA);
+			       udelay(5);
+			       data &= ~WINVIEW_PT2254_CLK;
+			       btwrite(data, BT848_GPIO_DATA);
+			   }
+			   data |=  WINVIEW_PT2254_STROBE;
+			   data &= ~WINVIEW_PT2254_DATA;
+			   btwrite(data, BT848_GPIO_DATA);
+			   udelay(10);			   
+			   data &= ~WINVIEW_PT2254_STROBE;
+			   btwrite(data, BT848_GPIO_DATA);
+			}
 			if (btv->have_msp3400) 
 			{
                                 i2c_control_device(&(btv->i2c),
@@ -3033,6 +3070,9 @@
 		case BTTV_VHX:
 			strcpy(btv->video_dev.name,"BT848(Aimslab-VHX)");
  			break;
+	        case BTTV_WINVIEW_601:
+			strcpy(btv->video_dev.name,"BT848(Leadtek WinView 601)");
+ 			break;	   
 	}
 	printk("%s\n",btv->video_dev.name);
 	audio(btv, AUDIO_MUTE);
diff -u --recursive --new-file v2.3.3/linux/drivers/char/bttv.h linux/drivers/char/bttv.h
--- v2.3.3/linux/drivers/char/bttv.h	Fri May 14 18:55:15 1999
+++ linux/drivers/char/bttv.h	Sun May 30 10:18:03 1999
@@ -210,6 +210,7 @@
 #define BTTV_VHX           0x0e
 #define BTTV_ZOLTRIX       0x0f
 #define BTTV_PIXVIEWPLAYTV 0x10
+#define BTTV_WINVIEW_601   0x11
 
 #define AUDIO_TUNER        0x00
 #define AUDIO_RADIO        0x01
@@ -259,5 +260,13 @@
 #define TEA6300_TR         0x03		/* treble control */
 #define TEA6300_FA         0x04		/* fader control */
 #define TEA6300_SW         0x05		/* mute and source switch */
+
+#define PT2254_L_CHANEL 0x10
+#define PT2254_R_CHANEL 0x08
+#define PT2254_DBS_IN_2 0x400
+#define PT2254_DBS_IN_10 0x20000
+#define WINVIEW_PT2254_CLK  0x40
+#define WINVIEW_PT2254_DATA 0x20
+#define WINVIEW_PT2254_STROBE 0x80
 
 #endif
diff -u --recursive --new-file v2.3.3/linux/drivers/char/cyclades.c linux/drivers/char/cyclades.c
--- v2.3.3/linux/drivers/char/cyclades.c	Mon May 17 09:55:21 1999
+++ linux/drivers/char/cyclades.c	Mon May 24 22:38:07 1999
@@ -1,7 +1,7 @@
 #define BLOCKMOVE
 #define	Z_WAKE
 static char rcsid[] =
-"$Revision: 2.2.2.1 $$Date: 1999/04/08 16:17:43 $";
+"$Revision: 2.2.2.2 $$Date: 1999/05/21 17:18:15 $";
 
 /*
  *  linux/drivers/char/cyclades.c
@@ -21,7 +21,7 @@
  * extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92,
  * and then fixed as suggested by Michael K. Johnson 12/12/92.
  *
- * This version does not support shared irq's.
+ * This version supports shared IRQ's (only for PCI boards).
  *
  * This module exports the following rs232 io functions:
  *   int cy_init(void);
@@ -31,6 +31,17 @@
  *   void cleanup_module(void);
  *
  * $Log: cyclades.c,v $
+ * Revision 2.2.2.2  1999/05/14 17:18:15 ivan
+ * /proc entry location changed to /proc/tty/driver/cyclades;
+ * Added support to shared IRQ's (only for PCI boards);
+ * Added support for Cobalt Qube2 systems;
+ * IRQ [de]allocation scheme revisited;
+ * BREAK implementation changed in order to make use of the 'break_ctl'
+ * TTY facility;
+ * Fixed typo in TTY structure field 'driver_name';
+ * Included a PCI bridge reset and EEPROM reload in the board 
+ * initialization code (for both Y and Z series).
+ *
  * Revision 2.2.2.1  1999/04/08 16:17:43 ivan
  * Fixed a bug in cy_wait_until_sent that was preventing the port to be 
  * closed properly after a SIGINT;
@@ -536,7 +547,7 @@
 #undef	CY_16Y_HACK
 #undef	CY_ENABLE_MONITORING
 #undef	CY_PCI_DEBUG
-#define	CY_PROC
+#undef	CY_PROC
 
 #if 0
 #define PAUSE __asm__("nop");
@@ -600,6 +611,14 @@
 #include <linux/stat.h>
 #include <linux/proc_fs.h>
 
+#ifdef CONFIG_COBALT_27
+#include <asm/page.h>
+#include <asm/pgtable.h>
+
+#define	CACHED_TO_UNCACHED(x)	(((unsigned long)(x) & \
+				  (unsigned long)0x1fffffff) + KSEG1)
+#endif
+
 #define cy_put_user	put_user
 
 static unsigned long cy_get_user(unsigned long *addr)
@@ -638,6 +657,7 @@
 static struct tty_driver cy_serial_driver, cy_callout_driver;
 static int serial_refcount;
 
+#ifndef CONFIG_COBALT_27
 static volatile int cy_irq_triggered;
 static volatile int cy_triggered;
 static int cy_wild_int_mask;
@@ -665,6 +685,8 @@
 };
 #define NR_ISA_ADDRS (sizeof(cy_isa_addresses)/sizeof(unsigned char*))
 
+#endif /* CONFIG_COBALT_27 */
+
 /* This is the per-card data structure containing address, irq, number of
    channels, etc. This driver supports a maximum of NR_CARDS cards.
 */
@@ -681,11 +703,6 @@
 static struct termios *serial_termios[NR_PORTS];
 static struct termios *serial_termios_locked[NR_PORTS];
 
-/* This is the per-irq data structure,
-   it maps an irq to the corresponding card */
-
-static struct cyclades_card     *IRQ_cards[NR_IRQS];
-
 /*
  * tmp_buf is used as a temporary buffer by serial_write.  We need to
  * lock it in case the copy_from_user blocks while swapping in a page,
@@ -790,7 +807,9 @@
 
 static void cy_start(struct tty_struct *);
 static void set_line_char(struct cyclades_port *);
+#ifndef CONFIG_COBALT_27
 static void cy_probe(int, void *, struct pt_regs *);
+#endif /* CONFIG_COBALT_27 */
 static void cyz_poll(unsigned long);
 #ifdef CYCLOM_SHOW_STATUS
 static void show_status(int);
@@ -959,6 +978,8 @@
     return(0);
 } /* cyy_issue_cmd */
 
+#ifndef CONFIG_COBALT_27	/* ISA interrupt detection code */
+
 static int probe_ready;
 
 /*
@@ -1149,6 +1170,8 @@
     return;
 } /* cy_probe */
 
+#endif /* CONFIG_COBALT_27 */
+
 /* The real interrupt service routine is called
    whenever the card wants its hand held--chars
    received, out buffer empty, modem change, etc.
@@ -1172,9 +1195,9 @@
   int mdm_change;
   int mdm_status;
 
-    if((cinfo = IRQ_cards[irq]) == 0){
+    if((cinfo = (struct cyclades_card *)dev_id) == 0){
 #ifdef CY_DEBUG_INTERRUPTS
-printk("cy_interrupt: spurious interrupt %d\n\r", irq);
+	printk("cy_interrupt: spurious interrupt %d\n\r", irq);
 #endif
         return; /* spurious interrupt */
     }
@@ -1206,7 +1229,7 @@
                 }
                 if (status & CySRReceive) { /* reception interrupt */
 #ifdef CY_DEBUG_INTERRUPTS
-printk("cy_interrupt: rcvd intr, chip %d\n\r", chip);
+		    printk("cy_interrupt: rcvd intr, chip %d\n\r", chip);
 #endif
                     /* determine the channel & change to that context */
                     save_xir = (u_char) cy_readb(base_addr+(CyRIR<<index));
@@ -1333,7 +1356,7 @@
                        is empty, we know we can always stuff a dozen
                        characters. */
 #ifdef CY_DEBUG_INTERRUPTS
-printk("cy_interrupt: xmit intr, chip %d\n\r", chip);
+		    printk("cy_interrupt: xmit intr, chip %d\n\r", chip);
 #endif
 
                     /* determine the channel & change to that context */
@@ -1368,37 +1391,19 @@
                         info->x_char = 0;
                     }
 
-                    if (info->x_break){
-                        /*  The Cirrus chip requires the "Embedded
-			    Transmit Commands" of start break, delay,
-			    and end break sequences to be sent.  The
-			    duration of the break is given in TICs,
-			    which runs at HZ (typically 100) and the
-			    PPR runs at 200 Hz, so the delay is
-			    duration * 200/HZ, and thus a break can
-			    run from 1/100 sec to about 5/4 sec.
-			    For CD1400 J or later, replace the 200 Hz
-			    by 500 Hz.
-                         */
-			/* start break */
-                        cy_writeb((u_long)base_addr + (CyTDR<<index), 0); 
-                        cy_writeb((u_long)base_addr + (CyTDR<<index), 0x81);
-			/* delay a bit */
-                        cy_writeb((u_long)base_addr + (CyTDR<<index), 0); 
-                        cy_writeb((u_long)base_addr + (CyTDR<<index), 0x82);
-                        if (info->chip_rev >= CD1400_REV_J ) {
-			    /* It is a CD1400 rev. J or later */
-                            cy_writeb((u_long)base_addr + (CyTDR<<index), 
-				      info->x_break*500/HZ);
-			} else {
-                            cy_writeb((u_long)base_addr + (CyTDR<<index), 
-				      info->x_break*200/HZ);
+                    if (info->breakon || info->breakoff) {
+			if (info->breakon) {
+			    cy_writeb((u_long)base_addr + (CyTDR<<index), 0); 
+			    cy_writeb((u_long)base_addr + (CyTDR<<index), 0x81);
+			    info->breakon = 0;
+                            char_count -= 2;
+			}
+			if (info->breakoff) {
+			    cy_writeb((u_long)base_addr + (CyTDR<<index), 0); 
+			    cy_writeb((u_long)base_addr + (CyTDR<<index), 0x83);
+			    info->breakoff = 0;
+                            char_count -= 2;
 			}
-			/* finish break */
-                        cy_writeb((u_long)base_addr + (CyTDR<<index), 0); 
-                        cy_writeb((u_long)base_addr + (CyTDR<<index), 0x83);
-                        char_count -= 7;
-                        info->x_break = 0;
                     }
 
                     while (char_count-- > 0){
@@ -1871,12 +1876,6 @@
 		    info->last_active = jiffies;
 		    info->jiffies[2] = jiffies;
 		}
-		if (info->x_break){
-		    printk("cyc cyz_poll shouldn't see x_break\n");
-		    info->x_break = 0;
-		    info->last_active = jiffies;
-		    info->jiffies[2] = jiffies;
-		}
 #ifdef BLOCKMOVE
 		while(0 < (small_count
 		    = cy_min((tx_bufsize - tx_put),
@@ -1946,26 +1945,35 @@
 startup(struct cyclades_port * info)
 {
   unsigned long flags;
+  int retval = 0;
   unsigned char *base_addr;
   int card,chip,channel,index;
+  unsigned long page;
+
+    page = get_free_page(GFP_KERNEL);
+    if (!page)
+	return -ENOMEM;
+
+    save_flags(flags); cli();
 
     if (info->flags & ASYNC_INITIALIZED){
-        return 0;
+	free_page(page);
+	goto errout;
     }
 
     if (!info->type){
         if (info->tty){
             set_bit(TTY_IO_ERROR, &info->tty->flags);
         }
-        return 0;
-    }
-    if (!info->xmit_buf){
-        info->xmit_buf = (unsigned char *) get_free_page (GFP_KERNEL);
-        if (!info->xmit_buf){
-            return -ENOMEM;
-        }
+	free_page(page);
+	goto errout;
     }
 
+    if (info->xmit_buf)
+	free_page(page);
+    else
+	info->xmit_buf = (unsigned char *) page;
+
     set_line_char(info);
 
     card = info->card;
@@ -1982,39 +1990,40 @@
 	     card, chip, channel, (long)base_addr);/**/
 #endif
 
-	save_flags(flags); cli();
-	    cy_writeb((ulong)base_addr+(CyCAR<<index), (u_char)channel);
+	cy_writeb((ulong)base_addr+(CyCAR<<index), (u_char)channel);
 
-	    cy_writeb((ulong)base_addr+(CyRTPR<<index), (info->default_timeout
-				 ? info->default_timeout
-				 : 0x02)); /* 10ms rx timeout */
+	cy_writeb((ulong)base_addr+(CyRTPR<<index), (info->default_timeout
+		 ? info->default_timeout : 0x02)); /* 10ms rx timeout */
 
-	    cyy_issue_cmd(base_addr,CyCHAN_CTL|CyENB_RCVR|CyENB_XMTR,index);
+	cyy_issue_cmd(base_addr,CyCHAN_CTL|CyENB_RCVR|CyENB_XMTR,index);
 
-	    cy_writeb((ulong)base_addr+(CyCAR<<index), (u_char)channel);
-	    cy_writeb((ulong)base_addr+(CyMSVR1<<index), CyRTS);
-	    cy_writeb((ulong)base_addr+(CyMSVR2<<index), CyDTR);
+	cy_writeb((ulong)base_addr+(CyCAR<<index), (u_char)channel);
+	cy_writeb((ulong)base_addr+(CyMSVR1<<index), CyRTS);
+	cy_writeb((ulong)base_addr+(CyMSVR2<<index), CyDTR);
 
 #ifdef CY_DEBUG_DTR
-	    printk("cyc:startup raising DTR\n");
-	    printk("     status: 0x%x, 0x%x\n",
-		   cy_readb(base_addr+(CyMSVR1<<index)), 
-                   cy_readb(base_addr+(CyMSVR2<<index)));
+	printk("cyc:startup raising DTR\n");
+	printk("     status: 0x%x, 0x%x\n",
+		cy_readb(base_addr+(CyMSVR1<<index)), 
+                cy_readb(base_addr+(CyMSVR2<<index)));
 #endif
 
-	    cy_writeb((u_long)base_addr+(CySRER<<index),
-               cy_readb(base_addr+(CySRER<<index)) | CyRxData);
-	    info->flags |= ASYNC_INITIALIZED;
+	cy_writeb((u_long)base_addr+(CySRER<<index),
+		cy_readb(base_addr+(CySRER<<index)) | CyRxData);
+	info->flags |= ASYNC_INITIALIZED;
+
+	if (info->tty){
+	    clear_bit(TTY_IO_ERROR, &info->tty->flags);
+	}
+	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+	info->breakon = info->breakoff = 0;
+	memset((char *)&info->idle_stats, 0, sizeof(info->idle_stats));
+	info->idle_stats.in_use    =
+	info->idle_stats.recv_idle =
+	info->idle_stats.xmit_idle = jiffies;
 
-	    if (info->tty){
-		clear_bit(TTY_IO_ERROR, &info->tty->flags);
-	    }
-	    info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
-	    memset((char *)&info->idle_stats, 0, sizeof(info->idle_stats));
-	    info->idle_stats.in_use    =
-	    info->idle_stats.recv_idle =
-	    info->idle_stats.xmit_idle = jiffies;
 	restore_flags(flags);
+
     } else {
       struct FIRM_ID *firm_id;
       struct ZFW_CTRL *zfw_ctrl;
@@ -2022,6 +2031,8 @@
       struct CH_CTRL *ch_ctrl;
       int retval;
 
+	restore_flags(flags);
+
 	base_addr = (unsigned char*) (cy_card[card].base_addr);
 
         firm_id = (struct FIRM_ID *) (base_addr + ID_ADDRESS);
@@ -2074,7 +2085,7 @@
 	    clear_bit(TTY_IO_ERROR, &info->tty->flags);
 	}
 	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
-
+	info->breakon = info->breakoff = 0;
 	memset((char *)&info->idle_stats, 0, sizeof(info->idle_stats));
 	info->idle_stats.in_use    =
 	info->idle_stats.recv_idle =
@@ -2085,6 +2096,10 @@
 	printk(" cyc startup done\n");
 #endif
 	return 0;
+
+errout:
+	restore_flags(flags);
+	return retval;
 } /* startup */
 
 
@@ -3763,36 +3778,62 @@
     return 0;
 } /* set_modem_info */
 
+/*
+ * cy_break() --- routine which turns the break handling on or off
+ */
 static void
-send_break( struct cyclades_port * info, int duration)
+cy_break(struct tty_struct *tty, int break_state)
 {
+    struct cyclades_port * info = (struct cyclades_port *)tty->driver_data;
+    unsigned long flags;
+
+    if (serial_paranoia_check(info, tty->device, "cy_break"))
+	return;
 
+    save_flags(flags); cli();
     if (!IS_CYC_Z(cy_card[info->card])) {
         /* Let the transmit ISR take care of this (since it
 	   requires stuffing characters into the output stream).
         */
-	info->x_break = duration;
-	if (!info->xmit_cnt ) {
-	    start_xmit(info);
+	if (break_state == -1) {
+	    if (!info->breakon) {
+		info->breakon = 1;
+		if (!info->xmit_cnt ) {
+		    start_xmit(info);
+		}
+	    }
+	} else {
+	    if (!info->breakoff) {
+		info->breakoff = 1;
+		if (!info->xmit_cnt ) {
+		    start_xmit(info);
+		}
+	    }
 	}
     } else {
-	/* For the moment we ignore the duration parameter!!!
-	   A better implementation will use C_CM_SET_BREAK
-	   and C_CM_CLR_BREAK with the appropriate delay.
-	 */
-#if 1
-// this appears to wedge the output data stream
-int retval;
-        retval = cyz_issue_cmd(&cy_card[info->card],
+	int retval;
+
+	if (break_state == -1) {
+	    retval = cyz_issue_cmd(&cy_card[info->card],
 		(info->line) - (cy_card[info->card].first_line),
-		C_CM_SENDBRK, 0L);
-	if (retval != 0){
-	    printk("cyc:send_break retval at %d was %x\n",
-	        __LINE__, retval);
+		C_CM_SET_BREAK, 0L);
+	    if (retval != 0) {
+		printk("cyc:cy_break (set) retval at %d was %x\n",
+		        __LINE__, retval);
+	    }
+	} else {
+	    retval = cyz_issue_cmd(&cy_card[info->card],
+		(info->line) - (cy_card[info->card].first_line),
+		C_CM_CLR_BREAK, 0L);
+	    if (retval != 0) {
+		printk("cyc:cy_break (clr) retval at %d was %x\n",
+		        __LINE__, retval);
+	    }
 	}
-#endif
     }
-} /* send_break */
+    restore_flags(flags);
+
+} /* cy_break */
 
 static int
 get_mon_info(struct cyclades_port * info, struct cyclades_monitor * mon)
@@ -4026,21 +4067,6 @@
 	case CYGETWAIT:
 	    ret_val = info->closing_wait / (HZ/100);
 	    break;
-	case TCSBRK:    /* SVID version: non-zero arg --> no break */
-	    ret_val = tty_check_change(tty);
-	    if (ret_val)
-		return ret_val;
-	    tty_wait_until_sent(tty,0);
-	    if (!arg)
-		send_break(info, HZ/4); /* 1/4 second */
-	    break;
-	case TCSBRKP:   /* support for POSIX tcsendbreak() */
-	    ret_val = tty_check_change(tty);
-	    if (ret_val)
-		return ret_val;
-	    tty_wait_until_sent(tty,0);
-	    send_break(info, arg ? arg*(HZ/10) : HZ/4);
-	    break;
         case TIOCMGET:
             ret_val = get_modem_info(info, (unsigned int *) arg);
             break;
@@ -4091,7 +4117,13 @@
             tty->stopped = 0;
             cy_start(tty);
     }
-#ifdef tytso_patch_94Nov25_1726
+#if 0
+    /*
+     * No need to wake up processes in open wait, since they
+     * sample the CLOCAL flag once, and don't recheck it.
+     * XXX  It's not clear whether the current behavior is correct
+     * or not.  Hence, this may change.....
+     */
     if (!(old_termios->c_cflag & CLOCAL) &&
         (tty->termios->c_cflag & CLOCAL))
             wake_up_interruptible(&info->open_wait);
@@ -4100,16 +4132,6 @@
     return;
 } /* cy_set_termios */
 
-
-/*
- * void (*set_ldisc)(struct tty_struct *tty);
- *
- * 	This routine allows the tty driver to be notified when the
- * 	device's termios settings have changed.
- * 
- */
-
-
 /* This routine is called by the upper-layer tty layer to signal
    that incoming characters should be throttled because the input
    buffers are close to full.
@@ -4467,6 +4489,7 @@
     return chip_number;
 } /* cyy_init_card */
 
+#ifndef CONFIG_COBALT_27
 /*
  * ---------------------------------------------------------------------
  * cy_detect_isa() - Probe for Cyclom-Y/ISA boards.
@@ -4530,7 +4553,7 @@
 
                 /* allocate IRQ */
                 if(request_irq(cy_isa_irq, cyy_interrupt,
-				   SA_INTERRUPT, "cyclomY", NULL))
+				   SA_INTERRUPT, "Cyclom-Y", &cy_card[j]))
                 {
                         printk("Cyclom-Y/ISA found at 0x%lx ",
                                 (unsigned long) cy_isa_address);
@@ -4546,7 +4569,6 @@
                 cy_card[j].bus_index = 0;
                 cy_card[j].first_line = cy_next_channel;
                 cy_card[j].num_chips = cy_isa_nchan/4;
-                IRQ_cards[cy_isa_irq] = &cy_card[j];
                 nboard++;
                         
                 /* print message */
@@ -4561,6 +4583,20 @@
         return(nboard);
 
 } /* cy_detect_isa */
+#endif /* CONFIG_COBALT_27 */
+
+static void plx_init(uclong addr, uclong initctl)
+{
+    /* Reset PLX */
+    cy_writel(addr + initctl, cy_readl(addr + initctl) | 0x40000000);
+    udelay(100L);
+    cy_writel(addr + initctl, cy_readl(addr + initctl) & ~0x40000000);
+
+    /* Reload Config. Registers from EEPROM */
+    cy_writel(addr + initctl, cy_readl(addr + initctl) | 0x20000000);
+    udelay(100L);
+    cy_writel(addr + initctl, cy_readl(addr + initctl) & ~0x20000000);
+}
 
 /*
  * ---------------------------------------------------------------------
@@ -4621,6 +4657,12 @@
 		cy_pci_addr0  &= PCI_BASE_ADDRESS_MEM_MASK;
 		cy_pci_addr2  &= PCI_BASE_ADDRESS_MEM_MASK;
 
+		if (cy_pci_addr2 & ~PCI_BASE_ADDRESS_IO_MASK) {
+		    printk("  Warning: PCI I/O bit incorrectly set. "
+			   "Ignoring it...\n");
+		    cy_pci_addr2 &= PCI_BASE_ADDRESS_IO_MASK;
+		}
+
 #if defined(__alpha__)
                 if (device_id  == PCI_DEVICE_ID_CYCLOM_Y_Lo) { /* below 1M? */
 		    printk("Cyclom-Y/PCI (bus=0x0%x, pci_id=0x%x, ",
@@ -4673,7 +4715,7 @@
 
                 /* allocate IRQ */
                 if(request_irq(cy_pci_irq, cyy_interrupt,
-		        SA_INTERRUPT, "cyclomY", NULL))
+		        SA_SHIRQ, "Cyclom-Y", &cy_card[j]))
                 {
                         printk("Cyclom-Y/PCI found at 0x%lx ",
 			    (ulong) cy_pci_addr2);
@@ -4689,13 +4731,14 @@
                 cy_card[j].bus_index = 1;
                 cy_card[j].first_line = cy_next_channel;
                 cy_card[j].num_chips = cy_pci_nchan/4;
-                IRQ_cards[cy_pci_irq] = &cy_card[j];
 
                 /* enable interrupts in the PCI interface */
 		plx_ver = cy_readb(cy_pci_addr2 + CyPLX_VER) & 0x0f;
 		switch (plx_ver) {
 		    case PLX_9050:
 
+		    plx_init(cy_pci_addr0, 0x50);
+
 		    cy_writew(cy_pci_addr0+0x4c, 
 			cy_readw(cy_pci_addr0+0x4c)|0x0040);
 		    break;
@@ -4704,6 +4747,8 @@
 		    case PLX_9080:
 		    default: /* Old boards, use PLX_9060 */
 
+		    plx_init(cy_pci_addr0, 0x6c);
+
 		    cy_writew(cy_pci_addr0+0x68, 
 			cy_readw(cy_pci_addr0+0x68)|0x0900);
 		    break;
@@ -4742,9 +4787,18 @@
 #if !defined(__alpha__)
                 cy_pci_addr0 = (ulong)ioremap(cy_pci_addr0, CyPCI_Zctl);
 #endif
+
+		plx_init(cy_pci_addr0, 0x6c);
+
 		mailbox = (uclong)cy_readl(&((struct RUNTIME_9060 *) 
 			   cy_pci_addr0)->mail_box_0);
                 cy_pci_addr2 &= PCI_BASE_ADDRESS_MEM_MASK;
+
+		if (cy_pci_addr2 & ~PCI_BASE_ADDRESS_IO_MASK) {
+		    printk("  Warning: PCI I/O bit incorrectly set. "
+			   "Ignoring it...\n");
+		    cy_pci_addr2 &= PCI_BASE_ADDRESS_IO_MASK;
+		}
 		if (mailbox == ZE_V1) {
 #if !defined(__alpha__)
                	    cy_pci_addr2 = (ulong)ioremap(cy_pci_addr2, CyPCI_Ze_win);
@@ -4821,7 +4875,7 @@
                 /* allocate IRQ only if board has an IRQ */
 		if( (1 < cy_pci_irq) && (cy_pci_irq < 15) ) {
 		    if(request_irq(cy_pci_irq,cyz_interrupt,
-			SA_INTERRUPT,"cyclomZ",NULL))
+			SA_SHIRQ,"Cyclades-Z",&cy_card[j]))
 		    {
 			printk("Could not allocate IRQ%d ",
 			    cy_pci_irq);
@@ -4839,7 +4893,6 @@
                 cy_card[j].bus_index = 1;
                 cy_card[j].first_line = cy_next_channel;
                 cy_card[j].num_chips = -1;
-                IRQ_cards[cy_pci_irq] = &cy_card[j];
 
                 /* print message */
 		/* don't report IRQ if board is no IRQ */
@@ -4905,7 +4958,7 @@
                 /* allocate IRQ only if board has an IRQ */
 		if( (1 < cy_pci_irq) && (cy_pci_irq < 15) ) {
 		    if(request_irq(cy_pci_irq,cyz_interrupt,
-			SA_INTERRUPT,"cyclomZ",NULL))
+			SA_SHIRQ,"Cyclades-Z",&cy_card[j]))
 		    {
 			printk("Could not allocate IRQ%d ",
 			    cy_pci_irq);
@@ -4922,7 +4975,6 @@
                 cy_card[j].bus_index = 1;
                 cy_card[j].first_line = cy_next_channel;
                 cy_card[j].num_chips = -1;
-                IRQ_cards[cy_pci_irq] = &cy_card[j];
 
                 /* print message */
 		/* don't report IRQ if board is no IRQ */
@@ -4971,7 +5023,6 @@
 	__DATE__, __TIME__);
 } /* show_version */
 
-#ifdef CY_PROC
 static int 
 cyclades_get_proc_info(char *buf, char **start, off_t offset, int length,
 		       int *eof, void *data)
@@ -5028,7 +5079,6 @@
 	len = 0;
     return len;
 }
-#endif
 
 /* The serial driver boot-time initialization code!
     Hardware I/O ports are mapped to character special devices on a
@@ -5062,13 +5112,15 @@
   struct proc_dir_entry *ent;
 #endif
 
+    init_bh(CYCLADES_BH, do_cyclades_bh);
+
     show_version();
 
     /* Initialize the tty_driver structure */
     
     memset(&cy_serial_driver, 0, sizeof(struct tty_driver));
     cy_serial_driver.magic = TTY_DRIVER_MAGIC;
-    cy_serial_driver.name = "cyclades";
+    cy_serial_driver.driver_name = "cyclades";
     cy_serial_driver.name = "ttyC";
     cy_serial_driver.major = CYCLADES_MAJOR;
     cy_serial_driver.minor_start = 0;
@@ -5083,6 +5135,7 @@
     cy_serial_driver.table = serial_table;
     cy_serial_driver.termios = serial_termios;
     cy_serial_driver.termios_locked = serial_termios_locked;
+
     cy_serial_driver.open = cy_open;
     cy_serial_driver.close = cy_close;
     cy_serial_driver.write = cy_write;
@@ -5098,7 +5151,9 @@
     cy_serial_driver.stop = cy_stop;
     cy_serial_driver.start = cy_start;
     cy_serial_driver.hangup = cy_hangup;
+    cy_serial_driver.break_ctl = cy_break;
     cy_serial_driver.wait_until_sent = cy_wait_until_sent;
+    cy_serial_driver.read_proc = cyclades_get_proc_info;
 
     /*
      * The callout device is just like normal device except for
@@ -5117,12 +5172,6 @@
     if (tty_register_driver(&cy_callout_driver))
             panic("Couldn't register Cyclades callout driver\n");
 
-    init_bh(CYCLADES_BH, do_cyclades_bh);
-
-    for (i = 0; i < NR_IRQS; i++) {
-            IRQ_cards[i] = 0;
-    }
-
     for (i = 0; i < NR_CARDS; i++) {
             /* base_addr=0 indicates board not found */
             cy_card[i].base_addr = 0;
@@ -5135,9 +5184,11 @@
        availability of cy_card and cy_port data structures and updating
        the cy_next_channel. */
 
+#ifndef CONFIG_COBALT_27
     /* look for isa boards */
     cy_isa_nboard = cy_detect_isa();
-    
+#endif /* CONFIG_COBALT_27 */
+
     /* look for pci boards */
     cy_pci_nboard = cy_detect_pci();
 
@@ -5279,9 +5330,9 @@
 		               cy_callout_driver.init_termios;
                     info->normal_termios =
 		               cy_serial_driver.init_termios;
-                    init_waitqueue(&info->open_wait);
-                    init_waitqueue(&info->close_wait);
-                    init_waitqueue(&info->shutdown_wait);
+                    init_waitqueue_head(&info->open_wait);
+                    init_waitqueue_head(&info->close_wait);
+                    init_waitqueue_head(&info->shutdown_wait);
                     /* info->session */
                     /* info->pgrp */
                     info->read_status_mask =
@@ -5323,6 +5374,7 @@
 cleanup_module(void)
 {
     int i;
+    int e1, e2;
     unsigned long flags;
 
     if (cyz_timeron){
@@ -5333,11 +5385,12 @@
     save_flags(flags); cli();
     remove_bh(CYCLADES_BH);
 
-    free_page((unsigned long)tmp_buf);
-    if (tty_unregister_driver(&cy_callout_driver))
-            printk("Couldn't unregister Cyclades callout driver\n");
-    if (tty_unregister_driver(&cy_serial_driver))
-            printk("Couldn't unregister Cyclades serial driver\n");
+    if ((e1 = tty_unregister_driver(&cy_serial_driver)))
+            printk("cyc: failed to unregister Cyclades serial driver(%d)\n",
+		e1);
+    if ((e2 = tty_unregister_driver(&cy_callout_driver)))
+            printk("cyc: failed to unregister Cyclades callout driver (%d)\n", 
+		e2);
 
     restore_flags(flags);
 
@@ -5345,9 +5398,13 @@
         if (cy_card[i].base_addr != 0
 	    && cy_card[i].irq)
         {
-            free_irq(cy_card[i].irq,NULL);
+            free_irq(cy_card[i].irq, &cy_card[i]);
         }
     }
+    if (tmp_buf) {
+	free_page((unsigned long) tmp_buf);
+	tmp_buf = NULL;
+    }
 #ifdef CY_PROC
     remove_proc_entry("cyclades", 0);
 #endif
@@ -5358,6 +5415,7 @@
 void
 cy_setup(char *str, int *ints)
 {
+#ifndef CONFIG_COBALT_27
   int i, j;
 
     for (i = 0 ; i < NR_ISA_ADDRS ; i++) {
@@ -5368,6 +5426,7 @@
             cy_isa_addresses[i++] = (unsigned char *)(ints[j]);
         }
     }
+#endif /* CONFIG_COBALT_27 */
 
 } /* cy_setup */
 #endif
diff -u --recursive --new-file v2.3.3/linux/drivers/char/i2c-parport.c linux/drivers/char/i2c-parport.c
--- v2.3.3/linux/drivers/char/i2c-parport.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/i2c-parport.c	Sat May 22 15:02:48 1999
@@ -0,0 +1,149 @@
+/*
+ * I2C driver for parallel port
+ *
+ * Author: Phil Blundell <philb@gnu.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * This driver implements a simple I2C protocol by bit-twiddling some
+ * signals on the parallel port.  Since the outputs on the parallel port
+ * aren't open collector, three lines rather than two are used:
+ *
+ *	D0	clock out
+ *	D1	data out
+ *	BUSY	data in	
+ */
+
+#include <linux/parport.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <asm/spinlock.h>
+
+#define I2C_DELAY   10
+
+static int debug = 0;
+
+struct parport_i2c_bus
+{
+  struct i2c_bus i2c;
+  struct parport_i2c_bus *next;
+};
+
+static struct parport_i2c_bus *bus_list;
+
+#ifdef __SMP__
+static spinlock_t bus_list_lock = SPIN_LOCK_UNLOCKED;
+#endif
+
+/* software I2C functions */
+
+static void i2c_setlines(struct i2c_bus *bus, int clk, int data)
+{
+  struct parport *p = bus->data;
+  parport_write_data(p, (clk?1:0) | (data?2:0)); 
+  udelay(I2C_DELAY);
+}
+
+static int i2c_getdataline(struct i2c_bus *bus)
+{
+  struct parport *p = bus->data;
+  return (parport_read_status(p) & PARPORT_STATUS_BUSY) ? 0 : 1;
+}
+
+static struct i2c_bus parport_i2c_bus_template = 
+{
+  "...",
+  I2C_BUSID_PARPORT,
+  NULL,
+  
+  SPIN_LOCK_UNLOCKED,
+  
+  NULL,
+  NULL,
+	
+  i2c_setlines,
+  i2c_getdataline,
+  NULL,
+  NULL,
+};
+
+static void i2c_parport_attach(struct parport *port)
+{
+  struct parport_i2c_bus *b = kmalloc(sizeof(struct parport_i2c_bus), 
+				      GFP_KERNEL);
+  b->i2c = parport_i2c_bus_template;
+  b->i2c.data = port;
+  strncpy(b->i2c.name, port->name, 32);
+  spin_lock(&bus_list_lock);
+  b->next = bus_list;
+  bus_list = b;
+  spin_unlock(&bus_list_lock);
+  i2c_register_bus(&b->i2c);
+  if (debug)
+    printk(KERN_DEBUG "i2c: attached to %s\n", port->name);
+}
+
+static void i2c_parport_detach(struct parport *port)
+{
+  struct parport_i2c_bus *b, *old_b = NULL;
+  spin_lock(&bus_list_lock);
+  b = bus_list;
+  while (b)
+  {
+    if (b->i2c.data == port)
+    {
+      if (old_b)
+	old_b->next = b->next;
+      else
+	bus_list = b->next;
+      i2c_unregister_bus(&b->i2c);
+      kfree(b);
+      break;
+    }
+    old_b = b;
+    b = b->next;
+  }
+  spin_unlock(&bus_list_lock);
+  if (debug)
+    printk(KERN_DEBUG "i2c: detached from %s\n", port->name);
+}
+
+static struct parport_driver parport_i2c_driver = 
+{
+  "i2c",
+  i2c_parport_attach,
+  i2c_parport_detach
+};
+
+#ifdef MODULE
+int init_module(void)
+#else
+int __init i2c_parport_init(void)
+#endif
+{
+  printk("I2C: driver for parallel port v0.1 philb@gnu.org\n");
+  parport_register_driver(&parport_i2c_driver);
+  return 0;
+}
+
+#ifdef MODULE
+MODULE_PARM(debug, "i");
+
+void cleanup_module(void)
+{
+  struct parport_i2c_bus *b = bus_list;
+  while (b)
+  {
+    struct parport_i2c_bus *next = b->next;
+    i2c_unregister_bus(&b->i2c);
+    kfree(b);
+    b = next;
+  }
+  parport_unregister_driver(&parport_i2c_driver);
+}
+#endif
diff -u --recursive --new-file v2.3.3/linux/drivers/char/keyboard.c linux/drivers/char/keyboard.c
--- v2.3.3/linux/drivers/char/keyboard.c	Mon May 17 09:55:21 1999
+++ linux/drivers/char/keyboard.c	Sat May 22 12:57:24 1999
@@ -247,9 +247,10 @@
 		sysrq_pressed = !up_flag;
 		return;
 	} else if (sysrq_pressed) {
-		if (!up_flag)
+		if (!up_flag) {
 			handle_sysrq(kbd_sysrq_xlate[keycode], kbd_pt_regs, kbd, tty);
-		return;
+			return;
+		}
 	}
 #endif
 
@@ -474,7 +475,6 @@
 
 static void boot_it(void)
 {
-	if (kbd->slockstate & ~shift_state) return;
 	ctrl_alt_del();
 }
 
diff -u --recursive --new-file v2.3.3/linux/drivers/char/stallion.c linux/drivers/char/stallion.c
--- v2.3.3/linux/drivers/char/stallion.c	Fri Apr 16 08:20:23 1999
+++ linux/drivers/char/stallion.c	Wed May 26 09:31:45 1999
@@ -154,7 +154,7 @@
  *	is already swapping a shared buffer won't make things any worse.
  */
 static char			*stl_tmpwritebuf;
-static struct semaphore		stl_tmpwritesem = MUTEX;
+static DECLARE_MUTEX(stl_tmpwritesem);
 
 /*
  *	Define a local default termios struct. All ports will be created
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/Config.in linux/drivers/isdn/Config.in
--- v2.3.3/linux/drivers/isdn/Config.in	Mon Mar 15 16:11:29 1999
+++ linux/drivers/isdn/Config.in	Sun May 23 10:03:41 1999
@@ -19,34 +19,39 @@
 if [ "$CONFIG_ISDN_DRV_HISAX" != "n" ]; then
     bool 'HiSax Support for EURO/DSS1' CONFIG_HISAX_EURO
     if [ "$CONFIG_HISAX_EURO" != "n" ]; then
-	    bool 'Support for german tarifinfo' CONFIG_DE_AOC
-	    bool 'Support for australian Microlink service (not for std. EURO)' CONFIG_HISAX_ML
+	    bool 'Support for german chargeinfo' CONFIG_DE_AOC
+	    bool 'Disable sending complete' CONFIG_HISAX_NO_SENDCOMPLETE
+	    bool 'Disable sending low layer compatibility' CONFIG_HISAX_NO_LLC
     fi
     bool 'HiSax Support for german 1TR6' CONFIG_HISAX_1TR6
     bool 'HiSax Support for Teles 16.0/8.0' CONFIG_HISAX_16_0
     bool 'HiSax Support for Teles 16.3 or PNP or PCMCIA' CONFIG_HISAX_16_3
     bool 'HiSax Support for Teles 16.3c' CONFIG_HISAX_TELES3C 
+    bool 'HiSax Support for Teles PCI' CONFIG_HISAX_TELESPCI 
+    bool 'HiSax Support for Teles S0Box' CONFIG_HISAX_S0BOX 
     bool 'HiSax Support for AVM A1 (Fritz)' CONFIG_HISAX_AVM_A1
+    bool 'HiSax Support for AVM PnP/PCI (Fritz!PnP/PCI)' CONFIG_HISAX_FRITZPCI
+    bool 'HiSax Support for AVM A1 PCMCIA (Fritz)' CONFIG_HISAX_AVM_A1_PCMCIA
     bool 'HiSax Support for Elsa cards' CONFIG_HISAX_ELSA
     bool 'HiSax Support for ITK ix1-micro Revision 2' CONFIG_HISAX_IX1MICROR2
     bool 'HiSax Support for Eicon.Diehl Diva cards' CONFIG_HISAX_DIEHLDIVA
     bool 'HiSax Support for ASUSCOM cards' CONFIG_HISAX_ASUSCOM
     bool 'HiSax Support for TELEINT cards' CONFIG_HISAX_TELEINT
-    bool 'HiSax Support for Sedlbauer speed card/win/star' CONFIG_HISAX_SEDLBAUER
+    bool 'HiSax Support for Sedlbauer speed card/win/star/fax' CONFIG_HISAX_SEDLBAUER
     bool 'HiSax Support for USR Sportster internal TA' CONFIG_HISAX_SPORTSTER
     bool 'HiSax Support for MIC card' CONFIG_HISAX_MIC
     bool 'HiSax Support for NETjet card' CONFIG_HISAX_NETJET
     bool 'HiSax Support for Niccy PnP/PCI card' CONFIG_HISAX_NICCY
     if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then
       if [ "$ARCH" = "sparc" -o "$ARCH" = "sparc64" ]; then
-        bool 'HiSax Support for SPARC Am7930' CONFIG_HISAX_AMD7930
-        bool 'HiSax Support for SPARC DBRI' CONFIG_HISAX_DBRI
+        bool 'HiSax Support for Am7930' CONFIG_HISAX_AMD7930
       fi
     fi
 fi
 if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then
 	dep_tristate 'Spellcaster support (EXPERIMENTAL)' CONFIG_ISDN_DRV_SC $CONFIG_ISDN
 	dep_tristate 'IBM Active 2000 support (EXPERIMENTAL)' CONFIG_ISDN_DRV_ACT2000 $CONFIG_ISDN
+	dep_tristate 'Eicon.Diehl active card support (EXPERIMENTAL)' CONFIG_ISDN_DRV_EICON $CONFIG_ISDN
 fi
 dep_tristate 'AVM-B1 with CAPI2.0 support' CONFIG_ISDN_DRV_AVMB1 $CONFIG_ISDN
 if [ "$CONFIG_ISDN_DRV_AVMB1" != "n" ]; then
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/Makefile linux/drivers/isdn/Makefile
--- v2.3.3/linux/drivers/isdn/Makefile	Wed Apr  1 16:20:57 1998
+++ linux/drivers/isdn/Makefile	Sun May 23 10:03:41 1999
@@ -1,6 +1,6 @@
 SUB_DIRS     :=
 MOD_SUB_DIRS :=
-ALL_SUB_DIRS := icn pcbit hisax avmb1 act2000
+ALL_SUB_DIRS := icn pcbit hisax avmb1 act2000 eicon
 
 L_OBJS :=
 LX_OBJS :=
@@ -33,6 +33,7 @@
     OX_OBJS += isdn_common.o
     ifdef CONFIG_ISDN_PPP
       O_OBJS += isdn_ppp.o
+      M_OBJS += isdn_bsdcomp.o
     endif
     ifdef CONFIG_ISDN_X25
       O_OBJS += isdn_x25iface.o
@@ -111,6 +112,16 @@
 else
   ifeq ($(CONFIG_ISDN_DRV_ACT2000),m)
     MOD_SUB_DIRS += act2000
+  endif
+endif
+
+ifeq ($(CONFIG_ISDN_DRV_EICON),y)
+  L_OBJS += eicon/eicon.o
+  SUB_DIRS += eicon
+  MOD_SUB_DIRS += eicon
+else
+  ifeq ($(CONFIG_ISDN_DRV_EICON),m)
+    MOD_SUB_DIRS += eicon
   endif
 endif
 
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/act2000/act2000.h linux/drivers/isdn/act2000/act2000.h
--- v2.3.3/linux/drivers/isdn/act2000/act2000.h	Mon May 10 13:00:10 1999
+++ linux/drivers/isdn/act2000/act2000.h	Sun May 23 10:03:41 1999
@@ -1,8 +1,8 @@
-/* $Id: act2000.h,v 1.5 1997/10/09 22:22:59 fritz Exp $
+/* $Id: act2000.h,v 1.7 1999/04/12 13:13:54 fritz Exp $
  *
  * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000.
  *
- * Copyright 1997 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1998 by Fritz Elfert (fritz@isdn4linux.de)
  * Thanks to Friedemann Baitinger and IBM Germany
  *
  * This program is free software; you can redistribute it and/or modify
@@ -20,6 +20,12 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: act2000.h,v $
+ * Revision 1.7  1999/04/12 13:13:54  fritz
+ * Made cards pointer static to avoid name-clash.
+ *
+ * Revision 1.6  1998/11/05 22:12:38  fritz
+ * Changed mail-address.
+ *
  * Revision 1.5  1997/10/09 22:22:59  fritz
  * New HL<->LL interface:
  *   New BSENT callback with nr. of bytes included.
@@ -212,8 +218,6 @@
         isdn_if interface;               /* Interface to upper layer         */
         char regname[35];                /* Name used for request_region     */
 } act2000_card;
-
-extern act2000_card *actcards;
 
 extern __inline__ void act2000_schedule_tx(act2000_card *card)
 {
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/act2000/act2000_isa.c linux/drivers/isdn/act2000/act2000_isa.c
--- v2.3.3/linux/drivers/isdn/act2000/act2000_isa.c	Thu Nov  5 09:58:43 1998
+++ linux/drivers/isdn/act2000/act2000_isa.c	Sun May 23 10:03:41 1999
@@ -1,8 +1,8 @@
-/* $Id: act2000_isa.c,v 1.5 1998/02/12 23:06:47 keil Exp $
+/* $Id: act2000_isa.c,v 1.8 1999/01/05 18:29:25 he Exp $
  *
  * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000 (ISA-Version).
  *
- * Copyright 1997 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1998 by Fritz Elfert (fritz@isdn4linux.de)
  * Thanks to Friedemann Baitinger and IBM Germany
  *
  * This program is free software; you can redistribute it and/or modify
@@ -20,6 +20,17 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: act2000_isa.c,v $
+ * Revision 1.8  1999/01/05 18:29:25  he
+ * merged remaining schedule_timeout() changes from 2.1.127
+ *
+ * Revision 1.7  1998/11/05 22:12:41  fritz
+ * Changed mail-address.
+ *
+ * Revision 1.6  1998/06/17 19:51:09  he
+ * merged with 2.1.10[34] (cosmetics and udelay() -> mdelay())
+ * brute force fix to avoid Ugh's in isdn_tty_write()
+ * cleaned up some dead code
+ *
  * Revision 1.5  1998/02/12 23:06:47  keil
  * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
  *
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/act2000/act2000_isa.h linux/drivers/isdn/act2000/act2000_isa.h
--- v2.3.3/linux/drivers/isdn/act2000/act2000_isa.h	Wed Apr  1 16:20:57 1998
+++ linux/drivers/isdn/act2000/act2000_isa.h	Sun May 23 10:03:41 1999
@@ -1,8 +1,8 @@
-/* $Id: act2000_isa.h,v 1.1 1997/09/23 18:00:07 fritz Exp $
+/* $Id: act2000_isa.h,v 1.2 1998/11/05 22:12:43 fritz Exp $
  *
  * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000 (ISA-Version).
  *
- * Copyright 1997 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1998 by Fritz Elfert (fritz@isdn4linux.de)
  * Thanks to Friedemann Baitinger and IBM Germany
  *
  * This program is free software; you can redistribute it and/or modify
@@ -20,6 +20,9 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: act2000_isa.h,v $
+ * Revision 1.2  1998/11/05 22:12:43  fritz
+ * Changed mail-address.
+ *
  * Revision 1.1  1997/09/23 18:00:07  fritz
  * New driver for IBM Active 2000.
  *
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/act2000/capi.c linux/drivers/isdn/act2000/capi.c
--- v2.3.3/linux/drivers/isdn/act2000/capi.c	Wed Apr  1 16:20:57 1998
+++ linux/drivers/isdn/act2000/capi.c	Sun May 23 10:03:41 1999
@@ -1,9 +1,9 @@
-/* $Id: capi.c,v 1.7 1998/02/23 23:35:41 fritz Exp $
+/* $Id: capi.c,v 1.8 1998/11/05 22:12:46 fritz Exp $
  *
  * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000.
  *        CAPI encoder/decoder
  *
- * Copyright 1997 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1998 by Fritz Elfert (fritz@isdn4linux.de)
  * Thanks to Friedemann Baitinger and IBM Germany
  *
  * This program is free software; you can redistribute it and/or modify
@@ -21,6 +21,9 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: capi.c,v $
+ * Revision 1.8  1998/11/05 22:12:46  fritz
+ * Changed mail-address.
+ *
  * Revision 1.7  1998/02/23 23:35:41  fritz
  * Eliminated some compiler warnings.
  *
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/act2000/capi.h linux/drivers/isdn/act2000/capi.h
--- v2.3.3/linux/drivers/isdn/act2000/capi.h	Wed Apr  1 16:20:57 1998
+++ linux/drivers/isdn/act2000/capi.h	Sun May 23 10:03:41 1999
@@ -1,8 +1,8 @@
-/* $Id: capi.h,v 1.4 1997/10/01 09:21:04 fritz Exp $
+/* $Id: capi.h,v 1.5 1998/11/05 22:12:48 fritz Exp $
  *
  * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000.
  *
- * Copyright 1997 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1998 by Fritz Elfert (fritz@isdn4linux.de)
  * Thanks to Friedemann Baitinger and IBM Germany
  *
  * This program is free software; you can redistribute it and/or modify
@@ -20,6 +20,9 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: capi.h,v $
+ * Revision 1.5  1998/11/05 22:12:48  fritz
+ * Changed mail-address.
+ *
  * Revision 1.4  1997/10/01 09:21:04  fritz
  * Removed old compatibility stuff for 2.0.X kernels.
  * From now on, this code is for 2.1.X ONLY!
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/act2000/module.c linux/drivers/isdn/act2000/module.c
--- v2.3.3/linux/drivers/isdn/act2000/module.c	Mon May 10 13:00:10 1999
+++ linux/drivers/isdn/act2000/module.c	Sun May 23 10:03:41 1999
@@ -1,8 +1,8 @@
-/* $Id: module.c,v 1.7 1998/02/12 23:06:52 keil Exp $
+/* $Id: module.c,v 1.9 1999/04/12 13:13:56 fritz Exp $
  *
  * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000.
  *
- * Copyright 1997 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1998 by Fritz Elfert (fritz@isdn4linux.de)
  * Thanks to Friedemann Baitinger and IBM Germany
  *
  * This program is free software; you can redistribute it and/or modify
@@ -20,6 +20,12 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: module.c,v $
+ * Revision 1.9  1999/04/12 13:13:56  fritz
+ * Made cards pointer static to avoid name-clash.
+ *
+ * Revision 1.8  1998/11/05 22:12:51  fritz
+ * Changed mail-address.
+ *
  * Revision 1.7  1998/02/12 23:06:52  keil
  * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
  *
@@ -57,7 +63,7 @@
 };
 #define ISA_NRPORTS (sizeof(isa_ports)/sizeof(unsigned short))
 
-act2000_card *actcards = (act2000_card *) NULL;
+static act2000_card *cards = (act2000_card *) NULL;
 
 /* Parameters to be set by insmod */
 static int   act_bus  =  0;
@@ -589,7 +595,7 @@
 static inline act2000_card *
 act2000_findcard(int driverid)
 {
-        act2000_card *p = actcards;
+        act2000_card *p = cards;
 
         while (p) {
                 if (p->myid == driverid)
@@ -714,8 +720,8 @@
         card->bus = bus;
         card->port = port;
         card->irq = irq;
-        card->next = actcards;
-        actcards = card;
+        card->next = cards;
+        cards = card;
 }
 
 /*
@@ -805,9 +811,9 @@
 				       bus);
 		}
 	}
-	if (!actcards)
+	if (!cards)
 		return 1;
-        p = actcards;
+        p = cards;
         while (p) {
 		initialized = 0;
 		if (!p->interface.statcallb) {
@@ -870,9 +876,9 @@
                                 kfree(p);
                                 p = q->next;
                         } else {
-                                actcards = p->next;
+                                cards = p->next;
                                 kfree(p);
-                                p = actcards;
+                                p = cards;
                         }
 			failed++;
                 }
@@ -890,9 +896,9 @@
 act2000_init(void)
 {
         printk(KERN_INFO "%s\n", DRIVERNAME);
-        if (!actcards)
+        if (!cards)
 		act2000_addcard(act_bus, act_port, act_irq, act_id);
-        if (!actcards)
+        if (!cards)
                 printk(KERN_INFO "act2000: No cards defined yet\n");
         /* No symbols to export, hide all symbols */
         EXPORT_NO_SYMBOLS;
@@ -903,14 +909,14 @@
 void
 cleanup_module(void)
 {
-        act2000_card *card = actcards;
+        act2000_card *card = cards;
         act2000_card *last;
         while (card) {
                 unregister_card(card);
 		del_timer(&card->ptimer);
                 card = card->next;
         }
-        card = actcards;
+        card = cards;
         while (card) {
                 last = card;
                 card = card->next;
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/avmb1/b1capi.c linux/drivers/isdn/avmb1/b1capi.c
--- v2.3.3/linux/drivers/isdn/avmb1/b1capi.c	Thu Nov  5 09:58:43 1998
+++ linux/drivers/isdn/avmb1/b1capi.c	Sun May 23 10:03:41 1999
@@ -1,11 +1,49 @@
 /*
- * $Id: b1capi.c,v 1.10 1998/02/13 07:09:10 calle Exp $
+ * $Id: b1capi.c,v 1.14 1999/04/15 19:49:29 calle Exp $
  * 
  * CAPI 2.0 Module for AVM B1-card.
  * 
  * (c) Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
  * $Log: b1capi.c,v $
+ * Revision 1.14  1999/04/15 19:49:29  calle
+ * fix fuer die B1-PCI. Jetzt geht z.B. auch IRQ 17 ...
+ *
+ * Revision 1.13  1999/01/05 18:29:31  he
+ * merged remaining schedule_timeout() changes from 2.1.127
+ *
+ * Revision 1.12  1998/10/25 14:38:58  fritz
+ * Backported from MIPS (Cobalt).
+ *
+ * Revision 1.11  1998/03/29 16:05:58  calle
+ * changes from 2.0 tree merged.
+ *
+ * Revision 1.4.2.18  1998/03/20 20:34:37  calle
+ * port valid check now only for T1, because of the PCI and PCMCIA cards.
+ *
+ * Revision 1.4.2.17  1998/03/20 14:38:17  calle
+ * capidrv: prepared state machines for suspend/resume/hold
+ * capidrv: fix bug in state machine if B1/T1 is out of nccis
+ * b1capi: changed some errno returns.
+ * b1capi: detect if you try to add same T1 to different io address.
+ * b1capi: change number of nccis depending on number of channels.
+ * b1lli: cosmetics
+ *
+ * Revision 1.4.2.16  1998/03/20 09:01:08  calle
+ * Changes capi_register handling to get full support for 30 bchannels.
+ *
+ * Revision 1.4.2.15  1998/03/18 17:43:26  calle
+ * T1 with fastlink, bugfix for multicontroller support in capidrv.c
+ *
+ * Revision 1.4.2.14  1998/03/04 17:33:47  calle
+ * Changes for T1.
+ *
+ * Revision 1.4.2.13  1998/02/27 15:40:41  calle
+ * T1 running with slow link. bugfix in capi_release.
+ *
+ * Revision 1.4.2.12  1998/02/24 17:58:25  calle
+ * changes for T1.
+ *
  * Revision 1.10  1998/02/13 07:09:10  calle
  * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
  *
@@ -76,7 +114,7 @@
 #include "capicmd.h"
 #include "capiutil.h"
 
-static char *revision = "$Revision: 1.10 $";
+static char *revision = "$Revision: 1.14 $";
 
 /* ------------------------------------------------------------- */
 
@@ -84,7 +122,7 @@
 int loaddebug = 0;
 
 MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>");
-MODULE_PARM(showcapimsgs, "0-3i");
+MODULE_PARM(showcapimsgs, "0-5i");
 MODULE_PARM(loaddebug, "0-1i");
 
 /* ------------------------------------------------------------- */
@@ -150,10 +188,11 @@
 {
 	switch (cardtype) {
 		default:
-		case AVM_CARDTYPE_B1: return "B1";
-		case AVM_CARDTYPE_M1: return "M1";
-		case AVM_CARDTYPE_M2: return "M2";
-		case AVM_CARDTYPE_T1: return "T1";
+		case AVM_CARDTYPE_B1:    return "B1-ISA";
+		case AVM_CARDTYPE_B1PCI: return "B1-PCI";
+		case AVM_CARDTYPE_M1:    return "M1";
+		case AVM_CARDTYPE_M2:    return "M2";
+		case AVM_CARDTYPE_T1:    return "T1";
 	}
 }
 
@@ -300,7 +339,7 @@
 			}
 		}
 		APPL(appl)->releasing--;
-		if (APPL(appl)->releasing == 0) {
+		if (APPL(appl)->releasing <= 0) {
 	                APPL(appl)->signal = 0;
 			APPL_MARK_FREE(appl);
 			printk(KERN_INFO "b1capi: appl %d down\n", appl);
@@ -433,6 +472,7 @@
 
 /* -------- card ready callback ------------------------------- */
 
+
 void avmb1_card_ready(avmb1_card * card)
 {
         struct capi_profile *profp =
@@ -441,6 +481,7 @@
 	__u16 appl;
 	char *cardname, cname[20];
 	__u32 flag;
+        int nbchan = profp->nbchannel;
 
 	card->cversion.majorversion = 2;
 	card->cversion.minorversion = 0;
@@ -453,9 +494,14 @@
 
 	for (appl = 1; appl <= CAPI_MAXAPPL; appl++) {
 		if (VALID_APPLID(appl) && !APPL(appl)->releasing) {
+			int nconn, want = APPL(appl)->rparam.level3cnt;
+
+			if (want > 0) nconn = want;
+			else nconn = nbchan * -want;
+			if (nconn == 0) nconn = nbchan;
+
 			B1_send_register(card->port, appl,
-				1024 * (APPL(appl)->rparam.level3cnt+1),
-				APPL(appl)->rparam.level3cnt,
+				1024 * (nconn+1), nconn,
 				APPL(appl)->rparam.datablkcnt,
 				APPL(appl)->rparam.datablklen);
 		}
@@ -553,8 +599,8 @@
 				 SA_SHIRQ, card->name, card)) != 0) {
 		printk(KERN_ERR "b1capi: unable to get IRQ %d (irqval=%d).\n",
 		       irq, irqval);
-		release_region((unsigned short) port, AVMB1_PORTLEN);
-		return -EIO;
+		release_region(port, AVMB1_PORTLEN);
+		return -EBUSY;
 	}
 
 	card->cardstate = CARD_DETECTED;
@@ -578,8 +624,14 @@
 	if (!B1_valid_irq(irq, cardtype)) {
 		printk(KERN_WARNING "b1capi: irq %d not valid for %s-card.\n",
 				irq, cardtype2str(cardtype));
-		return -EIO;
+		return -EINVAL;
+	}
+	if (!B1_valid_port(port, cardtype)) {
+		printk(KERN_WARNING "b1capi: port 0x%x not valid for %s-card.\n",
+				port, cardtype2str(cardtype));
+		return -EINVAL;
 	}
+	B1_reset(port);
 	if ((rc = B1_detect(port, cardtype)) != 0) {
 		printk(KERN_NOTICE "b1capi: NO %s-card at 0x%x (%d)\n",
 					  cardtype2str(cardtype), port, rc);
@@ -591,10 +643,10 @@
 	   	case AVM_CARDTYPE_M1:
 	   	case AVM_CARDTYPE_M2:
 	   	case AVM_CARDTYPE_B1:
+	   	case AVM_CARDTYPE_B1PCI:
 	    		printk(KERN_NOTICE "b1capi: AVM-%s-Controller detected at 0x%x\n", cardtype2str(cardtype), port);
 			break;
 	   	case AVM_CARDTYPE_T1:
-	    		printk(KERN_NOTICE "b1capi: AVM-%s-Controller may be at 0x%x\n", cardtype2str(cardtype), port);
 			break;
 	}
 
@@ -603,11 +655,11 @@
 
 int avmb1_probecard(int port, int irq, int cardtype)
 {
-	if (check_region((unsigned short) port, AVMB1_PORTLEN)) {
+	if (check_region(port, AVMB1_PORTLEN)) {
 		printk(KERN_WARNING
 		       "b1capi: ports 0x%03x-0x%03x in use.\n",
 		       port, port + AVMB1_PORTLEN);
-		return -EIO;
+		return -EBUSY;
 	}
         return avmb1_detectcard(port, irq, cardtype);
 }
@@ -618,11 +670,16 @@
    	if (!VALID_CARD(cnr)) 
 		return -ESRCH;
 	card = CARD(cnr);
+
 	if (card->cardstate == CARD_FREE)
 		return -ESRCH;
 	if (card->cardstate == CARD_RUNNING)
 		avmb1_card_down(card, freeio);
 
+	if (card->cardstate != CARD_FREE)
+		if (card->cardtype == AVM_CARDTYPE_T1)
+			T1_reset(card->port);
+
 	free_irq(card->irq, card);
 	if (freeio)
 		release_region(card->port, AVMB1_PORTLEN);
@@ -667,6 +724,7 @@
 
 static __u16 capi_register(capi_register_params * rparam, __u16 * applidp)
 {
+	int nconn, want = rparam->level3cnt;
 	int i;
 	int appl;
 
@@ -686,13 +744,20 @@
 	memcpy(&APPL(appl)->rparam, rparam, sizeof(capi_register_params));
 
 	for (i = 0; i < CAPI_MAXCONTR; i++) {
+		struct capi_profile *profp =
+			(struct capi_profile *)cards[i].version[VER_PROFILE];
+
 		if (cards[i].cardstate != CARD_RUNNING)
 			continue;
+
+		if (want > 0) nconn = want;
+		else nconn = profp->nbchannel * -want;
+		if (nconn == 0) nconn = profp->nbchannel;
+
 		B1_send_register(cards[i].port, appl,
-			       1024 * (APPL(appl)->rparam.level3cnt + 1),
-				 APPL(appl)->rparam.level3cnt,
-				 APPL(appl)->rparam.datablkcnt,
-				 APPL(appl)->rparam.datablklen);
+			1024 * (nconn+1), nconn,
+			APPL(appl)->rparam.datablkcnt,
+			APPL(appl)->rparam.datablklen);
 	}
 	*applidp = appl;
 	printk(KERN_INFO "b1capi: appl %d up\n", appl);
@@ -705,8 +770,6 @@
 	struct sk_buff *skb;
 	int i;
 
-	if (ncards == 0)
-		return CAPI_REGNOTINSTALLED;
 	if (!VALID_APPLID(applid) || APPL(applid)->releasing)
 		return CAPI_ILLAPPNR;
 	while ((skb = skb_dequeue(&APPL(applid)->recv_queue)) != 0)
@@ -718,7 +781,7 @@
 		APPL(applid)->releasing++;
 		B1_send_release(cards[i].port, applid);
 	}
-	if (APPL(applid)->releasing == 0) {
+	if (APPL(applid)->releasing <= 0) {
 	        APPL(applid)->signal = 0;
 		APPL_MARK_FREE(applid);
 		printk(KERN_INFO "b1capi: appl %d down\n", applid);
@@ -863,7 +926,43 @@
 		if ((rc = avmb1_probecard(cdef.port, cdef.irq, cdef.cardtype)) != 0)
 			return rc;
 
-		return avmb1_addcard(cdef.port, cdef.irq, cdef.cardtype);
+                if (cdef.cardtype == AVM_CARDTYPE_T1) {
+			int i;
+		        for (i=0; i < CAPI_MAXCONTR; i++) {
+	        	    	if (   cards[i].cardstate != CARD_FREE
+				    && cards[i].cardtype == AVM_CARDTYPE_T1
+				    && cards[i].cardnr == cdef.cardnr) {
+					printk(KERN_ERR
+						"b1capi: T1-HEMA-card-%d already at 0x%x\n",
+						cdef.cardnr, cards[i].port);
+					return -EBUSY;
+				}
+                        }
+			rc = T1_detectandinit(cdef.port,cdef.irq,cdef.cardnr);
+			if (rc) {
+			        printk(KERN_NOTICE "b1capi: NO T1-HEMA-card-%d at 0x%x (%d)\n",
+					  cdef.cardnr, cdef.port, rc);
+				return -EIO;
+                        }
+			printk(KERN_NOTICE "b1capi: T1-HEMA-card-%d at 0x%x\n",
+				  cdef.cardnr, cdef.port);
+		}
+
+		rc = avmb1_addcard(cdef.port, cdef.irq, cdef.cardtype);
+		if (rc < 0)
+			return rc;
+		/* don't want to change interface t
+		   addcard/probecard/registercard */
+                if (cdef.cardtype == AVM_CARDTYPE_T1) {
+			int i;
+		        for (i=0; i < CAPI_MAXCONTR; i++) {
+	        	    	if (cards[i].cnr == rc) {
+					cards[i].cardnr = cdef.cardnr;
+					break;
+				}
+                        }
+		}
+		return rc;
 
 	case AVMB1_LOAD:
 	case AVMB1_LOAD_AND_CONFIG:
@@ -883,8 +982,7 @@
 			return -ESRCH;
 
 		if (ldef.t4file.len <= 0) {
-			if (loaddebug)
-				printk(KERN_DEBUG "b1capi: load: invalid parameter length of t4file is %d ?\n", ldef.t4file.len);
+			printk(KERN_DEBUG "b1capi: load: invalid parameter: length of t4file is %d ?\n", ldef.t4file.len);
 			return -EINVAL;
 		}
 
@@ -906,12 +1004,19 @@
 		}
 
 		B1_reset(card->port);
+
+		if (loaddebug) {
+			printk(KERN_DEBUG "b1capi: loading contr %d\n",
+				ldef.contr);
+		}
+
 		if ((rc = B1_load_t4file(card->port, &ldef.t4file))) {
 			B1_reset(card->port);
 			printk(KERN_ERR "b1capi: failed to load t4file!!\n");
 			card->cardstate = CARD_DETECTED;
 			return rc;
 		}
+
 		B1_disable_irq(card->port);
 
 		if (ldef.t4config.len > 0) { /* load config */
@@ -944,8 +1049,7 @@
 		card->cardstate = CARD_INITSTATE;
 		save_flags(flags);
 		cli();
-		B1_assign_irq(card->port, card->irq, card->cardtype);
-		B1_enable_irq(card->port);
+		B1_setinterrupt(card->port, card->irq, card->cardtype);
 		restore_flags(flags);
 
 		if (loaddebug) {
@@ -956,7 +1060,14 @@
 		/*
 		 * init card
 		 */
-		B1_send_init(card->port, AVM_NAPPS, AVM_NNCCI, card->cnr - 1);
+                if (card->cardtype == AVM_CARDTYPE_T1)
+		   B1_send_init(card->port, AVM_NAPPS,
+					    AVM_NNCCI_PER_CHANNEL*30,
+				   	    card->cnr - 1);
+		else
+		   B1_send_init(card->port, AVM_NAPPS,
+					    AVM_NNCCI_PER_CHANNEL*2,
+				   	    card->cnr - 1);
 
 		if (loaddebug) {
 			printk(KERN_DEBUG "b1capi: load: waiting for init reply contr %d\n",
@@ -998,6 +1109,19 @@
 			return rc;
 
 		return 0;
+	case AVMB1_REMOVECARD:
+		if ((rc = copy_from_user((void *) &rdef, data,
+					 sizeof(avmb1_resetdef))))
+			return rc;
+		if (!VALID_CARD(rdef.contr))
+			return -ESRCH;
+
+		card = CARD(rdef.contr);
+
+		if (card->cardstate != CARD_DETECTED)
+			return -EBUSY;
+
+		return avmb1_unregistercard(rdef.contr, 1);
 	}
 	return -EINVAL;
 }
@@ -1035,6 +1159,7 @@
 	userp->next = capi_users;
 	capi_users = userp;
 	MOD_INC_USE_COUNT;
+	printk(KERN_NOTICE "b1capi: %s attached\n", userp->name);
 
 	return &avmb1_interface;
 }
@@ -1048,6 +1173,7 @@
 			*pp = userp->next;
 			userp->next = 0;
 			MOD_DEC_USE_COUNT;
+			printk(KERN_NOTICE "b1capi: %s detached\n", userp->name);
 			return 0;
 		}
 	}
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/avmb1/b1lli.c linux/drivers/isdn/avmb1/b1lli.c
--- v2.3.3/linux/drivers/isdn/avmb1/b1lli.c	Wed Dec 23 09:44:41 1998
+++ linux/drivers/isdn/avmb1/b1lli.c	Sun May 23 10:03:41 1999
@@ -1,11 +1,46 @@
 /*
- * $Id: b1lli.c,v 1.6 1998/02/13 07:09:11 calle Exp $
+ * $Id: b1lli.c,v 1.10 1999/04/15 19:49:31 calle Exp $
  * 
  * ISDN lowlevel-module for AVM B1-card.
  * 
  * (c) Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
  * $Log: b1lli.c,v $
+ * Revision 1.10  1999/04/15 19:49:31  calle
+ * fix fuer die B1-PCI. Jetzt geht z.B. auch IRQ 17 ...
+ *
+ * Revision 1.9  1999/01/05 18:33:23  he
+ * merged remaining 2.2pre{1,2} changes (jiffies and Config)
+ *
+ * Revision 1.8  1998/10/25 14:39:00  fritz
+ * Backported from MIPS (Cobalt).
+ *
+ * Revision 1.7  1998/03/29 16:06:00  calle
+ * changes from 2.0 tree merged.
+ *
+ * Revision 1.1.2.10  1998/03/20 20:34:41  calle
+ * port valid check now only for T1, because of the PCI and PCMCIA cards.
+ *
+ * Revision 1.1.2.9  1998/03/20 14:38:20  calle
+ * capidrv: prepared state machines for suspend/resume/hold
+ * capidrv: fix bug in state machine if B1/T1 is out of nccis
+ * b1capi: changed some errno returns.
+ * b1capi: detect if you try to add same T1 to different io address.
+ * b1capi: change number of nccis depending on number of channels.
+ * b1lli: cosmetics
+ *
+ * Revision 1.1.2.8  1998/03/18 17:43:29  calle
+ * T1 with fastlink, bugfix for multicontroller support in capidrv.c
+ *
+ * Revision 1.1.2.7  1998/03/04 17:33:50  calle
+ * Changes for T1.
+ *
+ * Revision 1.1.2.6  1998/02/27 15:40:44  calle
+ * T1 running with slow link. bugfix in capi_release.
+ *
+ * Revision 1.1.2.5  1998/02/13 16:28:28  calle
+ * first step for T1
+ *
  * Revision 1.6  1998/02/13 07:09:11  calle
  * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
  *
@@ -41,6 +76,7 @@
  *
  * 
  */
+/* #define FASTLINK_DEBUG */
 
 #include <linux/kernel.h>
 #include <linux/skbuff.h>
@@ -55,6 +91,8 @@
 #include "capicmd.h"
 #include "capiutil.h"
 
+extern int showcapimsgs;
+
 /*
  * LLI Messages to the ISDN-ControllerISDN Controller 
  */
@@ -93,6 +131,8 @@
 #define SEND_CONFIG		0x21    /*
                                          */
 
+#define SEND_POLLACK		0x73    /* T1 Watchdog */
+
 /*
  * LLI Messages from the ISDN-ControllerISDN Controller 
  */
@@ -136,6 +176,10 @@
 #define RECEIVE_RELEASE		0x26	/*
 					   * int32 AppllID int32 0xffffffff 
 					 */
+#define RECEIVE_TASK_READY	0x31	/*
+					   * int32 tasknr
+					   * int32 Length Taskname ...
+					 */
 
 #define WRITE_REGISTER		0x00
 #define READ_REGISTER		0x01
@@ -150,14 +194,48 @@
 #define B1_OUTSTAT		0x03
 #define B1_RESET		0x10
 #define B1_ANALYSE		0x04
-#define B1_IDENT		0x17  /* Hema card T1 */
-#define B1_IRQ_MASTER		0x12  /* Hema card T1 */
+
+/* Hema card T1 */
+
+#define T1_FASTLINK		0x00
+#define T1_SLOWLINK		0x08
+
+#define T1_READ			B1_READ
+#define T1_WRITE		B1_WRITE
+#define T1_INSTAT		B1_INSTAT
+#define T1_OUTSTAT		B1_OUTSTAT
+#define T1_IRQENABLE		0x05
+#define T1_FIFOSTAT		0x06
+#define T1_RESETLINK		0x10
+#define T1_ANALYSE		0x11
+#define T1_IRQMASTER		0x12
+#define T1_IDENT		0x17
+#define T1_RESETBOARD		0x1f
+
+#define	T1F_IREADY		0x01
+#define	T1F_IHALF		0x02
+#define	T1F_IFULL		0x04
+#define	T1F_IEMPTY		0x08
+#define	T1F_IFLAGS		0xF0
+
+#define	T1F_OREADY		0x10
+#define	T1F_OHALF		0x20
+#define	T1F_OEMPTY		0x40
+#define	T1F_OFULL		0x80
+#define	T1F_OFLAGS		0xF0
+
+/* there are HEMA cards with 1k and 4k FIFO out */
+#define FIFO_OUTBSIZE		256
+#define FIFO_INPBSIZE		512
+
+#define HEMA_VERSION_ID		0
+#define HEMA_PAL_ID		0
 
 #define B1_STAT0(cardtype)  ((cardtype) == AVM_CARDTYPE_M1 ? 0x81200000l : 0x80A00000l)
 #define B1_STAT1(cardtype)  (0x80E00000l)
 
 
-static inline unsigned char b1outp(unsigned short base,
+static inline unsigned char b1outp(unsigned int base,
 				   unsigned short offset,
 				   unsigned char value)
 {
@@ -165,22 +243,44 @@
 	return inb(base + B1_ANALYSE);
 }
 
-static inline int B1_rx_full(unsigned short base)
+static inline void t1outp(unsigned int base,
+			  unsigned short offset,
+			  unsigned char value)
+{
+	outb(value, base + offset);
+}
+
+static inline unsigned char t1inp(unsigned int base,
+			          unsigned short offset)
+{
+	return inb(base + offset);
+}
+
+static inline int B1_isfastlink(unsigned int base)
+{
+	return (inb(base + T1_IDENT) & ~0x82) == 1;
+}
+static inline unsigned char B1_fifostatus(unsigned int base)
+{
+	return inb(base + T1_FIFOSTAT);
+}
+
+static inline int B1_rx_full(unsigned int base)
 {
 	return inb(base + B1_INSTAT) & 0x1;
 }
 
-static inline unsigned char B1_get_byte(unsigned short base)
+static inline unsigned char B1_get_byte(unsigned int base)
 {
-	unsigned long i = jiffies + 5 * HZ;	/* maximum wait time 5 sec */
+	unsigned long i = jiffies + 1 * HZ;	/* maximum wait time 1 sec */
 	while (!B1_rx_full(base) && time_before(jiffies, i));
 	if (B1_rx_full(base))
 		return inb(base + B1_READ);
-	printk(KERN_CRIT "b1lli: rx not full after 5 second\n");
+	printk(KERN_CRIT "b1lli(0x%x): rx not full after 1 second\n", base);
 	return 0;
 }
 
-static inline unsigned int B1_get_word(unsigned short base)
+static inline unsigned int B1_get_word(unsigned int base)
 {
 	unsigned int val = 0;
 	val |= B1_get_byte(base);
@@ -190,18 +290,18 @@
 	return val;
 }
 
-static inline int B1_tx_empty(unsigned short base)
+static inline int B1_tx_empty(unsigned int base)
 {
 	return inb(base + B1_OUTSTAT) & 0x1;
 }
 
-static inline void B1_put_byte(unsigned short base, unsigned char val)
+static inline void B1_put_byte(unsigned int base, unsigned char val)
 {
 	while (!B1_tx_empty(base));
 	b1outp(base, B1_WRITE, val);
 }
 
-static inline void B1_put_word(unsigned short base, unsigned int val)
+static inline void B1_put_word(unsigned int base, unsigned int val)
 {
 	B1_put_byte(base, val & 0xff);
 	B1_put_byte(base, (val >> 8) & 0xff);
@@ -209,26 +309,95 @@
 	B1_put_byte(base, (val >> 24) & 0xff);
 }
 
-static inline unsigned int B1_get_slice(unsigned short base,
+static inline unsigned int B1_get_slice(unsigned int base,
 					unsigned char *dp)
 {
 	unsigned int len, i;
+#ifdef FASTLINK_DEBUG
+	unsigned wcnt = 0, bcnt = 0;
+#endif
 
 	len = i = B1_get_word(base);
-	while (i-- > 0)
-		*dp++ = B1_get_byte(base);
+        if (B1_isfastlink(base)) {
+		int status;
+		while (i > 0) {
+			status = B1_fifostatus(base) & (T1F_IREADY|T1F_IHALF);
+			if (i >= FIFO_INPBSIZE) status |= T1F_IFULL;
+
+			switch (status) {
+				case T1F_IREADY|T1F_IHALF|T1F_IFULL:
+					insb(base+B1_READ, dp, FIFO_INPBSIZE);
+					dp += FIFO_INPBSIZE;
+					i -= FIFO_INPBSIZE;
+#ifdef FASTLINK_DEBUG
+					wcnt += FIFO_INPBSIZE;
+#endif
+					break;
+				case T1F_IREADY|T1F_IHALF: 
+					insb(base+B1_READ,dp, i);
+#ifdef FASTLINK_DEBUG
+					wcnt += i;
+#endif
+					dp += i;
+					i = 0;
+					if (i == 0)
+						break;
+					/* fall through */
+				default:
+					*dp++ = B1_get_byte(base);
+					i--;
+#ifdef FASTLINK_DEBUG
+					bcnt++;
+#endif
+					break;
+			}
+	    }
+#ifdef FASTLINK_DEBUG
+	    if (wcnt)
+	    printk(KERN_DEBUG "b1lli(0x%x): get_slice l=%d w=%d b=%d\n",
+				base, len, wcnt, bcnt);
+#endif
+	} else {
+		while (i-- > 0)
+			*dp++ = B1_get_byte(base);
+	}
 	return len;
 }
 
-static inline void B1_put_slice(unsigned short base,
+static inline void B1_put_slice(unsigned int base,
 				unsigned char *dp, unsigned int len)
 {
-	B1_put_word(base, len);
-	while (len-- > 0)
-		B1_put_byte(base, *dp++);
+	unsigned i = len;
+	B1_put_word(base, i);
+        if (B1_isfastlink(base)) {
+		int status;
+		while (i > 0) {
+			status = B1_fifostatus(base) & (T1F_OREADY|T1F_OHALF);
+			if (i >= FIFO_OUTBSIZE) status |= T1F_OEMPTY;
+			switch (status) {
+				case T1F_OREADY|T1F_OHALF|T1F_OEMPTY: 
+					outsb(base+B1_WRITE, dp, FIFO_OUTBSIZE);
+					dp += FIFO_OUTBSIZE;
+					i -= FIFO_OUTBSIZE;
+					break;
+				case T1F_OREADY|T1F_OHALF: 
+					outsb(base+B1_WRITE, dp, i);
+					dp += i;
+					i = 0;
+				        break;
+				default:
+					B1_put_byte(base, *dp++);
+					i--;
+					break;
+			}
+		}
+	} else {
+		while (i-- > 0)
+			B1_put_byte(base, *dp++);
+	}
 }
 
-static void b1_wr_reg(unsigned short base,
+static void b1_wr_reg(unsigned int base,
                       unsigned int reg,
 		      unsigned int value)
 {
@@ -237,7 +406,7 @@
         B1_put_word(base, value);
 }
 
-static inline unsigned int b1_rd_reg(unsigned short base,
+static inline unsigned int b1_rd_reg(unsigned int base,
                                      unsigned int reg)
 {
 	B1_put_byte(base, READ_REGISTER);
@@ -246,14 +415,14 @@
 	
 }
 
-static inline void b1_set_test_bit(unsigned short base,
+static inline void b1_set_test_bit(unsigned int base,
 				   int cardtype,
 				   int onoff)
 {
     b1_wr_reg(base, B1_STAT0(cardtype), onoff ? 0x21 : 0x20);
 }
 
-static inline int b1_get_test_bit(unsigned short base,
+static inline int b1_get_test_bit(unsigned int base,
                                   int cardtype)
 {
     return (b1_rd_reg(base, B1_STAT0(cardtype)) & 0x01) != 0;
@@ -278,6 +447,26 @@
  112,				/* irq 15 */
 };
 
+static int hema_irq_table[16] =
+{0,
+ 0,
+ 0,
+ 0x80,				/* irq 3 */
+ 0,
+ 0x90,				/* irq 5 */
+ 0,
+ 0xA0,				/* irq 7 */
+ 0,
+ 0xB0,				/* irq 9 */
+ 0xC0,				/* irq 10 */
+ 0xD0,				/* irq 11 */
+ 0xE0,				/* irq 12 */
+ 0,
+ 0,
+ 0xF0,				/* irq 15 */
+};
+
+
 int B1_valid_irq(unsigned irq, int cardtype)
 {
 	switch (cardtype) {
@@ -285,36 +474,76 @@
 	   case AVM_CARDTYPE_M1:
 	   case AVM_CARDTYPE_M2:
 	   case AVM_CARDTYPE_B1:
-	   	return irq_table[irq] != 0;
+	   	return irq_table[irq & 0xf] != 0;
 	   case AVM_CARDTYPE_T1:
-	   	return irq == 5;
+	   	return hema_irq_table[irq & 0xf] != 0;
+	   case AVM_CARDTYPE_B1PCI:
+		return 1;
 	}
 }
 
-unsigned char B1_assign_irq(unsigned short base, unsigned irq, int cardtype)
+int B1_valid_port(unsigned port, int cardtype)
+{
+   switch (cardtype) {
+	   default:
+	   case AVM_CARDTYPE_M1:
+	   case AVM_CARDTYPE_M2:
+	   case AVM_CARDTYPE_B1:
+#if 0	/* problem with PCMCIA and PCI cards */
+		switch (port) {
+			case 0x150:
+			case 0x250:
+			case 0x300:
+			case 0x340:
+				return 1;
+		}
+		return 0;
+#else
+		return 1;
+#endif
+	   case AVM_CARDTYPE_B1PCI:
+		return 1;
+	   case AVM_CARDTYPE_T1:
+		return ((port & 0x7) == 0) && ((port & 0x30) != 0x30);
+   }
+}
+
+void B1_setinterrupt(unsigned int base,
+			         unsigned irq, int cardtype)
 {
 	switch (cardtype) {
 	   case AVM_CARDTYPE_T1:
-	      return b1outp(base, B1_IRQ_MASTER, 0x08);
+              t1outp(base, B1_INSTAT, 0x00);
+              t1outp(base, B1_INSTAT, 0x02);
+	      t1outp(base, T1_IRQMASTER, 0x08);
+	      break;
 	   default:
 	   case AVM_CARDTYPE_M1:
 	   case AVM_CARDTYPE_M2:
 	   case AVM_CARDTYPE_B1:
-	      return b1outp(base, B1_RESET, irq_table[irq]);
+	      b1outp(base, B1_INSTAT, 0x00);
+	      b1outp(base, B1_RESET, irq_table[irq]);
+	      b1outp(base, B1_INSTAT, 0x02);
+	      break;
+	   case AVM_CARDTYPE_B1PCI:
+	      b1outp(base, B1_INSTAT, 0x00);
+	      b1outp(base, B1_RESET, 0xf0);
+	      b1outp(base, B1_INSTAT, 0x02);
+	      break;
 	 }
 }
 
-unsigned char B1_enable_irq(unsigned short base)
+unsigned char B1_disable_irq(unsigned int base)
 {
-	return b1outp(base, B1_INSTAT, 0x02);
+	return b1outp(base, B1_INSTAT, 0x00);
 }
 
-unsigned char B1_disable_irq(unsigned short base)
+void T1_disable_irq(unsigned int base)
 {
-	return b1outp(base, B1_INSTAT, 0x00);
+      t1outp(base, T1_IRQMASTER, 0x00);
 }
 
-void B1_reset(unsigned short base)
+void B1_reset(unsigned int base)
 {
 	b1outp(base, B1_RESET, 0);
 	udelay(55 * 2 * 1000);	/* 2 TIC's */
@@ -326,7 +555,19 @@
 	udelay(55 * 2 * 1000);	/* 2 TIC's */
 }
 
-int B1_detect(unsigned short base, int cardtype)
+void T1_reset(unsigned int base)
+{
+        /* reset T1 Controller */
+        B1_reset(base);
+        /* disable irq on HEMA */
+        t1outp(base, B1_INSTAT, 0x00);
+        t1outp(base, B1_OUTSTAT, 0x00);
+        t1outp(base, T1_IRQMASTER, 0x00);
+        /* reset HEMA board configuration */
+	t1outp(base, T1_RESETBOARD, 0xf);
+}
+
+int B1_detect(unsigned int base, int cardtype)
 {
 	int onoff, i;
 
@@ -372,10 +613,79 @@
 	return 0;
 }
 
+int T1_detectandinit(unsigned int base, unsigned irq, int cardnr)
+{
+	unsigned char cregs[8];
+	unsigned char reverse_cardnr;
+	unsigned long flags;
+	unsigned char dummy;
+	int i;
+
+	reverse_cardnr =   ((cardnr & 0x01) << 3) | ((cardnr & 0x02) << 1)
+		         | ((cardnr & 0x04) >> 1) | ((cardnr & 0x08) >> 3);
+	cregs[0] = (HEMA_VERSION_ID << 4) | (reverse_cardnr & 0xf);
+	cregs[1] = 0x00; /* fast & slow link connected to CON1 */
+	cregs[2] = 0x05; /* fast link 20MBit, slow link 20 MBit */
+	cregs[3] = 0;
+	cregs[4] = 0x11; /* zero wait state */
+	cregs[5] = hema_irq_table[irq & 0xf];
+	cregs[6] = 0;
+	cregs[7] = 0;
+
+	save_flags(flags);
+	cli();
+	/* board reset */
+	t1outp(base, T1_RESETBOARD, 0xf);
+	udelay(100 * 1000);
+	dummy = t1inp(base, T1_FASTLINK+T1_OUTSTAT); /* first read */
+
+	/* write config */
+	dummy = (base >> 4) & 0xff;
+	for (i=1;i<=0xf;i++) t1outp(base, i, dummy);
+	t1outp(base, HEMA_PAL_ID & 0xf, dummy);
+	t1outp(base, HEMA_PAL_ID >> 4, cregs[0]);
+	for(i=1;i<7;i++) t1outp(base, 0, cregs[i]);
+	t1outp(base, ((base >> 4)) & 0x3, cregs[7]);
+	restore_flags(flags);
+
+	udelay(100 * 1000);
+	t1outp(base, T1_FASTLINK+T1_RESETLINK, 0);
+	t1outp(base, T1_SLOWLINK+T1_RESETLINK, 0);
+	udelay(10 * 1000);
+	t1outp(base, T1_FASTLINK+T1_RESETLINK, 1);
+	t1outp(base, T1_SLOWLINK+T1_RESETLINK, 1);
+	udelay(100 * 1000);
+	t1outp(base, T1_FASTLINK+T1_RESETLINK, 0);
+	t1outp(base, T1_SLOWLINK+T1_RESETLINK, 0);
+	udelay(10 * 1000);
+	t1outp(base, T1_FASTLINK+T1_ANALYSE, 0);
+	udelay(5 * 1000);
+	t1outp(base, T1_SLOWLINK+T1_ANALYSE, 0);
+
+	if (t1inp(base, T1_FASTLINK+T1_OUTSTAT) != 0x1) /* tx empty */
+		return 1;
+	if (t1inp(base, T1_FASTLINK+T1_INSTAT) != 0x0) /* rx empty */
+		return 2;
+	if (t1inp(base, T1_FASTLINK+T1_IRQENABLE) != 0x0)
+		return 3;
+	if ((t1inp(base, T1_FASTLINK+T1_FIFOSTAT) & 0xf0) != 0x70)
+		return 4;
+	if ((t1inp(base, T1_FASTLINK+T1_IRQMASTER) & 0x0e) != 0)
+		return 5;
+	if ((t1inp(base, T1_FASTLINK+T1_IDENT) & 0x7d) != 1)
+		return 6;
+	if (t1inp(base, T1_SLOWLINK+T1_OUTSTAT) != 0x1) /* tx empty */
+		return 7;
+	if ((t1inp(base, T1_SLOWLINK+T1_IRQMASTER) & 0x0e) != 0)
+		return 8;
+	if ((t1inp(base, T1_SLOWLINK+T1_IDENT) & 0x7d) != 0)
+		return 9;
+        return 0;
+}
 
 extern int loaddebug;
 
-int B1_load_t4file(unsigned short base, avmb1_t4file * t4file)
+int B1_load_t4file(unsigned int base, avmb1_t4file * t4file)
 {
 	/*
 	 * Data is in user space !!!
@@ -414,7 +724,7 @@
 	return 0;
 }
 
-int B1_load_config(unsigned short base, avmb1_t4file * config)
+int B1_load_config(unsigned int base, avmb1_t4file * config)
 {
 	/*
 	 * Data is in user space !!!
@@ -470,7 +780,7 @@
 	return 0;
 }
 
-int B1_loaded(unsigned short base)
+int B1_loaded(unsigned int base)
 {
 	int i;
 	unsigned char ans;
@@ -482,7 +792,7 @@
 			break;
 	}
 	if (!B1_tx_empty(base)) {
-		printk(KERN_ERR "b1lli: B1_loaded: timeout tx\n");
+		printk(KERN_ERR "b1lli(0x%x): B1_loaded: timeout tx\n", base);
 		return 0;
 	}
 	B1_put_byte(base, SEND_POLL);
@@ -494,11 +804,12 @@
 					printk(KERN_DEBUG "b1capi: loaded: ok\n");
 				return 1;
 			}
-			printk(KERN_ERR "b1lli: B1_loaded: got 0x%x ???\n", ans);
+			printk(KERN_ERR "b1lli(0x%x): B1_loaded: got 0x%x ???\n",
+				base, ans);
 			return 0;
 		}
 	}
-	printk(KERN_ERR "b1lli: B1_loaded: timeout rx\n");
+	printk(KERN_ERR "b1lli(0x%x): B1_loaded: timeout rx\n", base);
 	return 0;
 }
 
@@ -519,7 +830,7 @@
  * ------------------------------------------------------------------- 
  */
 
-void B1_send_init(unsigned short port,
+void B1_send_init(unsigned int port,
 	     unsigned int napps, unsigned int nncci, unsigned int cardnr)
 {
 	unsigned long flags;
@@ -533,7 +844,7 @@
 	restore_flags(flags);
 }
 
-void B1_send_register(unsigned short port,
+void B1_send_register(unsigned int port,
 		      __u16 appid, __u32 nmsg,
 		      __u32 nb3conn, __u32 nb3blocks, __u32 b3bsize)
 {
@@ -550,7 +861,7 @@
 	restore_flags(flags);
 }
 
-void B1_send_release(unsigned short port,
+void B1_send_release(unsigned int port,
 		     __u16 appid)
 {
 	unsigned long flags;
@@ -562,9 +873,7 @@
 	restore_flags(flags);
 }
 
-extern int showcapimsgs;
-
-void B1_send_message(unsigned short port, struct sk_buff *skb)
+void B1_send_message(unsigned int port, struct sk_buff *skb)
 {
 	unsigned long flags;
 	__u16 len = CAPIMSG_LEN(skb->data);
@@ -630,6 +939,7 @@
 	unsigned NCCI;
 	unsigned WindowSize;
 
+t1retry:
 	if (!B1_rx_full(card->port))
 		return;
 
@@ -704,7 +1014,7 @@
 		WindowSize = B1_get_word(card->port);
 
 		if (showcapimsgs)
-			printk(KERN_DEBUG "b1lli: NEW_NCCI app %u ncci 0x%x\n", ApplId, NCCI);
+			printk(KERN_DEBUG "b1lli(0x%x): NEW_NCCI app %u ncci 0x%x\n", card->port, ApplId, NCCI);
 
 		avmb1_handle_new_ncci(card, ApplId, NCCI, WindowSize);
 
@@ -716,19 +1026,23 @@
 		NCCI = B1_get_word(card->port);
 
 		if (showcapimsgs)
-			printk(KERN_DEBUG "b1lli: FREE_NCCI app %u ncci 0x%x\n", ApplId, NCCI);
+			printk(KERN_DEBUG "b1lli(0x%x): FREE_NCCI app %u ncci 0x%x\n", card->port, ApplId, NCCI);
 
 		avmb1_handle_free_ncci(card, ApplId, NCCI);
 		break;
 
 	case RECEIVE_START:
+                if (card->cardtype == AVM_CARDTYPE_T1) {
+	           B1_put_byte(card->port, SEND_POLLACK);
+		   /* printk(KERN_DEBUG "b1lli: T1 watchdog\n"); */
+                }
 		if (card->blocked)
-			printk(KERN_DEBUG "b1lli: RESTART\n");
+			printk(KERN_DEBUG "b1lli(0x%x): RESTART\n", card->port);
 		card->blocked = 0;
 		break;
 
 	case RECEIVE_STOP:
-		printk(KERN_DEBUG "b1lli: STOP\n");
+		printk(KERN_DEBUG "b1lli(0x%x): STOP\n", card->port);
 		card->blocked = 1;
 		break;
 
@@ -737,13 +1051,24 @@
 		card->versionlen = B1_get_slice(card->port, card->versionbuf);
 		card->cardstate = CARD_ACTIVE;
 		parse_version(card);
-		printk(KERN_INFO "b1lli: %s-card (%s) now active\n",
+		printk(KERN_INFO "b1lli(0x%x): %s-card (%s) now active\n",
+		       card->port,
 		       card->version[VER_CARDTYPE],
 		       card->version[VER_DRIVER]);
 		avmb1_card_ready(card);
 		break;
+        case RECEIVE_TASK_READY:
+		ApplId = (unsigned) B1_get_word(card->port);
+		MsgLen = B1_get_slice(card->port, card->msgbuf);
+		card->msgbuf[MsgLen] = 0;
+		printk(KERN_INFO "b1lli(0x%x): Task %d \"%s\" ready.\n",
+				card->port, ApplId, card->msgbuf);
+		break;
 	default:
-		printk(KERN_ERR "b1lli: B1_handle_interrupt: 0x%x ???\n", b1cmd);
+		printk(KERN_ERR "b1lli(0x%x): B1_handle_interrupt: 0x%x ???\n",
+				card->port, b1cmd);
 		break;
 	}
+	if (card->cardtype == AVM_CARDTYPE_T1) 
+		goto t1retry;
 }
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/avmb1/b1pci.c linux/drivers/isdn/avmb1/b1pci.c
--- v2.3.3/linux/drivers/isdn/avmb1/b1pci.c	Thu May 21 13:45:01 1998
+++ linux/drivers/isdn/avmb1/b1pci.c	Sun May 23 10:03:41 1999
@@ -1,11 +1,29 @@
 /*
- * $Id: b1pci.c,v 1.5 1998/01/31 11:14:43 calle Exp $
+ * $Id: b1pci.c,v 1.9 1999/04/15 19:49:32 calle Exp $
  * 
  * Module for AVM B1 PCI-card.
  * 
  * (c) Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
  * $Log: b1pci.c,v $
+ * Revision 1.9  1999/04/15 19:49:32  calle
+ * fix fuer die B1-PCI. Jetzt geht z.B. auch IRQ 17 ...
+ *
+ * Revision 1.8  1998/06/17 19:51:16  he
+ * merged with 2.1.10[34] (cosmetics and udelay() -> mdelay())
+ * brute force fix to avoid Ugh's in isdn_tty_write()
+ * cleaned up some dead code
+ *
+ * Revision 1.7  1998/03/29 16:06:02  calle
+ * changes from 2.0 tree merged.
+ *
+ * Revision 1.2.2.2  1998/01/23 16:49:30  calle
+ * added functions for pcmcia cards,
+ * avmb1_addcard returns now the controller number.
+ *
+ * Revision 1.6  1998/02/25 09:15:36  fritz
+ * apply Martin's pci driver patch to isdn drivers (vgerCVS)
+ *
  * Revision 1.5  1998/01/31 11:14:43  calle
  * merged changes to 2.0 tree, prepare 2.1.82 to work.
  *
@@ -44,7 +62,7 @@
 #define PCI_DEVICE_ID_AVM_B1	0x700
 #endif
 
-static char *revision = "$Revision: 1.5 $";
+static char *revision = "$Revision: 1.9 $";
 
 /* ------------------------------------------------------------- */
 
@@ -93,13 +111,13 @@
 		printk(KERN_INFO
 			"b1pci: PCI BIOS reports AVM-B1 at i/o %#x, irq %d\n",
 			ioaddr, irq);
-		if ((rc = avmb1_probecard(ioaddr, irq, AVM_CARDTYPE_B1)) != 0) {
+		if ((rc = avmb1_probecard(ioaddr, irq, AVM_CARDTYPE_B1PCI)) != 0) {
 		        printk(KERN_ERR
 			"b1pci: no AVM-B1 at i/o %#x, irq %d detected\n",
 			ioaddr, irq);
 			return rc;
 		}
-		if ((rc = avmb1_addcard(ioaddr, irq, AVM_CARDTYPE_B1)) < 0)
+		if ((rc = avmb1_addcard(ioaddr, irq, AVM_CARDTYPE_B1PCI)) < 0)
 			return rc;
 	}
 	return 0;
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/avmb1/capi.c linux/drivers/isdn/avmb1/capi.c
--- v2.3.3/linux/drivers/isdn/avmb1/capi.c	Mon Aug 24 13:02:44 1998
+++ linux/drivers/isdn/avmb1/capi.c	Sun May 23 10:03:41 1999
@@ -1,11 +1,23 @@
 /*
- * $Id: capi.c,v 1.10 1998/02/13 07:09:13 calle Exp $
+ * $Id: capi.c,v 1.13 1998/08/28 04:32:25 calle Exp $
  *
  * CAPI 2.0 Interface for Linux
  *
  * Copyright 1996 by Carsten Paeth (calle@calle.in-berlin.de)
  *
  * $Log: capi.c,v $
+ * Revision 1.13  1998/08/28 04:32:25  calle
+ * Added patch send by Michael.Mueller4@post.rwth-aachen.de, to get AVM B1
+ * driver running with 2.1.118.
+ *
+ * Revision 1.12  1998/05/26 22:39:34  he
+ * sync'ed with 2.1.102 where appropriate (CAPABILITY changes)
+ * concap typo
+ * cleared dev.tbusy in isdn_net BCONN status callback
+ *
+ * Revision 1.11  1998/03/09 17:46:37  he
+ * merged in 2.1.89 changes
+ *
  * Revision 1.10  1998/02/13 07:09:13  calle
  * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
  *
@@ -237,6 +249,9 @@
 		return POLLERR;
 
 	cdev = &capidevs[minor];
+#if (LINUX_VERSION_CODE < 0x020159) /* 2.1.89 */
+#define poll_wait(f,wq,w) poll_wait((wq),(w))
+#endif
 	poll_wait(file, &(cdev->recv_wait), wait);
 	mask = POLLOUT | POLLWRNORM;
 	if (!skb_queue_empty(&cdev->recv_queue))
@@ -464,7 +479,9 @@
 	capi_ioctl,
 	NULL,			/* capi_mmap */
 	capi_open,
-	NULL,			/* flush */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,118)
+        NULL,                   /* capi_flush */
+#endif
 	capi_release,
 	NULL,			/* capi_fsync */
 	NULL,			/* capi_fasync */
@@ -484,7 +501,16 @@
 
 int capi_init(void)
 {
+#if LINUX_VERSION_CODE >= 131841
+	int j;
+#endif
+	
 	memset(capidevs, 0, sizeof(capidevs));
+#if LINUX_VERSION_CODE >= 131841
+	for ( j = 0; j < CAPI_MAXMINOR+1; j++ ) {
+		init_waitqueue_head(&capidevs[j].recv_wait);
+	}
+#endif
 
 	if (register_chrdev(capi_major, "capi20", &capi_fops)) {
 		printk(KERN_ERR "capi20: unable to get major %d\n", capi_major);
@@ -496,6 +522,7 @@
 		unregister_chrdev(capi_major, "capi20");
 		return -EIO;
 	}
+	
 	return 0;
 }
 
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/avmb1/capidev.h linux/drivers/isdn/avmb1/capidev.h
--- v2.3.3/linux/drivers/isdn/avmb1/capidev.h	Mon May 17 09:55:22 1999
+++ linux/drivers/isdn/avmb1/capidev.h	Sun May 23 10:03:41 1999
@@ -22,7 +22,11 @@
 	int is_registered;
 	__u16 applid;
 	struct sk_buff_head recv_queue;
+#if LINUX_VERSION_CODE < 131841
+	struct wait_queue *recv_wait;
+#else
 	wait_queue_head_t recv_wait;
+#endif
 	__u16 errcode;
 };
 
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/avmb1/capidrv.c linux/drivers/isdn/avmb1/capidrv.c
--- v2.3.3/linux/drivers/isdn/avmb1/capidrv.c	Wed Apr  1 16:20:57 1998
+++ linux/drivers/isdn/avmb1/capidrv.c	Sun May 23 10:03:41 1999
@@ -1,11 +1,36 @@
 /*
- * $Id: capidrv.c,v 1.11 1998/02/13 07:09:15 calle Exp $
+ * $Id: capidrv.c,v 1.13 1998/06/26 15:12:55 fritz Exp $
  *
  * ISDN4Linux Driver, using capi20 interface (kernelcapi)
  *
  * Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de)
  *
  * $Log: capidrv.c,v $
+ * Revision 1.13  1998/06/26 15:12:55  fritz
+ * Added handling of STAT_ICALL with incomplete CPN.
+ * Added AT&L for ttyI emulator.
+ * Added more locking stuff in tty_write.
+ *
+ * Revision 1.12  1998/03/29 16:06:03  calle
+ * changes from 2.0 tree merged.
+ *
+ * Revision 1.3.2.10  1998/03/20 14:38:24  calle
+ * capidrv: prepared state machines for suspend/resume/hold
+ * capidrv: fix bug in state machine if B1/T1 is out of nccis
+ * b1capi: changed some errno returns.
+ * b1capi: detect if you try to add same T1 to different io address.
+ * b1capi: change number of nccis depending on number of channels.
+ * b1lli: cosmetics
+ *
+ * Revision 1.3.2.9  1998/03/20 09:01:12  calle
+ * Changes capi_register handling to get full support for 30 bchannels.
+ *
+ * Revision 1.3.2.8  1998/03/18 17:51:28  calle
+ * added controller number to error messages
+ *
+ * Revision 1.3.2.7  1998/02/27 15:40:47  calle
+ * T1 running with slow link. bugfix in capi_release.
+ *
  * Revision 1.11  1998/02/13 07:09:15  calle
  * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
  *
@@ -79,7 +104,7 @@
 #include "capicmd.h"
 #include "capidrv.h"
 
-static char *revision = "$Revision: 1.11 $";
+static char *revision = "$Revision: 1.13 $";
 int debugmode = 0;
 
 MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>");
@@ -378,8 +403,8 @@
 			return;
 		}
 	}
-	printk(KERN_ERR "capidrv: free_plci %p (0x%x) not found, Huh?\n",
-	       plcip, plcip->plci);
+	printk(KERN_ERR "capidrv-%d: free_plci %p (0x%x) not found, Huh?\n",
+	       card->contrnr, plcip, plcip->plci);
 }
 
 /* -------- ncci management ------------------------------------------ */
@@ -512,15 +537,15 @@
 
 static struct listenstatechange listentable[] =
 {
-	{ST_LISTEN_NONE, ST_LISTEN_WAIT_CONF, EV_LISTEN_REQ},
-	{ST_LISTEN_ACTIVE, ST_LISTEN_ACTIVE_WAIT_CONF, EV_LISTEN_REQ},
-	{ST_LISTEN_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_ERROR},
-    {ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_ERROR},
-	{ST_LISTEN_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_EMPTY},
-      {ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_EMPTY},
-	{ST_LISTEN_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_OK},
-	{ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_OK},
-	{},
+  {ST_LISTEN_NONE, ST_LISTEN_WAIT_CONF, EV_LISTEN_REQ},
+  {ST_LISTEN_ACTIVE, ST_LISTEN_ACTIVE_WAIT_CONF, EV_LISTEN_REQ},
+  {ST_LISTEN_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_ERROR},
+  {ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_ERROR},
+  {ST_LISTEN_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_EMPTY},
+  {ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_EMPTY},
+  {ST_LISTEN_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_OK},
+  {ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_OK},
+  {},
 };
 
 static void listen_change_state(capidrv_contr * card, int event)
@@ -529,15 +554,15 @@
 	while (p->event) {
 		if (card->state == p->actstate && p->event == event) {
 			if (debugmode)
-				printk(KERN_DEBUG "capidrv: listen_change_state %d -> %d\n",
-				       card->state, p->nextstate);
+				printk(KERN_DEBUG "capidrv-%d: listen_change_state %d -> %d\n",
+				       card->contrnr, card->state, p->nextstate);
 			card->state = p->nextstate;
 			return;
 		}
 		p++;
 	}
-	printk(KERN_ERR "capidrv: listen_change_state state=%d event=%d ????\n",
-	       card->state, event);
+	printk(KERN_ERR "capidrv-%d: listen_change_state state=%d event=%d ????\n",
+	       card->contrnr, card->state, event);
 
 }
 
@@ -567,46 +592,57 @@
 static struct plcistatechange plcitable[] =
 {
   /* P-0 */
-	{ST_PLCI_NONE, ST_PLCI_OUTGOING, EV_PLCI_CONNECT_REQ, 0},
-	{ST_PLCI_NONE, ST_PLCI_ALLOCATED, EV_PLCI_FACILITY_IND_UP, 0},
-	{ST_PLCI_NONE, ST_PLCI_INCOMING, EV_PLCI_CONNECT_IND, 0},
+  {ST_PLCI_NONE, ST_PLCI_OUTGOING, EV_PLCI_CONNECT_REQ, 0},
+  {ST_PLCI_NONE, ST_PLCI_ALLOCATED, EV_PLCI_FACILITY_IND_UP, 0},
+  {ST_PLCI_NONE, ST_PLCI_INCOMING, EV_PLCI_CONNECT_IND, 0},
+  {ST_PLCI_NONE, ST_PLCI_RESUMEING, EV_PLCI_RESUME_REQ, 0},
   /* P-0.1 */
-	{ST_PLCI_OUTGOING, ST_PLCI_NONE, EV_PLCI_CONNECT_CONF_ERROR, p0},
-	{ST_PLCI_OUTGOING, ST_PLCI_ALLOCATED, EV_PLCI_CONNECT_CONF_OK, 0},
-    {ST_PLCI_OUTGOING, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0},
- {ST_PLCI_OUTGOING, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0},
+  {ST_PLCI_OUTGOING, ST_PLCI_NONE, EV_PLCI_CONNECT_CONF_ERROR, p0},
+  {ST_PLCI_OUTGOING, ST_PLCI_ALLOCATED, EV_PLCI_CONNECT_CONF_OK, 0},
+  {ST_PLCI_OUTGOING, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0},
+  {ST_PLCI_OUTGOING, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0},
   /* P-1 */
-      {ST_PLCI_ALLOCATED, ST_PLCI_ACTIVE, EV_PLCI_CONNECT_ACTIVE_IND, 0},
-   {ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0},
-{ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0},
-    {ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0},
+  {ST_PLCI_ALLOCATED, ST_PLCI_ACTIVE, EV_PLCI_CONNECT_ACTIVE_IND, 0},
+  {ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0},
+  {ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0},
+  {ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0},
   /* P-ACT */
-      {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0},
-   {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0},
-	{ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0},
+  {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0},
+  {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0},
+  {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0},
+  {ST_PLCI_ACTIVE, ST_PLCI_HELD, EV_PLCI_HOLD_IND, 0},
+  {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTING, EV_PLCI_SUSPEND_IND, 0},
   /* P-2 */
-    {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_CONNECT_REJECT, 0},
-    {ST_PLCI_INCOMING, ST_PLCI_FACILITY_IND, EV_PLCI_FACILITY_IND_UP, 0},
-	{ST_PLCI_INCOMING, ST_PLCI_ACCEPTING, EV_PLCI_CONNECT_RESP, 0},
-    {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0},
- {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0},
-     {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0},
+  {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_CONNECT_REJECT, 0},
+  {ST_PLCI_INCOMING, ST_PLCI_FACILITY_IND, EV_PLCI_FACILITY_IND_UP, 0},
+  {ST_PLCI_INCOMING, ST_PLCI_ACCEPTING, EV_PLCI_CONNECT_RESP, 0},
+  {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0},
+  {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0},
+  {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0},
+  {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_CD_IND, 0},
   /* P-3 */
-{ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_CONNECT_REJECT, 0},
-{ST_PLCI_FACILITY_IND, ST_PLCI_ACCEPTING, EV_PLCI_CONNECT_ACTIVE_IND, 0},
-{ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0},
-	{ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0},
- {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0},
+  {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_CONNECT_REJECT, 0},
+  {ST_PLCI_FACILITY_IND, ST_PLCI_ACCEPTING, EV_PLCI_CONNECT_ACTIVE_IND, 0},
+  {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0},
+  {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0},
+  {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0},
   /* P-4 */
-      {ST_PLCI_ACCEPTING, ST_PLCI_ACTIVE, EV_PLCI_CONNECT_ACTIVE_IND, 0},
-   {ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0},
-{ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0},
-    {ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0},
+  {ST_PLCI_ACCEPTING, ST_PLCI_ACTIVE, EV_PLCI_CONNECT_ACTIVE_IND, 0},
+  {ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0},
+  {ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0},
+  {ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0},
   /* P-5 */
-{ST_PLCI_DISCONNECTING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0},
+  {ST_PLCI_DISCONNECTING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0},
   /* P-6 */
-	{ST_PLCI_DISCONNECTED, ST_PLCI_NONE, EV_PLCI_DISCONNECT_RESP, p0},
-	{},
+  {ST_PLCI_DISCONNECTED, ST_PLCI_NONE, EV_PLCI_DISCONNECT_RESP, p0},
+  /* P-0.Res */
+  {ST_PLCI_RESUMEING, ST_PLCI_NONE, EV_PLCI_RESUME_CONF_ERROR, p0},
+  {ST_PLCI_RESUMEING, ST_PLCI_RESUME, EV_PLCI_RESUME_CONF_OK, 0},
+  /* P-RES */
+  {ST_PLCI_RESUME, ST_PLCI_ACTIVE, EV_PLCI_RESUME_IND, 0},
+  /* P-HELD */
+  {ST_PLCI_HELD, ST_PLCI_ACTIVE, EV_PLCI_RETRIEVE_IND, 0},
+  {},
 };
 
 static void plci_change_state(capidrv_contr * card, capidrv_plci * plci, int event)
@@ -615,8 +651,8 @@
 	while (p->event) {
 		if (plci->state == p->actstate && p->event == event) {
 			if (debugmode)
-				printk(KERN_DEBUG "capidrv: plci_change_state:0x%x %d -> %d\n",
-				  plci->plci, plci->state, p->nextstate);
+				printk(KERN_DEBUG "capidrv-%d: plci_change_state:0x%x %d -> %d\n",
+				  card->contrnr, plci->plci, plci->state, p->nextstate);
 			plci->state = p->nextstate;
 			if (p->changefunc)
 				p->changefunc(card, plci);
@@ -624,8 +660,8 @@
 		}
 		p++;
 	}
-	printk(KERN_ERR "capidrv: plci_change_state:0x%x state=%d event=%d ????\n",
-	       plci->plci, plci->state, event);
+	printk(KERN_ERR "capidrv-%d: plci_change_state:0x%x state=%d event=%d ????\n",
+	       card->contrnr, plci->plci, plci->state, event);
 }
 
 /* ------------------------------------------------------------------ */
@@ -642,7 +678,7 @@
 				 ncci->plcip->plci,
 				 0,	/* BChannelinformation */
 				 0,	/* Keypadfacility */
-				 0,	/* Useruserdata */
+				 0,	/* Useruserdata */   /* $$$$ */
 				 0	/* Facilitydataarray */
 	);
 	send_message(card, &cmsg);
@@ -667,34 +703,35 @@
 static struct nccistatechange nccitable[] =
 {
   /* N-0 */
-	{ST_NCCI_NONE, ST_NCCI_OUTGOING, EV_NCCI_CONNECT_B3_REQ, 0},
-	{ST_NCCI_NONE, ST_NCCI_INCOMING, EV_NCCI_CONNECT_B3_IND, 0},
+  {ST_NCCI_NONE, ST_NCCI_OUTGOING, EV_NCCI_CONNECT_B3_REQ, 0},
+  {ST_NCCI_NONE, ST_NCCI_INCOMING, EV_NCCI_CONNECT_B3_IND, 0},
   /* N-0.1 */
-    {ST_NCCI_OUTGOING, ST_NCCI_ALLOCATED, EV_NCCI_CONNECT_B3_CONF_OK, 0},
-      {ST_NCCI_OUTGOING, ST_NCCI_NONE, EV_NCCI_CONNECT_B3_CONF_ERROR, 0},
+  {ST_NCCI_OUTGOING, ST_NCCI_ALLOCATED, EV_NCCI_CONNECT_B3_CONF_OK, 0},
+  {ST_NCCI_OUTGOING, ST_NCCI_NONE, EV_NCCI_CONNECT_B3_CONF_ERROR, n0},
   /* N-1 */
- {ST_NCCI_INCOMING, ST_NCCI_DISCONNECTING, EV_NCCI_CONNECT_B3_REJECT, 0},
-	{ST_NCCI_INCOMING, ST_NCCI_ALLOCATED, EV_NCCI_CONNECT_B3_RESP, 0},
+  {ST_NCCI_INCOMING, ST_NCCI_DISCONNECTING, EV_NCCI_CONNECT_B3_REJECT, 0},
+  {ST_NCCI_INCOMING, ST_NCCI_ALLOCATED, EV_NCCI_CONNECT_B3_RESP, 0},
   {ST_NCCI_INCOMING, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0},
- {ST_NCCI_INCOMING, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0},
+  {ST_NCCI_INCOMING, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0},
   /* N-2 */
-   {ST_NCCI_ALLOCATED, ST_NCCI_ACTIVE, EV_NCCI_CONNECT_B3_ACTIVE_IND, 0},
- {ST_NCCI_ALLOCATED, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0},
-{ST_NCCI_ALLOCATED, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0},
+  {ST_NCCI_ALLOCATED, ST_NCCI_ACTIVE, EV_NCCI_CONNECT_B3_ACTIVE_IND, 0},
+  {ST_NCCI_ALLOCATED, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0},
+  {ST_NCCI_ALLOCATED, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0},
   /* N-ACT */
-	{ST_NCCI_ACTIVE, ST_NCCI_RESETING, EV_NCCI_RESET_B3_REQ, 0},
-    {ST_NCCI_ACTIVE, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0},
-   {ST_NCCI_ACTIVE, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0},
+  {ST_NCCI_ACTIVE, ST_NCCI_ACTIVE, EV_NCCI_RESET_B3_IND, 0},
+  {ST_NCCI_ACTIVE, ST_NCCI_RESETING, EV_NCCI_RESET_B3_REQ, 0},
+  {ST_NCCI_ACTIVE, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0},
+  {ST_NCCI_ACTIVE, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0},
   /* N-3 */
-	{ST_NCCI_RESETING, ST_NCCI_ACTIVE, EV_NCCI_RESET_B3_IND, 0},
+  {ST_NCCI_RESETING, ST_NCCI_ACTIVE, EV_NCCI_RESET_B3_IND, 0},
   {ST_NCCI_RESETING, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0},
- {ST_NCCI_RESETING, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0},
+  {ST_NCCI_RESETING, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0},
   /* N-4 */
-	{ST_NCCI_DISCONNECTING, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0},
-	{ST_NCCI_DISCONNECTING, ST_NCCI_PREVIOUS, EV_NCCI_DISCONNECT_B3_CONF_ERROR, 0},
+  {ST_NCCI_DISCONNECTING, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0},
+  {ST_NCCI_DISCONNECTING, ST_NCCI_PREVIOUS, EV_NCCI_DISCONNECT_B3_CONF_ERROR,0},
   /* N-5 */
-    {ST_NCCI_DISCONNECTED, ST_NCCI_NONE, EV_NCCI_DISCONNECT_B3_RESP, n0},
-	{},
+  {ST_NCCI_DISCONNECTED, ST_NCCI_NONE, EV_NCCI_DISCONNECT_B3_RESP, n0},
+  {},
 };
 
 static void ncci_change_state(capidrv_contr * card, capidrv_ncci * ncci, int event)
@@ -703,8 +740,8 @@
 	while (p->event) {
 		if (ncci->state == p->actstate && p->event == event) {
 			if (debugmode)
-				printk(KERN_DEBUG "capidrv: ncci_change_state:0x%x %d -> %d\n",
-				  ncci->ncci, ncci->state, p->nextstate);
+				printk(KERN_DEBUG "capidrv-%d: ncci_change_state:0x%x %d -> %d\n",
+				  card->contrnr, ncci->ncci, ncci->state, p->nextstate);
 			if (p->nextstate == ST_NCCI_PREVIOUS) {
 				ncci->state = ncci->oldstate;
 				ncci->oldstate = p->actstate;
@@ -718,8 +755,8 @@
 		}
 		p++;
 	}
-	printk(KERN_ERR "capidrv: ncci_change_state:0x%x state=%d event=%d ????\n",
-	       ncci->ncci, ncci->state, event);
+	printk(KERN_ERR "capidrv-%d: ncci_change_state:0x%x state=%d event=%d ????\n",
+	       card->contrnr, ncci->ncci, ncci->state, event);
 }
 
 /* ------------------------------------------------------------------- */
@@ -752,8 +789,8 @@
 
 	case CAPI_LISTEN_CONF:	/* Controller */
 		if (debugmode)
-			printk(KERN_DEBUG "capidrv: listenconf Info=0x%4x (%s) cipmask=0x%x\n",
-			       cmsg->Info, capi_info2str(cmsg->Info), card->cipmask);
+			printk(KERN_DEBUG "capidrv-%d: listenconf Info=0x%4x (%s) cipmask=0x%x\n",
+			       card->contrnr, cmsg->Info, capi_info2str(cmsg->Info), card->cipmask);
 		if (cmsg->Info) {
 			listen_change_state(card, EV_LISTEN_CONF_ERROR);
 		} else if (card->cipmask == 0) {
@@ -789,7 +826,8 @@
 		      handle_dtrace_data(card, direction, 0, data, len);
 		      break;
 		   }
-	           printk(KERN_INFO "capidrv: %s from controller 0x%x layer 0x%x, ignored\n",
+	           printk(KERN_INFO "capidrv-%d: %s from controller 0x%x layer 0x%x, ignored\n",
+                        card->contrnr, 
 			capi_cmd2str(cmsg->Command, cmsg->Subcommand),
 			cmsg->adr.adrController, layer);
                    break;
@@ -805,7 +843,8 @@
 		      default: s = "unkown error"; break;
 		   }
 		   if (s)
-	           printk(KERN_INFO "capidrv: %s from controller 0x%x function %d: %s\n",
+	           printk(KERN_INFO "capidrv-%d: %s from controller 0x%x function %d: %s\n",
+			card->contrnr,
 			capi_cmd2str(cmsg->Command, cmsg->Subcommand),
 			cmsg->adr.adrController,
 			cmsg->Function, s);
@@ -822,14 +861,16 @@
 		goto ignored;
 
 	default:
-		printk(KERN_ERR "capidrv: got %s from controller 0x%x ???",
+		printk(KERN_ERR "capidrv-%d: got %s from controller 0x%x ???",
+		       card->contrnr,
 		       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
 		       cmsg->adr.adrController);
 	}
 	return;
 
       ignored:
-	printk(KERN_INFO "capidrv: %s from controller 0x%x ignored\n",
+	printk(KERN_INFO "capidrv-%d: %s from controller 0x%x ignored\n",
+	       card->contrnr,
 	       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
 	       cmsg->adr.adrController);
 }
@@ -842,12 +883,12 @@
 	int chan;
 
 	if ((chan = new_bchan(card)) == -1) {
-		printk(KERN_ERR "capidrv: incoming call on not existing bchan ?\n");
+		printk(KERN_ERR "capidrv-%d: incoming call on not existing bchan ?\n", card->contrnr);
 		return;
 	}
 	bchan = &card->bchans[chan];
 	if ((plcip = new_plci(card, chan)) == 0) {
-		printk(KERN_ERR "capidrv: incoming call: no memory, sorry.\n");
+		printk(KERN_ERR "capidrv-%d: incoming call: no memory, sorry.\n", card->contrnr);
 		return;
 	}
 	bchan->incoming = 1;
@@ -869,7 +910,8 @@
 	cmd.parm.setup.plan = cmsg->CallingPartyNumber[1];
 	cmd.parm.setup.screen = cmsg->CallingPartyNumber[2];
 
-	printk(KERN_INFO "capidrv: incoming call %s,%d,%d,%s\n", 
+	printk(KERN_INFO "capidrv-%d: incoming call %s,%d,%d,%s\n", 
+			card->contrnr,
 			cmd.parm.setup.phone,
 			cmd.parm.setup.si1,
 			cmd.parm.setup.si2,
@@ -877,6 +919,7 @@
 
 	switch (card->interface.statcallb(&cmd)) {
 	case 0:
+	case 3:
 		/* No device matching this call.
 		 * and isdn_common.c has send a HANGUP command
 		 * which is ignored in state ST_PLCI_INCOMING,
@@ -886,7 +929,8 @@
 		cmsg->Reject = 1;	/* ignore */
 		send_message(card, cmsg);
 		plci_change_state(card, plcip, EV_PLCI_CONNECT_REJECT);
-		printk(KERN_INFO "capidrv: incoming call %s,%d,%d,%s ignored\n",
+		printk(KERN_INFO "capidrv-%d: incoming call %s,%d,%d,%s ignored\n",
+			card->contrnr,
 			cmd.parm.setup.phone,
 			cmd.parm.setup.si1,
 			cmd.parm.setup.si2,
@@ -903,7 +947,8 @@
 		 * and CONNECT_RESP already sent.
 		 */
 		if (plcip->state == ST_PLCI_INCOMING) {
-			printk(KERN_INFO "capidrv: incoming call %s,%d,%d,%s tty alerting\n",
+			printk(KERN_INFO "capidrv-%d: incoming call %s,%d,%d,%s tty alerting\n",
+				card->contrnr,
 				cmd.parm.setup.phone,
 				cmd.parm.setup.si1,
 				cmd.parm.setup.si2,
@@ -920,7 +965,8 @@
 			plcip->msgid = cmsg->Messagenumber;
 			send_message(card, cmsg);
 		} else {
-			printk(KERN_INFO "capidrv: incoming call %s,%d,%d,%s on netdev\n",
+			printk(KERN_INFO "capidrv-%d: incoming call %s,%d,%d,%s on netdev\n",
+				card->contrnr,
 				cmd.parm.setup.phone,
 				cmd.parm.setup.si1,
 				cmd.parm.setup.si2,
@@ -963,7 +1009,8 @@
 
 	case CAPI_DISCONNECT_IND:	/* plci */
 		if (cmsg->Reason) {
-			printk(KERN_INFO "capidrv: %s reason 0x%x (%s) for plci 0x%x\n",
+			printk(KERN_INFO "capidrv-%d: %s reason 0x%x (%s) for plci 0x%x\n",
+			   card->contrnr,
 			   capi_cmd2str(cmsg->Command, cmsg->Subcommand),
 			       cmsg->Reason, capi_info2str(cmsg->Reason), cmsg->adr.adrPLCI);
 		}
@@ -981,7 +1028,8 @@
 
 	case CAPI_DISCONNECT_CONF:	/* plci */
 		if (cmsg->Info) {
-			printk(KERN_INFO "capidrv: %s info 0x%x (%s) for plci 0x%x\n",
+			printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for plci 0x%x\n",
+			   card->contrnr,
 			   capi_cmd2str(cmsg->Command, cmsg->Subcommand),
 			       cmsg->Info, capi_info2str(cmsg->Info), 
 			       cmsg->adr.adrPLCI);
@@ -994,7 +1042,8 @@
 
 	case CAPI_ALERT_CONF:	/* plci */
 		if (cmsg->Info) {
-			printk(KERN_INFO "capidrv: %s info 0x%x (%s) for plci 0x%x\n",
+			printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for plci 0x%x\n",
+			   card->contrnr,
 			   capi_cmd2str(cmsg->Command, cmsg->Subcommand),
 			       cmsg->Info, capi_info2str(cmsg->Info), 
 			       cmsg->adr.adrPLCI);
@@ -1007,7 +1056,8 @@
 
 	case CAPI_CONNECT_CONF:	/* plci */
 		if (cmsg->Info) {
-			printk(KERN_INFO "capidrv: %s info 0x%x (%s) for plci 0x%x\n",
+			printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for plci 0x%x\n",
+			   card->contrnr,
 			   capi_cmd2str(cmsg->Command, cmsg->Subcommand),
 			       cmsg->Info, capi_info2str(cmsg->Info), 
 			       cmsg->adr.adrPLCI);
@@ -1040,7 +1090,7 @@
 			nccip = new_ncci(card, plcip, cmsg->adr.adrPLCI);
 
 			if (!nccip) {
-				printk(KERN_ERR "capidrv: no mem for ncci, sorry\n");
+				printk(KERN_ERR "capidrv-%d: no mem for ncci, sorry\n", card->contrnr);
 				break;	/* $$$$ */
 			}
 			capi_fill_CONNECT_B3_REQ(cmsg,
@@ -1080,7 +1130,8 @@
 				break;
 			}
 		}
-		printk(KERN_ERR "capidrv: %s\n", capi_cmsg2str(cmsg));
+		printk(KERN_ERR "capidrv-%d: %s\n",
+				card->contrnr, capi_cmsg2str(cmsg));
 		break;
 
 	case CAPI_CONNECT_ACTIVE_CONF:		/* plci */
@@ -1096,18 +1147,21 @@
 		goto ignored;
 
 	default:
-		printk(KERN_ERR "capidrv: got %s for plci 0x%x ???",
+		printk(KERN_ERR "capidrv-%d: got %s for plci 0x%x ???",
+		       card->contrnr,
 		       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
 		       cmsg->adr.adrPLCI);
 	}
 	return;
       ignored:
-	printk(KERN_INFO "capidrv: %s for plci 0x%x ignored\n",
+	printk(KERN_INFO "capidrv-%d: %s for plci 0x%x ignored\n",
+	       card->contrnr,
 	       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
 	       cmsg->adr.adrPLCI);
 	return;
       notfound:
-	printk(KERN_ERR "capidrv: %s: plci 0x%x not found\n",
+	printk(KERN_ERR "capidrv-%d: %s: plci 0x%x not found\n",
+	       card->contrnr,
 	       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
 	       cmsg->adr.adrPLCI);
 	return;
@@ -1142,8 +1196,8 @@
 		cmd.arg = nccip->chan;
 		card->interface.statcallb(&cmd);
 
-		printk(KERN_INFO "capidrv: chan %d up with ncci 0x%x\n",
-		       nccip->chan, nccip->ncci);
+		printk(KERN_INFO "capidrv-%d: chan %d up with ncci 0x%x\n",
+		       card->contrnr, nccip->chan, nccip->ncci);
 		break;
 
 	case CAPI_CONNECT_B3_ACTIVE_CONF:	/* ncci */
@@ -1167,9 +1221,10 @@
 				ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_RESP);
 				break;
 			}
-			printk(KERN_ERR "capidrv: no mem for ncci, sorry\n");
+			printk(KERN_ERR "capidrv-%d: no mem for ncci, sorry\n",							card->contrnr);
 		} else {
-			printk(KERN_ERR "capidrv: %s: plci for ncci 0x%x not found\n",
+			printk(KERN_ERR "capidrv-%d: %s: plci for ncci 0x%x not found\n",
+			   card->contrnr,
 			   capi_cmd2str(cmsg->Command, cmsg->Subcommand),
 			       cmsg->adr.adrNCCI);
 		}
@@ -1192,7 +1247,8 @@
 
 		nccip->ncci = cmsg->adr.adrNCCI;
 		if (cmsg->Info) {
-			printk(KERN_INFO "capidrv: %s info 0x%x (%s) for ncci 0x%x\n",
+			printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for ncci 0x%x\n",
+			   card->contrnr,
 			   capi_cmd2str(cmsg->Command, cmsg->Subcommand),
 			       cmsg->Info, capi_info2str(cmsg->Info), 
 			       cmsg->adr.adrNCCI);
@@ -1242,7 +1298,8 @@
 		if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI)))
 			goto notfound;
 		if (cmsg->Info) {
-			printk(KERN_INFO "capidrv: %s info 0x%x (%s) for ncci 0x%x\n",
+			printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for ncci 0x%x\n",
+			   card->contrnr,
 			   capi_cmd2str(cmsg->Command, cmsg->Subcommand),
 			       cmsg->Info, capi_info2str(cmsg->Info), 
 			       cmsg->adr.adrNCCI);
@@ -1251,6 +1308,9 @@
 		break;
 
 	case CAPI_RESET_B3_IND:	/* ncci */
+		if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI)))
+			goto notfound;
+		ncci_change_state(card, nccip, EV_NCCI_RESET_B3_IND);
 		capi_cmsg_answer(cmsg);
 		send_message(card, cmsg);
 		break;
@@ -1264,18 +1324,21 @@
 		goto ignored;
 
 	default:
-		printk(KERN_ERR "capidrv: got %s for ncci 0x%x ???",
+		printk(KERN_ERR "capidrv-%d: got %s for ncci 0x%x ???",
+		       card->contrnr,
 		       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
 		       cmsg->adr.adrNCCI);
 	}
 	return;
       ignored:
-	printk(KERN_INFO "capidrv: %s for ncci 0x%x ignored\n",
+	printk(KERN_INFO "capidrv-%d: %s for ncci 0x%x ignored\n",
+	       card->contrnr,
 	       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
 	       cmsg->adr.adrNCCI);
 	return;
       notfound:
-	printk(KERN_ERR "capidrv: %s: ncci 0x%x not found\n",
+	printk(KERN_ERR "capidrv-%d: %s: ncci 0x%x not found\n",
+	       card->contrnr,
 	       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
 	       cmsg->adr.adrNCCI);
 }
@@ -1293,7 +1356,8 @@
 		return;
 	}
 	if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI))) {
-		printk(KERN_ERR "capidrv: %s: ncci 0x%x not found\n",
+		printk(KERN_ERR "capidrv-%d: %s: ncci 0x%x not found\n",
+		       card->contrnr,
 		       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
 		       cmsg->adr.adrNCCI);
 		kfree_skb(skb);
@@ -1314,7 +1378,8 @@
 	while ((*capifuncs->capi_get_message) (global.appid, &skb) == CAPI_NOERROR) {
 		capi_message2cmsg(&s_cmsg, skb->data);
 		if (debugmode > 1)
-			printk(KERN_DEBUG "capidrv_signal: %s\n", capi_cmsg2str(&s_cmsg));
+			printk(KERN_DEBUG "capidrv_signal: applid=%d %s\n",
+					applid, capi_cmsg2str(&s_cmsg));
 
 		if (s_cmsg.Command == CAPI_DATA_B3
 		    && s_cmsg.Subcommand == CAPI_IND) {
@@ -1348,7 +1413,8 @@
     isdn_ctrl cmd;
 
     if (!len) {
-       printk(KERN_DEBUG "avmb1_q931_data: len == %d\n", len);
+       printk(KERN_DEBUG "capidrv-%d: avmb1_q931_data: len == %d\n",
+				card->contrnr, len);
        return;
     }
 
@@ -1393,7 +1459,8 @@
 {
 	switch (c->arg) {
 	default:
-		printk(KERN_DEBUG "capidrv: capidrv_ioctl(%ld) called ??\n", c->arg);
+		printk(KERN_DEBUG "capidrv-%d: capidrv_ioctl(%ld) called ??\n",
+				card->contrnr, c->arg);
 		return -EINVAL;
 	}
 	return -EINVAL;
@@ -1414,7 +1481,8 @@
 			__u8 called[ISDN_MSNLEN + 2];
 
 			if (debugmode)
-				printk(KERN_DEBUG "capidrv: ISDN_CMD_DIAL(ch=%ld,\"%s,%d,%d,%s\")\n",
+				printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_DIAL(ch=%ld,\"%s,%d,%d,%s\")\n",
+					card->contrnr,
 					c->arg,
 				        c->parm.setup.phone,
 				        c->parm.setup.si1,
@@ -1424,7 +1492,8 @@
 			bchan = &card->bchans[c->arg % card->nbchan];
 
 			if (bchan->plcip) {
-				printk(KERN_ERR "capidrv: dail ch=%ld,\"%s,%d,%d,%s\" in use (plci=0x%x)\n",
+				printk(KERN_ERR "capidrv-%d: dail ch=%ld,\"%s,%d,%d,%s\" in use (plci=0x%x)\n",
+					card->contrnr,
 			        	c->arg, 
 				        c->parm.setup.phone,
 				        c->parm.setup.si1,
@@ -1486,10 +1555,11 @@
 
 	case ISDN_CMD_ACCEPTD:
 
-		if (debugmode)
-			printk(KERN_DEBUG "capidrv: ISDN_CMD_ACCEPTD(ch=%ld)\n",
-			       c->arg);
 		bchan = &card->bchans[c->arg % card->nbchan];
+		if (debugmode)
+			printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_ACCEPTD(ch=%ld) l2=%d l3=%d\n",
+			       card->contrnr,
+			       c->arg, bchan->l2, bchan->l3);
 
 		capi_fill_CONNECT_RESP(&cmdcmsg,
 				       global.appid,
@@ -1517,19 +1587,22 @@
 
 	case ISDN_CMD_ACCEPTB:
 		if (debugmode)
-			printk(KERN_DEBUG "capidrv: ISDN_CMD_ACCEPTB(ch=%ld)\n",
+			printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_ACCEPTB(ch=%ld)\n",
+			       card->contrnr,
 			       c->arg);
 		return -ENOSYS;
 
 	case ISDN_CMD_HANGUP:
 		if (debugmode)
-			printk(KERN_DEBUG "capidrv: ISDN_CMD_HANGUP(ch=%ld)\n",
+			printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_HANGUP(ch=%ld)\n",
+			       card->contrnr,
 			       c->arg);
 		bchan = &card->bchans[c->arg % card->nbchan];
 
 		if (bchan->disconnecting) {
 			if (debugmode)
-				printk(KERN_DEBUG "capidrv: chan %ld already disconnecting ...\n",
+				printk(KERN_DEBUG "capidrv-%d: chan %ld already disconnecting ...\n",
+				       card->contrnr,
 				       c->arg);
 			return 0;
 		}
@@ -1568,23 +1641,26 @@
 
 	case ISDN_CMD_SETL2:
 		if (debugmode)
-			printk(KERN_DEBUG "capidrv: set L2 on chan %ld to %ld\n",
+			printk(KERN_DEBUG "capidrv-%d: set L2 on chan %ld to %ld\n",
+			       card->contrnr,
 			       (c->arg & 0xff), (c->arg >> 8));
-		bchan = &card->bchans[c->arg % card->nbchan];
+		bchan = &card->bchans[(c->arg & 0xff) % card->nbchan];
 		bchan->l2 = (c->arg >> 8);
 		return 0;
 
 	case ISDN_CMD_SETL3:
 		if (debugmode)
-			printk(KERN_DEBUG "capidrv: set L3 on chan %ld to %ld\n",
+			printk(KERN_DEBUG "capidrv-%d: set L3 on chan %ld to %ld\n",
+			       card->contrnr,
 			       (c->arg & 0xff), (c->arg >> 8));
-		bchan = &card->bchans[c->arg % card->nbchan];
+		bchan = &card->bchans[(c->arg & 0xff) % card->nbchan];
 		bchan->l3 = (c->arg >> 8);
 		return 0;
 
 	case ISDN_CMD_SETEAZ:
 		if (debugmode)
-			printk(KERN_DEBUG "capidrv: set EAZ \"%s\" on chan %ld\n",
+			printk(KERN_DEBUG "capidrv-%d: set EAZ \"%s\" on chan %ld\n",
+			       card->contrnr,
 			       c->parm.num, c->arg);
 		bchan = &card->bchans[c->arg % card->nbchan];
 		strncpy(bchan->msn, c->parm.num, ISDN_MSNLEN);
@@ -1592,46 +1668,54 @@
 
 	case ISDN_CMD_CLREAZ:
 		if (debugmode)
-			printk(KERN_DEBUG "capidrv: clearing EAZ on chan %ld\n", c->arg);
+			printk(KERN_DEBUG "capidrv-%d: clearing EAZ on chan %ld\n",
+					card->contrnr, c->arg);
 		bchan = &card->bchans[c->arg % card->nbchan];
 		bchan->msn[0] = 0;
 		return 0;
 
 	case ISDN_CMD_LOCK:
 		if (debugmode > 1)
-			printk(KERN_DEBUG "capidrv: ISDN_CMD_LOCK (%ld)\n", c->arg);
+			printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_LOCK (%ld)\n", card->contrnr, c->arg);
 		MOD_INC_USE_COUNT;
 		break;
 
 	case ISDN_CMD_UNLOCK:
 		if (debugmode > 1)
-			printk(KERN_DEBUG "capidrv: ISDN_CMD_UNLOCK (%ld)\n", c->arg);
+			printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_UNLOCK (%ld)\n",
+					card->contrnr, c->arg);
 		MOD_DEC_USE_COUNT;
 		break;
 
 /* never called */
 	case ISDN_CMD_GETL2:
 		if (debugmode)
-			printk(KERN_DEBUG "capidrv: ISDN_CMD_GETL2\n");
+			printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_GETL2\n",
+					card->contrnr);
 		return -ENODEV;
 	case ISDN_CMD_GETL3:
 		if (debugmode)
-			printk(KERN_DEBUG "capidrv: ISDN_CMD_GETL3\n");
+			printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_GETL3\n",
+					card->contrnr);
 		return -ENODEV;
 	case ISDN_CMD_GETEAZ:
 		if (debugmode)
-			printk(KERN_DEBUG "capidrv: ISDN_CMD_GETEAZ\n");
+			printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_GETEAZ\n",
+					card->contrnr);
 		return -ENODEV;
 	case ISDN_CMD_SETSIL:
 		if (debugmode)
-			printk(KERN_DEBUG "capidrv: ISDN_CMD_SETSIL\n");
+			printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_SETSIL\n",
+					card->contrnr);
 		return -ENODEV;
 	case ISDN_CMD_GETSIL:
 		if (debugmode)
-			printk(KERN_DEBUG "capidrv: ISDN_CMD_GETSIL\n");
+			printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_GETSIL\n",
+					card->contrnr);
 		return -ENODEV;
 	default:
-		printk(KERN_ERR "capidrv: ISDN_CMD_%d, Huh?\n", c->command);
+		printk(KERN_ERR "capidrv-%d: ISDN_CMD_%d, Huh?\n",
+					card->contrnr, c->command);
 		return -EINVAL;
 	}
 	return 0;
@@ -1645,8 +1729,8 @@
 		return capidrv_command(c, card);
 
 	printk(KERN_ERR
-	     "capidrv: if_command %d called with invalid driverId %d!\n",
-	       c->command, c->driver);
+	     "capidrv-%d: if_command %d called with invalid driverId %d!\n",
+	       card->contrnr, c->command, c->driver);
 	return -ENODEV;
 }
 
@@ -1663,15 +1747,15 @@
 	__u16 datahandle;
 
 	if (!card) {
-		printk(KERN_ERR "capidrv: if_sendbuf called with invalid driverId %d!\n",
-		       id);
+		printk(KERN_ERR "capidrv-%d: if_sendbuf called with invalid driverId %d!\n",
+		       card->contrnr, id);
 		return 0;
 	}
 	bchan = &card->bchans[channel % card->nbchan];
 	nccip = bchan->nccip;
 	if (!nccip || nccip->state != ST_NCCI_ACTIVE) {
-		printk(KERN_ERR "capidrv: if_sendbuf: %s:%d: chan not up!\n",
-		       card->name, channel);
+		printk(KERN_ERR "capidrv-%d: if_sendbuf: %s:%d: chan not up!\n",
+		       card->contrnr, card->name, channel);
 		return 0;
 	}
 	datahandle = nccip->datahandle;
@@ -1691,13 +1775,14 @@
 	if (skb_headroom(skb) < msglen) {
 		struct sk_buff *nskb = dev_alloc_skb(msglen + skb->len);
 		if (!nskb) {
-			printk(KERN_ERR "capidrv: if_sendbuf: no memory\n");
+			printk(KERN_ERR "capidrv-%d: if_sendbuf: no memory\n",
+				card->contrnr);
 		        (void)capidrv_del_ack(nccip, datahandle);
 			return 0;
 		}
 #if 0
-		printk(KERN_DEBUG "capidrv: only %d bytes headroom\n",
-		       skb_headroom(skb));
+		printk(KERN_DEBUG "capidrv-%d: only %d bytes headroom\n",
+		       card->contrnr, skb_headroom(skb));
 #endif
 		memcpy(skb_put(nskb, msglen), sendcmsg.buf, msglen);
 		memcpy(skb_put(nskb, skb->len), skb->data, skb->len);
@@ -1729,8 +1814,8 @@
 	__u8 *p;
 
 	if (!card) {
-		printk(KERN_ERR "capidrv: if_readstat called with invalid driverId %d!\n",
-		       id);
+		printk(KERN_ERR "capidrv-%d: if_readstat called with invalid driverId %d!\n",
+		       card->contrnr, id);
 		return -ENODEV;
 	}
 
@@ -1776,7 +1861,7 @@
 	avmversion[1] |= (version.minormanuversion >> 4) & 0x0f;
 	avmversion[2] |= version.minormanuversion & 0x0f;
 
-        if (avmversion[0] > 3 || (avmversion[0] == 3 && avmversion[1] > 5)) {
+        if (avmversion[0] > 3 || (avmversion[0] == 3 && avmversion[1] > 6)) {
 		printk(KERN_INFO "%s: D2 trace enabled\n", card->name);
 		capi_fill_MANUFACTURER_REQ(&cmdcmsg, global.appid,
 					   card->msgid++,
@@ -1878,7 +1963,8 @@
 	printk(KERN_INFO "%s: now up (%d B channels)\n",
 		card->name, card->nbchan);
 
-        enable_dchannel_trace(card);
+        if (card->nbchan == 2)  /* no T1 */
+        	enable_dchannel_trace(card);
 
 	return 0;
 }
@@ -1965,8 +2051,8 @@
 	} else
 		strcpy(rev, " ??? ");
 
-	rparam.level3cnt = 2;
-	rparam.datablkcnt = 8;
+	rparam.level3cnt = -2;  /* number of bchannels twice */
+	rparam.datablkcnt = 16;
 	rparam.datablklen = 2048;
 	errcode = (*capifuncs->capi_register) (&rparam, &global.appid);
 	if (errcode) {
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/avmb1/capidrv.h linux/drivers/isdn/avmb1/capidrv.h
--- v2.3.3/linux/drivers/isdn/avmb1/capidrv.h	Thu May 29 21:53:05 1997
+++ linux/drivers/isdn/avmb1/capidrv.h	Sun May 23 10:03:41 1999
@@ -1,11 +1,22 @@
 /*
- * $Id: capidrv.h,v 1.1 1997/03/04 21:50:33 calle Exp $
+ * $Id: capidrv.h,v 1.2 1998/03/29 16:06:06 calle Exp $
  *
  * ISDN4Linux Driver, using capi20 interface (kernelcapi)
  *
  * Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de)
  *
  * $Log: capidrv.h,v $
+ * Revision 1.2  1998/03/29 16:06:06  calle
+ * changes from 2.0 tree merged.
+ *
+ * Revision 1.1.2.1  1998/03/20 14:38:28  calle
+ * capidrv: prepared state machines for suspend/resume/hold
+ * capidrv: fix bug in state machine if B1/T1 is out of nccis
+ * b1capi: changed some errno returns.
+ * b1capi: detect if you try to add same T1 to different io address.
+ * b1capi: change number of nccis depending on number of channels.
+ * b1lli: cosmetics
+ *
  * Revision 1.1  1997/03/04 21:50:33  calle
  * Frirst version in isdn4linux
  *
@@ -49,34 +60,70 @@
 #define ST_PLCI_ACCEPTING		6	/* P-4 */
 #define ST_PLCI_DISCONNECTING		7	/* P-5 */
 #define ST_PLCI_DISCONNECTED		8	/* P-6 */
+#define ST_PLCI_RESUMEING		9	/* P-0.Res */
+#define ST_PLCI_RESUME			10	/* P-Res */
+#define ST_PLCI_HELD			11	/* P-HELD */
 
-#define EV_PLCI_CONNECT_REQ		1	/* P-0 -> P-0.1 */
-#define EV_PLCI_CONNECT_CONF_ERROR	2	/* P-0.1 -> P-0 */
-#define EV_PLCI_CONNECT_CONF_OK		3	/* P-0.1 -> P-1 */
-#define EV_PLCI_FACILITY_IND_UP		4	/* P-0 -> P-1 */
-#define EV_PLCI_CONNECT_IND		5	/* P-0 -> P-2 */
-#define EV_PLCI_CONNECT_ACTIVE_IND	6	/* P-1 -> P-ACT */
+#define EV_PLCI_CONNECT_REQ		1	/* P-0 -> P-0.1
+                                                 */
+#define EV_PLCI_CONNECT_CONF_ERROR	2	/* P-0.1 -> P-0
+                                                 */
+#define EV_PLCI_CONNECT_CONF_OK		3	/* P-0.1 -> P-1
+                                                 */
+#define EV_PLCI_FACILITY_IND_UP		4	/* P-0 -> P-1
+                                                 */
+#define EV_PLCI_CONNECT_IND		5	/* P-0 -> P-2
+                                                 */
+#define EV_PLCI_CONNECT_ACTIVE_IND	6	/* P-1 -> P-ACT
+                                                 */
 #define EV_PLCI_CONNECT_REJECT		7	/* P-2 -> P-5
-						   P-3 -> P-5 */
+						   P-3 -> P-5
+						 */
 #define EV_PLCI_DISCONNECT_REQ		8	/* P-1 -> P-5
 						   P-2 -> P-5
 						   P-3 -> P-5
 						   P-4 -> P-5
-						   P-ACT -> P-5 */
+						   P-ACT -> P-5
+						   P-Res -> P-5 (*)
+						   P-HELD -> P-5 (*)
+						   */
 #define EV_PLCI_DISCONNECT_IND		9	/* P-1 -> P-6
 						   P-2 -> P-6
 						   P-3 -> P-6
 						   P-4 -> P-6
 						   P-5 -> P-6
-						   P-ACT -> P-6 */
+						   P-ACT -> P-6
+						   P-Res -> P-6 (*)
+						   P-HELD -> P-6 (*)
+						   */
 #define EV_PLCI_FACILITY_IND_DOWN	10	/* P-0.1 -> P-5
 						   P-1 -> P-5
 						   P-ACT -> P-5
 						   P-2 -> P-5
 						   P-3 -> P-5
-						   P-4 -> P-5 */
-#define EV_PLCI_DISCONNECT_RESP		11	/* P-6 -> P-0 */
-#define EV_PLCI_CONNECT_RESP		12	/* P-6 -> P-0 */
+						   P-4 -> P-5
+						   */
+#define EV_PLCI_DISCONNECT_RESP		11	/* P-6 -> P-0
+                                                   */
+#define EV_PLCI_CONNECT_RESP		12	/* P-6 -> P-0
+                                                   */
+
+#define EV_PLCI_RESUME_REQ		13	/* P-0 -> P-0.Res
+                                                 */
+#define EV_PLCI_RESUME_CONF_OK		14	/* P-0.Res -> P-Res
+                                                 */
+#define EV_PLCI_RESUME_CONF_ERROR	15	/* P-0.Res -> P-0
+                                                 */
+#define EV_PLCI_RESUME_IND		16	/* P-Res -> P-ACT
+                                                 */
+#define EV_PLCI_HOLD_IND		17	/* P-ACT -> P-HELD
+                                                 */
+#define EV_PLCI_RETRIEVE_IND		18	/* P-HELD -> P-ACT
+                                                 */
+#define EV_PLCI_SUSPEND_IND		19	/* P-ACT -> P-5
+                                                 */
+#define EV_PLCI_CD_IND			20	/* P-2 -> P-5
+                                                 */
 
 /*
  * per ncci state machine
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/avmb1/compat.h linux/drivers/isdn/avmb1/compat.h
--- v2.3.3/linux/drivers/isdn/avmb1/compat.h	Wed Apr  1 16:20:57 1998
+++ linux/drivers/isdn/avmb1/compat.h	Sun May 23 10:03:41 1999
@@ -1,11 +1,14 @@
 /*
- * $Id: compat.h,v 1.3 1997/11/04 06:12:15 calle Exp $
+ * $Id: compat.h,v 1.4 1998/10/25 14:39:02 fritz Exp $
  * 
  * Headerfile for Compartibility between different kernel versions
  * 
  * (c) Copyright 1996 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
  * $Log: compat.h,v $
+ * Revision 1.4  1998/10/25 14:39:02  fritz
+ * Backported from MIPS (Cobalt).
+ *
  * Revision 1.3  1997/11/04 06:12:15  calle
  * capi.c: new read/write in file_ops since 2.1.60
  * capidrv.c: prepared isdnlog interface for d2-trace in newer firmware.
@@ -32,6 +35,7 @@
 #define __COMPAT_H__
 
 #include <linux/version.h>
+#include <asm/segment.h>
 #include <linux/isdnif.h>
 
 #ifndef LinuxVersionCode
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/eicon/Makefile linux/drivers/isdn/eicon/Makefile
--- v2.3.3/linux/drivers/isdn/eicon/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/eicon/Makefile	Sun May 23 10:03:41 1999
@@ -0,0 +1,13 @@
+L_OBJS :=
+M_OBJS :=
+O_OBJS := eicon_mod.o eicon_isa.o eicon_pci.o eicon_idi.o eicon_io.o
+
+O_TARGET :=
+ifeq ($(CONFIG_ISDN_DRV_EICON),y)
+  O_TARGET += eicon.o
+else
+  O_TARGET += eicon.o
+  M_OBJS   = eicon.o
+endif
+
+include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/eicon/eicon.h linux/drivers/isdn/eicon/eicon.h
--- v2.3.3/linux/drivers/isdn/eicon/eicon.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/eicon/eicon.h	Sun May 23 10:03:41 1999
@@ -0,0 +1,528 @@
+/* $Id: eicon.h,v 1.5 1999/03/29 11:19:41 armin Exp $
+ *
+ * ISDN low-level module for Eicon.Diehl active ISDN-Cards.
+ *
+ * Copyright 1998    by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1998,99 by Armin Schindler (mac@melware.de) 
+ * Copyright 1999    Cytronics & Melware (info@melware.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: eicon.h,v $
+ * Revision 1.5  1999/03/29 11:19:41  armin
+ * I/O stuff now in seperate file (eicon_io.c)
+ * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented.
+ *
+ * Revision 1.4  1999/03/02 12:37:42  armin
+ * Added some important checks.
+ * Analog Modem with DSP.
+ * Channels will be added to Link-Level after loading firmware.
+ *
+ * Revision 1.3  1999/01/24 20:14:07  armin
+ * Changed and added debug stuff.
+ * Better data sending. (still problems with tty's flip buffer)
+ *
+ * Revision 1.2  1999/01/10 18:46:04  armin
+ * Bug with wrong values in HLC fixed.
+ * Bytes to send are counted and limited now.
+ *
+ * Revision 1.1  1999/01/01 18:09:41  armin
+ * First checkin of new eicon driver.
+ * DIVA-Server BRI/PCI and PRI/PCI are supported.
+ * Old diehl code is obsolete.
+ *
+ *
+ */
+
+
+#ifndef eicon_h
+#define eicon_h
+
+#define EICON_IOCTL_SETMMIO   0
+#define EICON_IOCTL_GETMMIO   1
+#define EICON_IOCTL_SETIRQ    2
+#define EICON_IOCTL_GETIRQ    3
+#define EICON_IOCTL_LOADBOOT  4
+#define EICON_IOCTL_ADDCARD   5
+#define EICON_IOCTL_GETTYPE   6
+#define EICON_IOCTL_LOADPCI   7 
+#define EICON_IOCTL_LOADISA   8 
+#define EICON_IOCTL_GETVER    9 
+
+#define EICON_IOCTL_MANIF    90 
+
+#define EICON_IOCTL_FREEIT   97
+#define EICON_IOCTL_TEST     98
+#define EICON_IOCTL_DEBUGVAR 99
+
+/* Bus types */
+#define EICON_BUS_ISA          1
+#define EICON_BUS_MCA          2
+#define EICON_BUS_PCI          3
+
+/* Constants for describing Card-Type */
+#define EICON_CTYPE_S            0
+#define EICON_CTYPE_SX           1
+#define EICON_CTYPE_SCOM         2
+#define EICON_CTYPE_QUADRO       3
+#define EICON_CTYPE_S2M          4
+#define EICON_CTYPE_MAESTRA      5
+#define EICON_CTYPE_MAESTRAQ     6
+#define EICON_CTYPE_MAESTRAQ_U   7
+#define EICON_CTYPE_MAESTRAP     8
+#define EICON_CTYPE_ISABRI       0x10
+#define EICON_CTYPE_ISAPRI       0x20
+#define EICON_CTYPE_MASK         0x0f
+#define EICON_CTYPE_QUADRO_NR(n) (n<<4)
+
+#define MAX_HEADER_LEN 10
+
+/* Struct for adding new cards */
+typedef struct eicon_cdef {
+        int membase;
+        int irq;
+        char id[10];
+} eicon_cdef;
+
+#define EICON_ISA_BOOT_MEMCHK 1
+#define EICON_ISA_BOOT_NORMAL 2
+
+/* Struct for downloading protocol via ioctl for ISA cards */
+typedef struct {
+	/* start-up parameters */
+	unsigned char tei;
+	unsigned char nt2;
+	unsigned char skip1;
+	unsigned char WatchDog;
+	unsigned char Permanent;
+	unsigned char XInterface;
+	unsigned char StableL2;
+	unsigned char NoOrderCheck;
+	unsigned char HandsetType;
+	unsigned char skip2;
+	unsigned char LowChannel;
+	unsigned char ProtVersion;
+	unsigned char Crc4;
+	unsigned char Loopback;
+	unsigned char oad[32];
+	unsigned char osa[32];
+	unsigned char spid[32];
+	unsigned char boot_opt;
+	unsigned long bootstrap_len;
+	unsigned long firmware_len;
+	unsigned char code[1]; /* Rest (bootstrap- and firmware code) will be allocated */
+} eicon_isa_codebuf;
+
+/* Struct for downloading protocol via ioctl for PCI cards */
+typedef struct {
+        /* start-up parameters */
+        unsigned char tei;
+        unsigned char nt2;
+        unsigned char WatchDog;
+        unsigned char Permanent;
+        unsigned char XInterface;
+        unsigned char StableL2;
+        unsigned char NoOrderCheck;
+        unsigned char HandsetType;
+        unsigned char LowChannel;
+        unsigned char ProtVersion;
+        unsigned char Crc4;
+        unsigned char NoHscx30Mode;  /* switch PRI into No HSCX30 test mode */
+        unsigned char Loopback;      /* switch card into Loopback mode */
+        struct q931_link_s
+        {
+          unsigned char oad[32];
+          unsigned char osa[32];
+          unsigned char spid[32];
+        } l[2];
+        unsigned long protocol_len;
+	unsigned int  dsp_code_num;
+        unsigned long dsp_code_len[9];
+        unsigned char code[1]; /* Rest (protocol- and dsp code) will be allocated */
+} eicon_pci_codebuf;
+
+/* Data for downloading protocol via ioctl */
+typedef union {
+	eicon_isa_codebuf isa;
+	eicon_pci_codebuf pci;
+} eicon_codebuf;
+
+/* Data for Management interface */
+typedef struct {
+	int count;
+	int pos;
+	int length[50];
+	unsigned char data[700]; 
+} eicon_manifbuf;
+
+
+#ifdef __KERNEL__
+
+/* Kernel includes */
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/tqueue.h>
+#include <linux/interrupt.h>
+#include <linux/skbuff.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/major.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/ioport.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+#include <linux/ctype.h>
+
+#include <linux/isdnif.h>
+
+typedef struct {
+  __u16 length __attribute__ ((packed)); /* length of data/parameter field */
+  __u8  P[1];                          /* data/parameter field */
+} eicon_PBUFFER;
+
+#include "eicon_isa.h"
+
+/* Macro for delay via schedule() */
+#define SLEEP(j) {                     \
+  current->state = TASK_INTERRUPTIBLE; \
+  schedule_timeout(j);                 \
+}
+
+#endif /* KERNEL */
+
+
+#define DSP_COMBIFILE_FORMAT_IDENTIFICATION_SIZE 48
+#define DSP_COMBIFILE_FORMAT_VERSION_BCD    0x0100
+
+#define DSP_FILE_FORMAT_IDENTIFICATION_SIZE 48
+#define DSP_FILE_FORMAT_VERSION_BCD         0x0100
+
+typedef struct tag_dsp_combifile_header
+{
+  char                  format_identification[DSP_COMBIFILE_FORMAT_IDENTIFICATION_SIZE] __attribute__ ((packed));
+  __u16                  format_version_bcd             __attribute__ ((packed));
+  __u16                  header_size                    __attribute__ ((packed));
+  __u16                  combifile_description_size     __attribute__ ((packed));
+  __u16                  directory_entries              __attribute__ ((packed));
+  __u16                  directory_size                 __attribute__ ((packed));
+  __u16                  download_count                 __attribute__ ((packed));
+  __u16                  usage_mask_size                __attribute__ ((packed));
+} t_dsp_combifile_header;
+
+typedef struct tag_dsp_combifile_directory_entry
+{
+  __u16                  card_type_number               __attribute__ ((packed));
+  __u16                  file_set_number                __attribute__ ((packed));
+} t_dsp_combifile_directory_entry;
+
+typedef struct tag_dsp_file_header
+{
+  char                  format_identification[DSP_FILE_FORMAT_IDENTIFICATION_SIZE] __attribute__ ((packed));
+  __u16                 format_version_bcd              __attribute__ ((packed));
+  __u16                 download_id                     __attribute__ ((packed));
+  __u16                 download_flags                  __attribute__ ((packed));
+  __u16                 required_processing_power       __attribute__ ((packed));
+  __u16                 interface_channel_count         __attribute__ ((packed));
+  __u16                 header_size                     __attribute__ ((packed));
+  __u16                 download_description_size       __attribute__ ((packed));
+  __u16                 memory_block_table_size         __attribute__ ((packed));
+  __u16                 memory_block_count              __attribute__ ((packed));
+  __u16                 segment_table_size              __attribute__ ((packed));
+  __u16                 segment_count                   __attribute__ ((packed));
+  __u16                 symbol_table_size               __attribute__ ((packed));
+  __u16                 symbol_count                    __attribute__ ((packed));
+  __u16                 total_data_size_dm              __attribute__ ((packed));
+  __u16                 data_block_count_dm             __attribute__ ((packed));
+  __u16                 total_data_size_pm              __attribute__ ((packed));
+  __u16                 data_block_count_pm             __attribute__ ((packed));
+} t_dsp_file_header;
+
+typedef struct tag_dsp_memory_block_desc
+{
+  __u16                 alias_memory_block;
+  __u16                 memory_type;
+  __u16                 address;
+  __u16                 size;             /* DSP words */
+} t_dsp_memory_block_desc;
+
+typedef struct tag_dsp_segment_desc
+{
+  __u16                 memory_block;
+  __u16                 attributes;
+  __u16                 base;
+  __u16                 size;
+  __u16                 alignment;        /* ==0 -> no other legal start address than base */
+} t_dsp_segment_desc;
+
+typedef struct tag_dsp_symbol_desc
+{
+  __u16                 symbol_id;
+  __u16                 segment;
+  __u16                 offset;
+  __u16                 size;             /* DSP words */
+} t_dsp_symbol_desc;
+
+typedef struct tag_dsp_data_block_header
+{
+  __u16                 attributes;
+  __u16                 segment;
+  __u16                 offset;
+  __u16                 size;             /* DSP words */
+} t_dsp_data_block_header;
+
+typedef struct tag_dsp_download_desc      /* be sure to keep native alignment for MAESTRA's */
+{
+  __u16                 download_id;
+  __u16                 download_flags;
+  __u16                 required_processing_power;
+  __u16                 interface_channel_count;
+  __u16                 excess_header_size;
+  __u16                 memory_block_count;
+  __u16                 segment_count;
+  __u16                 symbol_count;
+  __u16                 data_block_count_dm;
+  __u16                 data_block_count_pm;
+  __u8  *            p_excess_header_data               __attribute__ ((packed));
+  char  *            p_download_description             __attribute__ ((packed));
+  t_dsp_memory_block_desc  *p_memory_block_table        __attribute__ ((packed));
+  t_dsp_segment_desc  *p_segment_table                  __attribute__ ((packed));
+  t_dsp_symbol_desc  *p_symbol_table                    __attribute__ ((packed));
+  __u16 *            p_data_blocks_dm                   __attribute__ ((packed));
+  __u16 *            p_data_blocks_pm                   __attribute__ ((packed));
+} t_dsp_download_desc;
+
+
+#ifdef __KERNEL__
+
+typedef struct {
+  __u8                  Req;            /* pending request          */
+  __u8                  Rc;             /* return code received     */
+  __u8                  Ind;            /* indication received      */
+  __u8                  ReqCh;          /* channel of current Req   */
+  __u8                  RcCh;           /* channel of current Rc    */
+  __u8                  IndCh;          /* channel of current Ind   */
+  __u8                  D3Id;           /* ID used by this entity   */
+  __u8                  B2Id;           /* ID used by this entity   */
+  __u8                  GlobalId;       /* reserved field           */
+  __u8                  XNum;           /* number of X-buffers      */
+  __u8                  RNum;           /* number of R-buffers      */
+  struct sk_buff_head   X;              /* X-buffer queue           */
+  struct sk_buff_head   R;              /* R-buffer queue           */
+  __u8                  RNR;            /* receive not ready flag   */
+  __u8                  complete;       /* receive complete status  */
+  __u8                  busy;           /* busy flag                */
+  __u16                 ref;            /* saved reference          */
+} entity;
+
+
+typedef struct {
+	int	       No;		 /* Channel Number	        */
+	unsigned short callref;          /* Call Reference              */
+	unsigned short fsm_state;        /* Current D-Channel state     */
+	unsigned short eazmask;          /* EAZ-Mask for this Channel   */
+	unsigned int   queued;           /* User-Data Bytes in TX queue */
+	unsigned int   waitq;            /* User-Data Bytes in wait queue */
+	unsigned int   waitpq;           /* User-Data Bytes in packet queue */
+	unsigned short plci;
+	unsigned short ncci;
+	unsigned char  l2prot;           /* Layer 2 protocol            */
+	unsigned char  l3prot;           /* Layer 3 protocol            */
+	entity		e;		 /* Entity  			*/
+	char		cpn[32];	 /* remember cpn		*/
+	char		oad[32];	 /* remember oad		*/
+	unsigned char   cause[2];	 /* Last Cause			*/
+	unsigned char	si1;
+	unsigned char	si2;
+} eicon_chan;
+
+typedef struct {
+	eicon_chan *ptr;
+} eicon_chan_ptr;
+
+#include "eicon_pci.h"
+
+#define EICON_FLAGS_RUNNING  1 /* Cards driver activated */
+#define EICON_FLAGS_PVALID   2 /* Cards port is valid    */
+#define EICON_FLAGS_IVALID   4 /* Cards irq is valid     */
+#define EICON_FLAGS_MVALID   8 /* Cards membase is valid */
+#define EICON_FLAGS_LOADED   8 /* Firmware loaded        */
+
+#define EICON_BCH            2 /* # of channels per card */
+
+/* D-Channel states */
+#define EICON_STATE_NULL     0
+#define EICON_STATE_ICALL    1
+#define EICON_STATE_OCALL    2
+#define EICON_STATE_IWAIT    3
+#define EICON_STATE_OWAIT    4
+#define EICON_STATE_IBWAIT   5
+#define EICON_STATE_OBWAIT   6
+#define EICON_STATE_BWAIT    7
+#define EICON_STATE_BHWAIT   8
+#define EICON_STATE_BHWAIT2  9
+#define EICON_STATE_DHWAIT  10
+#define EICON_STATE_DHWAIT2 11
+#define EICON_STATE_BSETUP  12
+#define EICON_STATE_ACTIVE  13
+#define EICON_STATE_ICALLW  14
+#define EICON_STATE_LISTEN  15
+#define EICON_STATE_WMCONN  16
+
+#define EICON_MAX_QUEUED  8000 /* 2 * maxbuff */
+
+#define EICON_LOCK_TX 0
+#define EICON_LOCK_RX 1
+
+typedef struct {
+	int dummy;
+} eicon_mca_card;
+
+typedef union {
+	eicon_isa_card isa;
+	eicon_pci_card pci;
+	eicon_mca_card mca;
+} eicon_hwif;
+
+typedef struct {
+	__u8 ret;
+	__u8 id;
+	__u8 ch;
+} eicon_ack;
+
+typedef struct {
+	__u8 code;
+	__u8 id;
+	__u8 ch;
+} eicon_req;
+
+typedef struct {
+	__u8 ret;
+	__u8 id;
+	__u8 ch;
+	__u8 more;
+} eicon_indhdr;
+
+typedef struct msn_entry {
+	char eaz;
+        char msn[16];
+        struct msn_entry * next;
+} msn_entry;
+
+/*
+ * Per card driver data
+ */
+typedef struct eicon_card {
+	eicon_hwif hwif;                 /* Hardware dependant interface     */
+        u_char ptype;                    /* Protocol type (1TR6 or Euro)     */
+        u_char bus;                      /* Bustype (ISA, MCA, PCI)          */
+        u_char type;                     /* Cardtype (EICON_CTYPE_...)       */
+	struct eicon_card *qnext;  	 /* Pointer to next quadro adapter   */
+        int Feature;                     /* Protocol Feature Value           */
+        struct eicon_card *next;	 /* Pointer to next device struct    */
+        int myid;                        /* Driver-Nr. assigned by linklevel */
+        unsigned long flags;             /* Statusflags                      */
+        unsigned long ilock;             /* Semaphores for IRQ-Routines      */
+	struct sk_buff_head rcvq;        /* Receive-Message queue            */
+	struct sk_buff_head sndq;        /* Send-Message queue               */
+	struct sk_buff_head rackq;       /* Req-Ack-Message queue            */
+	struct sk_buff_head sackq;       /* Data-Ack-Message queue           */
+	u_char *ack_msg;                 /* Ptr to User Data in User skb     */
+	__u16 need_b3ack;                /* Flag: Need ACK for current skb   */
+	struct sk_buff *sbuf;            /* skb which is currently sent      */
+	struct tq_struct snd_tq;         /* Task struct for xmit bh          */
+	struct tq_struct rcv_tq;         /* Task struct for rcv bh           */
+	struct tq_struct ack_tq;         /* Task struct for ack bh           */
+	msn_entry *msn_list;
+	unsigned short msgnum;           /* Message number for sending       */
+	eicon_chan*	IdTable[256];	 /* Table to find entity   */
+	__u16  ref_in;
+	__u16  ref_out;
+	int    nchannels;                /* Number of B-Channels             */
+	int    ReadyInt;		 /* Ready Interrupt		     */
+	eicon_chan *bch;                 /* B-Channel status/control         */
+	char   status_buf[256];          /* Buffer for status messages       */
+	char   *status_buf_read;
+	char   *status_buf_write;
+	char   *status_buf_end;
+        isdn_if interface;               /* Interface to upper layer         */
+        char regname[35];                /* Name used for request_region     */
+} eicon_card;
+
+/* -----------------------------------------------------------**
+** The PROTOCOL_FEATURE_STRING                                **
+** defines capabilities and                                   **
+** features of the actual protocol code. It's used as a bit   **
+** mask.                                                      **
+** The following Bits are defined:                            **
+** -----------------------------------------------------------*/
+#define PROTCAP_TELINDUS  0x0001  /* Telindus Variant of protocol code   */
+#define PROTCAP_MANIF     0x0002  /* Management interface implemented    */
+#define PROTCAP_V_42      0x0004  /* V42 implemented                     */
+#define PROTCAP_V90D      0x0008  /* V.90D (implies up to 384k DSP code) */
+#define PROTCAP_EXTD_FAX  0x0010  /* Extended FAX (ECM, 2D, T6, Polling) */
+#define PROTCAP_FREE4     0x0020  /* not used                            */
+#define PROTCAP_FREE5     0x0040  /* not used                            */
+#define PROTCAP_FREE6     0x0080  /* not used                            */
+#define PROTCAP_FREE7     0x0100  /* not used                            */
+#define PROTCAP_FREE8     0x0200  /* not used                            */
+#define PROTCAP_FREE9     0x0400  /* not used                            */
+#define PROTCAP_FREE10    0x0800  /* not used                            */
+#define PROTCAP_FREE11    0x1000  /* not used                            */
+#define PROTCAP_FREE12    0x2000  /* not used                            */
+#define PROTCAP_FREE13    0x4000  /* not used                            */
+#define PROTCAP_EXTENSION 0x8000  /* used for future extentions          */
+
+#include "eicon_idi.h"
+
+extern eicon_card *cards;
+extern char *eicon_ctype_name[];
+
+
+extern __inline__ void eicon_schedule_tx(eicon_card *card)
+{
+        queue_task(&card->snd_tq, &tq_immediate);
+        mark_bh(IMMEDIATE_BH);
+}
+
+extern __inline__ void eicon_schedule_rx(eicon_card *card)
+{
+        queue_task(&card->rcv_tq, &tq_immediate);
+        mark_bh(IMMEDIATE_BH);
+}
+
+extern __inline__ void eicon_schedule_ack(eicon_card *card)
+{
+        queue_task(&card->ack_tq, &tq_immediate);
+        mark_bh(IMMEDIATE_BH);
+}
+
+extern char *eicon_find_eaz(eicon_card *, char);
+extern int eicon_addcard(int, int, int, char *);
+extern void eicon_io_transmit(eicon_card *card);
+extern void eicon_irq(int irq, void *dev_id, struct pt_regs *regs);
+extern void eicon_io_rcv_dispatch(eicon_card *ccard);
+extern void eicon_io_ack_dispatch(eicon_card *ccard);
+extern ulong DebugVar;
+
+#endif  /* __KERNEL__ */
+
+#endif	/* eicon_h */
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/eicon/eicon_dsp.h linux/drivers/isdn/eicon/eicon_dsp.h
--- v2.3.3/linux/drivers/isdn/eicon/eicon_dsp.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/eicon/eicon_dsp.h	Sun May 23 10:03:41 1999
@@ -0,0 +1,304 @@
+/* $Id: eicon_dsp.h,v 1.2 1999/03/29 11:19:42 armin Exp $
+ *
+ * ISDN lowlevel-module for Eicon.Diehl active cards.
+ *        DSP definitions
+ *
+ * Copyright 1999    by Armin Schindler (mac@melware.de)
+ * Copyright 1999    Cytronics & Melware (info@melware.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: eicon_dsp.h,v $
+ * Revision 1.2  1999/03/29 11:19:42  armin
+ * I/O stuff now in seperate file (eicon_io.c)
+ * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented.
+ *
+ * Revision 1.1  1999/03/02 12:18:54  armin
+ * First checkin of DSP defines for audio features.
+ *
+ *
+ */
+
+#ifndef DSP_H
+#define DSP_H
+
+#define DSP_UDATA_REQUEST_RECONFIGURE           0
+/*
+parameters:
+  <word> reconfigure delay (in 8kHz samples)
+  <word> reconfigure code
+  <byte> reconfigure hdlc preamble flags
+*/
+
+#define DSP_RECONFIGURE_TX_FLAG             0x8000
+#define DSP_RECONFIGURE_SHORT_TRAIN_FLAG    0x4000
+#define DSP_RECONFIGURE_ECHO_PROTECT_FLAG   0x2000
+#define DSP_RECONFIGURE_HDLC_FLAG           0x1000
+#define DSP_RECONFIGURE_SYNC_FLAG           0x0800
+#define DSP_RECONFIGURE_PROTOCOL_MASK       0x00ff
+#define DSP_RECONFIGURE_IDLE                0
+#define DSP_RECONFIGURE_V25                 1
+#define DSP_RECONFIGURE_V21_CH2             2
+#define DSP_RECONFIGURE_V27_2400            3
+#define DSP_RECONFIGURE_V27_4800            4
+#define DSP_RECONFIGURE_V29_7200            5
+#define DSP_RECONFIGURE_V29_9600            6
+#define DSP_RECONFIGURE_V33_12000           7
+#define DSP_RECONFIGURE_V33_14400           8
+#define DSP_RECONFIGURE_V17_7200            9
+#define DSP_RECONFIGURE_V17_9600            10
+#define DSP_RECONFIGURE_V17_12000           11
+#define DSP_RECONFIGURE_V17_14400           12
+
+/*
+data indications if transparent framer
+  <byte> data 0
+  <byte> data 1
+  ...
+
+data indications if HDLC framer
+  <byte> data 0
+  <byte> data 1
+  ...
+  <byte> CRC 0
+  <byte> CRC 1
+  <byte> preamble flags
+*/
+
+#define DSP_UDATA_REQUEST_SWITCH_FRAMER         1
+/*
+parameters:
+  <byte> transmit framer type
+  <byte> receive framer type
+*/
+
+#define DSP_REQUEST_SWITCH_FRAMER_HDLC          0
+#define DSP_REQUEST_SWITCH_FRAMER_TRANSPARENT   1
+#define DSP_REQUEST_SWITCH_FRAMER_ASYNC         2
+
+
+#define DSP_UDATA_REQUEST_CLEARDOWN             2
+/*
+parameters:
+  - none -
+*/
+
+
+#define DSP_UDATA_REQUEST_TX_CONFIRMATION_ON    3
+/*
+parameters:
+  - none -
+*/
+
+
+#define DSP_UDATA_REQUEST_TX_CONFIRMATION_OFF   4
+/*
+parameters:
+  - none -
+*/
+
+
+#define DSP_UDATA_INDICATION_SYNC               0
+/*
+returns:
+  <word> time of sync (sampled from counter at 8kHz)
+*/
+
+#define DSP_UDATA_INDICATION_DCD_OFF            1
+/*
+returns:
+  <word> time of DCD off (sampled from counter at 8kHz)
+*/
+
+#define DSP_UDATA_INDICATION_DCD_ON             2
+/*
+returns:
+  <word> time of DCD on (sampled from counter at 8kHz)
+  <byte> connected norm
+  <word> connected options
+  <dword> connected speed (bit/s, max of tx and rx speed)
+  <word> roundtrip delay (ms)
+  <dword> connected speed tx (bit/s)
+  <dword> connected speed rx (bit/s)
+*/
+
+#define DSP_UDATA_INDICATION_CTS_OFF            3
+/*
+returns:
+  <word> time of CTS off (sampled from counter at 8kHz)
+*/
+
+#define DSP_UDATA_INDICATION_CTS_ON             4
+/*
+returns:
+  <word> time of CTS on (sampled from counter at 8kHz)
+  <byte> connected norm
+  <word> connected options
+  <dword> connected speed (bit/s, max of tx and rx speed)
+  <word> roundtrip delay (ms)
+  <dword> connected speed tx (bit/s)
+  <dword> connected speed rx (bit/s)
+*/
+
+typedef struct eicon_dsp_ind {
+	__u16	time		__attribute__ ((packed));
+	__u8	norm		__attribute__ ((packed));
+	__u16	options		__attribute__ ((packed));
+	__u32	speed		__attribute__ ((packed));
+	__u16	delay		__attribute__ ((packed));
+	__u32	txspeed		__attribute__ ((packed));
+	__u32	rxspeed		__attribute__ ((packed));
+} eicon_dsp_ind;
+
+#define DSP_CONNECTED_NORM_UNSPECIFIED      0
+#define DSP_CONNECTED_NORM_V21              1
+#define DSP_CONNECTED_NORM_V23              2
+#define DSP_CONNECTED_NORM_V22              3
+#define DSP_CONNECTED_NORM_V22_BIS          4
+#define DSP_CONNECTED_NORM_V32_BIS          5
+#define DSP_CONNECTED_NORM_V34              6
+#define DSP_CONNECTED_NORM_V8               7
+#define DSP_CONNECTED_NORM_BELL_212A        8
+#define DSP_CONNECTED_NORM_BELL_103         9
+#define DSP_CONNECTED_NORM_V29_LEASED_LINE  10
+#define DSP_CONNECTED_NORM_V33_LEASED_LINE  11
+#define DSP_CONNECTED_NORM_V90              12
+#define DSP_CONNECTED_NORM_V21_CH2          13
+#define DSP_CONNECTED_NORM_V27_TER          14
+#define DSP_CONNECTED_NORM_V29              15
+#define DSP_CONNECTED_NORM_V33              16
+#define DSP_CONNECTED_NORM_V17              17
+
+#define DSP_CONNECTED_OPTION_TRELLIS             0x0001
+#define DSP_CONNECTED_OPTION_V42_TRANS           0x0002
+#define DSP_CONNECTED_OPTION_V42_LAPM            0x0004
+#define DSP_CONNECTED_OPTION_SHORT_TRAIN         0x0008
+#define DSP_CONNECTED_OPTION_TALKER_ECHO_PROTECT 0x0010
+
+
+#define DSP_UDATA_INDICATION_DISCONNECT         5
+/*
+returns:
+  <byte> cause
+*/
+
+#define DSP_DISCONNECT_CAUSE_NONE               0x00
+#define DSP_DISCONNECT_CAUSE_BUSY_TONE          0x01
+#define DSP_DISCONNECT_CAUSE_CONGESTION_TONE    0x02
+#define DSP_DISCONNECT_CAUSE_INCOMPATIBILITY    0x03
+#define DSP_DISCONNECT_CAUSE_CLEARDOWN          0x04
+#define DSP_DISCONNECT_CAUSE_TRAINING_TIMEOUT   0x05
+
+
+#define DSP_UDATA_INDICATION_TX_CONFIRMATION    6
+/*
+returns:
+  <word> confirmation number
+*/
+
+
+#define DSP_UDATA_REQUEST_SEND_DTMF_DIGITS      16
+/*
+parameters:
+  <word> tone duration (ms)
+  <word> gap duration (ms)
+  <byte> digit 0 tone code
+  ...
+  <byte> digit n tone code
+*/
+
+#define DSP_SEND_DTMF_DIGITS_HEADER_LENGTH      5
+
+#define DSP_DTMF_DIGIT_TONE_LOW_GROUP_697_HZ    0x00
+#define DSP_DTMF_DIGIT_TONE_LOW_GROUP_770_HZ    0x01
+#define DSP_DTMF_DIGIT_TONE_LOW_GROUP_852_HZ    0x02
+#define DSP_DTMF_DIGIT_TONE_LOW_GROUP_941_HZ    0x03
+#define DSP_DTMF_DIGIT_TONE_LOW_GROUP_MASK      0x03
+#define DSP_DTMF_DIGIT_TONE_HIGH_GROUP_1209_HZ  0x00
+#define DSP_DTMF_DIGIT_TONE_HIGH_GROUP_1336_HZ  0x04
+#define DSP_DTMF_DIGIT_TONE_HIGH_GROUP_1477_HZ  0x08
+#define DSP_DTMF_DIGIT_TONE_HIGH_GROUP_1633_HZ  0x0c
+#define DSP_DTMF_DIGIT_TONE_HIGH_GROUP_MASK     0x0c
+
+#define DSP_DTMF_DIGIT_TONE_CODE_0              0x07
+#define DSP_DTMF_DIGIT_TONE_CODE_1              0x00
+#define DSP_DTMF_DIGIT_TONE_CODE_2              0x04
+#define DSP_DTMF_DIGIT_TONE_CODE_3              0x08
+#define DSP_DTMF_DIGIT_TONE_CODE_4              0x01
+#define DSP_DTMF_DIGIT_TONE_CODE_5              0x05
+#define DSP_DTMF_DIGIT_TONE_CODE_6              0x09
+#define DSP_DTMF_DIGIT_TONE_CODE_7              0x02
+#define DSP_DTMF_DIGIT_TONE_CODE_8              0x06
+#define DSP_DTMF_DIGIT_TONE_CODE_9              0x0a
+#define DSP_DTMF_DIGIT_TONE_CODE_STAR           0x03
+#define DSP_DTMF_DIGIT_TONE_CODE_HASHMARK       0x0b
+#define DSP_DTMF_DIGIT_TONE_CODE_A              0x0c
+#define DSP_DTMF_DIGIT_TONE_CODE_B              0x0d
+#define DSP_DTMF_DIGIT_TONE_CODE_C              0x0e
+#define DSP_DTMF_DIGIT_TONE_CODE_D              0x0f
+
+
+#define DSP_UDATA_INDICATION_DTMF_DIGITS_SENT   16
+/*
+returns:
+  - none -
+  One indication will be sent for every request.
+*/
+
+
+#define DSP_UDATA_REQUEST_ENABLE_DTMF_RECEIVER  17
+/*
+parameters:
+  <word> tone duration (ms)
+  <word> gap duration (ms)
+*/
+
+#define DSP_UDATA_REQUEST_DISABLE_DTMF_RECEIVER 18
+/*
+parameters:
+  - none -
+*/
+
+#define DSP_UDATA_INDICATION_DTMF_DIGITS_RECEIVED 17
+/*
+returns:
+  <byte> digit 0 tone code
+  ...
+  <byte> digit n tone code
+*/
+
+#define DSP_DTMF_DIGITS_RECEIVED_HEADER_LENGTH  1
+
+
+#define DSP_UDATA_INDICATION_MODEM_CALLING_TONE 18
+/*
+returns:
+  - none -
+*/
+
+#define DSP_UDATA_INDICATION_FAX_CALLING_TONE   19
+/*
+returns:
+  - none -
+*/
+
+#define DSP_UDATA_INDICATION_ANSWER_TONE        20
+/*
+returns:
+  - none -
+*/
+
+#endif	/* DSP_H */
+
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/eicon/eicon_idi.c linux/drivers/isdn/eicon/eicon_idi.c
--- v2.3.3/linux/drivers/isdn/eicon/eicon_idi.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/eicon/eicon_idi.c	Sun May 23 10:03:41 1999
@@ -0,0 +1,1479 @@
+/* $Id: eicon_idi.c,v 1.9 1999/03/29 11:19:42 armin Exp $
+ *
+ * ISDN lowlevel-module for Eicon.Diehl active cards.
+ *        IDI interface 
+ *
+ * Copyright 1998,99 by Armin Schindler (mac@melware.de)
+ * Copyright 1999    Cytronics & Melware (info@melware.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: eicon_idi.c,v $
+ * Revision 1.9  1999/03/29 11:19:42  armin
+ * I/O stuff now in seperate file (eicon_io.c)
+ * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented.
+ *
+ * Revision 1.8  1999/03/02 12:37:43  armin
+ * Added some important checks.
+ * Analog Modem with DSP.
+ * Channels will be added to Link-Level after loading firmware.
+ *
+ * Revision 1.7  1999/02/03 18:34:35  armin
+ * Channel selection for outgoing calls w/o CHI.
+ * Added channel # in debug messages.
+ * L2 Transparent should work with 800 byte/packet now.
+ *
+ * Revision 1.6  1999/01/26 07:18:59  armin
+ * Bug with wrong added CPN fixed.
+ *
+ * Revision 1.5  1999/01/24 20:14:11  armin
+ * Changed and added debug stuff.
+ * Better data sending. (still problems with tty's flip buffer)
+ *
+ * Revision 1.4  1999/01/10 18:46:05  armin
+ * Bug with wrong values in HLC fixed.
+ * Bytes to send are counted and limited now.
+ *
+ * Revision 1.3  1999/01/05 14:49:34  armin
+ * Added experimental usage of full BC and HLC for
+ * speech, 3.1kHz audio, fax gr.2/3
+ *
+ * Revision 1.2  1999/01/04 13:19:29  armin
+ * Channel status with listen-request wrong - fixed.
+ *
+ * Revision 1.1  1999/01/01 18:09:41  armin
+ * First checkin of new eicon driver.
+ * DIVA-Server BRI/PCI and PRI/PCI are supported.
+ * Old diehl code is obsolete.
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include "eicon.h"
+#include "eicon_idi.h"
+#include "eicon_dsp.h"
+
+#undef EICON_FULL_SERVICE_OKTETT
+
+char *eicon_idi_revision = "$Revision: 1.9 $";
+
+eicon_manifbuf *manbuf;
+
+static char BC_Speech[3] = 	{ 0x80, 0x90, 0xa3 };
+static char BC_31khz[3] =  	{ 0x90, 0x90, 0xa3 };
+static char BC_64k[2] =    	{ 0x88, 0x90 };
+static char BC_video[3] =  	{ 0x91, 0x90, 0xa5 };
+
+#ifdef EICON_FULL_SERVICE_OKTETT
+/* 
+static char HLC_telephony[2] =	{ 0x91, 0x81 }; 
+*/
+static char HLC_faxg3[2] =  	{ 0x91, 0x84 };
+#endif
+
+int eicon_idi_manage_assign(eicon_card *card);
+int eicon_idi_manage_remove(eicon_card *card);
+
+int
+idi_assign_req(eicon_REQ *reqbuf, int signet, eicon_chan *chan)
+{
+	int l = 0;
+  if (!signet) {
+	/* Signal Layer */
+	reqbuf->XBuffer.P[l++] = CAI;
+	reqbuf->XBuffer.P[l++] = 1;
+	reqbuf->XBuffer.P[l++] = 0;
+	reqbuf->XBuffer.P[l++] = KEY;
+	reqbuf->XBuffer.P[l++] = 3;
+	reqbuf->XBuffer.P[l++] = 'I';
+	reqbuf->XBuffer.P[l++] = '4';
+	reqbuf->XBuffer.P[l++] = 'L';
+	reqbuf->XBuffer.P[l++] = SHIFT|6;
+	reqbuf->XBuffer.P[l++] = SIN;
+	reqbuf->XBuffer.P[l++] = 2;
+	reqbuf->XBuffer.P[l++] = 0;
+	reqbuf->XBuffer.P[l++] = 0;
+	reqbuf->XBuffer.P[l++] = 0; /* end */
+	reqbuf->Req = ASSIGN;
+	reqbuf->ReqCh = 0;
+	reqbuf->ReqId = 0;
+	reqbuf->XBuffer.length = l;
+	reqbuf->Reference = 0; /* Sig Entity */
+  }
+  else {
+	/* Network Layer */
+	reqbuf->XBuffer.P[l++] = CAI;
+	reqbuf->XBuffer.P[l++] = 1;
+	reqbuf->XBuffer.P[l++] = chan->e.D3Id;
+	reqbuf->XBuffer.P[l++] = LLC;
+	reqbuf->XBuffer.P[l++] = 2;
+	switch(chan->l2prot) {
+		case ISDN_PROTO_L2_HDLC:
+			reqbuf->XBuffer.P[l++] = 2;
+			break;
+		case ISDN_PROTO_L2_X75I:
+		case ISDN_PROTO_L2_X75UI:
+		case ISDN_PROTO_L2_X75BUI:
+			reqbuf->XBuffer.P[l++] = 5; 
+			break;
+		case ISDN_PROTO_L2_TRANS:
+		case ISDN_PROTO_L2_MODEM:
+			reqbuf->XBuffer.P[l++] = 2;
+			break;
+		default:
+			reqbuf->XBuffer.P[l++] = 1;
+	}
+	switch(chan->l3prot) {
+		case ISDN_PROTO_L3_TRANS:
+		default:
+			reqbuf->XBuffer.P[l++] = 4;
+	}
+	reqbuf->XBuffer.P[l++] = 0; /* end */
+	reqbuf->Req = ASSIGN;
+	reqbuf->ReqCh = 0;
+	reqbuf->ReqId = 0x20;
+	reqbuf->XBuffer.length = l;
+	reqbuf->Reference = 1; /* Net Entity */
+  }
+   return(0);
+}
+
+int
+idi_put_req(eicon_REQ *reqbuf, int rq, int signet)
+{
+	reqbuf->Req = rq;
+	reqbuf->ReqCh = 0;
+	reqbuf->ReqId = 1;
+	reqbuf->XBuffer.length = 1;
+	reqbuf->XBuffer.P[0] = 0;
+	reqbuf->Reference = signet;
+   return(0);
+}
+
+int
+idi_call_res_req(eicon_REQ *reqbuf, eicon_chan *chan)
+{
+	int l = 9;
+	reqbuf->Req = CALL_RES;
+	reqbuf->ReqCh = 0;
+	reqbuf->ReqId = 1;
+	reqbuf->XBuffer.P[0] = CAI;
+	reqbuf->XBuffer.P[1] = 6;
+	reqbuf->XBuffer.P[2] = 9;
+	reqbuf->XBuffer.P[3] = 0;
+	reqbuf->XBuffer.P[4] = 0;
+	reqbuf->XBuffer.P[5] = 0;
+	reqbuf->XBuffer.P[6] = 32;
+	reqbuf->XBuffer.P[7] = 3;
+	switch(chan->l2prot) {
+		case ISDN_PROTO_L2_X75I:
+		case ISDN_PROTO_L2_X75UI:
+		case ISDN_PROTO_L2_X75BUI:
+		case ISDN_PROTO_L2_HDLC:
+			reqbuf->XBuffer.P[1] = 1;
+			reqbuf->XBuffer.P[2] = 0x05;
+			l = 4;
+			break;
+		case ISDN_PROTO_L2_V11096:
+			reqbuf->XBuffer.P[2] = 0x0d;
+			reqbuf->XBuffer.P[3] = 5;
+			reqbuf->XBuffer.P[4] = 0;
+			break;
+		case ISDN_PROTO_L2_V11019:
+			reqbuf->XBuffer.P[2] = 0x0d;
+			reqbuf->XBuffer.P[3] = 6;
+			reqbuf->XBuffer.P[4] = 0;
+			break;
+		case ISDN_PROTO_L2_V11038:
+			reqbuf->XBuffer.P[2] = 0x0d;
+			reqbuf->XBuffer.P[3] = 7;
+			reqbuf->XBuffer.P[4] = 0;
+			break;
+		case ISDN_PROTO_L2_MODEM:
+			reqbuf->XBuffer.P[2] = 0x11;
+			reqbuf->XBuffer.P[3] = 7;
+			reqbuf->XBuffer.P[4] = 0;
+			reqbuf->XBuffer.P[5] = 0;
+			reqbuf->XBuffer.P[6] = 128;
+			reqbuf->XBuffer.P[7] = 0;
+			break;
+	}
+	reqbuf->XBuffer.P[8] = 0;
+	reqbuf->XBuffer.length = l;
+	reqbuf->Reference = 0; /* Sig Entity */
+	if (DebugVar & 8)
+		printk(KERN_DEBUG"idi_req: Ch%d: Call_Res\n", chan->No);
+   return(0);
+}
+
+int
+idi_do_req(eicon_card *card, eicon_chan *chan, int cmd, int layer)
+{
+        struct sk_buff *skb;
+        struct sk_buff *skb2;
+	eicon_REQ *reqbuf;
+	eicon_chan_ptr *chan2;
+
+        skb = alloc_skb(270 + sizeof(eicon_REQ), GFP_ATOMIC);
+        skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC);
+
+        if ((!skb) || (!skb2)) {
+		if (DebugVar & 1)
+                	printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed\n", chan->No);
+                return -ENOMEM; 
+	}
+
+	chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr));
+	chan2->ptr = chan;
+
+	reqbuf = (eicon_REQ *)skb_put(skb, 270 + sizeof(eicon_REQ));
+	if (DebugVar & 8)
+		printk(KERN_DEBUG "idi_req: Ch%d: 0x%02x (%s)\n", chan->No, cmd, (layer)?"Net":"Sig");
+	if (layer) cmd |= 0x700;
+	switch(cmd) {
+		case ASSIGN:
+		case ASSIGN|0x700:
+			idi_assign_req(reqbuf, layer, chan);
+			break;
+		case REMOVE:
+		case REMOVE|0x700:
+			idi_put_req(reqbuf, REMOVE, layer);
+			break;
+		case INDICATE_REQ:
+			idi_put_req(reqbuf, INDICATE_REQ, 0);
+			break;
+		case HANGUP:
+			idi_put_req(reqbuf, HANGUP, 0);
+			break;
+		case REJECT:
+			idi_put_req(reqbuf, REJECT, 0);
+			break;
+		case CALL_ALERT:
+			idi_put_req(reqbuf, CALL_ALERT, 0);
+			break;
+		case CALL_RES:
+			idi_call_res_req(reqbuf, chan);
+			break;
+		case IDI_N_CONNECT|0x700:
+			idi_put_req(reqbuf, IDI_N_CONNECT, 1);
+			break;
+		case IDI_N_CONNECT_ACK|0x700:
+			idi_put_req(reqbuf, IDI_N_CONNECT_ACK, 1);
+			break;
+		case IDI_N_DISC|0x700:
+			idi_put_req(reqbuf, IDI_N_DISC, 1);
+			break;
+		case IDI_N_DISC_ACK|0x700:
+			idi_put_req(reqbuf, IDI_N_DISC_ACK, 1);
+			break;
+		default:
+			if (DebugVar & 1)
+				printk(KERN_ERR "idi_req: Ch%d: Unknown request\n", chan->No);
+			return(-1);
+	}
+
+	skb_queue_tail(&chan->e.X, skb);
+	skb_queue_tail(&card->sndq, skb2); 
+	eicon_schedule_tx(card);
+	return(0);
+}
+
+int
+eicon_idi_listen_req(eicon_card *card, eicon_chan *chan)
+{
+	if (DebugVar & 16)
+		printk(KERN_DEBUG"idi_req: Ch%d: Listen_Req eazmask=0x%x\n",chan->No, chan->eazmask);
+	if (!chan->e.D3Id) {
+		idi_do_req(card, chan, ASSIGN, 0); 
+	}
+	if (chan->fsm_state == EICON_STATE_NULL) {
+		idi_do_req(card, chan, INDICATE_REQ, 0);
+		chan->fsm_state = EICON_STATE_LISTEN;
+	}
+  return(0);
+}
+
+unsigned char
+idi_si2bc(int si1, int si2, char *bc, char *hlc)
+{
+  hlc[0] = 0;
+  switch(si1) {
+	case 1:
+		bc[0] = 0x90;		/* 3,1 kHz audio */
+		bc[1] = 0x90;		/* 64 kbit/s */
+		bc[2] = 0xa3;		/* G.711 A-law */
+#ifdef EICON_FULL_SERVICE_OKTETT
+		if (si2 == 1) {
+			bc[0] = 0x80;	/* Speech */
+			hlc[0] = 0x02;	/* hlc len */
+			hlc[1] = 0x91;	/* first hic */
+			hlc[2] = 0x81;	/* Telephony */
+		}
+#endif
+		return(3);
+	case 2:
+		bc[0] = 0x90;		/* 3,1 kHz audio */
+		bc[1] = 0x90;		/* 64 kbit/s */
+		bc[2] = 0xa3;		/* G.711 A-law */
+#ifdef EICON_FULL_SERVICE_OKTETT
+		if (si2 == 2) {
+			hlc[0] = 0x02;	/* hlc len */
+			hlc[1] = 0x91;	/* first hic */
+			hlc[2] = 0x84;	/* Fax Gr.2/3 */
+		}
+#endif
+		return(3);
+	case 5:
+	case 7:
+	default:
+		bc[0] = 0x88;
+		bc[1] = 0x90;
+		return(2);
+  }
+ return (0);
+}
+
+int
+idi_hangup(eicon_card *card, eicon_chan *chan)
+{
+	if ((chan->fsm_state == EICON_STATE_ACTIVE) ||
+	    (chan->fsm_state == EICON_STATE_WMCONN)) {
+  		if (chan->e.B2Id) idi_do_req(card, chan, IDI_N_DISC, 1);
+	}
+	if (chan->e.B2Id) idi_do_req(card, chan, REMOVE, 1);
+	idi_do_req(card, chan, HANGUP, 0);
+	chan->fsm_state = EICON_STATE_NULL;
+	if (DebugVar & 8)
+		printk(KERN_DEBUG"idi_req: Ch%d: Hangup\n", chan->No);
+  return(0);
+}
+
+int
+idi_connect_res(eicon_card *card, eicon_chan *chan)
+{
+  chan->fsm_state = EICON_STATE_IWAIT;
+  idi_do_req(card, chan, CALL_RES, 0);
+  idi_do_req(card, chan, ASSIGN, 1);
+  return(0);
+}
+
+int
+idi_connect_req(eicon_card *card, eicon_chan *chan, char *phone,
+                    char *eazmsn, int si1, int si2)
+{
+	int l = 0;
+	int i;
+	unsigned char tmp;
+	unsigned char bc[5];
+	unsigned char hlc[5];
+        struct sk_buff *skb;
+        struct sk_buff *skb2;
+	eicon_REQ *reqbuf;
+	eicon_chan_ptr *chan2;
+
+        skb = alloc_skb(270 + sizeof(eicon_REQ), GFP_ATOMIC);
+        skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC);
+
+        if ((!skb) || (!skb2)) {
+		if (DebugVar & 1)
+                	printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed\n", chan->No);
+                return -ENOMEM; 
+	}
+
+	chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr));
+	chan2->ptr = chan;
+
+	reqbuf = (eicon_REQ *)skb_put(skb, 270 + sizeof(eicon_REQ));
+	reqbuf->Req = CALL_REQ;
+	reqbuf->ReqCh = 0;
+	reqbuf->ReqId = 1;
+
+	reqbuf->XBuffer.P[l++] = CPN;
+	reqbuf->XBuffer.P[l++] = strlen(phone) + 1;
+	reqbuf->XBuffer.P[l++] = 0xc1;
+	for(i=0; i<strlen(phone);i++) 
+		reqbuf->XBuffer.P[l++] = phone[i];
+
+	reqbuf->XBuffer.P[l++] = OAD;
+	reqbuf->XBuffer.P[l++] = strlen(eazmsn) + 2;
+	reqbuf->XBuffer.P[l++] = 0x01;
+	reqbuf->XBuffer.P[l++] = 0x81;
+	for(i=0; i<strlen(eazmsn);i++) 
+		reqbuf->XBuffer.P[l++] = eazmsn[i];
+
+	if ((tmp = idi_si2bc(si1, si2, bc, hlc)) > 0) {
+		reqbuf->XBuffer.P[l++] = BC;
+		reqbuf->XBuffer.P[l++] = tmp;
+		for(i=0; i<tmp;i++) 
+			reqbuf->XBuffer.P[l++] = bc[i];
+		if ((tmp=hlc[0])) {
+			reqbuf->XBuffer.P[l++] = HLC;
+			reqbuf->XBuffer.P[l++] = tmp;
+			for(i=1; i<=tmp;i++) 
+				reqbuf->XBuffer.P[l++] = hlc[i];
+		}
+	}
+        reqbuf->XBuffer.P[l++] = CAI;
+        reqbuf->XBuffer.P[l++] = 6;
+        reqbuf->XBuffer.P[l++] = 0x09;
+	reqbuf->XBuffer.P[l++] = 0;
+	reqbuf->XBuffer.P[l++] = 0;
+	reqbuf->XBuffer.P[l++] = 0;
+	reqbuf->XBuffer.P[l++] = 32;
+	reqbuf->XBuffer.P[l++] = 3;
+        switch(chan->l2prot) {
+		case ISDN_PROTO_L2_X75I:
+		case ISDN_PROTO_L2_X75UI:
+		case ISDN_PROTO_L2_X75BUI:
+                case ISDN_PROTO_L2_HDLC:
+                        reqbuf->XBuffer.P[l-6] = 5;
+                        reqbuf->XBuffer.P[l-7] = 1;
+			l -= 5; 
+                        break;
+                case ISDN_PROTO_L2_V11096:
+                        reqbuf->XBuffer.P[l-7] = 3;
+                        reqbuf->XBuffer.P[l-6] = 0x0d;
+                        reqbuf->XBuffer.P[l-5] = 5;
+                        reqbuf->XBuffer.P[l-4] = 0;
+                        l -= 3;
+                        break;
+                case ISDN_PROTO_L2_V11019:
+                        reqbuf->XBuffer.P[l-7] = 3;
+                        reqbuf->XBuffer.P[l-6] = 0x0d;
+                        reqbuf->XBuffer.P[l-5] = 6;
+                        reqbuf->XBuffer.P[l-4] = 0;
+                        l -= 3;
+                        break;
+                case ISDN_PROTO_L2_V11038:
+                        reqbuf->XBuffer.P[l-7] = 3;
+                        reqbuf->XBuffer.P[l-6] = 0x0d;
+                        reqbuf->XBuffer.P[l-5] = 7;
+                        reqbuf->XBuffer.P[l-4] = 0;
+                        l -= 3;
+                        break;
+                case ISDN_PROTO_L2_MODEM:
+			reqbuf->XBuffer.P[l-6] = 0x11;
+			reqbuf->XBuffer.P[l-5] = 7;
+			reqbuf->XBuffer.P[l-4] = 0;
+			reqbuf->XBuffer.P[l-3] = 0;
+			reqbuf->XBuffer.P[l-2] = 128;
+			reqbuf->XBuffer.P[l-1] = 0;
+                        break;
+        }
+	
+	reqbuf->XBuffer.P[l++] = 0; /* end */
+	reqbuf->XBuffer.length = l;
+	reqbuf->Reference = 0; /* Sig Entity */
+
+	skb_queue_tail(&chan->e.X, skb);
+	skb_queue_tail(&card->sndq, skb2); 
+	eicon_schedule_tx(card);
+
+	if (DebugVar & 8)
+  		printk(KERN_DEBUG"idi_req: Ch%d: Conn_Req %s -> %s\n",chan->No, eazmsn, phone);
+   return(0);
+}
+
+
+void
+idi_IndParse(eicon_card *ccard, eicon_chan *chan, idi_ind_message *message, unsigned char *buffer, int len)
+{
+	int i,j;
+	int pos = 0;
+	int codeset = 0;
+	int wlen = 0;
+	int lock = 0;
+	__u8 w;
+	__u16 code;
+	isdn_ctrl cmd;
+
+  memset(message, 0, sizeof(idi_ind_message));
+
+  if ((!len) || (!buffer[pos])) return;
+  while(pos <= len) {
+	w = buffer[pos++];
+	if (!w) return;
+	if (w & 0x80) {
+		wlen = 0;
+	}
+	else {
+		wlen = buffer[pos++];
+	}
+
+	if (pos > len) return;
+
+	if (lock & 0x80) lock &= 0x7f;
+	else codeset = lock;
+
+	if((w&0xf0) == SHIFT) {
+		codeset = w;
+		if(!(codeset & 0x08)) lock = codeset & 7;
+		codeset &= 7;
+		lock |= 0x80;
+	}
+	else {
+		if (w==ESC && wlen >=2) {
+			code = buffer[pos++]|0x800;
+			wlen--;
+		}
+		else code = w;
+		code |= (codeset<<8);
+
+		switch(code) {
+			case OAD:
+				j = 1;
+				if (wlen) {
+					message->plan = buffer[pos++];
+					if (message->plan &0x80) 
+						message->screen = 0;
+					else {
+						message->screen = buffer[pos++];
+						j = 2;
+					}
+				}
+				for(i=0; i < wlen-j; i++) 
+					message->oad[i] = buffer[pos++];
+				if (DebugVar & 2)
+					printk(KERN_DEBUG"idi_inf: Ch%d: OAD=(0x%02x,0x%02x) %s\n", chan->No, 
+						message->plan, message->screen, message->oad);
+				break;
+			case RDN:
+				j = 1;
+				if (wlen) {
+					if (!(buffer[pos++] & 0x80)) {
+						pos++; 
+						j = 2;
+					}
+				}
+				for(i=0; i < wlen-j; i++) 
+					message->rdn[i] = buffer[pos++];
+				if (DebugVar & 2)
+					printk(KERN_DEBUG"idi_inf: Ch%d: RDN= %s\n", chan->No, 
+						message->rdn);
+				break;
+			case CPN:
+				for(i=0; i < wlen; i++) 
+					message->cpn[i] = buffer[pos++];
+				if (DebugVar & 2)
+					printk(KERN_DEBUG"idi_inf: Ch%d: CPN=(0x%02x) %s\n", chan->No,
+						(__u8)message->cpn[0], message->cpn + 1);
+				break;
+			case DSA:
+				pos++;
+				for(i=0; i < wlen-1; i++) 
+					message->dsa[i] = buffer[pos++];
+				if (DebugVar & 2)
+					printk(KERN_DEBUG"idi_inf: Ch%d: DSA=%s\n", chan->No, message->dsa);
+				break;
+			case OSA:
+				pos++;
+				for(i=0; i < wlen-1; i++) 
+					message->osa[i] = buffer[pos++];
+				if (DebugVar & 2)
+					printk(KERN_DEBUG"idi_inf: Ch%d: OSA=%s\n", chan->No, message->osa);
+				break;
+			case BC:
+				for(i=0; i < wlen; i++) 
+					message->bc[i] = buffer[pos++];
+				if (DebugVar & 4)
+					printk(KERN_DEBUG"idi_inf: Ch%d: BC = 0x%02x 0x%02x 0x%02x\n", chan->No,
+						message->bc[0],message->bc[1],message->bc[2]);
+				break;
+			case 0x800|BC:
+				for(i=0; i < wlen; i++) 
+					message->e_bc[i] = buffer[pos++];
+				if (DebugVar & 4)
+					printk(KERN_DEBUG"idi_inf: Ch%d: ESC/BC=%d\n", chan->No, message->bc[0]);
+				break;
+			case LLC:
+				for(i=0; i < wlen; i++) 
+					message->llc[i] = buffer[pos++];
+				if (DebugVar & 4)
+					printk(KERN_DEBUG"idi_inf: Ch%d: LLC=%d %d %d %d\n", chan->No, message->llc[0],
+						message->llc[1],message->llc[2],message->llc[3]);
+				break;
+			case HLC:
+				for(i=0; i < wlen; i++) 
+					message->hlc[i] = buffer[pos++];
+				if (DebugVar & 4)
+					printk(KERN_DEBUG"idi_inf: Ch%d: HLC=%x %x %x %x %x\n", chan->No,
+						message->hlc[0], message->hlc[1],
+						message->hlc[2], message->hlc[3], message->hlc[4]);
+				break;
+			case DSP:
+			case 0x600|DSP:
+				for(i=0; i < wlen; i++) 
+					message->display[i] = buffer[pos++];
+				if (DebugVar & 4)
+					printk(KERN_DEBUG"idi_inf: Ch%d: Display: %s\n", chan->No,
+						message->display);
+				break;
+			case 0x600|KEY:
+				for(i=0; i < wlen; i++) 
+					message->keypad[i] = buffer[pos++];
+				if (DebugVar & 4)
+					printk(KERN_DEBUG"idi_inf: Ch%d: Keypad: %s\n", chan->No,
+						message->keypad);
+				break;
+			case NI:
+			case 0x600|NI:
+				if (wlen) {
+					if (DebugVar & 4) {
+						switch(buffer[pos] & 127) {
+							case 0:
+								printk(KERN_DEBUG"idi_inf: Ch%d: User suspended.\n", chan->No);
+								break;
+							case 1:
+								printk(KERN_DEBUG"idi_inf: Ch%d: User resumed.\n", chan->No);
+								break;
+							case 2:
+								printk(KERN_DEBUG"idi_inf: Ch%d: Bearer service change.\n", chan->No);
+								break;
+							default:
+								printk(KERN_DEBUG"idi_inf: Ch%d: Unknown Notification %x.\n", 
+										chan->No, buffer[pos] & 127);
+						}
+					}
+					pos += wlen;
+				}
+				break;
+			case PI:
+			case 0x600|PI:
+				if (wlen > 1) {
+					if (DebugVar & 4) {
+						switch(buffer[pos+1] & 127) {
+							case 1:
+								printk(KERN_DEBUG"idi_inf: Ch%d: Call is not end-to-end ISDN.\n", chan->No);
+								break;
+							case 2:
+								printk(KERN_DEBUG"idi_inf: Ch%d: Destination address is non ISDN.\n", chan->No);
+								break;
+							case 3:
+								printk(KERN_DEBUG"idi_inf: Ch%d: Origination address is non ISDN.\n", chan->No);
+								break;
+							case 4:
+								printk(KERN_DEBUG"idi_inf: Ch%d: Call has returned to the ISDN.\n", chan->No);
+								break;
+							case 5:
+								printk(KERN_DEBUG"idi_inf: Ch%d: Interworking has occurred.\n", chan->No);
+								break;
+							case 8:
+								printk(KERN_DEBUG"idi_inf: Ch%d: In-band information available.\n", chan->No);
+								break;
+							default:
+								printk(KERN_DEBUG"idi_inf: Ch%d: Unknown Progress %x.\n", 
+										chan->No, buffer[pos+1] & 127);
+						}
+					}
+				}
+				pos += wlen;
+				break;
+			case CAU:
+				for(i=0; i < wlen; i++) 
+					message->cau[i] = buffer[pos++];
+				memcpy(&chan->cause, &message->cau, 2);
+				if (DebugVar & 4)
+					printk(KERN_DEBUG"idi_inf: Ch%d: CAU=%d %d\n", chan->No,
+						message->cau[0],message->cau[1]);
+				break;
+			case 0x800|CAU:
+				for(i=0; i < wlen; i++) 
+					message->e_cau[i] = buffer[pos++];
+				if (DebugVar & 4)
+					printk(KERN_DEBUG"idi_inf: Ch%d: ECAU=%d %d\n", chan->No,
+						message->e_cau[0],message->e_cau[1]);
+				break;
+			case 0x800|CHI:
+				for(i=0; i < wlen; i++) 
+					message->e_chi[i] = buffer[pos++];
+				if (DebugVar & 4)
+					printk(KERN_DEBUG"idi_inf: Ch%d: ESC/CHI=%d\n", chan->No,
+						message->e_cau[0]);
+				break;
+			case 0x800|0x7a:
+				pos ++;
+				message->e_mt=buffer[pos++];
+				if (DebugVar & 2)
+					printk(KERN_DEBUG"idi_inf: Ch%d: EMT=0x%x\n", chan->No, message->e_mt);
+				break;
+			case DT:
+				for(i=0; i < wlen; i++) 
+					message->dt[i] = buffer[pos++];
+				if (DebugVar & 4)
+					printk(KERN_DEBUG"idi_inf: Ch%d: DT: %02d.%02d.%02d %02d:%02d:%02d\n", chan->No,
+						message->dt[2], message->dt[1], message->dt[0],
+						message->dt[3], message->dt[4], message->dt[5]);
+				break;
+			case 0x600|SIN:
+				for(i=0; i < wlen; i++) 
+					message->sin[i] = buffer[pos++];
+				if (DebugVar & 2)
+					printk(KERN_DEBUG"idi_inf: Ch%d: SIN=%d %d\n", chan->No,
+						message->sin[0],message->sin[1]);
+				break;
+			case 0x600|CPS:
+				if (DebugVar & 2)
+					printk(KERN_DEBUG"idi_inf: Ch%d: Called Party Status in ind\n", chan->No);
+				pos += wlen;
+				break;
+			case 0x600|CIF:
+				for (i = 0; i < wlen; i++)
+					if (buffer[pos + i] != '0') break;
+				memcpy(&cmd.parm.num, &buffer[pos + i], wlen - i);
+				cmd.parm.num[wlen - i] = 0;
+				if (DebugVar & 2)
+					printk(KERN_DEBUG"idi_inf: Ch%d: CIF=%s\n", chan->No, cmd.parm.num);
+				pos += wlen;
+				cmd.driver = ccard->myid;
+				cmd.command = ISDN_STAT_CINF;
+				cmd.arg = chan->No;
+				ccard->interface.statcallb(&cmd);
+				break;
+			case 0x600|DATE:
+				if (DebugVar & 2)
+					printk(KERN_DEBUG"idi_inf: Ch%d: Date in ind\n", chan->No);
+				pos += wlen;
+				break;
+			case 0xe08: 
+			case 0xe7a: 
+			case 0xe04: 
+			case 0xe00: 
+				/* *** TODO *** */
+			case CHA:
+				/* Charge advice */
+			case FTY:
+			case 0x600|FTY:
+			case CHI:
+			case 0x800:
+				/* Not yet interested in this */
+				pos += wlen;
+				break;
+			case 0x880:
+				/* Managment Information Element */
+				if (!manbuf) {
+					if (DebugVar & 1)
+						printk(KERN_WARNING"idi_err: manbuf not allocated\n");
+				}
+				else {
+					memcpy(&manbuf->data[manbuf->pos], &buffer[pos], wlen);
+					manbuf->length[manbuf->count] = wlen;
+					manbuf->count++;
+					manbuf->pos += wlen;
+				}
+				pos += wlen;
+				break;
+			default:
+				pos += wlen;
+				if (DebugVar & 6)
+					printk(KERN_WARNING"idi_inf: Ch%d: unknown information element 0x%x in ind, len:%x\n", 
+						chan->No, code, wlen);
+		}
+	}
+  }
+}
+
+void
+idi_bc2si(unsigned char *bc, unsigned char *hlc, unsigned char *si1, unsigned char *si2)
+{
+  si1[0] = 0;
+  si2[0] = 0;
+  if (memcmp(bc, BC_Speech, 3) == 0) {		/* Speech */
+	si1[0] = 1;
+#ifdef EICON_FULL_SERVICE_OKTETT
+	si2[0] = 1;
+#endif
+  }
+  if (memcmp(bc, BC_31khz, 3) == 0) {		/* 3.1kHz audio */
+	si1[0] = 1;
+#ifdef EICON_FULL_SERVICE_OKTETT
+	si2[0] = 2;
+  	if (memcmp(hlc, HLC_faxg3, 2) == 0) {	/* Fax Gr.2/3 */
+		si1[0] = 2;
+	}
+#endif
+  }
+  if (memcmp(bc, BC_64k, 2) == 0) {		/* unrestricted 64 kbits */
+	si1[0] = 7;
+  }
+  if (memcmp(bc, BC_video, 3) == 0) {		/* video */
+	si1[0] = 4;
+  }
+}
+
+void
+idi_parse_udata(eicon_card *ccard, eicon_chan *chan, unsigned char *buffer, int len)
+{
+	isdn_ctrl cmd;
+	eicon_dsp_ind *p = (eicon_dsp_ind *) (&buffer[1]);
+        static char *connmsg[] =
+        {"", "V.21", "V.23", "V.22", "V.22bis", "V.32bis", "V.34",
+         "V.8", "Bell 212A", "Bell 103", "V.29 Leased", "V.33 Leased", "V.90",
+         "V.21 CH2", "V.27ter", "V.29", "V.33", "V.17"};
+
+	switch (buffer[0]) {
+		case DSP_UDATA_INDICATION_SYNC:
+			if (DebugVar & 16)
+  				printk(KERN_DEBUG"idi_ind: Ch%d: UDATA_SYNC time %d\n", chan->No, p->time);
+			break;
+		case DSP_UDATA_INDICATION_DCD_OFF:
+			if (DebugVar & 8)
+  				printk(KERN_DEBUG"idi_ind: Ch%d: UDATA_DCD_OFF time %d\n", chan->No, p->time);
+			break;
+		case DSP_UDATA_INDICATION_DCD_ON:
+			if ((chan->l2prot == ISDN_PROTO_L2_MODEM) &&
+			    (chan->fsm_state == EICON_STATE_WMCONN)) {
+				chan->fsm_state = EICON_STATE_ACTIVE;
+				cmd.driver = ccard->myid;
+				cmd.command = ISDN_STAT_BCONN;
+				cmd.arg = chan->No;
+				sprintf(cmd.parm.num, "%d/%s", p->speed, connmsg[p->norm]);
+				ccard->interface.statcallb(&cmd);
+			}
+			if (DebugVar & 8) {
+  				printk(KERN_DEBUG"idi_ind: Ch%d: UDATA_DCD_ON time %d\n", chan->No, p->time);
+				printk(KERN_DEBUG"idi_ind: Ch%d: %d %d %d %d\n", chan->No,
+					    p->norm, p->options, p->speed, p->delay); 
+			    }
+			break;
+		case DSP_UDATA_INDICATION_CTS_OFF:
+			if (DebugVar & 8)
+  				printk(KERN_DEBUG"idi_ind: Ch%d: UDATA_CTS_OFF time %d\n", chan->No, p->time);
+			break;
+		case DSP_UDATA_INDICATION_CTS_ON:
+			if (DebugVar & 8) {
+  				printk(KERN_DEBUG"idi_ind: Ch%d: UDATA_CTS_ON time %d\n", chan->No, p->time);
+				printk(KERN_DEBUG"idi_ind: Ch%d: %d %d %d %d\n", chan->No,
+					    p->norm, p->options, p->speed, p->delay); 
+			}
+			break;
+		case DSP_UDATA_INDICATION_DISCONNECT:
+			if (DebugVar & 8)
+  				printk(KERN_DEBUG"idi_ind: Ch%d: UDATA_DISCONNECT cause %d\n", chan->No, buffer[1]);
+			break;
+		default:
+			if (DebugVar & 8)
+				printk(KERN_WARNING "idi_ind: Ch%d: UNHANDLED UDATA Indication 0x%02x\n", chan->No, buffer[0]);
+	}
+}
+
+void
+idi_handle_ind(eicon_card *ccard, struct sk_buff *skb)
+{
+	int tmp;
+	int free_buff;
+	struct sk_buff *skb2;
+        eicon_IND *ind = (eicon_IND *)skb->data;
+	eicon_chan *chan;
+	idi_ind_message message;
+	isdn_ctrl cmd;
+
+	if ((chan = ccard->IdTable[ind->IndId]) == NULL) {
+  		dev_kfree_skb(skb);
+		return;
+	}
+	
+	if ((DebugVar & 128) || 
+	   ((DebugVar & 16) && (ind->Ind != 8))) {
+        	printk(KERN_DEBUG "idi_hdl: Ch%d: Ind=%d Id=%d Ch=%d MInd=%d MLen=%d Len=%d\n", chan->No,
+		        ind->Ind,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,ind->RBuffer.length);
+	}
+
+	free_buff = 1;
+	/* Signal Layer */
+	if (chan->e.D3Id == ind->IndId) {
+		idi_IndParse(ccard, chan, &message, ind->RBuffer.P, ind->RBuffer.length);
+		switch(ind->Ind) {
+			case HANGUP:
+				if (DebugVar & 8)
+  					printk(KERN_DEBUG"idi_ind: Ch%d: Hangup\n", chan->No);
+		                while((skb2 = skb_dequeue(&chan->e.X))) {
+					dev_kfree_skb(skb2);
+				}
+				chan->e.busy = 0;
+				chan->queued = 0;
+				chan->waitq = 0;
+				chan->waitpq = 0;
+				chan->fsm_state = EICON_STATE_NULL;
+				if (message.e_cau[0] & 0x7f) {
+					cmd.driver = ccard->myid;
+					cmd.arg = chan->No;
+					sprintf(cmd.parm.num,"E%02x%02x", 
+						chan->cause[0]&0x7f, message.e_cau[0]&0x7f); 
+					cmd.command = ISDN_STAT_CAUSE;
+					ccard->interface.statcallb(&cmd);
+				}
+				chan->cause[0] = 0; 
+				cmd.driver = ccard->myid;
+				cmd.arg = chan->No;
+				cmd.command = ISDN_STAT_DHUP;
+				ccard->interface.statcallb(&cmd);
+				eicon_idi_listen_req(ccard, chan);
+				break;
+			case INDICATE_IND:
+				if (DebugVar & 8)
+  					printk(KERN_DEBUG"idi_ind: Ch%d: Indicate_Ind\n", chan->No);
+				chan->fsm_state = EICON_STATE_ICALL;
+				idi_bc2si(message.bc, message.hlc, &chan->si1, &chan->si2);
+				strcpy(chan->cpn, message.cpn + 1);
+				if (strlen(message.dsa)) {
+					strcat(chan->cpn, ".");
+					strcat(chan->cpn, message.dsa);
+				}
+				strcpy(chan->oad, message.oad);
+				try_stat_icall_again: 
+				cmd.driver = ccard->myid;
+				cmd.command = ISDN_STAT_ICALL;
+				cmd.arg = chan->No;
+				cmd.parm.setup.si1 = chan->si1;
+				cmd.parm.setup.si2 = chan->si2;
+				strcpy(cmd.parm.setup.eazmsn, chan->cpn);
+				strcpy(cmd.parm.setup.phone, chan->oad);
+				cmd.parm.setup.plan = message.plan;
+				cmd.parm.setup.screen = message.screen;
+				tmp = ccard->interface.statcallb(&cmd);
+				switch(tmp) {
+					case 0: /* no user responding */
+						idi_do_req(ccard, chan, HANGUP, 0);
+						break;
+					case 1: /* alert */
+						if (DebugVar & 8)
+  							printk(KERN_DEBUG"idi_req: Ch%d: Call Alert\n", chan->No);
+						if ((chan->fsm_state == EICON_STATE_ICALL) || (chan->fsm_state == EICON_STATE_ICALLW)) {
+							chan->fsm_state = EICON_STATE_ICALL;
+							idi_do_req(ccard, chan, CALL_ALERT, 0);
+						}
+						break;
+					case 2: /* reject */
+						if (DebugVar & 8)
+  							printk(KERN_DEBUG"idi_req: Ch%d: Call Reject\n", chan->No);
+						idi_do_req(ccard, chan, REJECT, 0);
+						break;
+					case 3: /* incomplete number */
+						if (DebugVar & 8)
+  							printk(KERN_DEBUG"idi_req: Ch%d: Incomplete Number\n", chan->No);
+					        switch(ccard->type) {
+					                case EICON_CTYPE_MAESTRAP:
+					                case EICON_CTYPE_S2M:
+								/* TODO (other protocols) */
+								chan->fsm_state = EICON_STATE_ICALLW;
+								break;
+							default:
+								idi_do_req(ccard, chan, HANGUP, 0);
+						}
+						break;
+				}
+				break;
+			case INFO_IND:
+				if (DebugVar & 8)
+  					printk(KERN_DEBUG"idi_ind: Ch%d: Info_Ind\n", chan->No);
+				if ((chan->fsm_state == EICON_STATE_ICALLW) &&
+				    (message.cpn[0])) {
+					strcat(chan->cpn, message.cpn + 1);
+					goto try_stat_icall_again;
+				}
+				break;
+			case CALL_IND:
+				if (DebugVar & 8)
+  					printk(KERN_DEBUG"idi_ind: Ch%d: Call_Ind\n", chan->No);
+				if ((chan->fsm_state == EICON_STATE_ICALL) || (chan->fsm_state == EICON_STATE_IWAIT)) {
+					chan->fsm_state = EICON_STATE_IBWAIT;
+					cmd.driver = ccard->myid;
+					cmd.command = ISDN_STAT_DCONN;
+					cmd.arg = chan->No;
+					ccard->interface.statcallb(&cmd);
+					idi_do_req(ccard, chan, IDI_N_CONNECT, 1);
+				} else
+				idi_hangup(ccard, chan);
+				break;
+			case CALL_CON:
+				if (DebugVar & 8)
+  					printk(KERN_DEBUG"idi_ind: Ch%d: Call_Con\n", chan->No);
+				if (chan->fsm_state == EICON_STATE_OCALL) {
+					chan->fsm_state = EICON_STATE_OBWAIT;
+					cmd.driver = ccard->myid;
+					cmd.command = ISDN_STAT_DCONN;
+					cmd.arg = chan->No;
+					ccard->interface.statcallb(&cmd);
+					idi_do_req(ccard, chan, ASSIGN, 1); 
+					idi_do_req(ccard, chan, IDI_N_CONNECT, 1);
+				} else
+				idi_hangup(ccard, chan);
+				break;
+			case AOC_IND:
+				if (DebugVar & 8)
+  					printk(KERN_DEBUG"idi_ind: Ch%d: Advice of Charge\n", chan->No);
+				break;
+			default:
+				if (DebugVar & 8)
+					printk(KERN_WARNING "idi_ind: Ch%d: UNHANDLED SigIndication 0x%02x\n", chan->No, ind->Ind);
+		}
+	}
+	/* Network Layer */
+	else if (chan->e.B2Id == ind->IndId) {
+
+		if (chan->No == ccard->nchannels) {
+			/* Management Indication */
+			idi_IndParse(ccard, chan, &message, ind->RBuffer.P, ind->RBuffer.length);
+			chan->fsm_state = 1;
+		} 
+		else
+		switch(ind->Ind) {
+			case IDI_N_CONNECT_ACK:
+				if (DebugVar & 16)
+  					printk(KERN_DEBUG"idi_ind: Ch%d: N_Connect_Ack\n", chan->No);
+				if (chan->l2prot == ISDN_PROTO_L2_MODEM) {
+					chan->fsm_state = EICON_STATE_WMCONN;
+					break;
+				}
+				chan->fsm_state = EICON_STATE_ACTIVE;
+				cmd.driver = ccard->myid;
+				cmd.command = ISDN_STAT_BCONN;
+				cmd.arg = chan->No;
+				ccard->interface.statcallb(&cmd);
+				break; 
+			case IDI_N_CONNECT:
+				if (DebugVar & 16)
+  					printk(KERN_DEBUG"idi_ind: Ch%d: N_Connect\n", chan->No);
+				if (chan->e.B2Id) idi_do_req(ccard, chan, IDI_N_CONNECT_ACK, 1);
+				if (chan->l2prot == ISDN_PROTO_L2_MODEM) {
+					chan->fsm_state = EICON_STATE_WMCONN;
+					break;
+				}
+				chan->fsm_state = EICON_STATE_ACTIVE;
+				cmd.driver = ccard->myid;
+				cmd.command = ISDN_STAT_BCONN;
+				cmd.arg = chan->No;
+				ccard->interface.statcallb(&cmd);
+				break; 
+			case IDI_N_DISC:
+				if (DebugVar & 16)
+  					printk(KERN_DEBUG"idi_ind: Ch%d: N_DISC\n", chan->No);
+				if (chan->e.B2Id) {
+					idi_do_req(ccard, chan, IDI_N_DISC_ACK, 1);
+					idi_do_req(ccard, chan, REMOVE, 1);
+				}
+				chan->queued = 0;
+				chan->waitq = 0;
+				chan->waitpq = 0;
+				if (chan->fsm_state == EICON_STATE_ACTIVE) {
+					cmd.driver = ccard->myid;
+					cmd.command = ISDN_STAT_BHUP;
+					cmd.arg = chan->No;
+					ccard->interface.statcallb(&cmd);
+				}
+				break; 
+			case IDI_N_DISC_ACK:
+				if (DebugVar & 16)
+  					printk(KERN_DEBUG"idi_ind: Ch%d: N_DISC_ACK\n", chan->No);
+				break; 
+			case IDI_N_DATA_ACK:
+				if (DebugVar & 16)
+  					printk(KERN_DEBUG"idi_ind: Ch%d: N_DATA_ACK\n", chan->No);
+				break;
+			case IDI_N_DATA:
+				skb_pull(skb, sizeof(eicon_IND) - 1);
+				if (DebugVar & 128)
+					printk(KERN_DEBUG"idi_rcv: Ch%d: %d bytes\n", chan->No, skb->len);
+				ccard->interface.rcvcallb_skb(ccard->myid, chan->No, skb);
+				free_buff = 0; 
+				break; 
+			case IDI_N_UDATA:
+				idi_parse_udata(ccard, chan, ind->RBuffer.P, ind->RBuffer.length);
+				break; 
+			default:
+				if (DebugVar & 8)
+					printk(KERN_WARNING "idi_ind: Ch%d: UNHANDLED NetIndication 0x%02x\n", chan->No, ind->Ind);
+		}
+	}
+	else {
+		if (DebugVar & 1)
+			printk(KERN_ERR "idi_ind: Ch%d: Ind is neither SIG nor NET !\n", chan->No);
+	}
+   if (free_buff) dev_kfree_skb(skb);
+}
+
+void
+idi_handle_ack(eicon_card *ccard, struct sk_buff *skb)
+{
+	int j;
+        eicon_RC *ack = (eicon_RC *)skb->data;
+	eicon_chan *chan;
+	isdn_ctrl cmd;
+
+	if ((ack->Rc != ASSIGN_OK) && (ack->Rc != OK)) {
+		if ((chan = ccard->IdTable[ack->RcId]) != NULL) {
+			chan->e.busy = 0;
+			if (DebugVar & 24)
+				printk(KERN_ERR "eicon_ack: Ch%d: Not OK: Rc=%d Id=%d Ch=%d\n", chan->No, 
+					ack->Rc, ack->RcId, ack->RcCh);
+			if (chan->No == ccard->nchannels) { /* Management */
+				chan->fsm_state = 2;
+			} else {	/* any other channel */
+					/* card reports error: we hangup */
+				idi_hangup(ccard, chan);
+				cmd.driver = ccard->myid;
+				cmd.command = ISDN_STAT_DHUP;
+				cmd.arg = chan->No;
+				ccard->interface.statcallb(&cmd);
+			}
+		}
+	} 
+	else {
+		if ((chan = ccard->IdTable[ack->RcId]) != NULL) {
+			if (ack->RcId != ((chan->e.ReqCh) ? chan->e.B2Id : chan->e.D3Id)) {
+				if (DebugVar & 16)
+					printk(KERN_DEBUG "idi_ack: Ch%d: RcId %d not equal to last %d\n", chan->No, 
+						ack->RcId, (chan->e.ReqCh) ? chan->e.B2Id : chan->e.D3Id);
+			} else {	
+				if (chan->No == ccard->nchannels) { /* Management */
+					if (chan->e.Req == 0x04) chan->fsm_state = 1;
+				}
+				if (chan->e.ReqCh) {
+					switch(chan->e.Req & 0x0f) {
+						case IDI_N_MDATA:
+						case IDI_N_DATA:
+							chan->queued -= chan->waitq;
+							if (chan->queued < 0) chan->queued = 0;
+							if ((chan->e.Req & 0x0f) == IDI_N_DATA) {
+								cmd.driver = ccard->myid;
+								cmd.command = ISDN_STAT_BSENT;
+								cmd.arg = chan->No;
+								cmd.parm.length = chan->waitpq;
+								chan->waitpq = 0;
+								ccard->interface.statcallb(&cmd);
+							}
+							break;
+						default:
+							if (DebugVar & 16)
+								printk(KERN_DEBUG "idi_ack: Ch%d: RC OK Id=%d Ch=%d (ref:%d)\n", chan->No,
+									ack->RcId, ack->RcCh, ack->Reference);
+					}
+				} 
+				else {
+					if (DebugVar & 16)
+						printk(KERN_DEBUG "idi_ack: Ch%d: RC OK Id=%d Ch=%d (ref:%d)\n", chan->No,
+							ack->RcId, ack->RcCh, ack->Reference);
+				}
+
+				if (chan->e.Req == REMOVE) {
+					if (ack->Reference == chan->e.ref) {
+						ccard->IdTable[ack->RcId] = NULL;
+						if (DebugVar & 16)
+							printk(KERN_DEBUG "idi_ack: Ch%d: Removed : Id=%d Ch=%d (%s)\n", chan->No,
+								ack->RcId, ack->RcCh, (chan->e.ReqCh)? "Net":"Sig");
+						if (!chan->e.ReqCh) 
+							chan->e.D3Id = 0;
+						else
+							chan->e.B2Id = 0;
+					}
+					else {
+						if (DebugVar & 16)
+							printk(KERN_DEBUG "idi_ack: Ch%d: Rc-Ref %d not equal to stored %d\n", chan->No,
+								ack->Reference, chan->e.ref);
+					}
+				}
+				chan->e.busy = 0;
+			}
+		}
+		else {
+			for(j = 0; j < ccard->nchannels + 1; j++) {
+				if (ccard->bch[j].e.ref == ack->Reference) {
+					if (!ccard->bch[j].e.ReqCh) 
+						ccard->bch[j].e.D3Id  = ack->RcId;
+					else
+						ccard->bch[j].e.B2Id  = ack->RcId;
+					ccard->IdTable[ack->RcId] = &ccard->bch[j];
+					ccard->bch[j].e.busy = 0;
+					ccard->bch[j].e.ref = 0;
+					if (DebugVar & 16)
+						printk(KERN_DEBUG"idi_ack: Ch%d: Id %d assigned (%s)\n", j, 
+							ack->RcId, (ccard->bch[j].e.ReqCh)? "Net":"Sig");
+					break;
+				}
+			}		
+			if (j > ccard->nchannels) {
+				if (DebugVar & 24)
+					printk(KERN_DEBUG"idi_ack: Ch??: ref %d not found for Id %d\n", 
+						ack->Reference, ack->RcId);
+			}
+		}
+	}
+  dev_kfree_skb(skb);
+  eicon_schedule_tx(ccard);
+}
+
+int
+idi_send_data(eicon_card *card, eicon_chan *chan, int ack, struct sk_buff *skb)
+{
+        struct sk_buff *xmit_skb;
+        struct sk_buff *skb2;
+        eicon_REQ *reqbuf;
+        eicon_chan_ptr *chan2;
+        int len, plen = 0, offset = 0;
+	unsigned long flags;
+
+        if (chan->fsm_state != EICON_STATE_ACTIVE) {
+		if (DebugVar & 1)
+			printk(KERN_DEBUG"idi_snd: Ch%d: send bytes on state %d !\n", chan->No, chan->fsm_state);
+                return -ENODEV;
+	}
+
+        len = skb->len;
+	if (len > 2138)	/* too much for the shared memory */
+		return -1;
+        if (!len)
+                return 0;
+	if (chan->queued + len > ((chan->l2prot == ISDN_PROTO_L2_TRANS) ? 4000 : EICON_MAX_QUEUED))
+		return 0;
+	if (DebugVar & 128)
+		printk(KERN_DEBUG"idi_snd: Ch%d: %d bytes\n", chan->No, len);
+	save_flags(flags);
+	cli();
+	while(offset < len) {
+
+		plen = ((len - offset) > 270) ? 270 : len - offset;
+
+	        xmit_skb = alloc_skb(plen + sizeof(eicon_REQ), GFP_ATOMIC);
+        	skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC);
+
+	        if ((!skb) || (!skb2)) {
+			restore_flags(flags);
+			if (DebugVar & 1)
+	        	        printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed\n", chan->No);
+                	return -ENOMEM;
+	        }
+
+	        chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr));
+        	chan2->ptr = chan;
+
+	        reqbuf = (eicon_REQ *)skb_put(xmit_skb, plen + sizeof(eicon_REQ));
+		if (((len - offset) > 270) &&
+			(chan->l2prot != ISDN_PROTO_L2_TRANS)) {
+		        reqbuf->Req = IDI_N_MDATA;
+		} else {
+		        reqbuf->Req = IDI_N_DATA;
+			if (ack) reqbuf->Req |= N_D_BIT;
+		}	
+        	reqbuf->ReqCh = 0;
+	        reqbuf->ReqId = 1;
+		memcpy(&reqbuf->XBuffer.P, skb->data + offset, plen);
+		reqbuf->XBuffer.length = plen;
+		reqbuf->Reference = 1; /* Net Entity */
+
+		skb_queue_tail(&chan->e.X, xmit_skb);
+		skb_queue_tail(&card->sndq, skb2); 
+
+		offset += plen;
+	}
+	chan->queued += len;
+	restore_flags(flags);
+	eicon_schedule_tx(card);
+        dev_kfree_skb(skb);
+        return len;
+}
+
+
+
+int
+eicon_idi_manage_assign(eicon_card *card)
+{
+        struct sk_buff *skb;
+        struct sk_buff *skb2;
+        eicon_REQ  *reqbuf;
+        eicon_chan     *chan;
+        eicon_chan_ptr *chan2;
+
+        chan = &(card->bch[card->nchannels]);
+
+        skb = alloc_skb(270 + sizeof(eicon_REQ), GFP_ATOMIC);
+        skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC);
+
+        if ((!skb) || (!skb2)) {
+		if (DebugVar & 1)
+			printk(KERN_WARNING "idi_err: alloc_skb failed\n");
+                return -ENOMEM;
+        }
+
+        chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr));
+        chan2->ptr = chan;
+
+        reqbuf = (eicon_REQ *)skb_put(skb, 270 + sizeof(eicon_REQ));
+
+        reqbuf->XBuffer.P[0] = 0;
+        reqbuf->Req = ASSIGN;
+        reqbuf->ReqCh = 0;
+        reqbuf->ReqId = 0xe0;
+        reqbuf->XBuffer.length = 1;
+        reqbuf->Reference = 2; /* Man Entity */
+
+        skb_queue_tail(&chan->e.X, skb);
+        skb_queue_tail(&card->sndq, skb2);
+        eicon_schedule_tx(card);
+        return(0);
+}
+
+
+int
+eicon_idi_manage_remove(eicon_card *card)
+{
+        struct sk_buff *skb;
+        struct sk_buff *skb2;
+        eicon_REQ  *reqbuf;
+        eicon_chan     *chan;
+        eicon_chan_ptr *chan2;
+
+        chan = &(card->bch[card->nchannels]);
+
+        skb = alloc_skb(270 + sizeof(eicon_REQ), GFP_ATOMIC);
+        skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC);
+
+        if ((!skb) || (!skb2)) {
+		if (DebugVar & 1)
+                	printk(KERN_WARNING "idi_err: alloc_skb failed\n");
+                return -ENOMEM;
+        }
+
+        chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr));
+        chan2->ptr = chan;
+
+        reqbuf = (eicon_REQ *)skb_put(skb, 270 + sizeof(eicon_REQ));
+
+        reqbuf->Req = REMOVE;
+        reqbuf->ReqCh = 0;
+        reqbuf->ReqId = 1;
+        reqbuf->XBuffer.length = 0;
+        reqbuf->Reference = 2; /* Man Entity */
+
+        skb_queue_tail(&chan->e.X, skb);
+        skb_queue_tail(&card->sndq, skb2);
+        eicon_schedule_tx(card);
+        return(0);
+}
+
+
+int
+eicon_idi_manage(eicon_card *card, eicon_manifbuf *mb)
+{
+	int l = 0;
+	int ret = 0;
+	int timeout;
+	int i;
+        struct sk_buff *skb;
+        struct sk_buff *skb2;
+        eicon_REQ  *reqbuf;
+        eicon_chan     *chan;
+        eicon_chan_ptr *chan2;
+
+        chan = &(card->bch[card->nchannels]);
+
+	if (chan->e.D3Id) return -EBUSY;
+	chan->e.D3Id = 1;
+	while((skb2 = skb_dequeue(&chan->e.X)))
+		dev_kfree_skb(skb2);
+	chan->e.busy = 0;
+ 
+	if ((ret = eicon_idi_manage_assign(card))) {
+		chan->e.D3Id = 0;
+		return(ret); 
+	}
+
+        timeout = jiffies + 50;
+        while (timeout > jiffies) {
+                if (chan->e.B2Id) break;
+                SLEEP(10);
+        }
+        if (!chan->e.B2Id) {
+		chan->e.D3Id = 0;
+		return -EIO;
+	}
+
+	chan->fsm_state = 0;
+
+	if (!(manbuf = kmalloc(sizeof(eicon_manifbuf), GFP_KERNEL))) {
+		if (DebugVar & 1)
+                	printk(KERN_WARNING "idi_err: alloc_manifbuf failed\n");
+		chan->e.D3Id = 0;
+		return -ENOMEM;
+	}
+	if (copy_from_user(manbuf, mb, sizeof(eicon_manifbuf))) {
+		chan->e.D3Id = 0;
+		return -EFAULT;
+	}
+
+        skb = alloc_skb(270 + sizeof(eicon_REQ), GFP_ATOMIC);
+        skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC);
+
+        if ((!skb) || (!skb2)) {
+		if (DebugVar & 1)
+                	printk(KERN_WARNING "idi_err_manif: alloc_skb failed\n");
+		kfree(manbuf);
+		chan->e.D3Id = 0;
+                return -ENOMEM;
+        }
+
+        chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr));
+        chan2->ptr = chan;
+
+        reqbuf = (eicon_REQ *)skb_put(skb, 270 + sizeof(eicon_REQ));
+
+        reqbuf->XBuffer.P[l++] = ESC;
+        reqbuf->XBuffer.P[l++] = 6;
+        reqbuf->XBuffer.P[l++] = 0x80;
+	for (i = 0; i < manbuf->length[0]; i++)
+	        reqbuf->XBuffer.P[l++] = manbuf->data[i];
+        reqbuf->XBuffer.P[1] = manbuf->length[0] + 1;
+
+        reqbuf->XBuffer.P[l++] = 0;
+        reqbuf->Req = (manbuf->count) ? manbuf->count : 0x02; /* Request */
+        reqbuf->ReqCh = 0;
+        reqbuf->ReqId = 1;
+        reqbuf->XBuffer.length = l;
+        reqbuf->Reference = 2; /* Man Entity */
+
+        skb_queue_tail(&chan->e.X, skb);
+        skb_queue_tail(&card->sndq, skb2);
+
+	manbuf->count = 0;
+	manbuf->pos = 0;
+
+        eicon_schedule_tx(card);
+
+        timeout = jiffies + 50;
+        while (timeout > jiffies) {
+                if (chan->fsm_state) break;
+                SLEEP(10);
+        }
+        if ((!chan->fsm_state) || (chan->fsm_state == 2)) {
+		eicon_idi_manage_remove(card);
+		kfree(manbuf);
+		chan->e.D3Id = 0;
+		return -EIO;
+	}
+
+	if ((ret = eicon_idi_manage_remove(card))) {
+		chan->e.D3Id = 0;
+		return(ret);
+	}
+
+	if (copy_to_user(mb, manbuf, sizeof(eicon_manifbuf))) {
+		chan->e.D3Id = 0;
+		return -EFAULT;
+	}
+
+	kfree(manbuf);
+	chan->e.D3Id = 0;
+  return(0);
+}
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/eicon/eicon_idi.h linux/drivers/isdn/eicon/eicon_idi.h
--- v2.3.3/linux/drivers/isdn/eicon/eicon_idi.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/eicon/eicon_idi.h	Sun May 23 10:03:41 1999
@@ -0,0 +1,248 @@
+/* $Id: eicon_idi.h,v 1.4 1999/03/29 11:19:44 armin Exp $
+ *
+ * ISDN lowlevel-module for the Eicon.Diehl active cards.
+ * IDI-Interface
+ *
+ * Copyright 1998,99 by Armin Schindler (mac@melware.de)
+ * Copyright 1999    Cytronics & Melware (info@melware.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: eicon_idi.h,v $
+ * Revision 1.4  1999/03/29 11:19:44  armin
+ * I/O stuff now in seperate file (eicon_io.c)
+ * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented.
+ *
+ * Revision 1.3  1999/03/02 12:37:45  armin
+ * Added some important checks.
+ * Analog Modem with DSP.
+ * Channels will be added to Link-Level after loading firmware.
+ *
+ * Revision 1.2  1999/01/24 20:14:18  armin
+ * Changed and added debug stuff.
+ * Better data sending. (still problems with tty's flip buffer)
+ *
+ * Revision 1.1  1999/01/01 18:09:42  armin
+ * First checkin of new eicon driver.
+ * DIVA-Server BRI/PCI and PRI/PCI are supported.
+ * Old diehl code is obsolete.
+ *
+ *
+ */
+
+#ifndef IDI_H
+#define IDI_H
+
+
+#define ASSIGN  0x01
+#define REMOVE  0xff
+	
+#define CALL_REQ 1      /* call request                             */
+#define CALL_CON 1      /* call confirmation                        */
+#define CALL_IND 2      /* incoming call connected                  */
+#define LISTEN_REQ 2    /* listen request                           */
+#define HANGUP 3        /* hangup request/indication                */
+#define SUSPEND 4       /* call suspend request/confirm             */
+#define RESUME 5        /* call resume request/confirm              */
+#define SUSPEND_REJ 6   /* suspend rejected indication              */
+#define USER_DATA 8     /* user data for user to user signaling     */
+#define CONGESTION 9    /* network congestion indication            */
+#define INDICATE_REQ 10 /* request to indicate an incoming call     */
+#define INDICATE_IND 10 /* indicates that there is an incoming call */
+#define CALL_RES 11     /* accept an incoming call                  */
+#define CALL_ALERT 12   /* send ALERT for incoming call             */
+#define INFO_REQ 13     /* INFO request                             */
+#define INFO_IND 13     /* INFO indication                          */
+#define REJECT 14       /* reject an incoming call                  */
+#define RESOURCES 15    /* reserve B-Channel hardware resources     */
+#define TEL_CTRL 16     /* Telephone control request/indication     */
+#define STATUS_REQ 17   /* Request D-State (returned in INFO_IND)   */
+#define FAC_REG_REQ 18  /* connection idependent fac registration   */
+#define FAC_REG_ACK 19  /* fac registration acknowledge             */
+#define FAC_REG_REJ 20  /* fac registration reject                  */
+#define CALL_COMPLETE 21/* send a CALL_PROC for incoming call       */
+#define AOC_IND       26/* Advice of Charge                         */
+
+#define IDI_N_MDATA         (0x01)
+#define IDI_N_CONNECT       (0x02)
+#define IDI_N_CONNECT_ACK   (0x03)
+#define IDI_N_DISC          (0x04)
+#define IDI_N_DISC_ACK      (0x05)
+#define IDI_N_RESET         (0x06)
+#define IDI_N_RESET_ACK     (0x07)
+#define IDI_N_DATA          (0x08)
+#define IDI_N_EDATA         (0x09)
+#define IDI_N_UDATA         (0x0a)
+#define IDI_N_BDATA         (0x0b)
+#define IDI_N_DATA_ACK      (0x0c)
+#define IDI_N_EDATA_ACK     (0x0d)
+
+#define N_Q_BIT         0x10    /* Q-bit for req/ind                */
+#define N_M_BIT         0x20    /* M-bit for req/ind                */
+#define N_D_BIT         0x40    /* D-bit for req/ind                */
+
+
+#define SHIFT 0x90              /* codeset shift                    */
+#define MORE 0xa0               /* more data                        */
+#define CL 0xb0                 /* congestion level                 */
+
+        /* codeset 0                                                */
+
+#define BC  0x04                /* Bearer Capability                */
+#define CAU 0x08                /* cause                            */
+#define CAD 0x0c                /* Connected address                */
+#define CAI 0x10                /* call identity                    */
+#define CHI 0x18                /* channel identification           */
+#define LLI 0x19                /* logical link id                  */
+#define CHA 0x1a                /* charge advice                    */
+#define FTY 0x1c
+#define PI  0x1e		/* Progress Indicator		    */
+#define NI  0x27		/* Notification Indicator	    */
+#define DT  0x29                /* ETSI date/time                   */
+#define KEY 0x2c                /* keypad information element       */
+#define DSP 0x28                /* display                          */
+#define OAD 0x6c                /* origination address              */
+#define OSA 0x6d                /* origination sub-address          */
+#define CPN 0x70                /* called party number              */
+#define DSA 0x71                /* destination sub-address          */
+#define RDN 0x74		/* redirecting number		    */
+#define LLC 0x7c                /* low layer compatibility          */
+#define HLC 0x7d                /* high layer compatibility         */
+#define UUI 0x7e                /* user user information            */
+#define ESC 0x7f                /* escape extension                 */
+
+#define DLC 0x20                /* data link layer configuration    */
+#define NLC 0x21                /* network layer configuration      */
+
+        /* codeset 6                                                */
+
+#define SIN 0x01                /* service indicator                */
+#define CIF 0x02                /* charging information             */
+#define DATE 0x03               /* date                             */
+#define CPS 0x07                /* called party status              */
+
+/*------------------------------------------------------------------*/
+/* return code coding                                               */
+/*------------------------------------------------------------------*/
+
+#define UNKNOWN_COMMAND         0x01    /* unknown command          */
+#define WRONG_COMMAND           0x02    /* wrong command            */
+#define WRONG_ID                0x03    /* unknown task/entity id   */
+#define WRONG_CH                0x04    /* wrong task/entity id     */
+#define UNKNOWN_IE              0x05    /* unknown information el.  */
+#define WRONG_IE                0x06    /* wrong information el.    */
+#define OUT_OF_RESOURCES        0x07    /* card out of res.         */
+#define N_FLOW_CONTROL          0x10    /* Flow-Control, retry      */
+#define ASSIGN_RC               0xe0    /* ASSIGN acknowledgement   */
+#define ASSIGN_OK               0xef    /* ASSIGN OK                */
+#define OK_FC                   0xfc    /* Flow-Control RC          */
+#define READY_INT               0xfd    /* Ready interrupt          */
+#define TIMER_INT               0xfe    /* timer interrupt          */
+#define OK                      0xff    /* command accepted         */
+
+/*------------------------------------------------------------------*/
+
+typedef struct {
+	char cpn[32];
+	char oad[32];
+	char dsa[32];
+	char osa[32];
+	__u8 plan;
+	__u8 screen;
+	__u8 sin[4];
+	__u8 chi[4];
+	__u8 e_chi[4];
+	__u8 bc[12];
+	__u8 e_bc[12];
+ 	__u8 llc[18];
+	__u8 hlc[5];
+	__u8 cau[4];
+	__u8 e_cau[2];
+	__u8 e_mt;
+	__u8 dt[6];
+	char display[83];
+	char keypad[35];
+	char rdn[32];
+} idi_ind_message;
+
+typedef struct { 
+  __u16 next            __attribute__ ((packed));
+  __u8  Req             __attribute__ ((packed));
+  __u8  ReqId           __attribute__ ((packed));
+  __u8  ReqCh           __attribute__ ((packed));
+  __u8  Reserved1       __attribute__ ((packed));
+  __u16 Reference       __attribute__ ((packed));
+  __u8  Reserved[8]     __attribute__ ((packed));
+  eicon_PBUFFER XBuffer; 
+} eicon_REQ;
+
+typedef struct {
+  __u16 next            __attribute__ ((packed));
+  __u8  Rc              __attribute__ ((packed));
+  __u8  RcId            __attribute__ ((packed));
+  __u8  RcCh            __attribute__ ((packed));
+  __u8  Reserved1       __attribute__ ((packed));
+  __u16 Reference       __attribute__ ((packed));
+  __u8  Reserved2[8]    __attribute__ ((packed));
+} eicon_RC;
+
+typedef struct {
+  __u16 next            __attribute__ ((packed));
+  __u8  Ind             __attribute__ ((packed));
+  __u8  IndId           __attribute__ ((packed));
+  __u8  IndCh           __attribute__ ((packed));
+  __u8  MInd            __attribute__ ((packed));
+  __u16 MLength         __attribute__ ((packed));
+  __u16 Reference       __attribute__ ((packed));
+  __u8  RNR             __attribute__ ((packed));
+  __u8  Reserved        __attribute__ ((packed));
+  __u32 Ack             __attribute__ ((packed));
+  eicon_PBUFFER RBuffer;
+} eicon_IND;
+
+typedef struct {
+  __u16 NextReq  __attribute__ ((packed));  /* pointer to next Req Buffer */
+  __u16 NextRc   __attribute__ ((packed));  /* pointer to next Rc Buffer  */
+  __u16 NextInd  __attribute__ ((packed));  /* pointer to next Ind Buffer */
+  __u8 ReqInput  __attribute__ ((packed));  /* number of Req Buffers sent */
+  __u8 ReqOutput  __attribute__ ((packed)); /* number of Req Buffers returned */
+  __u8 ReqReserved  __attribute__ ((packed));/*number of Req Buffers reserved */
+  __u8 Int  __attribute__ ((packed));       /* ISDN-P interrupt           */
+  __u8 XLock  __attribute__ ((packed));     /* Lock field for arbitration */
+  __u8 RcOutput  __attribute__ ((packed));  /* number of Rc buffers received */
+  __u8 IndOutput  __attribute__ ((packed)); /* number of Ind buffers received */
+  __u8 IMask  __attribute__ ((packed));     /* Interrupt Mask Flag        */
+  __u8 Reserved1[2]  __attribute__ ((packed)); /* reserved field, do not use */
+  __u8 ReadyInt  __attribute__ ((packed));  /* request field for ready int */
+  __u8 Reserved2[12]  __attribute__ ((packed)); /* reserved field, do not use */
+  __u8 InterfaceType  __attribute__ ((packed)); /* interface type 1=16K    */
+  __u16 Signature  __attribute__ ((packed));    /* ISDN-P initialized ind  */
+  __u8 B[1];                            /* buffer space for Req,Ind and Rc */
+} eicon_pr_ram;
+
+
+extern int idi_do_req(eicon_card *card, eicon_chan *chan, int cmd, int layer);
+extern int idi_hangup(eicon_card *card, eicon_chan *chan);
+extern int idi_connect_res(eicon_card *card, eicon_chan *chan);
+extern int eicon_idi_listen_req(eicon_card *card, eicon_chan *chan);
+extern int idi_connect_req(eicon_card *card, eicon_chan *chan, char *phone,
+	                    char *eazmsn, int si1, int si2);
+
+extern void idi_handle_ack(eicon_card *card, struct sk_buff *skb);
+extern void idi_handle_ind(eicon_card *card, struct sk_buff *skb);
+extern int eicon_idi_manage(eicon_card *card, eicon_manifbuf *mb);
+extern int idi_send_data(eicon_card *card, eicon_chan *chan, int ack, struct sk_buff *skb);
+
+#endif
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/eicon/eicon_io.c linux/drivers/isdn/eicon/eicon_io.c
--- v2.3.3/linux/drivers/isdn/eicon/eicon_io.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/eicon/eicon_io.c	Sun May 23 10:03:41 1999
@@ -0,0 +1,755 @@
+/* $Id: eicon_io.c,v 1.1 1999/03/29 11:19:45 armin Exp $
+ *
+ * ISDN low-level module for Eicon.Diehl active ISDN-Cards.
+ * Code for communicating with hardware.
+ *
+ * Copyright 1999    by Armin Schindler (mac@melware.de)
+ * Copyright 1999    Cytronics & Melware (info@melware.de)
+ *
+ * Thanks to	Eicon Technology Diehl GmbH & Co. oHG for 
+ *		documents, informations and hardware. 
+ *
+ * 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: eicon_io.c,v $
+ * Revision 1.1  1999/03/29 11:19:45  armin
+ * I/O stuff now in seperate file (eicon_io.c)
+ * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented.
+ *
+ *
+ */
+
+
+#include "eicon.h"
+
+void
+eicon_io_rcv_dispatch(eicon_card *ccard) {
+        struct sk_buff *skb, *skb2, *skb_new;
+        eicon_IND *ind, *ind2, *ind_new;
+        eicon_chan *chan;
+
+        if (!ccard) {
+		if (DebugVar & 1)
+	                printk(KERN_WARNING "eicon_io_rcv_dispatch: NULL card!\n");
+                return;
+        }
+
+	while((skb = skb_dequeue(&ccard->rcvq))) {
+        	ind = (eicon_IND *)skb->data;
+
+        	if ((chan = ccard->IdTable[ind->IndId]) == NULL) {
+			if (DebugVar & 1) {
+				switch(ind->Ind) {
+					case IDI_N_DISC_ACK: 
+						/* doesn't matter if this happens */ 
+						break;
+					default: 
+						printk(KERN_ERR "idi: Indication for unknown channel Ind=%d Id=%d\n", ind->Ind, ind->IndId);
+						printk(KERN_DEBUG "idi_hdl: Ch??: Ind=%d Id=%d Ch=%d MInd=%d MLen=%d Len=%d\n",
+							ind->Ind,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,ind->RBuffer.length);
+				}
+			}
+	                dev_kfree_skb(skb);
+	                continue;
+	        }
+
+		if (chan->e.complete) { /* check for rec-buffer chaining */
+			if (ind->MLength == ind->RBuffer.length) {
+				chan->e.complete = 1;
+				idi_handle_ind(ccard, skb);
+				continue;
+			}
+			else {
+				chan->e.complete = 0;
+				ind->Ind = ind->MInd;
+				skb_queue_tail(&chan->e.R, skb);
+				continue;
+			}
+		}
+		else {
+			if (!(skb2 = skb_dequeue(&chan->e.R))) {
+				chan->e.complete = 1;
+				if (DebugVar & 1)
+	                		printk(KERN_ERR "eicon: buffer incomplete, but 0 in queue\n");
+	                	dev_kfree_skb(skb);
+	                	dev_kfree_skb(skb2);
+				continue;	
+			}
+	        	ind2 = (eicon_IND *)skb2->data;
+			skb_new = alloc_skb(((sizeof(eicon_IND)-1)+ind->RBuffer.length+ind2->RBuffer.length),
+					GFP_ATOMIC);
+			ind_new = (eicon_IND *)skb_put(skb_new,
+					((sizeof(eicon_IND)-1)+ind->RBuffer.length+ind2->RBuffer.length));
+			ind_new->Ind = ind2->Ind;
+			ind_new->IndId = ind2->IndId;
+			ind_new->IndCh = ind2->IndCh;
+			ind_new->MInd = ind2->MInd;
+			ind_new->MLength = ind2->MLength;
+			ind_new->RBuffer.length = ind2->RBuffer.length + ind->RBuffer.length;
+			memcpy(&ind_new->RBuffer.P, &ind2->RBuffer.P, ind2->RBuffer.length);
+			memcpy((&ind_new->RBuffer.P)+ind2->RBuffer.length, &ind->RBuffer.P, ind->RBuffer.length);
+                	dev_kfree_skb(skb);
+                	dev_kfree_skb(skb2);
+			if (ind->MLength == ind->RBuffer.length) {
+				chan->e.complete = 2;
+				idi_handle_ind(ccard, skb_new);
+				continue;
+			}
+			else {
+				chan->e.complete = 0;
+				skb_queue_tail(&chan->e.R, skb_new);
+				continue;
+			}
+		}
+	}
+}
+
+void
+eicon_io_ack_dispatch(eicon_card *ccard) {
+        struct sk_buff *skb;
+
+        if (!ccard) {
+		if (DebugVar & 1)
+			printk(KERN_WARNING "eicon_io_ack_dispatch: NULL card!\n");
+                return;
+        }
+	while((skb = skb_dequeue(&ccard->rackq))) {
+		idi_handle_ack(ccard, skb);
+	}
+}
+
+
+/*
+ *  IO-Functions for different card-types
+ */
+
+u8 ram_inb(eicon_card *card, void *adr) {
+        eicon_pci_card *pcard;
+        eicon_isa_card *icard;
+        u32 addr = (u32) adr;
+	
+	pcard = &card->hwif.pci;
+	icard = &card->hwif.isa;
+
+        switch(card->type) {
+                case EICON_CTYPE_MAESTRA:
+                        outw((u16)addr, (u16)pcard->PCIreg + M_ADDR);
+                        return(inb((u16)pcard->PCIreg + M_DATA));
+                case EICON_CTYPE_MAESTRAP:
+                case EICON_CTYPE_S2M:
+		case EICON_CTYPE_S:
+		case EICON_CTYPE_SX:
+		case EICON_CTYPE_SCOM:
+		case EICON_CTYPE_QUADRO:
+                        return(readb(addr));
+        }
+ return(0);
+}
+
+u16 ram_inw(eicon_card *card, void *adr) {
+        eicon_pci_card *pcard;
+        eicon_isa_card *icard;
+        u32 addr = (u32) adr;
+	
+	pcard = &card->hwif.pci;
+	icard = &card->hwif.isa;
+
+        switch(card->type) {
+                case EICON_CTYPE_MAESTRA:
+                        outw((u16)addr, (u16)pcard->PCIreg + M_ADDR);
+                        return(inw((u16)pcard->PCIreg + M_DATA));
+                case EICON_CTYPE_MAESTRAP:
+                case EICON_CTYPE_S2M:
+		case EICON_CTYPE_S:
+		case EICON_CTYPE_SX:
+		case EICON_CTYPE_SCOM:
+		case EICON_CTYPE_QUADRO:
+                        return(readw(addr));
+        }
+ return(0);
+}
+
+void ram_outb(eicon_card *card, void *adr, u8 data) {
+        eicon_pci_card *pcard;
+        eicon_isa_card *icard;
+        u32 addr = (u32) adr;
+
+	pcard = &card->hwif.pci;
+	icard = &card->hwif.isa;
+
+        switch(card->type) {
+                case EICON_CTYPE_MAESTRA:
+                        outw((u16)addr, (u16)pcard->PCIreg + M_ADDR);
+                        outb((u8)data, (u16)pcard->PCIreg + M_DATA);
+                        break;
+                case EICON_CTYPE_MAESTRAP:
+                case EICON_CTYPE_S2M:
+		case EICON_CTYPE_S:
+		case EICON_CTYPE_SX:
+		case EICON_CTYPE_SCOM:
+		case EICON_CTYPE_QUADRO:
+                        writeb(data, addr);
+                        break;
+        }
+}
+
+void ram_outw(eicon_card *card, void *adr , u16 data) {
+        eicon_pci_card *pcard;
+        eicon_isa_card *icard;
+        u32 addr = (u32) adr;
+
+	pcard = &card->hwif.pci;
+	icard = &card->hwif.isa;
+
+        switch(card->type) {
+                case EICON_CTYPE_MAESTRA:
+                        outw((u16)addr, (u16)pcard->PCIreg + M_ADDR);
+                        outw((u16)data, (u16)pcard->PCIreg + M_DATA);
+                        break;
+                case EICON_CTYPE_MAESTRAP:
+                case EICON_CTYPE_S2M:
+		case EICON_CTYPE_S:
+		case EICON_CTYPE_SX:
+		case EICON_CTYPE_SCOM:
+		case EICON_CTYPE_QUADRO:
+                        writew(data, addr);
+                        break;
+        }
+}
+
+void ram_copyfromcard(eicon_card *card, void *adrto, void *adr, int len) {
+        int i;
+        switch(card->type) {
+                case EICON_CTYPE_MAESTRA:
+                        for(i = 0; i < len; i++) {
+                                writeb(ram_inb(card, adr + i), adrto + i);
+                        }
+                        break;
+                case EICON_CTYPE_MAESTRAP:
+                        memcpy(adrto, adr, len);
+                        break;
+                case EICON_CTYPE_S2M:
+		case EICON_CTYPE_S:
+		case EICON_CTYPE_SX:
+		case EICON_CTYPE_SCOM:
+		case EICON_CTYPE_QUADRO:
+                        memcpy_fromio(adrto, adr, len);
+                        break;
+        }
+}
+
+void ram_copytocard(eicon_card *card, void *adrto, void *adr, int len) {
+        int i;
+        switch(card->type) {
+                case EICON_CTYPE_MAESTRA:
+                        for(i = 0; i < len; i++) {
+                                ram_outb(card, adrto + i, readb(adr + i));
+                        }
+                        break;
+                case EICON_CTYPE_MAESTRAP:
+                        memcpy(adrto, adr, len);
+                        break;
+                case EICON_CTYPE_S2M:
+		case EICON_CTYPE_S:
+		case EICON_CTYPE_SX:
+		case EICON_CTYPE_SCOM:
+		case EICON_CTYPE_QUADRO:
+                        memcpy_toio(adrto, adr, len);
+                        break;
+        }
+}
+
+/*
+ *  Transmit-Function
+ */
+void
+eicon_io_transmit(eicon_card *ccard) {
+        eicon_pci_card *pci_card;
+        eicon_isa_card *isa_card;
+        struct sk_buff *skb;
+        struct sk_buff *skb2;
+        unsigned long flags;
+        char *ram, *reg, *cfg;
+	eicon_pr_ram  *prram = 0;
+	eicon_isa_com	*com = 0;
+	eicon_REQ *ReqOut = 0;
+	eicon_REQ *reqbuf = 0;
+	eicon_chan *chan;
+	eicon_chan_ptr *chan2;
+	int ReqCount;
+	int scom = 0;
+	int tmp = 0;
+	int quloop = 1;
+
+	pci_card = &ccard->hwif.pci;
+	isa_card = &ccard->hwif.isa;
+
+        if (!ccard) {
+		if (DebugVar & 1)
+                	printk(KERN_WARNING "eicon_transmit: NULL card!\n");
+                return;
+        }
+
+	switch(ccard->type) {
+		case EICON_CTYPE_S:
+		case EICON_CTYPE_SX:
+		case EICON_CTYPE_SCOM:
+		case EICON_CTYPE_QUADRO:
+			scom = 1;
+			com = (eicon_isa_com *)isa_card->shmem;
+			break;
+		case EICON_CTYPE_S2M:
+			scom = 0;
+			prram = (eicon_pr_ram *)isa_card->shmem;
+			break;
+		case EICON_CTYPE_MAESTRAP:
+			scom = 0;
+        		ram = (char *)pci_card->PCIram;
+		        reg = (char *)pci_card->PCIreg;
+        		cfg = (char *)pci_card->PCIcfg;
+			prram = (eicon_pr_ram *)ram;
+			break;
+		case EICON_CTYPE_MAESTRA:
+			scom = 0;
+        		ram = (char *)pci_card->PCIram;
+		        reg = (char *)pci_card->PCIreg;
+        		cfg = (char *)pci_card->PCIcfg;
+			prram = 0;
+			break;
+		default:
+                	printk(KERN_WARNING "eicon_transmit: unsupported card-type!\n");
+			return;
+	}
+
+	ReqCount = 0;
+	if (!(skb2 = skb_dequeue(&ccard->sndq)))
+		quloop = 0; 
+	while(quloop) { 
+                save_flags(flags);
+                cli();
+		if (scom) {
+			if (ram_inb(ccard, &com->Req)) {
+				if (!ccard->ReadyInt) {
+					tmp = ram_inb(ccard, &com->ReadyInt) + 1;
+					ram_outb(ccard, &com->ReadyInt, tmp);
+					ccard->ReadyInt++;
+				}
+        	                restore_flags(flags);
+                	        skb_queue_head(&ccard->sndq, skb2);
+				if (DebugVar & 32)
+        	                	printk(KERN_INFO "eicon: transmit: Card not ready\n");
+	                        return;
+			}
+		} else {
+	                if (!(ram_inb(ccard, &prram->ReqOutput) - ram_inb(ccard, &prram->ReqInput))) {
+        	                restore_flags(flags);
+                	        skb_queue_head(&ccard->sndq, skb2);
+				if (DebugVar & 32)
+        	                	printk(KERN_INFO "eicon: transmit: Card not ready\n");
+	                        return;
+        	        }
+		}
+		restore_flags(flags);
+		chan2 = (eicon_chan_ptr *)skb2->data;
+		chan = chan2->ptr;
+		if (!chan->e.busy) {
+		 if((skb = skb_dequeue(&chan->e.X))) { 
+                	save_flags(flags);
+	                cli();
+			reqbuf = (eicon_REQ *)skb->data;
+			if (scom) {
+				ram_outw(ccard, &com->XBuffer.length, reqbuf->XBuffer.length);
+				ram_copytocard(ccard, &com->XBuffer.P, &reqbuf->XBuffer.P, reqbuf->XBuffer.length);
+				ram_outb(ccard, &com->ReqCh, reqbuf->ReqCh);
+				
+			} else {
+				/* get address of next available request buffer */
+				ReqOut = (eicon_REQ *)&prram->B[ram_inw(ccard, &prram->NextReq)];
+				ram_outw(ccard, &ReqOut->XBuffer.length, reqbuf->XBuffer.length);
+				ram_copytocard(ccard, &ReqOut->XBuffer.P, &reqbuf->XBuffer.P, reqbuf->XBuffer.length);
+				ram_outb(ccard, &ReqOut->ReqCh, reqbuf->ReqCh);
+				ram_outb(ccard, &ReqOut->Req, reqbuf->Req); 
+			}
+
+			if (reqbuf->ReqId &0x1f) { /* if this is no ASSIGN */
+
+				if (!reqbuf->Reference) { /* Signal Layer */
+					if (scom)
+						ram_outb(ccard, &com->ReqId, chan->e.D3Id); 
+					else
+						ram_outb(ccard, &ReqOut->ReqId, chan->e.D3Id); 
+
+					chan->e.ReqCh = 0; 
+				}
+				else {			/* Net Layer */
+					if (scom)
+						ram_outb(ccard, &com->ReqId, chan->e.B2Id); 
+					else
+						ram_outb(ccard, &ReqOut->ReqId, chan->e.B2Id); 
+
+					chan->e.ReqCh = 1;
+					if (((reqbuf->Req & 0x0f) == 0x08) ||
+					   ((reqbuf->Req & 0x0f) == 0x01)) { /* Send Data */
+						chan->waitq = reqbuf->XBuffer.length;
+						chan->waitpq += reqbuf->XBuffer.length;
+					}
+				}
+
+			} else {	/* It is an ASSIGN */
+
+				if (scom)
+					ram_outb(ccard, &com->ReqId, reqbuf->ReqId); 
+				else
+					ram_outb(ccard, &ReqOut->ReqId, reqbuf->ReqId); 
+
+				if (!reqbuf->Reference) 
+					chan->e.ReqCh = 0; 
+				 else
+					chan->e.ReqCh = 1; 
+			} 
+			if (scom)
+			 	chan->e.ref = ccard->ref_out++;
+			else
+			 	chan->e.ref = ram_inw(ccard, &ReqOut->Reference);
+
+			chan->e.Req = reqbuf->Req;
+			ReqCount++; 
+			if (scom)
+				ram_outb(ccard, &com->Req, reqbuf->Req); 
+			else
+				ram_outw(ccard, &prram->NextReq, ram_inw(ccard, &ReqOut->next)); 
+
+			chan->e.busy = 1; 
+			restore_flags(flags);
+			if (DebugVar & 32)
+	                	printk(KERN_DEBUG "eicon: Req=%x Id=%x Ch=%x Len=%x Ref=%d\n", 
+							reqbuf->Req, 
+							ram_inb(ccard, &ReqOut->ReqId),
+							reqbuf->ReqCh, reqbuf->XBuffer.length,
+							chan->e.ref); 
+			dev_kfree_skb(skb);
+		 }
+		 dev_kfree_skb(skb2);
+		} 
+		else {
+		skb_queue_tail(&ccard->sackq, skb2);
+		if (DebugVar & 32)
+                	printk(KERN_INFO "eicon: transmit: busy chan %d\n", chan->No); 
+		}
+
+		if (scom)
+			quloop = 0;
+		else
+			if (!(skb2 = skb_dequeue(&ccard->sndq)))
+				quloop = 0;
+
+	}
+	if (!scom)
+		ram_outb(ccard, &prram->ReqInput, (__u8)(ram_inb(ccard, &prram->ReqInput) + ReqCount)); 
+
+	while((skb = skb_dequeue(&ccard->sackq))) { 
+		skb_queue_tail(&ccard->sndq, skb);
+	}
+}
+
+
+/*
+ * IRQ handler 
+ */
+void
+eicon_irq(int irq, void *dev_id, struct pt_regs *regs) {
+	eicon_card *ccard = (eicon_card *)dev_id;
+        eicon_pci_card *pci_card;
+        eicon_isa_card *isa_card;
+    	char *ram = 0;
+	char *reg = 0;
+	char *cfg = 0;	
+	eicon_pr_ram  *prram = 0;
+	eicon_isa_com	*com = 0;
+        eicon_RC *RcIn;
+        eicon_IND *IndIn;
+	struct sk_buff *skb;
+        int Count = 0;
+	int Rc = 0;
+	int Ind = 0;
+	unsigned char *irqprobe = 0;
+	int scom = 0;
+	int tmp = 0;
+
+
+        if (!ccard) {
+                printk(KERN_WARNING "eicon_irq: spurious interrupt %d\n", irq);
+                return;
+        }
+
+	if (ccard->type == EICON_CTYPE_QUADRO) {
+		tmp = 4;
+		while(tmp) {
+			com = (eicon_isa_com *)ccard->hwif.isa.shmem;
+			if ((readb(ccard->hwif.isa.intack))) { /* quadro found */
+				break;
+			}
+			ccard = ccard->qnext;
+			tmp--;
+		}
+	}
+
+	pci_card = &ccard->hwif.pci;
+	isa_card = &ccard->hwif.isa;
+
+	switch(ccard->type) {
+		case EICON_CTYPE_S:
+		case EICON_CTYPE_SX:
+		case EICON_CTYPE_SCOM:
+		case EICON_CTYPE_QUADRO:
+			scom = 1;
+			com = (eicon_isa_com *)isa_card->shmem;
+			irqprobe = &isa_card->irqprobe;
+			break;
+		case EICON_CTYPE_S2M:
+			scom = 0;
+			prram = (eicon_pr_ram *)isa_card->shmem;
+			irqprobe = &isa_card->irqprobe;
+			break;
+		case EICON_CTYPE_MAESTRAP:
+			scom = 0;
+			ram = (char *)pci_card->PCIram;
+			reg = (char *)pci_card->PCIreg;
+			cfg = (char *)pci_card->PCIcfg;
+			irqprobe = &pci_card->irqprobe;
+			prram = (eicon_pr_ram *)ram;
+			break;
+		case EICON_CTYPE_MAESTRA:
+			scom = 0;
+			ram = (char *)pci_card->PCIram;
+			reg = (char *)pci_card->PCIreg;
+			cfg = (char *)pci_card->PCIcfg;
+			irqprobe = &pci_card->irqprobe;
+			prram = 0;
+			break;
+		default:
+                	printk(KERN_WARNING "eicon_irq: unsupported card-type!\n");
+			return;
+	}
+
+	if (*irqprobe) {
+		switch(ccard->type) {
+			case EICON_CTYPE_S:
+			case EICON_CTYPE_SX:
+			case EICON_CTYPE_SCOM:
+			case EICON_CTYPE_QUADRO:
+				if (readb(isa_card->intack)) {
+        		               	writeb(0, &com->Rc);
+					writeb(0, isa_card->intack);
+				}
+				(*irqprobe)++;
+				break;
+			case EICON_CTYPE_S2M:
+				if (readb(isa_card->intack)) {
+        		               	writeb(0, &prram->RcOutput);
+					writeb(0, isa_card->intack);
+				}
+				(*irqprobe)++;
+				break;
+			case EICON_CTYPE_MAESTRAP:
+	        		if (readb(&ram[0x3fe])) { 
+        		               	writeb(0, &prram->RcOutput);
+				        writew(MP_IRQ_RESET_VAL, &cfg[MP_IRQ_RESET]);
+				        writew(0, &cfg[MP_IRQ_RESET + 2]);
+					writeb(0, &ram[0x3fe]);
+       			        } 
+				*irqprobe = 0;
+				break;
+			case EICON_CTYPE_MAESTRA:
+				outb(0x08, pci_card->PCIreg + M_RESET);
+				*irqprobe = 0;
+				break;
+		}
+		return;
+	}
+
+	switch(ccard->type) {
+		case EICON_CTYPE_S:
+		case EICON_CTYPE_SX:
+		case EICON_CTYPE_SCOM:
+		case EICON_CTYPE_QUADRO:
+		case EICON_CTYPE_S2M:
+			if (!(readb(isa_card->intack))) { /* card did not interrupt */
+				if (DebugVar & 1)
+					printk(KERN_DEBUG "eicon: IRQ: card tells no interrupt!\n");
+				return;
+			} 
+			break;
+		case EICON_CTYPE_MAESTRAP:
+			if (!(readb(&ram[0x3fe]))) { /* card did not interrupt */
+				if (DebugVar & 1)
+					printk(KERN_DEBUG "eicon: IRQ: card tells no interrupt!\n");
+				return;
+			} 
+			break;
+		case EICON_CTYPE_MAESTRA:
+			outw(0x3fe, pci_card->PCIreg + M_ADDR);
+			if (!(inb(pci_card->PCIreg + M_DATA))) { /* card did not interrupt */
+				if (DebugVar & 1)
+					printk(KERN_DEBUG "eicon: IRQ: card tells no interrupt!\n");
+				return;
+			} 
+			break;
+	}
+
+    if (scom) {
+
+        /* if a return code is available ...  */
+	if ((tmp = ram_inb(ccard, &com->Rc))) {
+		eicon_RC *ack;
+		if (tmp == READY_INT) {
+			if (DebugVar & 64)
+                        	printk(KERN_INFO "eicon: IRQ Rc=READY_INT\n");
+			if (ccard->ReadyInt) {
+				ccard->ReadyInt--;
+				ram_outb(ccard, &com->Rc, 0);
+			}
+		} else {
+			skb = alloc_skb(sizeof(eicon_RC), GFP_ATOMIC);
+			ack = (eicon_RC *)skb_put(skb, sizeof(eicon_RC));
+			ack->Rc = tmp;
+			ack->RcId = ram_inb(ccard, &com->RcId);
+			ack->RcCh = ram_inb(ccard, &com->RcCh);
+			ack->Reference = ccard->ref_in++;
+			if (DebugVar & 64)
+                        	printk(KERN_INFO "eicon: IRQ Rc=%d Id=%d Ch=%d Ref=%d\n",
+					tmp,ack->RcId,ack->RcCh,ack->Reference);
+			skb_queue_tail(&ccard->rackq, skb);
+			eicon_schedule_ack(ccard);
+			ram_outb(ccard, &com->Req, 0);
+			ram_outb(ccard, &com->Rc, 0);
+		}
+
+	} else {
+
+	        /* if an indication is available ...  */
+		if ((tmp = ram_inb(ccard, &com->Ind))) {
+			eicon_IND *ind;
+			int len = ram_inw(ccard, &com->RBuffer.length);
+			skb = alloc_skb((sizeof(eicon_IND) + len - 1), GFP_ATOMIC);
+			ind = (eicon_IND *)skb_put(skb, (sizeof(eicon_IND) + len - 1));
+			ind->Ind = tmp;
+			ind->IndId = ram_inb(ccard, &com->IndId);
+			ind->IndCh = ram_inb(ccard, &com->IndCh);
+			ind->MInd  = ram_inb(ccard, &com->MInd);
+			ind->MLength = ram_inw(ccard, &com->MLength);
+			ind->RBuffer.length = len; 
+			if (DebugVar & 64)
+                        	printk(KERN_INFO "eicon: IRQ Ind=%d Id=%d Ch=%d MInd=%d MLen=%d Len=%d\n",
+				tmp,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,len);
+			ram_copyfromcard(ccard, &ind->RBuffer.P, &com->RBuffer.P, len);
+			skb_queue_tail(&ccard->rcvq, skb);
+			eicon_schedule_rx(ccard);
+			ram_outb(ccard, &com->Ind, 0);
+		}
+	}
+
+    } else {
+
+        /* if return codes are available ...  */
+        if((Count = ram_inb(ccard, &prram->RcOutput))) {
+		eicon_RC *ack;
+                /* get the buffer address of the first return code */
+                RcIn = (eicon_RC *)&prram->B[ram_inw(ccard, &prram->NextRc)];
+                /* for all return codes do ...  */
+                while(Count--) {
+
+                        if((Rc=ram_inb(ccard, &RcIn->Rc))) {
+				skb = alloc_skb(sizeof(eicon_RC), GFP_ATOMIC);
+				ack = (eicon_RC *)skb_put(skb, sizeof(eicon_RC));
+				ack->Rc = Rc;
+				ack->RcId = ram_inb(ccard, &RcIn->RcId);
+				ack->RcCh = ram_inb(ccard, &RcIn->RcCh);
+				ack->Reference = ram_inw(ccard, &RcIn->Reference);
+				if (DebugVar & 64)
+	                        	printk(KERN_INFO "eicon: IRQ Rc=%d Id=%d Ch=%d Ref=%d\n",
+						Rc,ack->RcId,ack->RcCh,ack->Reference);
+                        	ram_outb(ccard, &RcIn->Rc, 0);
+				 skb_queue_tail(&ccard->rackq, skb);
+				 eicon_schedule_ack(ccard);
+                        }
+                        /* get buffer address of next return code   */
+                        RcIn = (eicon_RC *)&prram->B[ram_inw(ccard, &RcIn->next)];
+                }
+                /* clear all return codes (no chaining!) */
+                ram_outb(ccard, &prram->RcOutput, 0);
+        }
+
+        /* if indications are available ... */
+        if((Count = ram_inb(ccard, &prram->IndOutput))) {
+		eicon_IND *ind;
+                /* get the buffer address of the first indication */
+                IndIn = (eicon_IND *)&prram->B[ram_inw(ccard, &prram->NextInd)];
+                /* for all indications do ... */
+                while(Count--) {
+			Ind = ram_inb(ccard, &IndIn->Ind);
+			if(Ind) {
+				int len = ram_inw(ccard, &IndIn->RBuffer.length);
+				skb = alloc_skb((sizeof(eicon_IND) + len - 1), GFP_ATOMIC);
+				ind = (eicon_IND *)skb_put(skb, (sizeof(eicon_IND) + len - 1));
+				ind->Ind = Ind;
+				ind->IndId = ram_inb(ccard, &IndIn->IndId);
+				ind->IndCh = ram_inb(ccard, &IndIn->IndCh);
+				ind->MInd  = ram_inb(ccard, &IndIn->MInd);
+				ind->MLength = ram_inw(ccard, &IndIn->MLength);
+				ind->RBuffer.length = len;
+				if (DebugVar & 64)
+	                        	printk(KERN_INFO "eicon: IRQ Ind=%d Id=%d Ch=%d MInd=%d MLen=%d Len=%d\n",
+					Ind,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,len);
+                                ram_copyfromcard(ccard, &ind->RBuffer.P, &IndIn->RBuffer.P, len);
+				skb_queue_tail(&ccard->rcvq, skb);
+				eicon_schedule_rx(ccard);
+				ram_outb(ccard, &IndIn->Ind, 0);
+                        }
+                        /* get buffer address of next indication  */
+                        IndIn = (eicon_IND *)&prram->B[ram_inw(ccard, &IndIn->next)];
+                }
+                ram_outb(ccard, &prram->IndOutput, 0);
+        }
+
+    } 
+
+	/* clear interrupt */
+	switch(ccard->type) {
+		case EICON_CTYPE_QUADRO:
+			writeb(0, isa_card->intack);
+			writeb(0, &com[0x401]);
+			break;
+		case EICON_CTYPE_S:
+		case EICON_CTYPE_SX:
+		case EICON_CTYPE_SCOM:
+		case EICON_CTYPE_S2M:
+			writeb(0, isa_card->intack);
+			break;
+		case EICON_CTYPE_MAESTRAP:
+			writew(MP_IRQ_RESET_VAL, &cfg[MP_IRQ_RESET]);
+			writew(0, &cfg[MP_IRQ_RESET + 2]); 
+			writeb(0, &ram[0x3fe]); 
+			break;
+		case EICON_CTYPE_MAESTRA:
+			outb(0x08, pci_card->PCIreg + M_RESET);
+			outw(0x3fe, pci_card->PCIreg + M_ADDR);
+			outb(0, pci_card->PCIreg + M_DATA);
+			break;
+	}
+
+  return;
+}
+
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/eicon/eicon_isa.c linux/drivers/isdn/eicon/eicon_isa.c
--- v2.3.3/linux/drivers/isdn/eicon/eicon_isa.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/eicon/eicon_isa.c	Sun May 23 10:03:41 1999
@@ -0,0 +1,432 @@
+/* $Id: eicon_isa.c,v 1.5 1999/04/01 12:48:33 armin Exp $
+ *
+ * ISDN low-level module for Eicon.Diehl active ISDN-Cards.
+ * Hardware-specific code for old ISA cards.
+ *
+ * Copyright 1998    by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1998,99 by Armin Schindler (mac@melware.de)
+ * Copyright 1999    Cytronics & Melware (info@melware.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: eicon_isa.c,v $
+ * Revision 1.5  1999/04/01 12:48:33  armin
+ * Changed some log outputs.
+ *
+ * Revision 1.4  1999/03/29 11:19:46  armin
+ * I/O stuff now in seperate file (eicon_io.c)
+ * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented.
+ *
+ * Revision 1.3  1999/03/02 12:37:45  armin
+ * Added some important checks.
+ * Analog Modem with DSP.
+ * Channels will be added to Link-Level after loading firmware.
+ *
+ * Revision 1.2  1999/01/24 20:14:19  armin
+ * Changed and added debug stuff.
+ * Better data sending. (still problems with tty's flip buffer)
+ *
+ * Revision 1.1  1999/01/01 18:09:43  armin
+ * First checkin of new eicon driver.
+ * DIVA-Server BRI/PCI and PRI/PCI are supported.
+ * Old diehl code is obsolete.
+ *
+ *
+ */
+
+#include "eicon.h"
+#include "eicon_isa.h"
+
+#define check_shmem   check_region
+#define release_shmem release_region
+#define request_shmem request_region
+
+char *eicon_isa_revision = "$Revision: 1.5 $";
+
+/* Mask for detecting invalid IRQ parameter */
+static int eicon_isa_valid_irq[] = {
+	0x1c1c, /* 2, 3, 4, 10, 11, 12 (S)*/
+	0x1c1c, /* 2, 3, 4, 10, 11, 12 (SX) */
+	0x1cbc, /* 2, 3, 4, 5, 7, 10, 11, 12 (SCOM) */
+	0x1cbc, /* 2, 3, 4, 5, 6, 10, 11, 12 (Quadro) */
+	0x1cbc  /* 2, 3, 4, 5, 7, 10, 11, 12 (S2M) */
+};
+
+static void
+eicon_isa_release_shmem(eicon_isa_card *card) {
+	if (card->mvalid)
+		release_shmem((unsigned long)card->shmem, card->ramsize);
+	card->mvalid = 0;
+}
+
+static void
+eicon_isa_release_irq(eicon_isa_card *card) {
+	if (!card->master)
+		return;
+	if (card->ivalid)
+		free_irq(card->irq, card);
+	card->ivalid = 0;
+}
+
+void
+eicon_isa_release(eicon_isa_card *card) {
+	eicon_isa_release_irq(card);
+	eicon_isa_release_shmem(card);
+}
+
+void
+eicon_isa_printpar(eicon_isa_card *card) {
+	switch (card->type) {
+		case EICON_CTYPE_S:
+		case EICON_CTYPE_SX:
+		case EICON_CTYPE_SCOM:
+		case EICON_CTYPE_QUADRO:
+		case EICON_CTYPE_S2M:
+			printk(KERN_INFO "Eicon %s at 0x%lx, irq %d\n",
+			       eicon_ctype_name[card->type],
+			       (unsigned long)card->shmem,
+			       card->irq);
+	}
+}
+
+int
+eicon_isa_find_card(int Mem, int Irq, char * Id)
+{
+	int primary = 1;
+
+	if (!strlen(Id))
+		return -1;
+
+	/* Check for valid membase address */
+	if ((Mem < 0x0c0000) ||
+	    (Mem > 0x0fc000) ||
+	    (Mem & 0xfff)) { 
+		printk(KERN_WARNING "eicon_isa: illegal membase 0x%x for %s\n",
+			 Mem, Id);
+		return -1;
+	}
+	if (check_shmem(Mem, RAMSIZE)) {
+		printk(KERN_WARNING "eicon_isa_boot: memory at 0x%x already in use.\n", Mem);
+		return -1;
+	}
+
+        writew(0x55aa, Mem + 0x402);
+        if (readw(Mem + 0x402) != 0x55aa) primary = 0;
+	writew(0, Mem + 0x402);
+	if (readw(Mem + 0x402) != 0) primary = 0;
+
+	printk(KERN_INFO "Eicon: Driver-ID: %s\n", Id);
+	if (primary) {
+		printk(KERN_INFO "Eicon: assuming pri card at 0x%x\n", Mem);
+		writeb(0, Mem + 0x3ffe);
+		return EICON_CTYPE_ISAPRI;
+	} else {
+		printk(KERN_INFO "Eicon: assuming bri card at 0x%x\n", Mem);
+		writeb(0, Mem + 0x400);
+		return EICON_CTYPE_ISABRI;
+	}
+	return -1;
+}
+
+int
+eicon_isa_bootload(eicon_isa_card *card, eicon_isa_codebuf *cb) {
+	int	tmp;
+	int               timeout;
+	eicon_isa_codebuf cbuf;
+	unsigned char     *code;
+	eicon_isa_boot    *boot;
+
+	if (copy_from_user(&cbuf, cb, sizeof(eicon_isa_codebuf)))
+		return -EFAULT;
+
+	/* Allocate code-buffer and copy code from userspace */
+	if (cbuf.bootstrap_len > 1024) {
+		printk(KERN_WARNING "eicon_isa_boot: Invalid startup-code size %ld\n",
+		       cbuf.bootstrap_len);
+		return -EINVAL;
+	}
+	if (!(code = kmalloc(cbuf.bootstrap_len, GFP_KERNEL))) {
+		printk(KERN_WARNING "eicon_isa_boot: Couldn't allocate code buffer\n");
+		return -ENOMEM;
+	}
+	if (copy_from_user(code, &cb->code, cbuf.bootstrap_len)) {
+		kfree(code);
+		return -EFAULT;
+	}
+
+	switch(card->type) {
+		case EICON_CTYPE_S:
+		case EICON_CTYPE_SX:
+		case EICON_CTYPE_SCOM:
+		case EICON_CTYPE_QUADRO:
+		case EICON_CTYPE_ISABRI:
+			card->ramsize  = RAMSIZE;
+			card->intack   = (__u8 *)card->shmem + INTACK;
+			card->startcpu = (__u8 *)card->shmem + STARTCPU;
+			card->stopcpu  = (__u8 *)card->shmem + STOPCPU;
+			break;
+		case EICON_CTYPE_S2M:
+		case EICON_CTYPE_ISAPRI:
+			card->ramsize  = RAMSIZE_P;
+			card->intack   = (__u8 *)card->shmem + INTACK_P;
+			card->startcpu = (__u8 *)card->shmem + STARTCPU_P;
+			card->stopcpu  = (__u8 *)card->shmem + STOPCPU_P;
+			break;
+		default:
+			printk(KERN_WARNING "eicon_isa_boot: Invalid card type %d\n", card->type);
+			return -EINVAL;
+	}
+
+	/* Register shmem */
+	if (check_shmem((unsigned long)card->shmem, card->ramsize)) {
+		printk(KERN_WARNING "eicon_isa_boot: memory at 0x%lx already in use.\n",
+			(unsigned long)card->shmem);
+		kfree(code);
+		return -EBUSY;
+	}
+	request_shmem((unsigned long)card->shmem, card->ramsize, "Eicon ISA ISDN");
+	card->mvalid = 1;
+
+	/* clear any pending irq's */
+	readb(card->intack);
+	/* set reset-line active */
+	writeb(0, card->stopcpu); 
+	/* clear irq-requests */
+	writeb(0, card->intack);
+	readb(card->intack);
+
+	/* Copy code into card */
+	memcpy_toio(&card->shmem->c, code, cbuf.bootstrap_len);
+
+	/* Check for properly loaded code */
+	if (!check_signature((unsigned long)&card->shmem->c, code, 1020)) {
+		printk(KERN_WARNING "eicon_isa_boot: Could not load startup-code\n");
+		eicon_isa_release_shmem(card);
+		kfree(code);
+		return -EIO;
+	}
+	/* if 16k-ramsize, duplicate the reset-jump-code */
+	if (card->ramsize == RAMSIZE_P)
+		memcpy_toio((__u8 *)card->shmem + 0x3ff0, &code[0x3f0], 12);
+
+	kfree(code);
+	boot = &card->shmem->boot;
+
+	/* Delay 0.2 sec. */
+	SLEEP(20);
+
+	/* Start CPU */
+	writeb(cbuf.boot_opt, &boot->ctrl);
+	writeb(0, card->startcpu); 
+
+	/* Delay 0.2 sec. */
+	SLEEP(20);
+
+	timeout = jiffies + (HZ * 22);
+	while (timeout > jiffies) {
+		if (readb(&boot->ctrl) == 0)
+			break;
+		SLEEP(10);
+	}
+	if (readb(&boot->ctrl) != 0) {
+		printk(KERN_WARNING "eicon_isa_boot: CPU test failed\n");
+		eicon_isa_release_shmem(card);
+		return -EIO;
+	}
+
+	/* Check for memory-test errors */
+	if (readw(&boot->ebit)) {
+		printk(KERN_WARNING "eicon_isa_boot: memory test failed (bit 0x%04x at 0x%08x)\n",
+		       readw(&boot->ebit), readl(&boot->eloc));
+		eicon_isa_release_shmem(card);
+		return -EIO;
+	}
+
+        /* Check card type and memory size */
+        tmp = readb(&boot->card);
+	if ((tmp < 0) || (tmp > 4)) {
+		printk(KERN_WARNING "eicon_isa_boot: Type detect failed\n");
+		eicon_isa_release_shmem(card);
+		return -EIO;
+	}
+	card->type = tmp;
+	((eicon_card *)card->card)->type = tmp;
+
+        tmp = readb(&boot->msize);
+        if (tmp != 8 && tmp != 16 && tmp != 24 &&
+            tmp != 32 && tmp != 48 && tmp != 60) {
+                printk(KERN_WARNING "eicon_isa_boot: invalid memsize\n");
+		eicon_isa_release_shmem(card);
+                return -EIO;
+        }
+	printk(KERN_INFO "%s: startup-code loaded\n", eicon_ctype_name[card->type]); 
+	if ((card->type == EICON_CTYPE_QUADRO) && (card->master)) {
+		tmp = eicon_addcard(card->type, (unsigned long)card->shmem, card->irq, 
+					((eicon_card *)card->card)->regname);
+		printk(KERN_INFO "Eicon: %d adapters added\n", tmp);
+	}
+	return 0;
+}
+
+int
+eicon_isa_load(eicon_isa_card *card, eicon_isa_codebuf *cb) {
+	eicon_isa_boot    *boot;
+	int               tmp;
+	int               timeout;
+	int 		  j;
+	eicon_isa_codebuf cbuf;
+	unsigned char     *code;
+	unsigned char     *p;
+
+	if (copy_from_user(&cbuf, cb, sizeof(eicon_isa_codebuf)))
+		return -EFAULT;
+
+	if (!(code = kmalloc(cbuf.firmware_len, GFP_KERNEL))) {
+		printk(KERN_WARNING "eicon_isa_boot: Couldn't allocate code buffer\n");
+		return -ENOMEM;
+	}
+
+	if (copy_from_user(code, &cb->code, cbuf.firmware_len)) {
+		kfree(code);
+		return -EFAULT;
+	}
+
+	boot = &card->shmem->boot;
+
+	if ((!card->ivalid) && card->master) {
+		card->irqprobe = 1;
+		/* Check for valid IRQ */
+		if ((card->irq < 0) || (card->irq > 15) || 
+		    (!((1 << card->irq) & eicon_isa_valid_irq[card->type & 0x0f]))) {
+			printk(KERN_WARNING "eicon_isa_boot: illegal irq: %d\n", card->irq);
+			eicon_isa_release_shmem(card);
+			kfree(code);
+			return -EINVAL;
+		}
+		/* Register irq */
+		if (!request_irq(card->irq, &eicon_irq, 0, "Eicon ISA ISDN", card))
+			card->ivalid = 1;
+		else {
+			printk(KERN_WARNING "eicon_isa_boot: irq %d already in use.\n",
+			       card->irq);
+			eicon_isa_release_shmem(card);
+			kfree(code);
+			return -EBUSY;
+		}
+	}
+
+        tmp = readb(&boot->msize);
+        if (tmp != 8 && tmp != 16 && tmp != 24 &&
+            tmp != 32 && tmp != 48 && tmp != 60) {
+                printk(KERN_WARNING "eicon_isa_boot: invalid memsize\n");
+		eicon_isa_release_shmem(card);
+                return -EIO;
+        }
+
+	eicon_isa_printpar(card);
+
+	/* Download firmware */
+	printk(KERN_INFO "%s %dkB, loading firmware ...\n", 
+	       eicon_ctype_name[card->type],
+	       tmp * 16);
+	tmp = cbuf.firmware_len >> 8;
+	p = code;
+	while (tmp--) {
+		memcpy_toio(&boot->b, p, 256);
+		writeb(1, &boot->ctrl);
+		timeout = jiffies + 10;
+		while (timeout > jiffies) {
+			if (readb(&boot->ctrl) == 0)
+				break;
+			SLEEP(2);
+		}
+		if (readb(&boot->ctrl)) {
+			printk(KERN_WARNING "eicon_isa_boot: download timeout at 0x%x\n", p-code);
+			eicon_isa_release(card);
+			kfree(code);
+			return -EIO;
+		}
+		p += 256;
+	}
+	kfree(code);
+
+	/* Initialize firmware parameters */
+	memcpy_toio(&card->shmem->c[8], &cbuf.tei, 14);
+	memcpy_toio(&card->shmem->c[32], &cbuf.oad, 96);
+	memcpy_toio(&card->shmem->c[128], &cbuf.oad, 96);
+	
+	/* Start firmware, wait for signature */
+	writeb(2, &boot->ctrl);
+	timeout = jiffies + (5*HZ);
+	while (timeout > jiffies) {
+		if (readw(&boot->signature) == 0x4447)
+			break;
+		SLEEP(2);
+	}
+	if (readw(&boot->signature) != 0x4447) {
+		printk(KERN_WARNING "eicon_isa_boot: firmware selftest failed %04x\n",
+		       readw(&boot->signature));
+		eicon_isa_release(card);
+		return -EIO;
+	}
+
+	card->channels = readb(&card->shmem->c[0x3f6]);
+
+	/* clear irq-requests, reset irq-count */
+	readb(card->intack);
+	writeb(0, card->intack);
+
+	if (card->master) {
+		card->irqprobe = 1;
+		/* Trigger an interrupt and check if it is delivered */
+		tmp = readb(&card->shmem->com.ReadyInt);
+		tmp ++;
+		writeb(tmp, &card->shmem->com.ReadyInt);
+		timeout = jiffies + 20;
+		while (timeout > jiffies) {
+			if (card->irqprobe > 1)
+				break;
+			SLEEP(2);
+		}
+		if (card->irqprobe == 1) {
+			printk(KERN_WARNING "eicon_isa_boot: IRQ test failed\n");
+			eicon_isa_release(card);
+			return -EIO;
+		}
+	}
+	writeb(card->irq, &card->shmem->com.Int);
+
+	/* initializing some variables */
+	((eicon_card *)card->card)->ReadyInt = 0;
+	((eicon_card *)card->card)->ref_in  = 1;
+	((eicon_card *)card->card)->ref_out = 1;
+	for(j=0; j<256; j++) ((eicon_card *)card->card)->IdTable[j] = NULL;
+	for(j=0; j< (card->channels + 1); j++) {
+		((eicon_card *)card->card)->bch[j].e.busy = 0;
+		((eicon_card *)card->card)->bch[j].e.D3Id = 0;
+		((eicon_card *)card->card)->bch[j].e.B2Id = 0;
+		((eicon_card *)card->card)->bch[j].e.ref = 0;
+		((eicon_card *)card->card)->bch[j].e.Req = 0;
+		((eicon_card *)card->card)->bch[j].e.complete = 1;
+		((eicon_card *)card->card)->bch[j].fsm_state = EICON_STATE_NULL;
+	}
+
+	printk(KERN_INFO "Eicon: Supported channels: %d\n", card->channels); 
+	printk(KERN_INFO "%s successfully started\n", eicon_ctype_name[card->type]);
+
+	/* Enable normal IRQ processing */
+	card->irqprobe = 0;
+	return 0;
+}
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/eicon/eicon_isa.h linux/drivers/isdn/eicon/eicon_isa.h
--- v2.3.3/linux/drivers/isdn/eicon/eicon_isa.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/eicon/eicon_isa.h	Sun May 23 10:03:41 1999
@@ -0,0 +1,144 @@
+/* $Id: eicon_isa.h,v 1.3 1999/03/29 11:19:47 armin Exp $
+ *
+ * ISDN low-level module for Eicon.Diehl active ISDN-Cards.
+ *
+ * Copyright 1998    by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1998,99 by Armin Schindler (mac@melware.de)
+ * Copyright 1999    Cytronics & Melware (info@melware.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: eicon_isa.h,v $
+ * Revision 1.3  1999/03/29 11:19:47  armin
+ * I/O stuff now in seperate file (eicon_io.c)
+ * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented.
+ *
+ * Revision 1.2  1999/03/02 12:37:46  armin
+ * Added some important checks.
+ * Analog Modem with DSP.
+ * Channels will be added to Link-Level after loading firmware.
+ *
+ * Revision 1.1  1999/01/01 18:09:44  armin
+ * First checkin of new eicon driver.
+ * DIVA-Server BRI/PCI and PRI/PCI are supported.
+ * Old diehl code is obsolete.
+ *
+ *
+ */
+
+#ifndef eicon_isa_h
+#define eicon_isa_h
+
+#ifdef __KERNEL__
+
+/* Factory defaults for ISA-Cards */
+#define EICON_ISA_MEMBASE 0xd0000
+#define EICON_ISA_IRQ     3
+/* shmem offset for Quadro parts */
+#define EICON_ISA_QOFFSET 0x0800
+
+typedef struct {
+        __u16 length __attribute__ ((packed));   /* length of data/parameter field         */
+        __u8  P[270];                            /* data/parameter field                   */
+} eicon_scom_PBUFFER;
+
+/* General communication buffer */
+typedef struct {
+        __u8   Req;                                /* request register                       */
+	__u8   ReqId;                              /* request task/entity identification     */
+	__u8   Rc;                                 /* return code register                   */
+	__u8   RcId;                               /* return code task/entity identification */
+	__u8   Ind;                                /* Indication register                    */
+	__u8   IndId;                              /* Indication task/entity identification  */
+	__u8   IMask;                              /* Interrupt Mask Flag                    */
+	__u8   RNR;                                /* Receiver Not Ready (set by PC)         */
+	__u8   XLock;                              /* XBuffer locked Flag                    */
+	__u8   Int;                                /* ISDN interrupt                         */
+	__u8   ReqCh;                              /* Channel field for layer-3 Requests     */
+	__u8   RcCh;                               /* Channel field for layer-3 Returncodes  */
+	__u8   IndCh;                              /* Channel field for layer-3 Indications  */
+	__u8   MInd;                               /* more data indication field             */
+	__u16  MLength;                            /* more data total packet length          */
+	__u8   ReadyInt;                           /* request field for ready interrupt      */
+	__u8   Reserved[12];                       /* reserved space                         */
+	__u8   IfType;                             /* 1 = 16k-Interface                      */
+	__u16  Signature __attribute__ ((packed)); /* ISDN adapter Signature                 */
+	eicon_scom_PBUFFER XBuffer;                /* Transmit Buffer                        */
+	eicon_scom_PBUFFER RBuffer;                /* Receive Buffer                         */
+} eicon_isa_com;
+
+/* struct for downloading firmware */
+typedef struct {
+	__u8  ctrl;
+	__u8  card;
+	__u8  msize;
+	__u8  fill0;
+	__u16 ebit __attribute__ ((packed));
+	__u32 eloc __attribute__ ((packed));
+	__u8  reserved[20];
+	__u16 signature __attribute__ ((packed));
+	__u8  fill[224];
+	__u8  b[256];
+} eicon_isa_boot;
+
+/* Shared memory */
+typedef union {
+	unsigned char  c[0x400];
+	eicon_isa_com  com;
+	eicon_isa_boot boot;
+} eicon_isa_shmem;
+
+/*
+ * card's description
+ */
+typedef struct {
+	int               ramsize;
+	int               irq;	    /* IRQ                        */
+	void*             card;
+	eicon_isa_shmem*  shmem;    /* Shared-memory area         */
+	unsigned char*    intack;   /* Int-Acknowledge            */
+	unsigned char*    stopcpu;  /* Writing here stops CPU     */
+	unsigned char*    startcpu; /* Writing here starts CPU    */
+	unsigned char     type;     /* card type                  */
+	int		  channels; /* No. of channels		  */
+	unsigned char     irqprobe; /* Flag: IRQ-probing          */
+	unsigned char     mvalid;   /* Flag: Memory is valid      */
+	unsigned char     ivalid;   /* Flag: IRQ is valid         */
+	unsigned char     master;   /* Flag: Card ist Quadro 1/4  */
+	void*             generic;  /* Ptr to generic card struct */
+} eicon_isa_card;
+
+/* Offsets for special locations on standard cards */
+#define INTACK     0x03fe 
+#define STOPCPU    0x0400
+#define STARTCPU   0x0401
+#define RAMSIZE    0x0400
+/* Offsets for special location on PRI card */
+#define INTACK_P   0x3ffc
+#define STOPCPU_P  0x3ffe
+#define STARTCPU_P 0x3fff
+#define RAMSIZE_P  0x4000
+
+
+extern int eicon_isa_load(eicon_isa_card *card, eicon_isa_codebuf *cb);
+extern int eicon_isa_bootload(eicon_isa_card *card, eicon_isa_codebuf *cb);
+extern void eicon_isa_release(eicon_isa_card *card);
+extern void eicon_isa_printpar(eicon_isa_card *card);
+extern void eicon_isa_transmit(eicon_isa_card *card);
+extern int eicon_isa_find_card(int Mem, int Irq, char * Id);
+
+#endif  /* __KERNEL__ */
+
+#endif	/* eicon_isa_h */
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/eicon/eicon_mod.c linux/drivers/isdn/eicon/eicon_mod.c
--- v2.3.3/linux/drivers/isdn/eicon/eicon_mod.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/eicon/eicon_mod.c	Sun May 23 10:03:41 1999
@@ -0,0 +1,1210 @@
+/* $Id: eicon_mod.c,v 1.5 1999/04/01 12:48:35 armin Exp $
+ *
+ * ISDN lowlevel-module for Eicon.Diehl active cards.
+ * 
+ * Copyright 1997    by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1998,99 by Armin Schindler (mac@melware.de) 
+ * Copyright 1999    Cytronics & Melware (info@melware.de)
+ * 
+ * Thanks to    Eicon Technology Diehl GmbH & Co. oHG for
+ *              documents, informations and hardware.
+ *
+ *              Deutsche Telekom AG for S2M support.
+ *
+ * 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: eicon_mod.c,v $
+ * Revision 1.5  1999/04/01 12:48:35  armin
+ * Changed some log outputs.
+ *
+ * Revision 1.4  1999/03/29 11:19:47  armin
+ * I/O stuff now in seperate file (eicon_io.c)
+ * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented.
+ *
+ * Revision 1.3  1999/03/02 12:37:47  armin
+ * Added some important checks.
+ * Analog Modem with DSP.
+ * Channels will be added to Link-Level after loading firmware.
+ *
+ * Revision 1.2  1999/01/24 20:14:21  armin
+ * Changed and added debug stuff.
+ * Better data sending. (still problems with tty's flip buffer)
+ *
+ * Revision 1.1  1999/01/01 18:09:44  armin
+ * First checkin of new eicon driver.
+ * DIVA-Server BRI/PCI and PRI/PCI are supported.
+ * Old diehl code is obsolete.
+ *
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include "eicon.h"
+
+#define INCLUDE_INLINE_FUNCS
+
+static eicon_card *cards = (eicon_card *) NULL;
+
+static char *eicon_revision = "$Revision: 1.5 $";
+
+extern char *eicon_pci_revision;
+extern char *eicon_isa_revision;
+extern char *eicon_idi_revision;
+
+#ifdef MODULE
+#define MOD_USE_COUNT (GET_USE_COUNT (&__this_module))
+#endif
+
+#define EICON_CTRL_VERSION 1
+
+ulong DebugVar;
+
+/* Parameters to be set by insmod */
+static int   membase      = -1;
+static int   irq          = -1;
+static char *id           = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
+
+MODULE_DESCRIPTION(             "Driver for Eicon.Diehl active ISDN cards");
+MODULE_AUTHOR(                  "Armin Schindler");
+MODULE_SUPPORTED_DEVICE(        "ISDN subsystem");
+MODULE_PARM_DESC(membase,	"Base address of first ISA card");
+MODULE_PARM_DESC(irq,    	"IRQ of first card");
+MODULE_PARM_DESC(id,   		"ID-String of first card");
+MODULE_PARM(membase,    	"i");
+MODULE_PARM(irq,          	"i");
+MODULE_PARM(id,           	"s");
+
+char *eicon_ctype_name[] = {
+        "ISDN-S",
+        "ISDN-SX",
+        "ISDN-SCOM",
+        "ISDN-QUADRO",
+        "ISDN-S2M",
+        "DIVA Server BRI/PCI",
+        "DIVA Server 4BRI/PCI",
+        "DIVA Server 4BRI/PCI",
+        "DIVA Server PRI/PCI"
+};
+
+static int
+getrel(char *p)
+{
+        int v = 0;
+	char *tmp = 0;
+
+	if ((tmp = strchr(p, '.')))
+		p = tmp + 1;
+        while (p[0] >= '0' && p[0] <= '9') {
+                v = ((v < 0) ? 0 : (v * 10)) + (int) (p[0] - '0');
+		p++;
+	}
+        return v;
+
+
+}
+
+static char *
+eicon_getrev(const char *revision)
+{
+	char *rev;
+	char *p;
+	if ((p = strchr(revision, ':'))) {
+		rev = p + 2;
+		p = strchr(rev, '$');
+		*--p = 0;
+	} else rev = "?.??";
+	return rev;
+
+}
+
+static eicon_chan *
+find_channel(eicon_card *card, int channel)
+{
+	if ((channel >= 0) && (channel < card->nchannels))
+        	return &(card->bch[channel]);
+	if (DebugVar & 1)
+		printk(KERN_WARNING "eicon: Invalid channel %d\n", channel);
+	return NULL;
+}
+
+/*
+ * Free MSN list
+ */
+static void
+eicon_clear_msn(eicon_card *card)
+{
+        struct msn_entry *p = card->msn_list;
+        struct msn_entry *q;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+        card->msn_list = NULL;
+	restore_flags(flags);
+        while (p) {
+                q  = p->next;
+                kfree(p);
+                p = q;
+        }
+}
+
+/*
+ * Find an MSN entry in the list.
+ * If ia5 != 0, return IA5-encoded EAZ, else
+ * return a bitmask with corresponding bit set.
+ */
+static __u16
+eicon_find_msn(eicon_card *card, char *msn, int ia5)
+{
+        struct msn_entry *p = card->msn_list;
+	__u8 eaz = '0';
+
+	while (p) {
+		if (!strcmp(p->msn, msn)) {
+			eaz = p->eaz;
+			break;
+		}
+		p = p->next;
+	}
+	if (!ia5)
+		return (1 << (eaz - '0'));
+	else
+		return eaz;
+}
+
+/*
+ * Find an EAZ entry in the list.
+ * return a string with corresponding msn.
+ */
+char *
+eicon_find_eaz(eicon_card *card, char eaz)
+{
+        struct msn_entry *p = card->msn_list;
+
+	while (p) {
+		if (p->eaz == eaz)
+			return(p->msn);
+		p = p->next;
+	}
+	return("\0");
+}
+
+#if 0
+/*
+ * Add or delete an MSN to the MSN list
+ *
+ * First character of msneaz is EAZ, rest is MSN.
+ * If length of eazmsn is 1, delete that entry.
+ */
+static int
+eicon_set_msn(eicon_card *card, char *eazmsn)
+{
+        struct msn_entry *p = card->msn_list;
+        struct msn_entry *q = NULL;
+	unsigned long flags;
+	int i;
+	
+	if (!strlen(eazmsn))
+		return 0;
+	if (strlen(eazmsn) > 16)
+		return -EINVAL;
+	for (i = 0; i < strlen(eazmsn); i++)
+		if (!isdigit(eazmsn[i]))
+			return -EINVAL;
+        if (strlen(eazmsn) == 1) {
+		/* Delete a single MSN */
+		while (p) {
+			if (p->eaz == eazmsn[0]) {
+				save_flags(flags);
+				cli();
+				if (q)
+					q->next = p->next;
+				else
+					card->msn_list = p->next;
+				restore_flags(flags);
+				kfree(p);
+				if (DebugVar & 8)
+					printk(KERN_DEBUG
+					       "Mapping for EAZ %c deleted\n",
+					       eazmsn[0]);
+				return 0;
+			}
+			q = p;
+			p = p->next;
+		}
+		return 0;
+        }
+	/* Add a single MSN */
+	while (p) {
+		/* Found in list, replace MSN */
+		if (p->eaz == eazmsn[0]) {
+			save_flags(flags);
+			cli();
+			strcpy(p->msn, &eazmsn[1]);
+			restore_flags(flags);
+			if (DebugVar & 8)
+				printk(KERN_DEBUG
+				       "Mapping for EAZ %c changed to %s\n",
+				       eazmsn[0],
+				       &eazmsn[1]);
+			return 0;
+		}
+		p = p->next;
+	}
+	/* Not found in list, add new entry */
+	p = kmalloc(sizeof(msn_entry), GFP_KERNEL);
+	if (!p)
+		return -ENOMEM;
+	p->eaz = eazmsn[0];
+	strcpy(p->msn, &eazmsn[1]);
+	p->next = card->msn_list;
+	save_flags(flags);
+	cli();
+	card->msn_list = p;
+	restore_flags(flags);
+	if (DebugVar & 8)
+		printk(KERN_DEBUG
+		       "Mapping %c -> %s added\n",
+		       eazmsn[0],
+		       &eazmsn[1]);
+	return 0;
+}
+#endif
+
+static void
+eicon_rcv_dispatch(struct eicon_card *card)
+{
+	switch (card->bus) {
+		case EICON_BUS_ISA:
+		case EICON_BUS_PCI:
+			eicon_io_rcv_dispatch(card);
+			break;
+		case EICON_BUS_MCA:
+		default:
+			if (DebugVar & 1)
+				printk(KERN_WARNING
+				       "eicon_ack_dispatch: Illegal bustype %d\n", card->bus);
+	}
+}
+
+static void
+eicon_ack_dispatch(struct eicon_card *card)
+{
+	switch (card->bus) {
+		case EICON_BUS_ISA:
+		case EICON_BUS_PCI:
+			eicon_io_ack_dispatch(card);
+			break;
+		case EICON_BUS_MCA:
+		default:
+			if (DebugVar & 1)
+				printk(KERN_WARNING
+			       		"eicon_ack_dispatch: Illegal bustype %d\n", card->bus);
+	}
+}
+
+static void
+eicon_transmit(struct eicon_card *card)
+{
+	switch (card->bus) {
+		case EICON_BUS_ISA:
+		case EICON_BUS_PCI:
+			eicon_io_transmit(card);
+			break;
+		case EICON_BUS_MCA:
+		default:
+			if (DebugVar & 1)
+				printk(KERN_WARNING
+				       "eicon_transmit: Illegal bustype %d\n", card->bus);
+	}
+}
+
+static int
+eicon_command(eicon_card * card, isdn_ctrl * c)
+{
+        ulong a;
+        eicon_chan *chan;
+	eicon_cdef cdef;
+	isdn_ctrl cmd;
+	char tmp[17];
+	int ret = 0;
+	unsigned long flags;
+ 
+        switch (c->command) {
+		case ISDN_CMD_IOCTL:
+			memcpy(&a, c->parm.num, sizeof(ulong));
+			switch (c->arg) {
+				case EICON_IOCTL_GETVER:
+					return(EICON_CTRL_VERSION);
+				case EICON_IOCTL_GETTYPE:
+					return(card->type);
+				case EICON_IOCTL_GETMMIO:
+					switch (card->bus) {
+						case EICON_BUS_ISA:
+							return (int)card->hwif.isa.shmem;
+#if CONFIG_PCI
+						case EICON_BUS_PCI:
+							return card->hwif.pci.PCIram;
+#endif
+						default:
+							if (DebugVar & 1)
+								printk(KERN_WARNING
+								       "eicon: Illegal BUS type %d\n",
+							       card->bus);
+							ret = -ENODEV;
+					}
+				case EICON_IOCTL_SETMMIO:
+					if (card->flags & EICON_FLAGS_LOADED)
+						return -EBUSY;
+					switch (card->bus) {
+						case EICON_BUS_ISA:
+							if (eicon_isa_find_card(a,
+								card->hwif.isa.irq,
+								card->regname) < 0)
+								return -EFAULT;
+							card->hwif.isa.shmem = (eicon_isa_shmem *)a;
+							return 0;
+						default:
+							if (DebugVar & 1)
+								printk(KERN_WARNING
+							      		"eicon: Illegal BUS type %d\n",
+							       card->bus);
+							ret = -ENODEV;
+					}					
+				case EICON_IOCTL_GETIRQ:
+					switch (card->bus) {
+						case EICON_BUS_ISA:
+							return card->hwif.isa.irq;
+#if CONFIG_PCI
+						case EICON_BUS_PCI:
+							return card->hwif.pci.irq;
+#endif
+						default:
+							if (DebugVar & 1)
+								printk(KERN_WARNING
+								       "eicon: Illegal BUS type %d\n",
+							       card->bus);
+							ret = -ENODEV;
+					}
+				case EICON_IOCTL_SETIRQ:
+					if (card->flags & EICON_FLAGS_LOADED)
+						return -EBUSY;
+					if ((a < 2) || (a > 15))
+						return -EFAULT;
+					switch (card->bus) {
+						case EICON_BUS_ISA:
+							card->hwif.isa.irq = a;
+							return 0;
+						default:
+							if (DebugVar & 1)
+								printk(KERN_WARNING
+							      		"eicon: Illegal BUS type %d\n",
+							       card->bus);
+							ret = -ENODEV;
+					}					
+				case EICON_IOCTL_LOADBOOT:
+					if (card->flags & EICON_FLAGS_RUNNING)
+						return -EBUSY;  
+					switch (card->bus) {
+						case EICON_BUS_ISA:
+							ret = eicon_isa_bootload(
+								&(card->hwif.isa),
+								&(((eicon_codebuf *)a)->isa));
+							break;
+						default:
+							if (DebugVar & 1)
+								printk(KERN_WARNING
+								       "eicon: Illegal BUS type %d\n",
+							       card->bus);
+							ret = -ENODEV;
+					}
+					return ret;
+				case EICON_IOCTL_LOADISA:
+					if (card->flags & EICON_FLAGS_RUNNING)
+						return -EBUSY;  
+					switch (card->bus) {
+						case EICON_BUS_ISA:
+							ret = eicon_isa_load(
+								&(card->hwif.isa),
+								&(((eicon_codebuf *)a)->isa));
+							if (!ret) {
+                                                                card->flags |= EICON_FLAGS_LOADED;
+                                                                card->flags |= EICON_FLAGS_RUNNING;
+								if (card->hwif.isa.channels > 1) {
+									cmd.command = ISDN_STAT_ADDCH;
+									cmd.driver = card->myid;
+									cmd.arg = card->hwif.isa.channels - 1;
+									card->interface.statcallb(&cmd);
+								}
+								cmd.command = ISDN_STAT_RUN;    
+								cmd.driver = card->myid;        
+								cmd.arg = 0;                    
+								card->interface.statcallb(&cmd);
+							}
+							break;
+						default:
+							if (DebugVar & 1)
+								printk(KERN_WARNING
+								       "eicon: Illegal BUS type %d\n",
+							       card->bus);
+							ret = -ENODEV;
+					}
+					return ret;
+
+				case EICON_IOCTL_MANIF:
+					if (!card->flags & EICON_FLAGS_RUNNING)
+						return -ENODEV;
+					if (!card->Feature & PROTCAP_MANIF)
+						return -ENODEV;
+					ret = eicon_idi_manage(
+						card, 
+						(eicon_manifbuf *)a);
+					return ret;
+#if CONFIG_PCI 
+				case EICON_IOCTL_LOADPCI:
+						if (card->flags & EICON_FLAGS_RUNNING)
+							return -EBUSY;  
+                                                if (card->bus == EICON_BUS_PCI) {
+							switch(card->type) {
+								case EICON_CTYPE_MAESTRA:
+                                                		        ret = eicon_pci_load_bri(
+		                                                                &(card->hwif.pci),
+                		                                                &(((eicon_codebuf *)a)->pci)); 
+									break;
+
+								case EICON_CTYPE_MAESTRAP:
+		                                                        ret = eicon_pci_load_pri(
+                		                                                &(card->hwif.pci),
+                                		                                &(((eicon_codebuf *)a)->pci)); 
+									break;
+							}
+                                                        if (!ret) {
+                                                                card->flags |= EICON_FLAGS_LOADED;
+                                                                card->flags |= EICON_FLAGS_RUNNING;
+								if (card->hwif.pci.channels > 1) {
+									cmd.command = ISDN_STAT_ADDCH;
+									cmd.driver = card->myid;
+									cmd.arg = card->hwif.pci.channels - 1;
+									card->interface.statcallb(&cmd);
+								}
+								cmd.command = ISDN_STAT_RUN;    
+								cmd.driver = card->myid;        
+								cmd.arg = 0;                    
+								card->interface.statcallb(&cmd);
+							} 
+                                                        return ret;
+						} else return -ENODEV;
+#endif
+#if 0
+				case EICON_IOCTL_SETMSN:
+					if ((ret = copy_from_user(tmp, (char *)a, sizeof(tmp))))
+						return -EFAULT;
+					if ((ret = eicon_set_msn(card, tmp)))
+						return ret;
+#if 0
+					if (card->flags & EICON_FLAGS_RUNNING)
+						return(eicon_capi_manufacturer_req_msn(card));
+#endif
+					return 0;
+#endif
+				case EICON_IOCTL_ADDCARD:
+					if ((ret = copy_from_user(&cdef, (char *)a, sizeof(cdef))))
+						return -EFAULT;
+					if (!(eicon_addcard(0, cdef.membase, cdef.irq, cdef.id)))
+						return -EIO;
+					return 0;
+				case EICON_IOCTL_DEBUGVAR:
+					DebugVar = a;
+					printk(KERN_DEBUG"Eicon: Debug Value set to %ld\n", DebugVar);
+					return 0;
+#ifdef MODULE
+				case EICON_IOCTL_FREEIT:
+					while (MOD_USE_COUNT > 0) MOD_DEC_USE_COUNT;
+					MOD_INC_USE_COUNT;
+					return 0;
+#endif
+				default:
+					return -EINVAL;
+			}
+			break;
+		case ISDN_CMD_DIAL:
+			if (!card->flags & EICON_FLAGS_RUNNING)
+				return -ENODEV;
+			if (!(chan = find_channel(card, c->arg & 0x1f)))
+				break;
+			save_flags(flags);
+			cli();
+			if ((chan->fsm_state != EICON_STATE_NULL) && (chan->fsm_state != EICON_STATE_LISTEN)) {
+				restore_flags(flags);
+				if (DebugVar & 1)
+					printk(KERN_WARNING "Dial on channel %d with state %d\n",
+					chan->No, chan->fsm_state);
+				return -EBUSY;
+			}
+			if (card->ptype == ISDN_PTYPE_EURO)
+				tmp[0] = eicon_find_msn(card, c->parm.setup.eazmsn, 1);
+			else
+				tmp[0] = c->parm.setup.eazmsn[0];
+			chan->fsm_state = EICON_STATE_OCALL;
+			chan->callref = 0xffff;
+			restore_flags(flags);
+			
+			ret = idi_connect_req(card, chan, c->parm.setup.phone,
+						     c->parm.setup.eazmsn,
+						     c->parm.setup.si1,
+						     c->parm.setup.si2);
+			if (ret) {
+				cmd.driver = card->myid;
+				cmd.command = ISDN_STAT_DHUP;
+				cmd.arg &= 0x1f;
+				card->interface.statcallb(&cmd);
+			}
+			return ret;
+		case ISDN_CMD_ACCEPTD:
+			if (!card->flags & EICON_FLAGS_RUNNING)
+				return -ENODEV;
+			if (!(chan = find_channel(card, c->arg & 0x1f)))
+				break;
+			if (chan->fsm_state == EICON_STATE_ICALL) { 
+				idi_connect_res(card, chan);
+			}
+			return 0;
+		case ISDN_CMD_ACCEPTB:
+			if (!card->flags & EICON_FLAGS_RUNNING)
+				return -ENODEV;
+			return 0;
+		case ISDN_CMD_HANGUP:
+			if (!card->flags & EICON_FLAGS_RUNNING)
+				return -ENODEV;
+			if (!(chan = find_channel(card, c->arg & 0x1f)))
+				break;
+			idi_hangup(card, chan);
+			return 0;
+		case ISDN_CMD_SETEAZ:
+			if (!card->flags & EICON_FLAGS_RUNNING)
+				return -ENODEV;
+			if (!(chan = find_channel(card, c->arg & 0x1f)))
+				break;
+			if (strlen(c->parm.num)) {
+				if (card->ptype == ISDN_PTYPE_EURO) {
+					chan->eazmask = eicon_find_msn(card, c->parm.num, 0);
+				}
+				if (card->ptype == ISDN_PTYPE_1TR6) {
+					int i;
+					chan->eazmask = 0;
+					for (i = 0; i < strlen(c->parm.num); i++)
+						if (isdigit(c->parm.num[i]))
+							chan->eazmask |= (1 << (c->parm.num[i] - '0'));
+				}
+			} else
+				chan->eazmask = 0x3ff;
+			eicon_idi_listen_req(card, chan);
+			return 0;
+		case ISDN_CMD_CLREAZ:
+			if (!card->flags & EICON_FLAGS_RUNNING)
+				return -ENODEV;
+			if (!(chan = find_channel(card, c->arg & 0x1f)))
+				break;
+			chan->eazmask = 0;
+			eicon_idi_listen_req(card, chan);
+			return 0;
+		case ISDN_CMD_SETL2:
+			if (!card->flags & EICON_FLAGS_RUNNING)
+				return -ENODEV;
+			if (!(chan = find_channel(card, c->arg & 0x1f)))
+				break;
+			chan->l2prot = (c->arg >> 8);
+			return 0;
+		case ISDN_CMD_GETL2:
+			if (!card->flags & EICON_FLAGS_RUNNING)
+				return -ENODEV;
+			if (!(chan = find_channel(card, c->arg & 0x1f)))
+				break;
+			return chan->l2prot;
+		case ISDN_CMD_SETL3:
+			if (!card->flags & EICON_FLAGS_RUNNING)
+				return -ENODEV;
+			if ((c->arg >> 8) != ISDN_PROTO_L3_TRANS) {
+				if (DebugVar & 1)
+					printk(KERN_WARNING "L3 protocol unknown\n");
+				return -1;
+			}
+			if (!(chan = find_channel(card, c->arg & 0x1f)))
+				break;
+			chan->l3prot = (c->arg >> 8);
+			return 0;
+		case ISDN_CMD_GETL3:
+			if (!card->flags & EICON_FLAGS_RUNNING)
+				return -ENODEV;
+			if (!(chan = find_channel(card, c->arg & 0x1f)))
+				break;
+			return chan->l3prot;
+		case ISDN_CMD_GETEAZ:
+			if (!card->flags & EICON_FLAGS_RUNNING)
+				return -ENODEV;
+			if (DebugVar & 1)
+				printk(KERN_DEBUG "eicon CMD_GETEAZ not implemented\n");
+			return 0;
+		case ISDN_CMD_SETSIL:
+			if (!card->flags & EICON_FLAGS_RUNNING)
+				return -ENODEV;
+			if (DebugVar & 1)
+				printk(KERN_DEBUG "eicon CMD_SETSIL not implemented\n");
+			return 0;
+		case ISDN_CMD_GETSIL:
+			if (!card->flags & EICON_FLAGS_RUNNING)
+				return -ENODEV;
+			if (DebugVar & 1)
+				printk(KERN_DEBUG "eicon CMD_GETSIL not implemented\n");
+			return 0;
+		case ISDN_CMD_LOCK:
+			MOD_INC_USE_COUNT;
+			return 0;
+		case ISDN_CMD_UNLOCK:
+			MOD_DEC_USE_COUNT;
+			return 0;
+        }
+	
+        return -EINVAL;
+}
+
+/*
+ * Find card with given driverId
+ */
+static inline eicon_card *
+eicon_findcard(int driverid)
+{
+        eicon_card *p = cards;
+
+        while (p) {
+                if (p->myid == driverid)
+                        return p;
+                p = p->next;
+        }
+        return (eicon_card *) 0;
+}
+
+/*
+ * Wrapper functions for interface to linklevel
+ */
+static int
+if_command(isdn_ctrl * c)
+{
+        eicon_card *card = eicon_findcard(c->driver);
+
+        if (card)
+                return (eicon_command(card, c));
+        printk(KERN_ERR
+             "eicon: if_command %d called with invalid driverId %d!\n",
+               c->command, c->driver);
+        return -ENODEV;
+}
+
+static int
+if_writecmd(const u_char * buf, int len, int user, int id, int channel)
+{
+        eicon_card *card = eicon_findcard(id);
+
+        if (card) {
+                if (!card->flags & EICON_FLAGS_RUNNING)
+                        return -ENODEV;
+                return (len);
+        }
+        printk(KERN_ERR
+               "eicon: if_writecmd called with invalid driverId!\n");
+        return -ENODEV;
+}
+
+static int
+if_readstatus(u_char * buf, int len, int user, int id, int channel)
+{
+#if 0
+	/* Not yet used */
+        eicon_card *card = eicon_findcard(id);
+	
+        if (card) {
+                if (!card->flags & EICON_FLAGS_RUNNING)
+                        return -ENODEV;
+                return (eicon_readstatus(buf, len, user, card));
+        }
+        printk(KERN_ERR
+               "eicon: if_readstatus called with invalid driverId!\n");
+#endif
+        return -ENODEV;
+}
+
+static int
+if_sendbuf(int id, int channel, int ack, struct sk_buff *skb)
+{
+        eicon_card *card = eicon_findcard(id);
+	eicon_chan *chan;
+	
+        if (card) {
+                if (!card->flags & EICON_FLAGS_RUNNING) {
+			dev_kfree_skb(skb);
+                        return -ENODEV;
+		}
+        	if (!(chan = find_channel(card, channel))) {
+			dev_kfree_skb(skb);
+			return -ENODEV;
+		}
+		if (chan->fsm_state == EICON_STATE_ACTIVE)
+			return (idi_send_data(card, chan, ack, skb));
+		else {
+			dev_kfree_skb(skb);
+			return -ENODEV;
+		}
+        }
+        printk(KERN_ERR
+               "eicon: if_sendbuf called with invalid driverId!\n");
+	dev_kfree_skb(skb);
+        return -ENODEV;
+}
+
+
+/*
+ * Allocate a new card-struct, initialize it
+ * link it into cards-list.
+ */
+static void
+eicon_alloccard(int Type, int membase, int irq, char *id)
+{
+	int i;
+	int j;
+	int qloop;
+	char qid[5];
+        eicon_card *card;
+#if CONFIG_PCI
+	eicon_pci_card *pcic;
+#endif
+
+	qloop = (Type == EICON_CTYPE_QUADRO)?2:0;
+	for (i = 0; i <= qloop; i++) {
+		if (!(card = (eicon_card *) kmalloc(sizeof(eicon_card), GFP_KERNEL))) {
+			printk(KERN_WARNING
+			       "eicon: (%s) Could not allocate card-struct.\n", id);
+			return;
+		}
+		memset((char *) card, 0, sizeof(eicon_card));
+		skb_queue_head_init(&card->sndq);
+		skb_queue_head_init(&card->rcvq);
+		skb_queue_head_init(&card->rackq);
+		skb_queue_head_init(&card->sackq);
+		card->snd_tq.routine = (void *) (void *) eicon_transmit;
+		card->snd_tq.data = card;
+		card->rcv_tq.routine = (void *) (void *) eicon_rcv_dispatch;
+		card->rcv_tq.data = card;
+		card->ack_tq.routine = (void *) (void *) eicon_ack_dispatch;
+		card->ack_tq.data = card;
+		card->interface.maxbufsize = 4000;
+		card->interface.command = if_command;
+		card->interface.writebuf_skb = if_sendbuf;
+		card->interface.writecmd = if_writecmd;
+		card->interface.readstat = if_readstatus;
+		card->interface.features =
+			ISDN_FEATURE_L2_X75I |
+			ISDN_FEATURE_L2_HDLC |
+			ISDN_FEATURE_L2_TRANS |
+			ISDN_FEATURE_L3_TRANS |
+			ISDN_FEATURE_P_UNKNOWN;
+		card->interface.hl_hdrlen = 20;
+		card->ptype = ISDN_PTYPE_UNKNOWN;
+		strncpy(card->interface.id, id, sizeof(card->interface.id) - 1);
+		card->myid = -1;
+		card->type = Type;
+		switch (Type) {
+			case EICON_CTYPE_QUADRO:
+				if (membase == -1)
+					membase = EICON_ISA_MEMBASE;
+				if (irq == -1)
+					irq = EICON_ISA_IRQ;
+				card->bus = EICON_BUS_ISA;
+				card->hwif.isa.card = (void *)card;
+				card->hwif.isa.shmem = (eicon_isa_shmem *)(membase + (i+1) * EICON_ISA_QOFFSET);
+				card->hwif.isa.master = 0;
+				strcpy(card->interface.id, id);
+				if (id[strlen(id) - 1] == 'a') {
+					card->interface.id[strlen(id) - 1] = 'a' + i + 1;
+				} else {
+					sprintf(qid, "_%c",'2' + i);
+					strcat(card->interface.id, qid);
+				}
+				printk(KERN_INFO "Eicon: Quadro: Driver-Id %s added.\n",
+					card->interface.id);
+				if (i == 0) {
+					eicon_card *p = cards;
+					while(p) {
+						if ((p->hwif.isa.master) && (p->hwif.isa.irq == irq)) {
+							p->qnext = card;
+							break;
+						}
+						p = p->next;
+					}
+					if (!p) {
+						printk(KERN_WARNING "eicon_alloccard: Quadro Master not found.\n");
+						kfree(card);
+						return;
+					}
+				} else {
+					cards->qnext = card;
+				}
+				card->hwif.isa.irq = irq;
+				card->hwif.isa.type = Type;
+				card->nchannels = 2;
+				card->interface.channels = 1;
+				break;
+#if CONFIG_PCI
+			case EICON_CTYPE_MAESTRA:
+				(eicon_pci_card *)pcic = (eicon_pci_card *)membase;
+                                card->bus = EICON_BUS_PCI;
+				card->interface.features |=
+					ISDN_FEATURE_L2_V11096 |
+					ISDN_FEATURE_L2_V11019 |
+					ISDN_FEATURE_L2_V11038 |
+					ISDN_FEATURE_L2_MODEM;
+                                card->hwif.pci.card = (void *)card;
+				card->hwif.pci.PCIreg = pcic->PCIreg;
+				card->hwif.pci.PCIcfg = pcic->PCIcfg;
+                                card->hwif.pci.master = 1;
+                                card->hwif.pci.mvalid = pcic->mvalid;
+                                card->hwif.pci.ivalid = 0;
+                                card->hwif.pci.irq = irq;
+                                card->hwif.pci.type = Type;
+				card->flags = 0;
+                                card->nchannels = 2;
+				card->interface.channels = 1;
+				break;
+
+			case EICON_CTYPE_MAESTRAP:
+				(eicon_pci_card *)pcic = (eicon_pci_card *)membase;
+                                card->bus = EICON_BUS_PCI;
+				card->interface.features |=
+					ISDN_FEATURE_L2_V11096 |
+					ISDN_FEATURE_L2_V11019 |
+					ISDN_FEATURE_L2_V11038 |
+					ISDN_FEATURE_L2_MODEM;
+                                card->hwif.pci.card = (void *)card;
+                                card->hwif.pci.shmem = (eicon_pci_shmem *)pcic->shmem;
+				card->hwif.pci.PCIreg = pcic->PCIreg;
+				card->hwif.pci.PCIram = pcic->PCIram;
+				card->hwif.pci.PCIcfg = pcic->PCIcfg;
+                                card->hwif.pci.master = 1;
+                                card->hwif.pci.mvalid = pcic->mvalid;
+                                card->hwif.pci.ivalid = 0;
+                                card->hwif.pci.irq = irq;
+                                card->hwif.pci.type = Type;
+				card->flags = 0;
+                                card->nchannels = 30;
+				card->interface.channels = 1;
+				break;
+#endif
+			case EICON_CTYPE_ISABRI:
+				if (membase == -1)
+					membase = EICON_ISA_MEMBASE;
+				if (irq == -1)
+					irq = EICON_ISA_IRQ;
+				card->bus = EICON_BUS_ISA;
+				card->hwif.isa.card = (void *)card;
+				card->hwif.isa.shmem = (eicon_isa_shmem *)membase;
+				card->hwif.isa.master = 1;
+				card->hwif.isa.irq = irq;
+				card->hwif.isa.type = Type;
+				card->nchannels = 2;
+				card->interface.channels = 1;
+				break;
+			case EICON_CTYPE_ISAPRI:
+				if (membase == -1)
+					membase = EICON_ISA_MEMBASE;
+				if (irq == -1)
+					irq = EICON_ISA_IRQ;
+				card->bus = EICON_BUS_ISA;
+				card->hwif.isa.card = (void *)card;
+				card->hwif.isa.shmem = (eicon_isa_shmem *)membase;
+				card->hwif.isa.master = 1;
+				card->hwif.isa.irq = irq;
+				card->hwif.isa.type = Type;
+				card->nchannels = 30;
+				card->interface.channels = 1;
+				break;
+			default:
+				printk(KERN_WARNING "eicon_alloccard: Invalid type %d\n", Type);
+				kfree(card);
+				return;
+		}
+		if (!(card->bch = (eicon_chan *) kmalloc(sizeof(eicon_chan) * (card->nchannels + 1)
+							 , GFP_KERNEL))) {
+			printk(KERN_WARNING
+			       "eicon: (%s) Could not allocate bch-struct.\n", id);
+			kfree(card);
+			return;
+		}
+		for (j=0; j< (card->nchannels + 1); j++) {
+			memset((char *)&card->bch[j], 0, sizeof(eicon_chan));
+			card->bch[j].plci = 0x8000;
+			card->bch[j].ncci = 0x8000;
+			card->bch[j].l2prot = ISDN_PROTO_L2_X75I;
+			card->bch[j].l3prot = ISDN_PROTO_L3_TRANS;
+			card->bch[j].e.D3Id = 0;
+			card->bch[j].e.B2Id = 0;
+			card->bch[j].e.Req = 0;
+			card->bch[j].No = j;
+			skb_queue_head_init(&card->bch[j].e.X);
+			skb_queue_head_init(&card->bch[j].e.R);
+		}
+		card->next = cards;
+		cards = card;
+	}
+}
+
+/*
+ * register card at linklevel
+ */
+static int
+eicon_registercard(eicon_card * card)
+{
+        switch (card->bus) {
+		case EICON_BUS_ISA:
+			/* TODO something to print */
+			break;
+		case EICON_BUS_PCI:
+#if CONFIG_PCI
+			eicon_pci_printpar(&card->hwif.pci); 
+			break;
+#endif
+		case EICON_BUS_MCA:
+		default:
+			if (DebugVar & 1)
+				printk(KERN_WARNING
+				       "eicon_registercard: Illegal BUS type %d\n",
+			       card->bus);
+			return -1;
+        }
+        if (!register_isdn(&card->interface)) {
+                printk(KERN_WARNING
+                       "eicon_registercard: Unable to register %s\n",
+                       card->interface.id);
+                return -1;
+        }
+        card->myid = card->interface.channels;
+        sprintf(card->regname, "%s", card->interface.id);
+        return 0;
+}
+
+#ifdef MODULE
+static void
+unregister_card(eicon_card * card)
+{
+        isdn_ctrl cmd;
+
+        cmd.command = ISDN_STAT_UNLOAD;
+        cmd.driver = card->myid;
+        card->interface.statcallb(&cmd);
+        switch (card->bus) {
+		case EICON_BUS_ISA:
+			eicon_isa_release(&card->hwif.isa);
+			break;
+		case EICON_BUS_PCI:
+#if CONFIG_PCI
+			eicon_pci_release(&card->hwif.pci);
+			break;
+#endif
+		case EICON_BUS_MCA:
+		default:
+			if (DebugVar & 1)
+				printk(KERN_WARNING
+				       "eicon: Invalid BUS type %d\n",
+			       card->bus);
+			break;
+        }
+}
+#endif /* MODULE */
+
+static void
+eicon_freecard(eicon_card *card) {
+	eicon_clear_msn(card);
+	kfree(card->bch);
+	kfree(card);
+}
+
+int
+eicon_addcard(int Type, int membase, int irq, char *id)
+{
+	eicon_card *p;
+	eicon_card *q = NULL;
+	int registered;
+	int added = 0;
+	int failed = 0;
+
+	if (!Type) /* ISA */
+		if ((Type = eicon_isa_find_card(membase, irq, id)) < 0)
+			return 0;
+	eicon_alloccard(Type, membase, irq, id);
+        p = cards;
+        while (p) {
+		registered = 0;
+		if (!p->interface.statcallb) {
+			/* Not yet registered.
+			 * Try to register and activate it.
+			 */
+			added++;
+			switch (p->bus) {
+				case EICON_BUS_ISA:
+					if (eicon_registercard(p))
+						break;
+					registered = 1;
+					break;
+				case EICON_BUS_PCI:
+#if CONFIG_PCI
+					if (eicon_registercard(p))
+						break;
+					registered = 1;
+					break;
+#endif
+				case EICON_BUS_MCA:
+				default:
+					if (DebugVar & 1)
+						printk(KERN_WARNING
+						       "eicon: addcard: Invalid BUS type %d\n",
+					       p->bus);
+			}
+		} else
+			/* Card already registered */
+			registered = 1;
+                if (registered) {
+			/* Init OK, next card ... */
+                        q = p;
+                        p = p->next;
+                } else {
+                        /* registering failed, remove card from list, free memory */
+                        printk(KERN_WARNING
+                               "eicon: Initialization of %s failed\n",
+                               p->interface.id);
+                        if (q) {
+                                q->next = p->next;
+                                eicon_freecard(p);
+                                p = q->next;
+                        } else {
+                                cards = p->next;
+                                eicon_freecard(p);
+                                p = cards;
+                        }
+			failed++;
+                }
+	}
+        return (added - failed);
+}
+
+#define DRIVERNAME "Eicon active ISDN driver"
+#define DRIVERRELEASE "1"
+
+#ifdef MODULE
+#define eicon_init init_module
+#endif
+
+__initfunc(int
+eicon_init(void))
+{
+	int tmp = 0;
+	int release = 0;
+	char tmprev[50];
+
+	DebugVar = 1;
+
+        printk(KERN_INFO "%s Rev: ", DRIVERNAME);
+	strcpy(tmprev, eicon_revision);
+	printk("%s/", eicon_getrev(tmprev));
+	release += getrel(tmprev);
+	strcpy(tmprev, eicon_pci_revision);
+	printk("%s/", eicon_getrev(tmprev));
+	release += getrel(tmprev);
+	strcpy(tmprev, eicon_isa_revision);
+	printk("%s/", eicon_getrev(tmprev));
+	release += getrel(tmprev);
+	strcpy(tmprev, eicon_idi_revision);
+	printk("%s\n", eicon_getrev(tmprev));
+	release += getrel(tmprev);
+	sprintf(tmprev,"%d", release);
+        printk(KERN_INFO "%s Release: %s.%s\n", DRIVERNAME,
+		DRIVERRELEASE, tmprev);
+
+	tmp = eicon_addcard(0, membase, irq, id);
+#if CONFIG_PCI
+	tmp += eicon_pci_find_card(id);
+#endif
+        if (!cards) {
+#ifdef MODULE
+                printk(KERN_INFO "Eicon: No cards defined, driver not loaded !\n");
+#endif
+		return -ENODEV;
+
+	} else
+		printk(KERN_INFO "Eicon: %d card%s added\n", tmp, (tmp>1)?"s":"");
+        /* No symbols to export, hide all symbols */
+        EXPORT_NO_SYMBOLS;
+        return 0;
+}
+
+#ifdef MODULE
+void
+cleanup_module(void)
+{
+        eicon_card *card = cards;
+        eicon_card *last;
+        while (card) {
+                unregister_card(card); 
+                card = card->next;
+        }
+        card = cards;
+        while (card) {
+                last = card;
+                card = card->next;
+		eicon_freecard(last);
+        }
+        printk(KERN_INFO "%s unloaded\n", DRIVERNAME);
+}
+
+#else
+__initfunc(void
+eicon_setup(char *str, int *ints))
+{
+        int i, argc;
+
+        argc = ints[0];
+        i = 1;
+        if (argc) {
+		membase = irq = -1;
+		if (argc) {
+			membase = ints[i];
+			i++;
+			argc--;
+		}
+		if (argc) {
+			irq = ints[i];
+			i++;
+			argc--;
+		}
+		if (strlen(str)) {
+			strcpy(id, str);
+		} else {
+			strcpy(id, "eicon");
+		} 
+		/* eicon_addcard(0, membase, irq, id); */
+       		printk(KERN_INFO "eicon: membase=0x%x irq=%d id=%s\n", membase, irq, id);
+	}
+}
+#endif
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/eicon/eicon_pci.c linux/drivers/isdn/eicon/eicon_pci.c
--- v2.3.3/linux/drivers/isdn/eicon/eicon_pci.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/eicon/eicon_pci.c	Wed May 26 16:55:40 1999
@@ -0,0 +1,952 @@
+/* $Id: eicon_pci.c,v 1.6 1999/04/01 12:48:37 armin Exp $
+ *
+ * ISDN low-level module for Eicon.Diehl active ISDN-Cards.
+ * Hardware-specific code for PCI cards.
+ *
+ * Copyright 1998,99 by Armin Schindler (mac@melware.de)
+ * Copyright 1999    Cytronics & Melware (info@melware.de)
+ *
+ * Thanks to	Eicon Technology Diehl GmbH & Co. oHG for 
+ *		documents, informations and hardware. 
+ *
+ *		Deutsche Telekom AG for S2M support.
+ *
+ * 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: eicon_pci.c,v $
+ * Revision 1.6  1999/04/01 12:48:37  armin
+ * Changed some log outputs.
+ *
+ * Revision 1.5  1999/03/29 11:19:49  armin
+ * I/O stuff now in seperate file (eicon_io.c)
+ * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented.
+ *
+ * Revision 1.4  1999/03/02 12:37:48  armin
+ * Added some important checks.
+ * Analog Modem with DSP.
+ * Channels will be added to Link-Level after loading firmware.
+ *
+ * Revision 1.3  1999/01/24 20:14:24  armin
+ * Changed and added debug stuff.
+ * Better data sending. (still problems with tty's flip buffer)
+ *
+ * Revision 1.2  1999/01/10 18:46:06  armin
+ * Bug with wrong values in HLC fixed.
+ * Bytes to send are counted and limited now.
+ *
+ * Revision 1.1  1999/01/01 18:09:45  armin
+ * First checkin of new eicon driver.
+ * DIVA-Server BRI/PCI and PRI/PCI are supported.
+ * Old diehl code is obsolete.
+ *
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/pci.h>
+
+#include "eicon.h"
+#include "eicon_pci.h"
+
+
+char *eicon_pci_revision = "$Revision: 1.6 $";
+
+#if CONFIG_PCI	         /* intire stuff is only for PCI */
+
+#undef EICON_PCI_DEBUG 
+
+int eicon_pci_find_card(char *ID)
+{
+  if (pci_present()) { 
+    struct pci_dev *pdev = NULL;  
+    int pci_nextindex=0, pci_cards=0, pci_akt=0; 
+    int pci_type = PCI_MAESTRA;
+    int NoMorePCICards = FALSE;
+    char *ram, *reg, *cfg;	
+    unsigned int pram=0, preg=0, pcfg=0;
+    char did[12];
+    eicon_pci_card *aparms;
+
+   if (!(aparms = (eicon_pci_card *) kmalloc(sizeof(eicon_pci_card), GFP_KERNEL))) {
+                  printk(KERN_WARNING
+                      "eicon_pci: Could not allocate card-struct.\n");
+                  return 0;
+   }
+
+  for (pci_cards = 0; pci_cards < 0x0f; pci_cards++)
+  {
+  do {
+      if ((pdev = pci_find_device(PCI_VENDOR_EICON,          
+                                  pci_type,                  
+                                  pdev)))                    
+	{
+              pci_nextindex++;
+              break;
+	}
+	else {
+              pci_nextindex = 0;
+              switch (pci_type) /* switch to next card type */
+               {
+               case PCI_MAESTRA:
+                 pci_type = PCI_MAESTRAQ; break;
+               case PCI_MAESTRAQ:
+                 pci_type = PCI_MAESTRAQ_U; break;
+               case PCI_MAESTRAQ_U:
+                 pci_type = PCI_MAESTRAP; break;
+               default:
+               case PCI_MAESTRAP:
+                 NoMorePCICards = TRUE;
+               }
+	}
+     }
+     while (!NoMorePCICards);
+     if (NoMorePCICards)
+        {
+           if (pci_cards < 1) {
+           printk(KERN_INFO "Eicon: No supported PCI cards found.\n");
+	   kfree(aparms);	
+           return 0;
+           }
+           else
+           {
+           printk(KERN_INFO "Eicon: %d PCI card%s registered.\n",
+			pci_cards, (pci_cards > 1) ? "s":"");
+	   kfree(aparms);	
+           return (pci_cards);
+           }
+        }
+
+   pci_akt = 0;
+   switch(pci_type)
+   {
+    case PCI_MAESTRA:
+         printk(KERN_INFO "Eicon: DIVA Server BRI/PCI detected !\n");
+          aparms->type = EICON_CTYPE_MAESTRA;
+
+          aparms->irq = pdev->irq;
+          preg = pdev->base_address[2] & 0xfffffffc;
+          pcfg = pdev->base_address[1] & 0xffffff80;
+
+#ifdef EICON_PCI_DEBUG
+          printk(KERN_DEBUG "eicon_pci: irq=%d\n", aparms->irq);
+          printk(KERN_DEBUG "eicon_pci: reg=0x%x\n", preg);
+          printk(KERN_DEBUG "eicon_pci: cfg=0x%x\n", pcfg);
+#endif
+	 pci_akt = 1;
+         break;
+
+    case PCI_MAESTRAQ:
+    case PCI_MAESTRAQ_U:
+         printk(KERN_ERR "Eicon: DIVA Server 4BRI/PCI detected but not supported !\n");
+         pci_cards--;
+	 pci_akt = 0;
+         break;
+
+    case PCI_MAESTRAP:
+         printk(KERN_INFO "Eicon: DIVA Server PRI/PCI detected !\n");
+          aparms->type = EICON_CTYPE_MAESTRAP; /*includes 9M,30M*/
+          aparms->irq = pdev->irq;
+          pram = pdev->base_address[0] & 0xfffff000;
+          preg = pdev->base_address[2] & 0xfffff000;
+          pcfg = pdev->base_address[4] & 0xfffff000;
+
+#ifdef EICON_PCI_DEBUG
+          printk(KERN_DEBUG "eicon_pci: irq=%d\n", aparms->irq);
+          printk(KERN_DEBUG "eicon_pci: ram=0x%x\n",
+               (pram));
+          printk(KERN_DEBUG "eicon_pci: reg=0x%x\n",
+               (preg));
+          printk(KERN_DEBUG "eicon_pci: cfg=0x%x\n",
+               (pcfg));
+#endif
+	  pci_akt = 1;
+	  break;	
+    default:
+         printk(KERN_ERR "eicon_pci: Unknown PCI card detected !\n");
+         pci_cards--;
+	 pci_akt = 0;
+	 break;
+   }
+
+	if (pci_akt) {
+		/* remapping memory */
+		switch(pci_type)
+		{
+    		case PCI_MAESTRA:
+			aparms->PCIreg = (unsigned int) preg;
+			aparms->PCIcfg = (unsigned int) pcfg;
+			if (check_region((aparms->PCIreg), 0x20)) {
+				printk(KERN_WARNING "eicon_pci: reg port already in use !\n");
+				aparms->PCIreg = 0;
+				break;	
+			} else {
+				request_region(aparms->PCIreg, 0x20, "eicon reg");
+			}
+			if (check_region((aparms->PCIcfg), 0x100)) {
+				printk(KERN_WARNING "eicon_pci: cfg port already in use !\n");
+				aparms->PCIcfg = 0;
+				break;	
+			} else {
+				request_region(aparms->PCIcfg, 0x100, "eicon cfg");
+			}
+			break;
+    		case PCI_MAESTRAQ:
+		case PCI_MAESTRAQ_U:
+		case PCI_MAESTRAP:
+			aparms->shmem = (eicon_pci_shmem *) ioremap(pram, 0x10000);
+			ram = (u8 *) ((u32)aparms->shmem + MP_SHARED_RAM_OFFSET);
+			reg =  ioremap(preg, 0x4000);
+			cfg =  ioremap(pcfg, 0x1000);	
+			aparms->PCIram = (unsigned int) ram;
+			aparms->PCIreg = (unsigned int) reg;
+			aparms->PCIcfg = (unsigned int) cfg;
+			break;
+		 }
+		if ((!aparms->PCIreg) || (!aparms->PCIcfg)) {
+			printk(KERN_ERR "eicon_pci: Card could not be added !\n");
+			pci_cards--;
+		} else {
+			aparms->mvalid = 1;
+	
+			sprintf(did, "%s%d", (strlen(ID) < 1) ? "eicon":ID, pci_cards);
+
+			printk(KERN_INFO "%s: DriverID: '%s'\n",eicon_ctype_name[aparms->type] , did);
+
+			if (!(eicon_addcard(aparms->type, (int) aparms, aparms->irq, did))) {
+				printk(KERN_ERR "eicon_pci: Card could not be added !\n");
+				pci_cards--;
+			}
+		}
+	}
+
+  }
+ } else
+	printk(KERN_ERR "eicon_pci: Kernel compiled with PCI but no PCI-bios found !\n");
+ return 0;
+}
+
+/*
+ * Checks protocol file id for "F#xxxx" string fragment to
+ * extract the features, supported by this protocol version.
+ * binary representation of the feature string value is returned
+ * in *value. The function returns 0 if feature string was not
+ * found or has a wrong format, else 1.
+ */
+static int GetProtFeatureValue(char *sw_id, int *value)
+{
+  __u8 i, offset;
+
+  while (*sw_id)
+  {
+    if ((sw_id[0] == 'F') && (sw_id[1] == '#'))
+    {
+      sw_id = &sw_id[2];
+      for (i=0, *value=0; i<4; i++, sw_id++)
+      {
+        if ((*sw_id >= '0') && (*sw_id <= '9'))
+        {
+          offset = '0';
+        }
+        else if ((*sw_id >= 'A') && (*sw_id <= 'F'))
+        {
+          offset = 'A' + 10;
+        }
+        else if ((*sw_id >= 'a') && (*sw_id <= 'f'))
+        {
+          offset = 'a' + 10;
+        }
+        else
+        {
+          return 0;
+        }
+        *value |= (*sw_id - offset) << (4*(3-i));
+      }
+      return 1;
+    }
+    else
+    {
+      sw_id++;
+    }
+  }
+  return 0;
+}
+
+
+void
+eicon_pci_printpar(eicon_pci_card *card) {
+        switch (card->type) {
+                case EICON_CTYPE_MAESTRA:
+			printk(KERN_INFO "%s at 0x%x / 0x%x, irq %d\n",
+				eicon_ctype_name[card->type],
+				(unsigned int)card->PCIreg,
+				(unsigned int)card->PCIcfg,
+				card->irq); 
+			break;
+                case EICON_CTYPE_MAESTRAQ:
+                case EICON_CTYPE_MAESTRAQ_U:
+                case EICON_CTYPE_MAESTRAP:
+			printk(KERN_INFO "%s at 0x%x, irq %d\n",
+				eicon_ctype_name[card->type],
+				(unsigned int)card->shmem,
+				card->irq); 
+#ifdef EICON_PCI_DEBUG
+		        printk(KERN_INFO "eicon_pci: remapped ram= 0x%x\n",(unsigned int)card->PCIram);
+		        printk(KERN_INFO "eicon_pci: remapped reg= 0x%x\n",(unsigned int)card->PCIreg);
+		        printk(KERN_INFO "eicon_pci: remapped cfg= 0x%x\n",(unsigned int)card->PCIcfg); 
+#endif
+			break;
+	}
+}
+
+
+static void
+eicon_pci_release_shmem(eicon_pci_card *card) {
+	if (!card->master)
+		return;
+	if (card->mvalid) {
+        	switch (card->type) {
+                	case EICON_CTYPE_MAESTRA:
+			        /* reset board */
+				outb(0, card->PCIcfg + 0x4c);	/* disable interrupts from PLX */
+				outb(0, card->PCIreg + M_RESET);
+				SLEEP(20);
+				outb(0, card->PCIreg + M_ADDRH);
+				outw(0, card->PCIreg + M_ADDR);
+				outw(0, card->PCIreg + M_DATA);
+
+				release_region(card->PCIreg, 0x20);
+				release_region(card->PCIcfg, 0x100);
+				break;
+                	case EICON_CTYPE_MAESTRAQ:
+	                case EICON_CTYPE_MAESTRAQ_U:
+	                case EICON_CTYPE_MAESTRAP:
+			        /* reset board */
+		        	writeb(_MP_RISC_RESET | _MP_LED1 | _MP_LED2, card->PCIreg + MP_RESET);
+			        SLEEP(20);
+			        writeb(0, card->PCIreg + MP_RESET);
+			        SLEEP(20);
+
+				iounmap((void *)card->shmem);
+				iounmap((void *)card->PCIreg);
+				iounmap((void *)card->PCIcfg);
+				break;
+		}
+	}
+	card->mvalid = 0;
+}
+
+static void
+eicon_pci_release_irq(eicon_pci_card *card) {
+	if (!card->master)
+		return;
+	if (card->ivalid)
+		free_irq(card->irq, card);
+	card->ivalid = 0;
+}
+
+void
+eicon_pci_release(eicon_pci_card *card) {
+        eicon_pci_release_irq(card);
+        eicon_pci_release_shmem(card);
+}
+
+/*
+ * Upload buffer content to adapters shared memory
+ * on verify error, 1 is returned and a message is printed on screen
+ * else 0 is returned
+ * Can serve IO-Type and Memory type adapters
+ */
+int eicon_upload(t_dsp_download_space   *p_para,
+            __u16                 length,   /* byte count */
+            __u8                  *buffer,
+            int                   verify)
+{
+  __u32               i, dwdata = 0, val = 0, timeout;
+  __u16               data;
+  eicon_pci_boot *boot = 0;
+
+  switch (p_para->type) /* actions depend on type of union */
+  {
+    case DL_PARA_IO_TYPE:
+      for (i=0; i<length; i+=2)
+      {
+	outb ((u8) ((p_para->dat.io.r3addr + i) >> 16), p_para->dat.io.ioADDRH);
+	outw ((u16) (p_para->dat.io.r3addr + i), p_para->dat.io.ioADDR); 
+	/* outw (((u16 *)code)[i >> 1], p_para->dat.io.ioDATA); */
+	outw (*(u16 *)&buffer[i], p_para->dat.io.ioDATA); 
+      }
+      if (verify) /* check written block */
+      {
+        for (i=0; i<length; i+=2)
+        {
+	  outb ((u8) ((p_para->dat.io.r3addr + i) >> 16), p_para->dat.io.ioADDRH);
+          outw ((u16) (p_para->dat.io.r3addr + i), p_para->dat.io.ioADDR); 
+          data = inw(p_para->dat.io.ioDATA);
+          if (data != *(u16 *)&buffer[i])
+          {
+            p_para->dat.io.r3addr  += i;
+            p_para->dat.io.BadData  = data;
+            p_para->dat.io.GoodData = *(u16 *)&buffer[i];
+            return 1;
+          }
+        }
+      }
+      break;
+
+    case DL_PARA_MEM_TYPE:
+      boot = p_para->dat.mem.boot;
+      writel(p_para->dat.mem.r3addr, &boot->addr);
+      for (i=0; i<length; i+=4)
+      {
+        writel(((u32 *)buffer)[i >> 2], &boot->data[i]);
+      }
+      if (verify) /* check written block */
+      {
+        for (i=0; i<length; i+=4)
+        {
+          dwdata = readl(&boot->data[i]);
+          if (((u32 *)buffer)[i >> 2] != dwdata)
+          {
+            p_para->dat.mem.r3addr  += i;
+            p_para->dat.mem.BadData  = dwdata;
+            p_para->dat.mem.GoodData = ((u32 *)buffer)[i >> 2];
+            return 1;
+          }
+        }
+      }
+      writel(((length + 3) / 4), &boot->len);  /* len in dwords */
+      writel(2, &boot->cmd);
+
+	timeout = jiffies + 20;
+	while (timeout > jiffies) {
+		val = readl(&boot->cmd);
+		if (!val) break;
+		SLEEP(2);
+	}
+	if (val)
+         {
+		p_para->dat.mem.timeout = 1;
+		return 1;
+	 }
+      break;
+  }
+  return 0;
+}
+
+
+/* show header information of code file */
+static
+int eicon_pci_print_hdr(unsigned char *code, int offset)
+{
+  unsigned char hdr[80];
+  int i, fvalue = 0;
+
+  i = 0;
+  while ((i < (sizeof(hdr) -1))
+          && (code[offset + i] != '\0')
+          && (code[offset + i] != '\r')
+          && (code[offset + i] != '\n'))
+   {
+     hdr[i] = code[offset + i];
+     i++;
+   }
+   hdr[i] = '\0';
+   printk(KERN_DEBUG "Eicon: loading %s\n", hdr);
+   if (GetProtFeatureValue(hdr, &fvalue)) return(fvalue);
+    else return(0);
+}
+
+
+/*
+ * Configure a card, download code into BRI card,
+ * check if we get interrupts and return 0 on succes.
+ * Return -ERRNO on failure.
+ */
+int
+eicon_pci_load_bri(eicon_pci_card *card, eicon_pci_codebuf *cb) {
+        int               i,j;
+        int               timeout;
+	unsigned int	  offset, offp=0, size, length;
+	int		  signature = 0;
+	int		  FeatureValue = 0;
+        eicon_pci_codebuf cbuf;
+	t_dsp_download_space dl_para;
+	t_dsp_download_desc  dsp_download_table;
+        unsigned char     *code;
+	unsigned int	  reg;
+	unsigned int	  cfg;
+
+        if (copy_from_user(&cbuf, cb, sizeof(eicon_pci_codebuf)))
+                return -EFAULT;
+
+	reg = card->PCIreg;
+	cfg = card->PCIcfg;
+
+	/* reset board */
+	outb(0, reg + M_RESET);
+	SLEEP(10);
+	outb(0, reg + M_ADDRH);
+	outw(0, reg + M_ADDR);
+	outw(0, reg + M_DATA);
+
+#ifdef EICON_PCI_DEBUG
+	 printk(KERN_DEBUG "eicon_pci: reset card\n");
+#endif
+
+	/* clear shared memory */
+	outb(0xff, reg + M_ADDRH);
+	outw(0, reg + M_ADDR);
+	for(i = 0; i < 0xffff; i++) outw(0, reg + M_DATA);
+	SLEEP(10);
+
+#ifdef EICON_PCI_DEBUG
+	 printk(KERN_DEBUG "eicon_pci: clear shared memory\n");
+#endif
+
+	/* download protocol and dsp file */
+
+#ifdef EICON_PCI_DEBUG
+	printk(KERN_DEBUG "eicon_pci: downloading firmware...\n");
+#endif
+
+       	/* Allocate code-buffer */
+       	if (!(code = kmalloc(400, GFP_KERNEL))) {
+                printk(KERN_WARNING "eicon_pci_boot: Couldn't allocate code buffer\n");
+       	        return -ENOMEM;
+        }
+
+	/* prepare protocol upload */
+	dl_para.type		= DL_PARA_IO_TYPE;
+	dl_para.dat.io.ioADDR	= reg + M_ADDR;
+	dl_para.dat.io.ioADDRH	= reg + M_ADDRH;
+	dl_para.dat.io.ioDATA	= reg + M_DATA;
+
+	for (j = 0; j <= cbuf.dsp_code_num; j++) 
+	 {	
+	   if (j == 0)  size = cbuf.protocol_len;
+	           else size = cbuf.dsp_code_len[j];
+
+        	offset = 0;
+
+		if (j == 0) dl_para.dat.io.r3addr = 0;
+		if (j == 1) dl_para.dat.io.r3addr = M_DSP_CODE_BASE +
+					((sizeof(__u32) + (sizeof(dsp_download_table) * 35) + 3) &0xfffffffc);
+		if (j == 2) dl_para.dat.io.r3addr = M_DSP_CODE_BASE;
+		if (j == 3) dl_para.dat.io.r3addr = M_DSP_CODE_BASE + sizeof(__u32);
+
+           do  /* download block of up to 400 bytes */
+            {
+              length = ((size - offset) >= 400) ? 400 : (size - offset);
+
+        	if (copy_from_user(code, (&cb->code) + offp + offset, length)) {
+                	kfree(code);
+	                return -EFAULT;
+        	}
+
+		if ((offset == 0) && (j < 2)) {
+		       	FeatureValue = eicon_pci_print_hdr(code, j ? 0x00 : 0x80); 
+#ifdef EICON_PCI_DEBUG
+	if (FeatureValue) printk(KERN_DEBUG "eicon_pci: Feature Value : 0x%04x.\n", FeatureValue);
+#endif
+			if ((j==0) && (!(FeatureValue & PROTCAP_TELINDUS))) {
+                  		printk(KERN_ERR "eicon_pci: Protocol Code cannot handle Telindus\n");
+				kfree(code);
+		                return -EFAULT;
+			}
+                	((eicon_card *)card->card)->Feature = FeatureValue;
+		}
+
+		if (eicon_upload(&dl_para, length, code, 1))
+		{
+                  printk(KERN_ERR "eicon_pci: code block check failed at 0x%x !\n",dl_para.dat.io.r3addr);
+		  kfree(code);
+                  return -EIO;
+		}
+              /* move onto next block */
+              offset += length;
+	      dl_para.dat.io.r3addr += length;
+            } while (offset < size);
+
+#ifdef EICON_PCI_DEBUG
+	printk(KERN_DEBUG "Eicon: %d bytes loaded.\n", offset);
+#endif
+	offp += size;
+	}
+	kfree(code);	
+
+	/* clear signature */
+	outb(0xff, reg + M_ADDRH);
+	outw(0x1e, reg + M_ADDR);
+	outw(0, reg + M_DATA);
+
+#ifdef EICON_PCI_DEBUG
+	printk(KERN_DEBUG "eicon_pci: copy configuration data into shared memory...\n");
+#endif
+	/* copy configuration data into shared memory */
+	outw(8, reg + M_ADDR); outb(cbuf.tei, reg + M_DATA);
+	outw(9, reg + M_ADDR); outb(cbuf.nt2, reg + M_DATA);
+	outw(10,reg + M_ADDR); outb(0, reg + M_DATA);
+	outw(11,reg + M_ADDR); outb(cbuf.WatchDog, reg + M_DATA);
+	outw(12,reg + M_ADDR); outb(cbuf.Permanent, reg + M_DATA);
+	outw(13,reg + M_ADDR); outb(0, reg + M_DATA);                 /* XInterface */
+	outw(14,reg + M_ADDR); outb(cbuf.StableL2, reg + M_DATA);
+	outw(15,reg + M_ADDR); outb(cbuf.NoOrderCheck, reg + M_DATA);
+	outw(16,reg + M_ADDR); outb(0, reg + M_DATA);                 /* HandsetType */
+	outw(17,reg + M_ADDR); outb(0, reg + M_DATA);                 /* SigFlags */
+	outw(18,reg + M_ADDR); outb(cbuf.LowChannel, reg + M_DATA);
+	outw(19,reg + M_ADDR); outb(cbuf.ProtVersion, reg + M_DATA);
+	outw(20,reg + M_ADDR); outb(cbuf.Crc4, reg + M_DATA);
+	outw(21,reg + M_ADDR); outb((cbuf.Loopback) ? 2:0, reg + M_DATA);
+
+	for (i=0;i<32;i++)
+	{
+		outw( 32+i, reg + M_ADDR); outb(cbuf.l[0].oad[i], reg + M_DATA);
+		outw( 64+i, reg + M_ADDR); outb(cbuf.l[0].osa[i], reg + M_DATA);
+		outw( 96+i, reg + M_ADDR); outb(cbuf.l[0].spid[i], reg + M_DATA);
+		outw(128+i, reg + M_ADDR); outb(cbuf.l[1].oad[i], reg + M_DATA);
+		outw(160+i, reg + M_ADDR); outb(cbuf.l[1].osa[i], reg + M_DATA);
+		outw(192+i, reg + M_ADDR); outb(cbuf.l[1].spid[i], reg + M_DATA);
+	}
+
+#ifdef EICON_PCI_DEBUG
+           printk(KERN_ERR "eicon_pci: starting CPU...\n");
+#endif
+	/* let the CPU run */
+	outw(0x08, reg + M_RESET);
+
+        timeout = jiffies + (5*HZ);
+        while (timeout > jiffies) {
+	   outw(0x1e, reg + M_ADDR);	
+           signature = inw(reg + M_DATA);
+           if (signature == DIVAS_SIGNATURE) break;
+           SLEEP(2);
+         }
+        if (signature != DIVAS_SIGNATURE)
+         {
+#ifdef EICON_PCI_DEBUG
+           printk(KERN_ERR "eicon_pci: signature 0x%x expected 0x%x\n",signature,DIVAS_SIGNATURE);
+#endif
+           printk(KERN_ERR "eicon_pci: Timeout, protocol code not running !\n");
+           return -EIO; 
+         }
+#ifdef EICON_PCI_DEBUG
+        printk(KERN_DEBUG "eicon_pci: Protocol code running, signature OK\n");
+#endif
+
+        /* get serial number and number of channels supported by card */
+	outb(0xff, reg + M_ADDRH);
+	outw(0x3f6, reg + M_ADDR);
+        card->channels = inw(reg + M_DATA);
+        card->serial = (u32)inw(cfg + 0x22) << 16 | (u32)inw(cfg + 0x26);
+        printk(KERN_INFO "Eicon: Supported channels : %d\n", card->channels);
+        printk(KERN_INFO "Eicon: Card serial no. = %lu\n", card->serial);
+
+        /* test interrupt */
+        card->irqprobe = 1;
+
+        if (!card->ivalid) {
+                if (request_irq(card->irq, &eicon_irq, 0, "Eicon PCI ISDN", card->card))
+                 {
+                  printk(KERN_ERR "eicon_pci: Couldn't request irq %d\n", card->irq);
+                  return -EIO;
+                 }
+        }
+        card->ivalid = 1;
+
+#ifdef EICON_PCI_DEBUG
+        printk(KERN_DEBUG "eicon_pci: testing interrupt\n");
+#endif
+        /* Trigger an interrupt and check if it is delivered */
+        outb(0x41, cfg + 0x4c);		/* enable PLX for interrupts */
+	outb(0x89, reg + M_RESET);	/* place int request */
+
+        timeout = jiffies + 20;
+        while (timeout > jiffies) {
+          if (card->irqprobe != 1) break;
+          SLEEP(5);
+         }
+        if (card->irqprobe == 1) {
+           free_irq(card->irq, card); 
+           card->ivalid = 0; 
+           printk(KERN_ERR "eicon_pci: Getting no interrupts !\n");
+           return -EIO;
+         }
+
+   /* initializing some variables */
+   ((eicon_card *)card->card)->ReadyInt = 0;
+   for(j=0; j<256; j++) ((eicon_card *)card->card)->IdTable[j] = NULL;
+   for(j=0; j< (card->channels + 1); j++) {
+                ((eicon_card *)card->card)->bch[j].e.busy = 0;
+                ((eicon_card *)card->card)->bch[j].e.D3Id = 0;
+                ((eicon_card *)card->card)->bch[j].e.B2Id = 0;
+                ((eicon_card *)card->card)->bch[j].e.ref = 0;
+                ((eicon_card *)card->card)->bch[j].e.Req = 0;
+                ((eicon_card *)card->card)->bch[j].e.complete = 1;
+                ((eicon_card *)card->card)->bch[j].fsm_state = EICON_STATE_NULL;
+   }
+
+   printk(KERN_INFO "Eicon: Card successfully started\n");
+
+ return 0;
+}
+
+
+/*
+ * Configure a card, download code into PRI card,
+ * check if we get interrupts and return 0 on succes.
+ * Return -ERRNO on failure.
+ */
+int
+eicon_pci_load_pri(eicon_pci_card *card, eicon_pci_codebuf *cb) {
+        eicon_pci_boot    *boot;
+	eicon_pr_ram  *prram;
+        int               i,j;
+        int               timeout;
+	int		  FeatureValue = 0;
+	unsigned int	  offset, offp=0, size, length;
+	unsigned long int signature = 0;
+	t_dsp_download_space dl_para;
+	t_dsp_download_desc  dsp_download_table;
+        eicon_pci_codebuf cbuf;
+        unsigned char     *code;
+	unsigned char	  req_int;
+    	char *ram, *reg, *cfg;	
+
+        if (copy_from_user(&cbuf, cb, sizeof(eicon_pci_codebuf)))
+                return -EFAULT;
+
+        boot = &card->shmem->boot;
+	ram = (char *)card->PCIram;
+	reg = (char *)card->PCIreg;
+	cfg = (char *)card->PCIcfg;
+	prram = (eicon_pr_ram *)ram;
+
+	/* reset board */
+	writeb(_MP_RISC_RESET | _MP_LED1 | _MP_LED2, card->PCIreg + MP_RESET);
+	SLEEP(20);
+	writeb(0, card->PCIreg + MP_RESET);
+	SLEEP(20);
+
+	/* set command count to 0 */
+	writel(0, &boot->reserved); 
+
+	/* check if CPU increments the life word */
+        i = readw(&boot->live);
+        SLEEP(20);
+        if (i == readw(&boot->live)) {
+           printk(KERN_ERR "eicon_pci: card is reset, but CPU not running !\n");
+           return -EIO;
+         }
+#ifdef EICON_PCI_DEBUG
+	 printk(KERN_DEBUG "eicon_pci: reset card OK (CPU running)\n");
+#endif
+
+	/* download firmware : DSP and Protocol */
+#ifdef EICON_PCI_DEBUG
+	printk(KERN_DEBUG "eicon_pci: downloading firmware...\n");
+#endif
+
+       	/* Allocate code-buffer */
+       	if (!(code = kmalloc(400, GFP_KERNEL))) {
+                printk(KERN_WARNING "eicon_pci_boot: Couldn't allocate code buffer\n");
+       	        return -ENOMEM;
+        }
+
+	/* prepare protocol upload */
+	dl_para.type		= DL_PARA_MEM_TYPE;
+	dl_para.dat.mem.boot	= boot;
+
+        for (j = 0; j <= cbuf.dsp_code_num; j++)
+         {
+	   if (j==0) size = cbuf.protocol_len;
+		else size = cbuf.dsp_code_len[j];	
+
+           if (j==1) writel(MP_DSP_ADDR, &boot->addr); /* DSP code entry point */
+
+		if (j == 0) dl_para.dat.io.r3addr = MP_PROTOCOL_ADDR;
+		if (j == 1) dl_para.dat.io.r3addr = MP_DSP_CODE_BASE +
+					((sizeof(__u32) + (sizeof(dsp_download_table) * 35) + 3) &0xfffffffc);
+		if (j == 2) dl_para.dat.io.r3addr = MP_DSP_CODE_BASE;
+		if (j == 3) dl_para.dat.io.r3addr = MP_DSP_CODE_BASE + sizeof(__u32);
+
+           offset = 0;
+           do  /* download block of up to 400 bytes */
+            {
+              length = ((size - offset) >= 400) ? 400 : (size - offset);
+
+        	if (copy_from_user(code, (&cb->code) + offp + offset, length)) {
+                	kfree(code);
+	                return -EFAULT;
+        	}
+
+		if ((offset == 0) && (j < 2)) {
+	           	FeatureValue = eicon_pci_print_hdr(code, j ? 0x00 : 0x80); 
+#ifdef EICON_PCI_DEBUG
+	if (FeatureValue) printk(KERN_DEBUG "eicon_pci: Feature Value : 0x%x.\n", FeatureValue);
+#endif
+			if ((j==0) && (!(FeatureValue & PROTCAP_TELINDUS))) {
+                  		printk(KERN_ERR "eicon_pci: Protocol Code cannot handle Telindus\n");
+				kfree(code);
+		                return -EFAULT;
+			}
+                	((eicon_card *)card->card)->Feature = FeatureValue;
+		}
+
+		if (eicon_upload(&dl_para, length, code, 1))
+		{
+		  if (dl_para.dat.mem.timeout == 0)
+	                  printk(KERN_ERR "eicon_pci: code block check failed at 0x%x !\n",dl_para.dat.io.r3addr);
+			else
+			  printk(KERN_ERR "eicon_pci: timeout, no ACK to load !\n");
+		  kfree(code);
+                  return -EIO;
+		}
+
+              /* move onto next block */
+              offset += length;
+	      dl_para.dat.mem.r3addr += length;
+            } while (offset < size);
+#ifdef EICON_PCI_DEBUG
+	printk(KERN_DEBUG "eicon_pci: %d bytes loaded.\n", offset);
+#endif
+	 offp += size;
+         }
+	 kfree(code);	
+
+	/* initialize the adapter data structure */
+#ifdef EICON_PCI_DEBUG
+	printk(KERN_DEBUG "eicon_pci: copy configuration data into shared memory...\n");
+#endif
+        /* clear out config space */
+        for (i = 0; i < 256; i++) writeb(0, &ram[i]);
+
+        /* copy configuration down to the card */
+        writeb(cbuf.tei, &ram[8]);
+        writeb(cbuf.nt2, &ram[9]);
+        writeb(0, &ram[10]);
+        writeb(cbuf.WatchDog, &ram[11]);
+        writeb(cbuf.Permanent, &ram[12]);
+        writeb(cbuf.XInterface, &ram[13]);
+        writeb(cbuf.StableL2, &ram[14]);
+        writeb(cbuf.NoOrderCheck, &ram[15]);
+        writeb(cbuf.HandsetType, &ram[16]);
+        writeb(0, &ram[17]);
+        writeb(cbuf.LowChannel, &ram[18]);
+        writeb(cbuf.ProtVersion, &ram[19]);
+        writeb(cbuf.Crc4, &ram[20]);
+        for (i = 0; i < 32; i++)
+         {
+           writeb(cbuf.l[0].oad[i], &ram[32 + i]);
+           writeb(cbuf.l[0].osa[i], &ram[64 + i]);
+           writeb(cbuf.l[0].spid[i], &ram[96 + i]);
+           writeb(cbuf.l[1].oad[i], &ram[128 + i]);
+           writeb(cbuf.l[1].osa[i], &ram[160 + i]);
+           writeb(cbuf.l[1].spid[i], &ram[192 + i]);
+         }
+#ifdef EICON_PCI_DEBUG
+	printk(KERN_DEBUG "eicon_pci: configured card OK\n");
+#endif
+
+	/* start adapter */
+#ifdef EICON_PCI_DEBUG
+	printk(KERN_DEBUG "eicon_pci: tell card to start...\n");
+#endif
+        writel(MP_PROTOCOL_ADDR, &boot->addr); /* RISC code entry point */
+        writel(3, &boot->cmd); /* DIVAS_START_CMD */
+
+        /* wait till card ACKs */
+        timeout = jiffies + (5*HZ);
+        while (timeout > jiffies) {
+           signature = readl(&boot->signature);
+           if ((signature >> 16) == DIVAS_SIGNATURE) break;
+           SLEEP(2);
+         }
+        if ((signature >> 16) != DIVAS_SIGNATURE)
+         {
+#ifdef EICON_PCI_DEBUG
+           printk(KERN_ERR "eicon_pci: signature 0x%lx expected 0x%x\n",(signature >> 16),DIVAS_SIGNATURE);
+#endif
+           printk(KERN_ERR "eicon_pci: timeout, protocol code not running !\n");
+           return -EIO;
+         }
+#ifdef EICON_PCI_DEBUG
+	printk(KERN_DEBUG "eicon_pci: Protocol code running, signature OK\n");
+#endif
+
+	/* get serial number and number of channels supported by card */
+        card->channels = readb(&ram[0x3f6]);
+        card->serial = readl(&ram[0x3f0]);
+        printk(KERN_INFO "Eicon: Supported channels : %d\n", card->channels);
+        printk(KERN_INFO "Eicon: Card serial no. = %lu\n", card->serial);
+
+	/* test interrupt */
+	readb(&ram[0x3fe]);
+        writeb(0, &ram[0x3fe]); /* reset any pending interrupt */
+	readb(&ram[0x3fe]);
+
+        writew(MP_IRQ_RESET_VAL, &cfg[MP_IRQ_RESET]);
+        writew(0, &cfg[MP_IRQ_RESET + 2]);
+
+        card->irqprobe = 1;
+
+	if (!card->ivalid) {
+	        if (request_irq(card->irq, &eicon_irq, 0, "Eicon PCI ISDN", card->card)) 
+        	 {
+	          printk(KERN_ERR "eicon_pci: Couldn't request irq %d\n", card->irq);
+        	  return -EIO;
+	         }
+	}
+	card->ivalid = 1;
+
+        req_int = readb(&prram->ReadyInt);
+#ifdef EICON_PCI_DEBUG
+	printk(KERN_DEBUG "eicon_pci: testing interrupt\n");
+#endif
+        req_int++;
+        /* Trigger an interrupt and check if it is delivered */
+        writeb(req_int, &prram->ReadyInt);
+
+        timeout = jiffies + 20;
+        while (timeout > jiffies) {
+          if (card->irqprobe != 1) break;
+          SLEEP(2);
+         }
+        if (card->irqprobe == 1) {
+           free_irq(card->irq, card);
+	   card->ivalid = 0;
+           printk(KERN_ERR "eicon_pci: Getting no interrupts !\n");
+           return -EIO;
+         }
+
+   /* initializing some variables */
+   ((eicon_card *)card->card)->ReadyInt = 0;
+   for(j=0; j<256; j++) ((eicon_card *)card->card)->IdTable[j] = NULL;
+   for(j=0; j< (card->channels + 1); j++) {
+		((eicon_card *)card->card)->bch[j].e.busy = 0;
+		((eicon_card *)card->card)->bch[j].e.D3Id = 0;
+		((eicon_card *)card->card)->bch[j].e.B2Id = 0;
+		((eicon_card *)card->card)->bch[j].e.ref = 0;
+		((eicon_card *)card->card)->bch[j].e.Req = 0;
+                ((eicon_card *)card->card)->bch[j].e.complete = 1;
+                ((eicon_card *)card->card)->bch[j].fsm_state = EICON_STATE_NULL;
+   }
+
+   printk(KERN_INFO "Eicon: Card successfully started\n");
+
+ return 0;
+}
+
+#endif	/* CONFIG_PCI */
+
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/eicon/eicon_pci.h linux/drivers/isdn/eicon/eicon_pci.h
--- v2.3.3/linux/drivers/isdn/eicon/eicon_pci.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/eicon/eicon_pci.h	Sun May 23 10:03:41 1999
@@ -0,0 +1,188 @@
+/* $Id: eicon_pci.h,v 1.3 1999/03/29 11:19:51 armin Exp $
+ *
+ * ISDN low-level module for Eicon.Diehl active ISDN-Cards (PCI part).
+ *
+ * Copyright 1998,99 by Armin Schindler (mac@melware.de)
+ * Copyright 1999    Cytronics & Melware (info@melware.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: eicon_pci.h,v $
+ * Revision 1.3  1999/03/29 11:19:51  armin
+ * I/O stuff now in seperate file (eicon_io.c)
+ * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented.
+ *
+ * Revision 1.2  1999/03/02 12:37:50  armin
+ * Added some important checks.
+ * Analog Modem with DSP.
+ * Channels will be added to Link-Level after loading firmware.
+ *
+ * Revision 1.1  1999/01/01 18:09:46  armin
+ * First checkin of new eicon driver.
+ * DIVA-Server BRI/PCI and PRI/PCI are supported.
+ * Old diehl code is obsolete.
+ *
+ *
+ */
+
+#ifndef eicon_pci_h
+#define eicon_pci_h
+
+#ifdef __KERNEL__
+
+
+#define PCI_VENDOR_EICON        0x1133
+#define PCI_DIVA_PRO20          0xe001	/* Not supported */
+#define PCI_DIVA20              0xe002	/* Not supported */
+#define PCI_DIVA_PRO20_U        0xe003	/* Not supported */
+#define PCI_DIVA20_U            0xe004	/* Not supported */
+#define PCI_MAESTRA             0xe010
+#define PCI_MAESTRAQ            0xe012
+#define PCI_MAESTRAQ_U          0xe013
+#define PCI_MAESTRAP            0xe014
+
+#define DIVA_PRO20          1
+#define DIVA20              2
+#define DIVA_PRO20_U        3
+#define DIVA20_U            4
+#define MAESTRA             5
+#define MAESTRAQ            6
+#define MAESTRAQ_U          7
+#define MAESTRAP            8
+
+#define TRUE  1
+#define FALSE 0
+
+#define DIVAS_SIGNATURE 0x4447
+
+
+/* MAESTRA BRI PCI */
+
+#define M_RESET		0x10		/* offset of reset register */
+#define M_DATA		0x00		/* offset of data register */
+#define M_ADDR		0x04		/* offset of address register */
+#define M_ADDRH		0x0c		/* offset of high address register */
+
+#define M_DSP_CODE_LEN            0xbf7d0000
+#define M_DSP_CODE                0xbf7d0004  /* max 128K DSP-Code */ 
+#define M_DSP_CODE_BASE           0xbf7a0000  
+#define M_MAX_DSP_CODE_SIZE       0x00050000  /* max 320K DSP-Code (Telindus) */
+
+
+
+/* MAESTRA PRI PCI */
+
+#define MP_SHARED_RAM_OFFSET 0x1000  /* offset of shared RAM base in the DRAM memory bar */
+
+#define MP_IRQ_RESET     0xc18       /* offset of interrupt status register in the CONFIG memory bar */
+#define MP_IRQ_RESET_VAL 0xfe        /* value to clear an interrupt */
+
+#define MP_PROTOCOL_ADDR 0xa0011000  /* load address of protocol code */
+#define MP_DSP_ADDR      0xa03c0000  /* load address of DSP code */
+#define MP_MAX_PROTOCOL_CODE_SIZE  0x000a0000   /* max 640K Protocol-Code */
+#define MP_DSP_CODE_BASE           0xa03a0000
+#define MP_MAX_DSP_CODE_SIZE       0x00060000   /* max 384K DSP-Code */
+
+#define MP_RESET         0x20        /* offset of RESET register in the DEVICES memory bar */
+
+/* RESET register bits */
+#define _MP_S2M_RESET    0x10        /* active lo   */
+#define _MP_LED2         0x08        /* 1 = on      */
+#define _MP_LED1         0x04        /* 1 = on      */
+#define _MP_DSP_RESET    0x02        /* active lo   */
+#define _MP_RISC_RESET   0x81        /* active hi, bit 7 for compatibility with old boards */
+
+/* boot interface structure */
+typedef struct {
+	__u32 cmd	__attribute__ ((packed));
+	__u32 addr	__attribute__ ((packed));
+	__u32 len	__attribute__ ((packed));
+	__u32 err	__attribute__ ((packed));
+	__u32 live	__attribute__ ((packed));
+	__u32 reserved[(0x1020>>2)-6] __attribute__ ((packed));
+	__u32 signature	__attribute__ ((packed));
+	__u8 data[1];    /* real interface description */
+} eicon_pci_boot;
+
+
+#define DL_PARA_IO_TYPE   0
+#define DL_PARA_MEM_TYPE  1
+
+typedef struct tag_dsp_download_space
+{
+  __u16 type;  /* see definitions above to differ union elements */
+  union
+  {
+    struct
+    {
+      __u32               r3addr;
+      __u16               ioADDR;
+      __u16               ioADDRH;
+      __u16               ioDATA;
+      __u16               BadData;  /* in case of verify error */
+      __u16               GoodData;
+    } io;     /* for io based adapters */
+    struct
+    {
+      __u32               r3addr;
+      eicon_pci_boot	  *boot;
+      __u32               BadData;  /* in case of verify error */
+      __u32               GoodData;
+      __u16               timeout;
+    } mem;    /* for memory based adapters */
+  } dat;
+} t_dsp_download_space;
+
+
+/* Shared memory */
+typedef union {
+	eicon_pci_boot boot;
+} eicon_pci_shmem;
+
+/*
+ * card's description
+ */
+typedef struct {
+	int		  ramsize;
+	int   		  irq;	    /* IRQ		          */
+	unsigned int      PCIram;
+	unsigned int	  PCIreg;
+	unsigned int	  PCIcfg;
+	long int   	  serial;   /* Serial No.		  */
+	int		  channels; /* No. of supported channels  */
+        void*             card;
+        eicon_pci_shmem*  shmem;    /* Shared-memory area         */
+        unsigned char*    intack;   /* Int-Acknowledge            */
+        unsigned char*    stopcpu;  /* Writing here stops CPU     */
+        unsigned char*    startcpu; /* Writing here starts CPU    */
+        unsigned char     type;     /* card type                  */
+        unsigned char     irqprobe; /* Flag: IRQ-probing          */
+        unsigned char     mvalid;   /* Flag: Memory is valid      */
+        unsigned char     ivalid;   /* Flag: IRQ is valid         */
+        unsigned char     master;   /* Flag: Card is Quadro 1/4   */
+        void*             generic;  /* Ptr to generic card struct */
+} eicon_pci_card;
+
+
+
+extern int eicon_pci_load_pri(eicon_pci_card *card, eicon_pci_codebuf *cb);
+extern int eicon_pci_load_bri(eicon_pci_card *card, eicon_pci_codebuf *cb);
+extern void eicon_pci_release(eicon_pci_card *card);
+extern void eicon_pci_printpar(eicon_pci_card *card);
+extern int eicon_pci_find_card(char *ID);
+
+#endif  /* __KERNEL__ */
+
+#endif	/* eicon_pci_h */
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/Makefile linux/drivers/isdn/hisax/Makefile
--- v2.3.3/linux/drivers/isdn/hisax/Makefile	Mon Mar 15 16:11:29 1999
+++ linux/drivers/isdn/hisax/Makefile	Sun May 23 10:03:41 1999
@@ -8,7 +8,7 @@
 O_TARGET :=
 
 O_OBJS := isdnl1.o tei.o isdnl2.o isdnl3.o \
-          lmgr.o q931.o callc.o fsm.o
+          lmgr.o q931.o callc.o fsm.o cert.o
 
 # EXTRA_CFLAGS += -S
 
@@ -27,6 +27,7 @@
 ISAC_OBJ :=
 ARCOFI_OBJ :=
 HSCX_OBJ :=
+ISAR_OBJ :=
 HFC_OBJ :=
 HFC_2BDS0 :=
 RAWHDLC_OBJ :=
@@ -43,12 +44,36 @@
 	 HSCX_OBJ := hscx.o
 endif
 
+ifeq ($(CONFIG_HISAX_TELESPCI),y)
+	 O_OBJS += telespci.o
+	 ISAC_OBJ := isac.o
+	 HSCX_OBJ := hscx.o
+endif
+
+ifeq ($(CONFIG_HISAX_S0BOX),y)
+	 O_OBJS += s0box.o
+	 ISAC_OBJ := isac.o
+	 HSCX_OBJ := hscx.o
+endif
+
 ifeq ($(CONFIG_HISAX_AVM_A1),y)
 	 O_OBJS += avm_a1.o
 	 ISAC_OBJ := isac.o
 	 HSCX_OBJ := hscx.o
 endif
 
+ifeq ($(CONFIG_HISAX_AVM_A1_PCMCIA),y)
+	 O_OBJS += avm_a1p.o
+ 	 ISAC_OBJ := isac.o
+	 HSCX_OBJ := hscx.o
+endif
+ 
+ifeq ($(CONFIG_HISAX_FRITZPCI),y)
+	 O_OBJS += avm_pci.o
+ 	 ISAC_OBJ := isac.o
+endif
+
+
 ifeq ($(CONFIG_HISAX_ELSA),y)
 	 O_OBJS += elsa.o
 	 ISAC_OBJ := isac.o
@@ -84,6 +109,7 @@
 	 O_OBJS += sedlbauer.o
 	 ISAC_OBJ := isac.o
 	 HSCX_OBJ := hscx.o
+	 ISAR_OBJ := isar.o
 endif
 
 ifeq ($(CONFIG_HISAX_SPORTSTER),y)
@@ -101,6 +127,7 @@
 ifeq ($(CONFIG_HISAX_NETJET),y)
 	 O_OBJS += netjet.o
 	 ISAC_OBJ := isac.o
+#	 RAWHDLC_OBJ := rawhdlc.o
 endif
 
 ifeq ($(CONFIG_HISAX_TELES3C),y)
@@ -108,10 +135,8 @@
 	 HFC_2BDS0 := hfc_2bds0.o
 endif
 ifeq ($(CONFIG_HISAX_AMD7930),y)
-	 RAWHDLC_OBJ := foreign.o rawhdlc.o
-endif
-ifeq ($(CONFIG_HISAX_DBRI),y)
-	 RAWHDLC_OBJ := foreign.o rawhdlc.o
+	 O_OBJS += amd7930.o
+	 RAWHDLC_OBJ := rawhdlc.o
 endif
 
 ifeq ($(CONFIG_HISAX_NICCY),y)
@@ -120,7 +145,8 @@
 	 HSCX_OBJ := hscx.o
 endif
 
-O_OBJS += $(ISAC_OBJ) $(HSCX_OBJ) $(HFC_OBJ) $(ARCOFI_OBJ) $(HFC_2BDS0) $(RAWHDLC_OBJ)
+O_OBJS += $(ISAC_OBJ) $(HSCX_OBJ) $(ISAR_OBJ) $(ARCOFI_OBJ) 
+O_OBJS += $(HFC_OBJ) $(HFC_2BDS0) $(RAWHDLC_OBJ) 
 OX_OBJS += config.o
 
 O_TARGET :=
@@ -134,4 +160,14 @@
   endif
 endif
 
+
 include $(TOPDIR)/Rules.make
+
+MD5FILES += isac.c isdnl1.c isdnl2.c isdnl3.c \
+	    tei.c callc.c cert.c l3dss1.c l3_1tr6.c elsa.c
+
+CERT = $(shell md5sum -c md5sums.asc >> /dev/null;echo $$?)
+
+cert.o:	$(MD5FILES) md5sums.asc
+	$(CC) $(CFLAGS) $(EXTRA_CFLAGS) -D CERTIFICATION=$(CERT) -c -o cert.o cert.c
+
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/arcofi.c linux/drivers/isdn/hisax/arcofi.c
--- v2.3.3/linux/drivers/isdn/hisax/arcofi.c	Thu Nov  5 09:58:43 1998
+++ linux/drivers/isdn/hisax/arcofi.c	Sun May 23 10:03:41 1999
@@ -1,12 +1,28 @@
-/* $Id: arcofi.c,v 1.1 1997/10/29 18:51:20 keil Exp $
+/* $Id: arcofi.c,v 1.6 1998/09/30 22:21:56 keil Exp $
 
- * arcofi.h   Ansteuerung ARCOFI 2165
+ * arcofi.c   Ansteuerung ARCOFI 2165
  *
  * Author     Karsten Keil (keil@temic-ech.spacenet.de)
  *
  *
  *
  * $Log: arcofi.c,v $
+ * Revision 1.6  1998/09/30 22:21:56  keil
+ * cosmetics
+ *
+ * Revision 1.5  1998/09/27 12:52:57  keil
+ * cosmetics
+ *
+ * Revision 1.4  1998/08/20 13:50:24  keil
+ * More support for hybrid modem (not working yet)
+ *
+ * Revision 1.3  1998/05/25 12:57:38  keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
+ * Revision 1.2  1998/04/15 16:47:16  keil
+ * new interface
+ *
  * Revision 1.1  1997/10/29 18:51:20  keil
  * New files
  *
@@ -18,18 +34,25 @@
 #include "isac.h"
 
 int
-send_arcofi(struct IsdnCardState *cs, const u_char *msg) {
+send_arcofi(struct IsdnCardState *cs, const u_char *msg, int bc, int receive) {
 	u_char val;
-	char tmp[32];
 	long flags;
-	int cnt=2;
+	int cnt=30;
 	
 	cs->mon_txp = 0;
 	cs->mon_txc = msg[0];
 	memcpy(cs->mon_tx, &msg[1], cs->mon_txc);
+	switch(bc) {
+		case 0: break;
+		case 1: cs->mon_tx[1] |= 0x40;
+			break;
+		default: break;
+	}
 	cs->mocr &= 0x0f;
 	cs->mocr |= 0xa0;
 	test_and_clear_bit(HW_MON1_TX_END, &cs->HW_Flags);
+	if (receive)
+		test_and_clear_bit(HW_MON1_RX_END, &cs->HW_Flags);
 	cs->writeisac(cs, ISAC_MOCR, cs->mocr);
 	val = cs->readisac(cs, ISAC_MOSR);
 	cs->writeisac(cs, ISAC_MOX1, cs->mon_tx[cs->mon_txp++]);
@@ -39,12 +62,18 @@
 	sti();
 	while (cnt && !test_bit(HW_MON1_TX_END, &cs->HW_Flags)) {
 		cnt--;
-		current->state = TASK_INTERRUPTIBLE;
-		schedule_timeout((10*HZ)/1000);	/* Timeout 10ms */
+		udelay(500);
+	}
+	if (receive) {
+		while (cnt && !test_bit(HW_MON1_RX_END, &cs->HW_Flags)) {
+			cnt--;
+			udelay(500);
+		}
 	}
 	restore_flags(flags);
-	sprintf(tmp, "arcofi tout %d", cnt);
-	debugl1(cs, tmp);
+	if (cnt <= 0) {
+		printk(KERN_WARNING"HiSax arcofi monitor timed out\n");
+		debugl1(cs, "HiSax arcofi monitor timed out");
+	}
 	return(cnt);	
 }
-
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/arcofi.h linux/drivers/isdn/hisax/arcofi.h
--- v2.3.3/linux/drivers/isdn/hisax/arcofi.h	Wed Apr  1 16:20:57 1998
+++ linux/drivers/isdn/hisax/arcofi.h	Sun May 23 10:03:41 1999
@@ -1,4 +1,4 @@
-/* $Id: arcofi.h,v 1.1 1997/10/29 18:51:20 keil Exp $
+/* $Id: arcofi.h,v 1.3 1998/05/25 12:57:39 keil Exp $
 
  * arcofi.h   Ansteuerung ARCOFI 2165
  *
@@ -7,6 +7,13 @@
  *
  *
  * $Log: arcofi.h,v $
+ * Revision 1.3  1998/05/25 12:57:39  keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
+ * Revision 1.2  1998/04/15 16:47:17  keil
+ * new interface
+ *
  * Revision 1.1  1997/10/29 18:51:20  keil
  * New files
  *
@@ -14,4 +21,4 @@
  
 #define ARCOFI_USE	1
 
-extern int send_arcofi(struct IsdnCardState *cs, const u_char *msg);
+extern int send_arcofi(struct IsdnCardState *cs, const u_char *msg, int bc, int receive);
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/asuscom.c linux/drivers/isdn/hisax/asuscom.c
--- v2.3.3/linux/drivers/isdn/hisax/asuscom.c	Thu Nov  5 09:58:43 1998
+++ linux/drivers/isdn/hisax/asuscom.c	Sun May 23 10:03:41 1999
@@ -1,13 +1,22 @@
-/* $Id: asuscom.c,v 1.2 1998/02/02 13:27:06 keil Exp $
+/* $Id: asuscom.c,v 1.5 1998/11/15 23:54:19 keil Exp $
 
  * asuscom.c     low level stuff for ASUSCOM NETWORK INC. ISDNLink cards
  *
- * Author     Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author     Karsten Keil (keil@isdn4linux.de)
  *
  * Thanks to  ASUSCOM NETWORK INC. Taiwan and  Dynalink NL for informations
  *
  *
  * $Log: asuscom.c,v $
+ * Revision 1.5  1998/11/15 23:54:19  keil
+ * changes from 2.0
+ *
+ * Revision 1.4  1998/06/18 23:18:20  keil
+ * Support for new IPAC card
+ *
+ * Revision 1.3  1998/04/15 16:46:53  keil
+ * new init code
+ *
  * Revision 1.2  1998/02/02 13:27:06  keil
  * New
  *
@@ -17,12 +26,13 @@
 #define __NO_VERSION__
 #include "hisax.h"
 #include "isac.h"
+#include "ipac.h"
 #include "hscx.h"
 #include "isdnl1.h"
 
 extern const char *CardType[];
 
-const char *Asuscom_revision = "$Revision: 1.2 $";
+const char *Asuscom_revision = "$Revision: 1.5 $";
 
 #define byteout(addr,val) outb(val,addr)
 #define bytein(addr) inb(addr)
@@ -33,6 +43,12 @@
 #define ASUS_CTRL_U7	3
 #define ASUS_CTRL_POTS	5
 
+#define ASUS_IPAC_ALE	0
+#define ASUS_IPAC_DATA	1
+
+#define ASUS_ISACHSCX	1
+#define ASUS_IPAC	2
+
 /* CARD_ADR (Write) */
 #define ASUS_RESET      0x80	/* Bit 7 Reset-Leitung */
 
@@ -107,6 +123,30 @@
 }
 
 static u_char
+ReadISAC_IPAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.asus.adr, cs->hw.asus.isac, offset|0x80));
+}
+
+static void
+WriteISAC_IPAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.asus.adr, cs->hw.asus.isac, offset|0x80, value);
+}
+
+static void
+ReadISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+	readfifo(cs->hw.asus.adr, cs->hw.asus.isac, 0x80, data, size);
+}
+
+static void
+WriteISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+	writefifo(cs->hw.asus.adr, cs->hw.asus.isac, 0x80, data, size);
+}
+
+static u_char
 ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
 {
 	return (readreg(cs->hw.asus.adr,
@@ -183,6 +223,52 @@
 	}
 }
 
+static void
+asuscom_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char ista, val, icnt = 20;
+
+	if (!cs) {
+		printk(KERN_WARNING "ISDNLink: Spurious interrupt!\n");
+		return;
+	}
+	ista = readreg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_ISTA);
+Start_IPAC:
+	if (cs->debug & L1_DEB_IPAC)
+		debugl1(cs, "IPAC ISTA %02X", ista);
+	if (ista & 0x0f) {
+		val = readreg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_ISTA + 0x40);
+		if (ista & 0x01)
+			val |= 0x01;
+		if (ista & 0x04)
+			val |= 0x02;
+		if (ista & 0x08)
+			val |= 0x04;
+		if (val)
+			hscx_int_main(cs, val);
+	}
+	if (ista & 0x20) {
+		val = 0xfe & readreg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_ISTA | 0x80);
+		if (val) {
+			isac_interrupt(cs, val);
+		}
+	}
+	if (ista & 0x10) {
+		val = 0x01;
+		isac_interrupt(cs, val);
+	}
+	ista  = readreg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_ISTA);
+	if ((ista & 0x3f) && icnt) {
+		icnt--;
+		goto Start_IPAC;
+	}
+	if (!icnt)
+		printk(KERN_WARNING "ASUS IRQ LOOP\n");
+	writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_MASK, 0xFF);
+	writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_MASK, 0xC0);
+}
+
 void
 release_io_asuscom(struct IsdnCardState *cs)
 {
@@ -197,14 +283,27 @@
 {
 	long flags;
 
-	byteout(cs->hw.asus.adr, ASUS_RESET);	/* Reset On */
+	if (cs->subtyp == ASUS_IPAC)
+		writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_POTA2, 0x20);
+	else
+		byteout(cs->hw.asus.adr, ASUS_RESET);	/* Reset On */
 	save_flags(flags);
 	sti();
 	current->state = TASK_INTERRUPTIBLE;
 	schedule_timeout(1);
-	byteout(cs->hw.asus.adr, 0);	/* Reset Off */
+	if (cs->subtyp == ASUS_IPAC)
+		writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_POTA2, 0x0);
+	else
+		byteout(cs->hw.asus.adr, 0);	/* Reset Off */
 	current->state = TASK_INTERRUPTIBLE;
 	schedule_timeout(1);
+	if (cs->subtyp == ASUS_IPAC) {
+		writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_CONF, 0x0);
+		writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_ACFG, 0xff);
+		writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_AOE, 0x0);
+		writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_MASK, 0xc0);
+		writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_PCFG, 0x12);
+	}
 	restore_flags(flags);
 }
 
@@ -219,13 +318,15 @@
 			release_io_asuscom(cs);
 			return(0);
 		case CARD_SETIRQ:
-			return(request_irq(cs->irq, &asuscom_interrupt,
+			if (cs->subtyp == ASUS_IPAC)
+				return(request_irq(cs->irq, &asuscom_interrupt_ipac,
+					I4L_IRQ_FLAG, "HiSax", cs));
+			else
+				return(request_irq(cs->irq, &asuscom_interrupt,
 					I4L_IRQ_FLAG, "HiSax", cs));
 		case CARD_INIT:
-			clear_pending_isac_ints(cs);
-			clear_pending_hscx_ints(cs);
-			initisac(cs);
-			inithscx(cs);
+			cs->debug |= L1_DEB_IPAC;
+			inithscxisac(cs, 3);
 			return(0);
 		case CARD_TEST:
 			return(0);
@@ -238,6 +339,7 @@
 {
 	int bytecnt;
 	struct IsdnCardState *cs = card->cs;
+	u_char val;
 	char tmp[64];
 
 	strcpy(tmp, Asuscom_revision);
@@ -248,12 +350,6 @@
 	bytecnt = 8;
 	cs->hw.asus.cfg_reg = card->para[1];
 	cs->irq = card->para[0];
-	cs->hw.asus.adr = cs->hw.asus.cfg_reg + ASUS_ADR;
-	cs->hw.asus.isac = cs->hw.asus.cfg_reg + ASUS_ISAC;
-	cs->hw.asus.hscx = cs->hw.asus.cfg_reg + ASUS_HSCX;
-	cs->hw.asus.u7 = cs->hw.asus.cfg_reg + ASUS_CTRL_U7;
-	cs->hw.asus.pots = cs->hw.asus.cfg_reg + ASUS_CTRL_POTS;
-
 	if (check_region((cs->hw.asus.cfg_reg), bytecnt)) {
 		printk(KERN_WARNING
 		       "HiSax: %s config port %x-%x already in use\n",
@@ -264,27 +360,45 @@
 	} else {
 		request_region(cs->hw.asus.cfg_reg, bytecnt, "asuscom isdn");
 	}
-
-	printk(KERN_INFO
-	       "ISDNLink: defined at 0x%x IRQ %d\n",
-	       cs->hw.asus.cfg_reg,
-	       cs->irq);
-	printk(KERN_INFO "ISDNLink: resetting card\n");
-	reset_asuscom(cs);
-	cs->readisac = &ReadISAC;
-	cs->writeisac = &WriteISAC;
-	cs->readisacfifo = &ReadISACfifo;
-	cs->writeisacfifo = &WriteISACfifo;
+	printk(KERN_INFO "ISDNLink: defined at 0x%x IRQ %d\n",
+		cs->hw.asus.cfg_reg, cs->irq);
 	cs->BC_Read_Reg = &ReadHSCX;
 	cs->BC_Write_Reg = &WriteHSCX;
 	cs->BC_Send_Data = &hscx_fill_fifo;
 	cs->cardmsg = &Asus_card_msg;
-	ISACVersion(cs, "ISDNLink:");
-	if (HscxVersion(cs, "ISDNLink:")) {
-		printk(KERN_WARNING
-		     "ISDNLink: wrong HSCX versions check IO address\n");
-		release_io_asuscom(cs);
-		return (0);
+	val = readreg(cs->hw.asus.cfg_reg + ASUS_IPAC_ALE, 
+		cs->hw.asus.cfg_reg + ASUS_IPAC_DATA, IPAC_ID);
+	if (val == 1) {
+		cs->subtyp = ASUS_IPAC;
+		cs->hw.asus.adr  = cs->hw.asus.cfg_reg + ASUS_IPAC_ALE;
+		cs->hw.asus.isac = cs->hw.asus.cfg_reg + ASUS_IPAC_DATA;
+		cs->hw.asus.hscx = cs->hw.asus.cfg_reg + ASUS_IPAC_DATA;
+		test_and_set_bit(HW_IPAC, &cs->HW_Flags);
+		cs->readisac = &ReadISAC_IPAC;
+		cs->writeisac = &WriteISAC_IPAC;
+		cs->readisacfifo = &ReadISACfifo_IPAC;
+		cs->writeisacfifo = &WriteISACfifo_IPAC;
+		printk(KERN_INFO "Asus: IPAC version %x\n", val);
+	} else {
+		cs->subtyp = ASUS_ISACHSCX;
+		cs->hw.asus.adr = cs->hw.asus.cfg_reg + ASUS_ADR;
+		cs->hw.asus.isac = cs->hw.asus.cfg_reg + ASUS_ISAC;
+		cs->hw.asus.hscx = cs->hw.asus.cfg_reg + ASUS_HSCX;
+		cs->hw.asus.u7 = cs->hw.asus.cfg_reg + ASUS_CTRL_U7;
+		cs->hw.asus.pots = cs->hw.asus.cfg_reg + ASUS_CTRL_POTS;
+		cs->readisac = &ReadISAC;
+		cs->writeisac = &WriteISAC;
+		cs->readisacfifo = &ReadISACfifo;
+		cs->writeisacfifo = &WriteISACfifo;
+		ISACVersion(cs, "ISDNLink:");
+		if (HscxVersion(cs, "ISDNLink:")) {
+			printk(KERN_WARNING
+		     	"ISDNLink: wrong HSCX versions check IO address\n");
+			release_io_asuscom(cs);
+			return (0);
+		}
 	}
+	printk(KERN_INFO "ISDNLink: resetting card\n");
+	reset_asuscom(cs);
 	return (1);
 }
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/avm_a1.c linux/drivers/isdn/hisax/avm_a1.c
--- v2.3.3/linux/drivers/isdn/hisax/avm_a1.c	Wed Apr  1 16:20:58 1998
+++ linux/drivers/isdn/hisax/avm_a1.c	Sun May 23 10:03:41 1999
@@ -1,11 +1,20 @@
-/* $Id: avm_a1.c,v 2.7 1998/02/02 13:29:37 keil Exp $
+/* $Id: avm_a1.c,v 2.10 1998/11/15 23:54:21 keil Exp $
 
  * avm_a1.c     low level stuff for AVM A1 (Fritz) isdn cards
  *
- * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author       Karsten Keil (keil@isdn4linux.de)
  *
  *
  * $Log: avm_a1.c,v $
+ * Revision 2.10  1998/11/15 23:54:21  keil
+ * changes from 2.0
+ *
+ * Revision 2.9  1998/08/13 23:36:12  keil
+ * HiSax 3.1 - don't work stable with current LinkLevel
+ *
+ * Revision 2.8  1998/04/15 16:44:27  keil
+ * new init code
+ *
  * Revision 2.7  1998/02/02 13:29:37  keil
  * fast io
  *
@@ -57,7 +66,7 @@
 #include "isdnl1.h"
 
 extern const char *CardType[];
-const char *avm_revision = "$Revision: 2.7 $";
+static const char *avm_revision = "$Revision: 2.10 $";
 
 #define	 AVM_A1_STAT_ISAC	0x01
 #define	 AVM_A1_STAT_HSCX	0x02
@@ -145,7 +154,6 @@
 {
 	struct IsdnCardState *cs = dev_id;
 	u_char val, sval, stat = 0;
-	char tmp[32];
 
 	if (!cs) {
 		printk(KERN_WARNING "AVM A1: Spurious interrupt!\n");
@@ -155,10 +163,8 @@
 		if (!(sval & AVM_A1_STAT_TIMER)) {
 			byteout(cs->hw.avm.cfg_reg, 0x1E);
 			sval = bytein(cs->hw.avm.cfg_reg);
-		} else if (cs->debug & L1_DEB_INTSTAT) {
-			sprintf(tmp, "avm IntStatus %x", sval);
-			debugl1(cs, tmp);
-		}
+		} else if (cs->debug & L1_DEB_INTSTAT)
+			debugl1(cs, "avm IntStatus %x", sval);
 		if (!(sval & AVM_A1_STAT_HSCX)) {
 			val = readreg(cs->hw.avm.hscx[1], HSCX_ISTA);
 			if (val) {
@@ -217,10 +223,10 @@
 			return(request_irq(cs->irq, &avm_a1_interrupt,
 					I4L_IRQ_FLAG, "HiSax", cs));
 		case CARD_INIT:
-			clear_pending_isac_ints(cs);
-			clear_pending_hscx_ints(cs);
-			initisac(cs);
-			inithscx(cs);
+			inithscxisac(cs, 1);
+			byteout(cs->hw.avm.cfg_reg, 0x16);
+			byteout(cs->hw.avm.cfg_reg, 0x1E);
+			inithscxisac(cs, 2);
 			return(0);
 		case CARD_TEST:
 			return(0);
@@ -348,7 +354,6 @@
 	val = bytein(cs->hw.avm.cfg_reg + 2);
 	printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
 	       cs->hw.avm.cfg_reg + 2, val);
-	byteout(cs->hw.avm.cfg_reg, 0x1E);
 	val = bytein(cs->hw.avm.cfg_reg);
 	printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
 	       cs->hw.avm.cfg_reg, val);
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/avm_a1p.c linux/drivers/isdn/hisax/avm_a1p.c
--- v2.3.3/linux/drivers/isdn/hisax/avm_a1p.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/avm_a1p.c	Sun May 23 10:03:41 1999
@@ -0,0 +1,334 @@
+/* $Id: avm_a1p.c,v 2.3 1998/11/15 23:54:22 keil Exp $
+ *
+ * avm_a1p.c    low level stuff for the following AVM cards:
+ *              A1 PCMCIA
+ *		FRITZ!Card PCMCIA
+ *		FRITZ!Card PCMCIA 2.0
+ *
+ * Author       Carsten Paeth (calle@calle.in-berlin.de)
+ *
+ * $Log: avm_a1p.c,v $
+ * Revision 2.3  1998/11/15 23:54:22  keil
+ * changes from 2.0
+ *
+ * Revision 2.2  1998/08/13 23:36:13  keil
+ * HiSax 3.1 - don't work stable with current LinkLevel
+ *
+ * Revision 2.1  1998/07/15 15:01:23  calle
+ * Support for AVM passive PCMCIA cards:
+ *    A1 PCMCIA, FRITZ!Card PCMCIA and FRITZ!Card PCMCIA 2.0
+ *
+ * Revision 1.1.2.1  1998/07/15 14:43:26  calle
+ * Support for AVM passive PCMCIA cards:
+ *    A1 PCMCIA, FRITZ!Card PCMCIA and FRITZ!Card PCMCIA 2.0
+ *
+ *
+ */
+#define __NO_VERSION__
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+
+/* register offsets */
+#define ADDRREG_OFFSET		0x02
+#define DATAREG_OFFSET		0x03
+#define ASL0_OFFSET		0x04
+#define ASL1_OFFSET		0x05
+#define MODREG_OFFSET		0x06
+#define VERREG_OFFSET		0x07
+
+/* address offsets */
+#define ISAC_FIFO_OFFSET	0x00
+#define ISAC_REG_OFFSET		0x20
+#define HSCX_CH_DIFF		0x40
+#define HSCX_FIFO_OFFSET	0x80
+#define HSCX_REG_OFFSET		0xa0
+
+/* read bits ASL0 */
+#define	 ASL0_R_TIMER		0x10 /* active low */
+#define	 ASL0_R_ISAC		0x20 /* active low */
+#define	 ASL0_R_HSCX		0x40 /* active low */
+#define	 ASL0_R_TESTBIT		0x80
+#define  ASL0_R_IRQPENDING	(ASL0_R_ISAC|ASL0_R_HSCX|ASL0_R_TIMER)
+
+/* write bits ASL0 */
+#define	 ASL0_W_RESET		0x01
+#define	 ASL0_W_TDISABLE	0x02
+#define	 ASL0_W_TRESET		0x04
+#define	 ASL0_W_IRQENABLE	0x08
+#define	 ASL0_W_TESTBIT		0x80
+
+/* write bits ASL1 */
+#define	 ASL1_W_LED0		0x10
+#define	 ASL1_W_LED1		0x20
+#define	 ASL1_W_ENABLE_S0	0xC0
+ 
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+static const char *avm_revision = "$Revision: 2.3 $";
+
+static inline u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	long flags;
+        u_char ret;
+
+        offset -= 0x20;
+	save_flags(flags);
+	cli();
+        byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,ISAC_REG_OFFSET+offset);
+	ret = bytein(cs->hw.avm.cfg_reg+DATAREG_OFFSET);
+	restore_flags(flags);
+	return ret;
+}
+
+static inline void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	long flags;
+
+        offset -= 0x20;
+
+	save_flags(flags);
+	cli();
+        byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,ISAC_REG_OFFSET+offset);
+	byteout(cs->hw.avm.cfg_reg+DATAREG_OFFSET, value);
+	restore_flags(flags);
+}
+
+static inline void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	long flags;
+
+	save_flags(flags);
+	cli();
+	byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,ISAC_FIFO_OFFSET);
+	insb(cs->hw.avm.cfg_reg+DATAREG_OFFSET, data, size);
+	restore_flags(flags);
+}
+
+static inline void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	long flags;
+
+	save_flags(flags);
+	cli();
+	byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,ISAC_FIFO_OFFSET);
+	outsb(cs->hw.avm.cfg_reg+DATAREG_OFFSET, data, size);
+	restore_flags(flags);
+}
+
+static inline u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	u_char ret;
+	long flags;
+
+        offset -= 0x20;
+
+	save_flags(flags);
+	cli();
+	byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,
+			HSCX_REG_OFFSET+hscx*HSCX_CH_DIFF+offset);
+	ret = bytein(cs->hw.avm.cfg_reg+DATAREG_OFFSET);
+	restore_flags(flags);
+	return ret;
+}
+
+static inline void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	long flags;
+
+        offset -= 0x20;
+
+	save_flags(flags);
+	cli();
+	byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,
+			HSCX_REG_OFFSET+hscx*HSCX_CH_DIFF+offset);
+	byteout(cs->hw.avm.cfg_reg+DATAREG_OFFSET, value);
+	restore_flags(flags);
+}
+
+static inline void
+ReadHSCXfifo(struct IsdnCardState *cs, int hscx, u_char * data, int size)
+{
+	long flags;
+
+	save_flags(flags);
+	cli();
+	byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,
+			HSCX_FIFO_OFFSET+hscx*HSCX_CH_DIFF);
+	insb(cs->hw.avm.cfg_reg+DATAREG_OFFSET, data, size);
+	restore_flags(flags);
+}
+
+static inline void
+WriteHSCXfifo(struct IsdnCardState *cs, int hscx, u_char * data, int size)
+{
+	long flags;
+
+	save_flags(flags);
+	cli();
+	byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,
+			HSCX_FIFO_OFFSET+hscx*HSCX_CH_DIFF);
+	outsb(cs->hw.avm.cfg_reg+DATAREG_OFFSET, data, size);
+	restore_flags(flags);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) ReadHSCX(cs, nr, reg)
+#define WRITEHSCX(cs, nr, reg, data) WriteHSCX(cs, nr, reg, data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) ReadHSCXfifo(cs, nr, ptr, cnt) 
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) WriteHSCXfifo(cs, nr, ptr, cnt)
+
+#include "hscx_irq.c"
+
+static void
+avm_a1p_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val, sval, stat = 0;
+
+	if (!cs) {
+		printk(KERN_WARNING "AVM A1 PCMCIA: Spurious interrupt!\n");
+		return;
+	}
+	while ((sval = (~bytein(cs->hw.avm.cfg_reg+ASL0_OFFSET) & ASL0_R_IRQPENDING))) {
+		if (cs->debug & L1_DEB_INTSTAT)
+			debugl1(cs, "avm IntStatus %x", sval);
+		if (sval & ASL0_R_HSCX) {
+                        val = ReadHSCX(cs, 1, HSCX_ISTA);
+			if (val) {
+				hscx_int_main(cs, val);
+				stat |= 1;
+			}
+		}
+		if (sval & ASL0_R_ISAC) {
+			val = ReadISAC(cs, ISAC_ISTA);
+			if (val) {
+				isac_interrupt(cs, val);
+				stat |= 2;
+			}
+		}
+	}
+	if (stat & 1) {
+		WriteHSCX(cs, 0, HSCX_MASK, 0xff);
+		WriteHSCX(cs, 1, HSCX_MASK, 0xff);
+		WriteHSCX(cs, 0, HSCX_MASK, 0x00);
+		WriteHSCX(cs, 1, HSCX_MASK, 0x00);
+	}
+	if (stat & 2) {
+		WriteISAC(cs, ISAC_MASK, 0xff);
+		WriteISAC(cs, ISAC_MASK, 0x00);
+	}
+}
+
+static int
+AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	int ret;
+	switch (mt) {
+		case CARD_RESET:
+			byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,0x00);
+			HZDELAY(HZ / 5 + 1);
+			byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,ASL0_W_RESET);
+			HZDELAY(HZ / 5 + 1);
+			byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,0x00);
+			return 0;
+
+		case CARD_RELEASE:
+			/* free_irq is done in HiSax_closecard(). */
+		        /* free_irq(cs->irq, cs); */
+			return 0;
+
+		case CARD_SETIRQ:
+			ret = request_irq(cs->irq, &avm_a1p_interrupt,
+					I4L_IRQ_FLAG, "HiSax", cs);
+			if (ret)
+				return ret;
+			byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,
+				ASL0_W_TDISABLE|ASL0_W_TRESET|ASL0_W_IRQENABLE);
+                        return 0;
+
+		case CARD_INIT:
+			clear_pending_isac_ints(cs);
+			clear_pending_hscx_ints(cs);
+			inithscxisac(cs, 1);
+			inithscxisac(cs, 2);
+			return 0;
+
+		case CARD_TEST:
+			/* we really don't need it for the PCMCIA Version */
+			return 0;
+
+		default:
+			/* all card drivers ignore others, so we do the same */
+			return 0;
+	}
+	return 0;
+}
+
+__initfunc(int
+setup_avm_a1_pcmcia(struct IsdnCard *card))
+{
+	u_char model, vers;
+	struct IsdnCardState *cs = card->cs;
+	long flags;
+	char tmp[64];
+
+
+	strcpy(tmp, avm_revision);
+	printk(KERN_INFO "HiSax: AVM A1 PCMCIA driver Rev. %s\n",
+						 HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_A1_PCMCIA)
+		return (0);
+
+	cs->hw.avm.cfg_reg = card->para[1];
+	cs->irq = card->para[0];
+
+
+	save_flags(flags);
+	outb(cs->hw.avm.cfg_reg+ASL1_OFFSET, ASL1_W_ENABLE_S0);
+        sti();
+
+	byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,0x00);
+	HZDELAY(HZ / 5 + 1);
+	byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,ASL0_W_RESET);
+	HZDELAY(HZ / 5 + 1);
+	byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,0x00);
+
+	byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET, ASL0_W_TDISABLE|ASL0_W_TRESET);
+
+	restore_flags(flags);
+
+	model = bytein(cs->hw.avm.cfg_reg+MODREG_OFFSET);
+	vers = bytein(cs->hw.avm.cfg_reg+VERREG_OFFSET);
+
+	printk(KERN_INFO "AVM A1 PCMCIA: io 0x%x irq %d model %d version %d\n",
+				cs->hw.avm.cfg_reg, cs->irq, model, vers);
+
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &AVM_card_msg;
+
+	ISACVersion(cs, "AVM A1 PCMCIA:");
+	if (HscxVersion(cs, "AVM A1 PCMCIA:")) {
+		printk(KERN_WARNING
+		       "AVM A1 PCMCIA: wrong HSCX versions check IO address\n");
+		return (0);
+	}
+	return (1);
+}
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/avm_pci.c linux/drivers/isdn/hisax/avm_pci.c
--- v2.3.3/linux/drivers/isdn/hisax/avm_pci.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/avm_pci.c	Sun May 23 10:03:41 1999
@@ -0,0 +1,865 @@
+/* $Id: avm_pci.c,v 1.7 1999/02/22 18:26:30 keil Exp $
+
+ * avm_pci.c    low level stuff for AVM Fritz!PCI and ISA PnP isdn cards
+ *              Thanks to AVM, Berlin for informations
+ *
+ * Author       Karsten Keil (keil@isdn4linux.de)
+ *
+ *
+ * $Log: avm_pci.c,v $
+ * Revision 1.7  1999/02/22 18:26:30  keil
+ * Argh ! ISAC address was only set with PCI
+ *
+ * Revision 1.6  1998/11/27 19:59:28  keil
+ * set subtype for Fritz!PCI
+ *
+ * Revision 1.5  1998/11/27 12:56:45  keil
+ * forgot to update setup function name
+ *
+ * Revision 1.4  1998/11/15 23:53:19  keil
+ * Fritz!PnP; changes from 2.0
+ *
+ * Revision 1.3  1998/09/27 23:53:39  keil
+ * Fix error handling
+ *
+ * Revision 1.2  1998/09/27 12:54:55  keil
+ * bcs assign was lost in setstack, very bad results
+ *
+ * Revision 1.1  1998/08/20 13:47:30  keil
+ * first version
+ *
+ *
+ *
+ */
+#define __NO_VERSION__
+#include <linux/config.h>
+#include "hisax.h"
+#include "isac.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+
+extern const char *CardType[];
+static const char *avm_pci_rev = "$Revision: 1.7 $";
+
+#define  AVM_FRITZ_PCI		1
+#define  AVM_FRITZ_PNP		2
+
+#define  PCI_VENDOR_AVM		0x1244
+#define  PCI_FRITZPCI_ID	0xa00
+
+#define  HDLC_FIFO		0x0
+#define  HDLC_STATUS		0x4
+
+#define	 AVM_HDLC_1		0x00
+#define	 AVM_HDLC_2		0x01
+#define	 AVM_ISAC_FIFO		0x02
+#define	 AVM_ISAC_REG_LOW	0x04
+#define	 AVM_ISAC_REG_HIGH	0x06
+
+#define  AVM_STATUS0_IRQ_ISAC	0x01
+#define  AVM_STATUS0_IRQ_HDLC	0x02
+#define  AVM_STATUS0_IRQ_TIMER	0x04
+#define  AVM_STATUS0_IRQ_MASK	0x07
+
+#define  AVM_STATUS0_RESET	0x01
+#define  AVM_STATUS0_DIS_TIMER	0x02
+#define  AVM_STATUS0_RES_TIMER	0x04
+#define  AVM_STATUS0_ENA_IRQ	0x08
+#define  AVM_STATUS0_TESTBIT	0x10
+
+#define  AVM_STATUS1_INT_SEL	0x0f
+#define  AVM_STATUS1_ENA_IOM	0x80
+
+#define  HDLC_MODE_ITF_FLG	0x01
+#define  HDLC_MODE_TRANS	0x02
+#define  HDLC_MODE_CCR_7	0x04
+#define  HDLC_MODE_CCR_16	0x08
+#define  HDLC_MODE_TESTLOOP	0x80
+
+#define  HDLC_INT_XPR		0x80
+#define  HDLC_INT_XDU		0x40
+#define  HDLC_INT_RPR		0x20
+#define  HDLC_INT_MASK		0xE0
+
+#define  HDLC_STAT_RME		0x01
+#define  HDLC_STAT_RDO		0x10
+#define  HDLC_STAT_CRCVFRRAB	0x0E
+#define  HDLC_STAT_CRCVFR	0x06
+#define  HDLC_STAT_RML_MASK	0x3f00
+
+#define  HDLC_CMD_XRS		0x80
+#define  HDLC_CMD_XME		0x01
+#define  HDLC_CMD_RRS		0x20
+#define  HDLC_CMD_XML_MASK	0x3f00
+
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	register u_char idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW;
+	register u_char val;
+	register long flags;
+
+	save_flags(flags);
+	cli();
+	outb(idx, cs->hw.avm.cfg_reg + 4);
+	val = inb(cs->hw.avm.isac + (offset & 0xf));
+	restore_flags(flags);
+	return (val);
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	register u_char idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW;
+	register long flags;
+
+	save_flags(flags);
+	cli();
+	outb(idx, cs->hw.avm.cfg_reg + 4);
+	outb(value, cs->hw.avm.isac + (offset & 0xf));
+	restore_flags(flags);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	outb(AVM_ISAC_FIFO, cs->hw.avm.cfg_reg + 4);
+	insb(cs->hw.avm.isac, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	outb(AVM_ISAC_FIFO, cs->hw.avm.cfg_reg + 4);
+	outsb(cs->hw.avm.isac, data, size);
+}
+
+static inline u_int
+ReadHDLCPCI(struct IsdnCardState *cs, int chan, u_char offset)
+{
+	register u_int idx = chan ? AVM_HDLC_2 : AVM_HDLC_1;
+	register u_int val;
+	register long flags;
+
+	save_flags(flags);
+	cli();
+	outl(idx, cs->hw.avm.cfg_reg + 4);
+	val = inl(cs->hw.avm.isac + offset);
+	restore_flags(flags);
+	return (val);
+}
+
+static inline void
+WriteHDLCPCI(struct IsdnCardState *cs, int chan, u_char offset, u_int value)
+{
+	register u_int idx = chan ? AVM_HDLC_2 : AVM_HDLC_1;
+	register long flags;
+
+	save_flags(flags);
+	cli();
+	outl(idx, cs->hw.avm.cfg_reg + 4);
+	outl(value, cs->hw.avm.isac + offset);
+	restore_flags(flags);
+}
+
+static inline u_char
+ReadHDLCPnP(struct IsdnCardState *cs, int chan, u_char offset)
+{
+	register u_char idx = chan ? AVM_HDLC_2 : AVM_HDLC_1;
+	register u_char val;
+	register long flags;
+
+	save_flags(flags);
+	cli();
+	outb(idx, cs->hw.avm.cfg_reg + 4);
+	val = inb(cs->hw.avm.isac + offset);
+	restore_flags(flags);
+	return (val);
+}
+
+static inline void
+WriteHDLCPnP(struct IsdnCardState *cs, int chan, u_char offset, u_char value)
+{
+	register u_char idx = chan ? AVM_HDLC_2 : AVM_HDLC_1;
+	register long flags;
+
+	save_flags(flags);
+	cli();
+	outb(idx, cs->hw.avm.cfg_reg + 4);
+	outb(value, cs->hw.avm.isac + offset);
+	restore_flags(flags);
+}
+
+static u_char
+ReadHDLC_s(struct IsdnCardState *cs, int chan, u_char offset)
+{
+	return(0xff & ReadHDLCPCI(cs, chan, offset));
+}
+
+static void
+WriteHDLC_s(struct IsdnCardState *cs, int chan, u_char offset, u_char value)
+{
+	WriteHDLCPCI(cs, chan, offset, value);
+}
+
+static inline
+struct BCState *Sel_BCS(struct IsdnCardState *cs, int channel)
+{
+	if (cs->bcs[0].mode && (cs->bcs[0].channel == channel))
+		return(&cs->bcs[0]);
+	else if (cs->bcs[1].mode && (cs->bcs[1].channel == channel))
+		return(&cs->bcs[1]);
+	else
+		return(NULL);
+}
+
+void inline
+hdlc_sched_event(struct BCState *bcs, int event)
+{
+	bcs->event |= 1 << event;
+	queue_task(&bcs->tqueue, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+}
+
+void
+write_ctrl(struct BCState *bcs, int which) {
+
+	if (bcs->cs->debug & L1_DEB_HSCX)
+		debugl1(bcs->cs, "hdlc %c wr%x ctrl %x",
+			'A' + bcs->channel, which, bcs->hw.hdlc.ctrl.ctrl);
+	if (bcs->cs->subtyp == AVM_FRITZ_PCI) {
+		WriteHDLCPCI(bcs->cs, bcs->channel, HDLC_STATUS, bcs->hw.hdlc.ctrl.ctrl);
+	} else {
+		if (which & 4)
+			WriteHDLCPnP(bcs->cs, bcs->channel, HDLC_STATUS + 2,
+				bcs->hw.hdlc.ctrl.sr.mode);
+		if (which & 2)
+			WriteHDLCPnP(bcs->cs, bcs->channel, HDLC_STATUS + 1,
+				bcs->hw.hdlc.ctrl.sr.xml);
+		if (which & 1)
+			WriteHDLCPnP(bcs->cs, bcs->channel, HDLC_STATUS,
+				bcs->hw.hdlc.ctrl.sr.cmd);
+	}
+}
+
+void
+modehdlc(struct BCState *bcs, int mode, int bc)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int hdlc = bcs->channel;
+
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "hdlc %c mode %d ichan %d",
+			'A' + hdlc, mode, bc);
+	bcs->mode = mode;
+	bcs->channel = bc;
+	bcs->hw.hdlc.ctrl.ctrl = 0;
+	switch (mode) {
+		case (L1_MODE_NULL):
+			bcs->hw.hdlc.ctrl.sr.cmd  = HDLC_CMD_XRS | HDLC_CMD_RRS;
+			bcs->hw.hdlc.ctrl.sr.mode = HDLC_MODE_TRANS;
+			write_ctrl(bcs, 5);
+			break;
+		case (L1_MODE_TRANS):
+			bcs->hw.hdlc.ctrl.sr.cmd  = HDLC_CMD_XRS | HDLC_CMD_RRS;
+			bcs->hw.hdlc.ctrl.sr.mode = HDLC_MODE_TRANS;
+			write_ctrl(bcs, 5);
+			bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS;
+			write_ctrl(bcs, 1);
+			bcs->hw.hdlc.ctrl.sr.cmd = 0;
+			hdlc_sched_event(bcs, B_XMTBUFREADY);
+			break;
+		case (L1_MODE_HDLC):
+			bcs->hw.hdlc.ctrl.sr.cmd  = HDLC_CMD_XRS | HDLC_CMD_RRS;
+			bcs->hw.hdlc.ctrl.sr.mode = HDLC_MODE_ITF_FLG;
+			write_ctrl(bcs, 5);
+			bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS;
+			write_ctrl(bcs, 1);
+			bcs->hw.hdlc.ctrl.sr.cmd = 0;
+			hdlc_sched_event(bcs, B_XMTBUFREADY);
+			break;
+	}
+}
+
+static inline void
+hdlc_empty_fifo(struct BCState *bcs, int count)
+{
+	register u_int *ptr;
+	u_char *p;
+	u_char idx = bcs->channel ? AVM_HDLC_2 : AVM_HDLC_1;
+	int cnt=0;
+	struct IsdnCardState *cs = bcs->cs;
+
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "hdlc_empty_fifo %d", count);
+	if (bcs->hw.hdlc.rcvidx + count > HSCX_BUFMAX) {
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "hdlc_empty_fifo: incoming packet too large");
+		return;
+	}
+	ptr = (u_int *) p = bcs->hw.hdlc.rcvbuf + bcs->hw.hdlc.rcvidx;
+	bcs->hw.hdlc.rcvidx += count;
+	if (cs->subtyp == AVM_FRITZ_PCI) {
+		outl(idx, cs->hw.avm.cfg_reg + 4);
+		while (cnt < count) {
+			*ptr++ = inl(cs->hw.avm.isac);
+			cnt += 4;
+		}
+	} else {
+		outb(idx, cs->hw.avm.cfg_reg + 4);
+		while (cnt < count) {
+			*p++ = inb(cs->hw.avm.isac);
+			cnt++;
+		}
+	}
+	if (cs->debug & L1_DEB_HSCX_FIFO) {
+		char *t = bcs->blog;
+
+		if (cs->subtyp == AVM_FRITZ_PNP)
+			p = (u_char *) ptr;
+		t += sprintf(t, "hdlc_empty_fifo %c cnt %d",
+			     bcs->channel ? 'B' : 'A', count);
+		QuickHex(t, p, count);
+		debugl1(cs, bcs->blog);
+	}
+}
+
+static inline void
+hdlc_fill_fifo(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int count, cnt =0;
+	int fifo_size = 32;
+	u_char *p;
+	u_int *ptr;
+
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "hdlc_fill_fifo");
+	if (!bcs->tx_skb)
+		return;
+	if (bcs->tx_skb->len <= 0)
+		return;
+
+	bcs->hw.hdlc.ctrl.sr.cmd &= ~HDLC_CMD_XME;
+	if (bcs->tx_skb->len > fifo_size) {
+		count = fifo_size;
+	} else {
+		count = bcs->tx_skb->len;
+		if (bcs->mode != L1_MODE_TRANS)
+			bcs->hw.hdlc.ctrl.sr.cmd |= HDLC_CMD_XME;
+	}
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "hdlc_fill_fifo %d/%ld", count, bcs->tx_skb->len);
+	ptr = (u_int *) p = bcs->tx_skb->data;
+	skb_pull(bcs->tx_skb, count);
+	bcs->tx_cnt -= count;
+	bcs->hw.hdlc.count += count;
+	bcs->hw.hdlc.ctrl.sr.xml = ((count == fifo_size) ? 0 : count);
+	write_ctrl(bcs, 3);  /* sets the correct index too */
+	if (cs->subtyp == AVM_FRITZ_PCI) {
+		while (cnt<count) {
+			outl(*ptr++, cs->hw.avm.isac);
+			cnt += 4;
+		}
+	} else {
+		while (cnt<count) {
+			outb(*p++, cs->hw.avm.isac);
+			cnt++;
+		}
+	}
+	if (cs->debug & L1_DEB_HSCX_FIFO) {
+		char *t = bcs->blog;
+
+		if (cs->subtyp == AVM_FRITZ_PNP)
+			p = (u_char *) ptr;
+		t += sprintf(t, "hdlc_fill_fifo %c cnt %d",
+			     bcs->channel ? 'B' : 'A', count);
+		QuickHex(t, p, count);
+		debugl1(cs, bcs->blog);
+	}
+}
+
+static void
+fill_hdlc(struct BCState *bcs)
+{
+	long flags;
+	save_flags(flags);
+	cli();
+	hdlc_fill_fifo(bcs);
+	restore_flags(flags);
+}
+
+static inline void
+HDLC_irq(struct BCState *bcs, u_int stat) {
+	int len;
+	struct sk_buff *skb;
+
+	if (bcs->cs->debug & L1_DEB_HSCX)
+		debugl1(bcs->cs, "ch%d stat %#x", bcs->channel, stat);
+	if (stat & HDLC_INT_RPR) {
+		if (stat & HDLC_STAT_RDO) {
+			if (bcs->cs->debug & L1_DEB_HSCX)
+				debugl1(bcs->cs, "RDO");
+			else
+				debugl1(bcs->cs, "ch%d stat %#x", bcs->channel, stat);
+			bcs->hw.hdlc.ctrl.sr.xml = 0;
+			bcs->hw.hdlc.ctrl.sr.cmd |= HDLC_CMD_RRS;
+			write_ctrl(bcs, 1);
+			bcs->hw.hdlc.ctrl.sr.cmd &= ~HDLC_CMD_RRS;
+			write_ctrl(bcs, 1);
+			bcs->hw.hdlc.rcvidx = 0;
+		} else {
+			if (!(len = (stat & HDLC_STAT_RML_MASK)>>8))
+				len = 32;
+			hdlc_empty_fifo(bcs, len);
+			if ((stat & HDLC_STAT_RME) || (bcs->mode == L1_MODE_TRANS)) {
+				if (((stat & HDLC_STAT_CRCVFRRAB)==HDLC_STAT_CRCVFR) ||
+					(bcs->mode == L1_MODE_TRANS)) {
+					if (!(skb = dev_alloc_skb(bcs->hw.hdlc.rcvidx)))
+						printk(KERN_WARNING "HDLC: receive out of memory\n");
+					else {
+						memcpy(skb_put(skb, bcs->hw.hdlc.rcvidx),
+							bcs->hw.hdlc.rcvbuf, bcs->hw.hdlc.rcvidx);
+						skb_queue_tail(&bcs->rqueue, skb);
+					}
+					bcs->hw.hdlc.rcvidx = 0;
+					hdlc_sched_event(bcs, B_RCVBUFREADY);
+				} else {
+					if (bcs->cs->debug & L1_DEB_HSCX)
+						debugl1(bcs->cs, "invalid frame");
+					else
+						debugl1(bcs->cs, "ch%d invalid frame %#x", bcs->channel, stat);
+					bcs->hw.hdlc.rcvidx = 0;
+				}
+			}
+		}
+	}
+	if (stat & HDLC_INT_XDU) {
+		/* Here we lost an TX interrupt, so
+		 * restart transmitting the whole frame.
+		 */
+		if (bcs->tx_skb) {
+			skb_push(bcs->tx_skb, bcs->hw.hdlc.count);
+			bcs->tx_cnt += bcs->hw.hdlc.count;
+			bcs->hw.hdlc.count = 0;
+//			hdlc_sched_event(bcs, B_XMTBUFREADY);
+			if (bcs->cs->debug & L1_DEB_WARN)
+				debugl1(bcs->cs, "ch%d XDU", bcs->channel);
+		} else if (bcs->cs->debug & L1_DEB_WARN)
+			debugl1(bcs->cs, "ch%d XDU without skb", bcs->channel);
+		bcs->hw.hdlc.ctrl.sr.xml = 0;
+		bcs->hw.hdlc.ctrl.sr.cmd |= HDLC_CMD_XRS;
+		write_ctrl(bcs, 1);
+		bcs->hw.hdlc.ctrl.sr.cmd &= ~HDLC_CMD_XRS;
+		write_ctrl(bcs, 1);
+		hdlc_fill_fifo(bcs);
+	} else if (stat & HDLC_INT_XPR) {
+		if (bcs->tx_skb) {
+			if (bcs->tx_skb->len) {
+				hdlc_fill_fifo(bcs);
+				return;
+			} else {
+				if (bcs->st->lli.l1writewakeup &&
+					(PACKET_NOACK != bcs->tx_skb->pkt_type))
+					bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.hdlc.count);
+				dev_kfree_skb(bcs->tx_skb);
+				bcs->hw.hdlc.count = 0;
+				bcs->tx_skb = NULL;
+			}
+		}
+		if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+			bcs->hw.hdlc.count = 0;
+			test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+			hdlc_fill_fifo(bcs);
+		} else {
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			hdlc_sched_event(bcs, B_XMTBUFREADY);
+		}
+	}
+}
+
+inline void
+HDLC_irq_main(struct IsdnCardState *cs)
+{
+	u_int stat;
+	long  flags;
+	struct BCState *bcs;
+
+	save_flags(flags);
+	cli();
+	if (cs->subtyp == AVM_FRITZ_PCI) {
+		stat = ReadHDLCPCI(cs, 0, HDLC_STATUS);
+	} else {
+		stat = ReadHDLCPnP(cs, 0, HDLC_STATUS);
+		if (stat & HDLC_INT_RPR)
+			stat |= (ReadHDLCPnP(cs, 0, HDLC_STATUS+1))<<8;
+	}
+	if (stat & HDLC_INT_MASK) {
+		if (!(bcs = Sel_BCS(cs, 0))) {
+			if (cs->debug)
+				debugl1(cs, "hdlc spurious channel 0 IRQ");
+		} else
+			HDLC_irq(bcs, stat);
+	}
+	if (cs->subtyp == AVM_FRITZ_PCI) {
+		stat = ReadHDLCPCI(cs, 1, HDLC_STATUS);
+	} else {
+		stat = ReadHDLCPnP(cs, 1, HDLC_STATUS);
+		if (stat & HDLC_INT_RPR)
+			stat |= (ReadHDLCPnP(cs, 1, HDLC_STATUS+1))<<8;
+	}
+	if (stat & HDLC_INT_MASK) {
+		if (!(bcs = Sel_BCS(cs, 1))) {
+			if (cs->debug)
+				debugl1(cs, "hdlc spurious channel 1 IRQ");
+		} else
+			HDLC_irq(bcs, stat);
+	}
+	restore_flags(flags);
+}
+
+void
+hdlc_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	long flags;
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+			save_flags(flags);
+			cli();
+			if (st->l1.bcs->tx_skb) {
+				skb_queue_tail(&st->l1.bcs->squeue, skb);
+				restore_flags(flags);
+			} else {
+				st->l1.bcs->tx_skb = skb;
+				test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+				st->l1.bcs->hw.hdlc.count = 0;
+				restore_flags(flags);
+				st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
+			}
+			break;
+		case (PH_PULL | INDICATION):
+			if (st->l1.bcs->tx_skb) {
+				printk(KERN_WARNING "hdlc_l2l1: this shouldn't happen\n");
+				break;
+			}
+			test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+			st->l1.bcs->tx_skb = skb;
+			st->l1.bcs->hw.hdlc.count = 0;
+			st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
+			break;
+		case (PH_PULL | REQUEST):
+			if (!st->l1.bcs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (PH_ACTIVATE | REQUEST):
+			test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+			modehdlc(st->l1.bcs, st->l1.mode, st->l1.bc);
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | REQUEST):
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | CONFIRM):
+			test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+			test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+			modehdlc(st->l1.bcs, 0, st->l1.bc);
+			st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+			break;
+	}
+}
+
+void
+close_hdlcstate(struct BCState *bcs)
+{
+	modehdlc(bcs, 0, 0);
+	if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (bcs->hw.hdlc.rcvbuf) {
+			kfree(bcs->hw.hdlc.rcvbuf);
+			bcs->hw.hdlc.rcvbuf = NULL;
+		}
+		if (bcs->blog) {
+			kfree(bcs->blog);
+			bcs->blog = NULL;
+		}
+		discard_queue(&bcs->rqueue);
+		discard_queue(&bcs->squeue);
+		if (bcs->tx_skb) {
+			dev_kfree_skb(bcs->tx_skb);
+			bcs->tx_skb = NULL;
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		}
+	}
+}
+
+int
+open_hdlcstate(struct IsdnCardState *cs, struct BCState *bcs)
+{
+	if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (!(bcs->hw.hdlc.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) {
+			printk(KERN_WARNING
+			       "HiSax: No memory for hdlc.rcvbuf\n");
+			return (1);
+		}
+		if (!(bcs->blog = kmalloc(MAX_BLOG_SPACE, GFP_ATOMIC))) {
+			printk(KERN_WARNING
+				"HiSax: No memory for bcs->blog\n");
+			test_and_clear_bit(BC_FLG_INIT, &bcs->Flag);
+			kfree(bcs->hw.hdlc.rcvbuf);
+			bcs->hw.hdlc.rcvbuf = NULL;
+			return (2);
+		}
+		skb_queue_head_init(&bcs->rqueue);
+		skb_queue_head_init(&bcs->squeue);
+	}
+	bcs->tx_skb = NULL;
+	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	bcs->event = 0;
+	bcs->hw.hdlc.rcvidx = 0;
+	bcs->tx_cnt = 0;
+	return (0);
+}
+
+int
+setstack_hdlc(struct PStack *st, struct BCState *bcs)
+{
+	bcs->channel = st->l1.bc;
+	if (open_hdlcstate(st->l1.hardware, bcs))
+		return (-1);
+	st->l1.bcs = bcs;
+	st->l2.l2l1 = hdlc_l2l1;
+	setstack_manager(st);
+	bcs->st = st;
+	setstack_l1_B(st);
+	return (0);
+}
+
+HISAX_INITFUNC(void
+clear_pending_hdlc_ints(struct IsdnCardState *cs))
+{
+	u_int val;
+
+	if (cs->subtyp == AVM_FRITZ_PCI) {
+		val = ReadHDLCPCI(cs, 0, HDLC_STATUS);
+		debugl1(cs, "HDLC 1 STA %x", val);
+		val = ReadHDLCPCI(cs, 1, HDLC_STATUS);
+		debugl1(cs, "HDLC 2 STA %x", val);
+	} else {
+		val = ReadHDLCPnP(cs, 0, HDLC_STATUS);
+		debugl1(cs, "HDLC 1 STA %x", val);
+		val = ReadHDLCPnP(cs, 0, HDLC_STATUS + 1);
+		debugl1(cs, "HDLC 1 RML %x", val);
+		val = ReadHDLCPnP(cs, 0, HDLC_STATUS + 2);
+		debugl1(cs, "HDLC 1 MODE %x", val);
+		val = ReadHDLCPnP(cs, 0, HDLC_STATUS + 3);
+		debugl1(cs, "HDLC 1 VIN %x", val);
+		val = ReadHDLCPnP(cs, 1, HDLC_STATUS);
+		debugl1(cs, "HDLC 2 STA %x", val);
+		val = ReadHDLCPnP(cs, 1, HDLC_STATUS + 1);
+		debugl1(cs, "HDLC 2 RML %x", val);
+		val = ReadHDLCPnP(cs, 1, HDLC_STATUS + 2);
+		debugl1(cs, "HDLC 2 MODE %x", val);
+		val = ReadHDLCPnP(cs, 1, HDLC_STATUS + 3);
+		debugl1(cs, "HDLC 2 VIN %x", val);
+	}
+}
+
+HISAX_INITFUNC(void
+inithdlc(struct IsdnCardState *cs))
+{
+	cs->bcs[0].BC_SetStack = setstack_hdlc;
+	cs->bcs[1].BC_SetStack = setstack_hdlc;
+	cs->bcs[0].BC_Close = close_hdlcstate;
+	cs->bcs[1].BC_Close = close_hdlcstate;
+	modehdlc(cs->bcs, 0, 0);
+	modehdlc(cs->bcs + 1, 0, 0);
+}
+
+static void
+avm_pcipnp_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val, stat = 0;
+	u_char sval;
+
+	if (!cs) {
+		printk(KERN_WARNING "AVM PCI: Spurious interrupt!\n");
+		return;
+	}
+	sval = inb(cs->hw.avm.cfg_reg + 2);
+	if ((sval & AVM_STATUS0_IRQ_MASK) == AVM_STATUS0_IRQ_MASK)
+		/* possible a shared  IRQ reqest */
+		return;
+	if (!(sval & AVM_STATUS0_IRQ_ISAC)) {
+		val = ReadISAC(cs, ISAC_ISTA);
+		isac_interrupt(cs, val);
+		stat |= 2;
+	}
+	if (!(sval & AVM_STATUS0_IRQ_HDLC)) {
+		HDLC_irq_main(cs);
+	}
+	if (stat & 2) {
+		WriteISAC(cs, ISAC_MASK, 0xFF);
+		WriteISAC(cs, ISAC_MASK, 0x0);
+	}
+}
+
+static void
+reset_avmpcipnp(struct IsdnCardState *cs)
+{
+	long flags;
+
+	printk(KERN_INFO "AVM PCI/PnP: reset\n");
+	save_flags(flags);
+	sti();
+	outb(AVM_STATUS0_RESET | AVM_STATUS0_DIS_TIMER, cs->hw.avm.cfg_reg + 2);
+	current->state = TASK_INTERRUPTIBLE;
+	schedule_timeout((10*HZ)/1000); /* Timeout 10ms */
+	outb(AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER | AVM_STATUS0_ENA_IRQ, cs->hw.avm.cfg_reg + 2);
+	outb(AVM_STATUS1_ENA_IOM | cs->irq, cs->hw.avm.cfg_reg + 3);
+	current->state = TASK_INTERRUPTIBLE;
+	schedule_timeout((10*HZ)/1000); /* Timeout 10ms */
+	printk(KERN_INFO "AVM PCI/PnP: S1 %x\n", inb(cs->hw.avm.cfg_reg + 3));
+}
+
+static int
+AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_int irq_flag;
+
+	switch (mt) {
+		case CARD_RESET:
+			reset_avmpcipnp(cs);
+			return(0);
+		case CARD_RELEASE:
+			outb(0, cs->hw.avm.cfg_reg + 2);
+			release_region(cs->hw.avm.cfg_reg, 32);
+			return(0);
+		case CARD_SETIRQ:
+			if (cs->subtyp == AVM_FRITZ_PCI)
+				irq_flag = I4L_IRQ_FLAG | SA_SHIRQ;
+			else
+				irq_flag = I4L_IRQ_FLAG;
+			return(request_irq(cs->irq, &avm_pcipnp_interrupt,
+					irq_flag, "HiSax", cs));
+		case CARD_INIT:
+			clear_pending_isac_ints(cs);
+			initisac(cs);
+			clear_pending_hdlc_ints(cs);
+			inithdlc(cs);
+			outb(AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER,
+				cs->hw.avm.cfg_reg + 2);
+			WriteISAC(cs, ISAC_MASK, 0);
+			outb(AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER |
+				AVM_STATUS0_ENA_IRQ, cs->hw.avm.cfg_reg + 2);
+			/* RESET Receiver and Transmitter */
+			WriteISAC(cs, ISAC_CMDR, 0x41);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+static 	struct pci_dev *dev_avm __initdata = NULL;
+
+__initfunc(int
+setup_avm_pcipnp(struct IsdnCard *card))
+{
+	u_int val, ver;
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, avm_pci_rev);
+	printk(KERN_INFO "HiSax: AVM PCI driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_FRITZPCI)
+		return (0);
+	if (card->para[1]) {
+		cs->hw.avm.cfg_reg = card->para[1];
+		cs->irq = card->para[0];
+		cs->subtyp = AVM_FRITZ_PNP;
+	} else {
+#if CONFIG_PCI
+		if (!pci_present()) {
+			printk(KERN_ERR "FritzPCI: no PCI bus present\n");
+			return(0);
+		}
+		if ((dev_avm = pci_find_device(PCI_VENDOR_AVM,
+			PCI_FRITZPCI_ID,  dev_avm))) {
+			cs->irq = dev_avm->irq;
+			if (!cs->irq) {
+				printk(KERN_WARNING "FritzPCI: No IRQ for PCI card found\n");
+				return(0);
+			}
+			cs->hw.avm.cfg_reg = dev_avm->base_address[1] &
+				PCI_BASE_ADDRESS_IO_MASK; 
+			if (!cs->hw.avm.cfg_reg) {
+				printk(KERN_WARNING "FritzPCI: No IO-Adr for PCI card found\n");
+				return(0);
+			}
+			cs->subtyp = AVM_FRITZ_PCI;
+		} else {
+			printk(KERN_WARNING "FritzPCI: No PCI card found\n");
+			return(0);
+		}
+#else
+		printk(KERN_WARNING "FritzPCI: NO_PCI_BIOS\n");
+		return (0);
+#endif /* CONFIG_PCI */
+	}
+	cs->hw.avm.isac = cs->hw.avm.cfg_reg + 0x10;
+	if (check_region((cs->hw.avm.cfg_reg), 32)) {
+		printk(KERN_WARNING
+		       "HiSax: %s config port %x-%x already in use\n",
+		       CardType[card->typ],
+		       cs->hw.avm.cfg_reg,
+		       cs->hw.avm.cfg_reg + 31);
+		return (0);
+	} else {
+		request_region(cs->hw.avm.cfg_reg, 32,
+			(cs->subtyp == AVM_FRITZ_PCI) ? "avm PCI" : "avm PnP");
+	}
+	switch (cs->subtyp) {
+	  case AVM_FRITZ_PCI:
+		val = inl(cs->hw.avm.cfg_reg);
+		printk(KERN_INFO "AVM PCI: stat %#x\n", val);
+		printk(KERN_INFO "AVM PCI: Class %X Rev %d\n",
+			val & 0xff, (val>>8) & 0xff);
+		cs->BC_Read_Reg = &ReadHDLC_s;
+		cs->BC_Write_Reg = &WriteHDLC_s;
+		break;
+	  case AVM_FRITZ_PNP:
+		val = inb(cs->hw.avm.cfg_reg);
+		ver = inb(cs->hw.avm.cfg_reg + 1);
+		printk(KERN_INFO "AVM PnP: Class %X Rev %d\n", val, ver);
+		reset_avmpcipnp(cs);
+		cs->BC_Read_Reg = &ReadHDLCPnP;
+		cs->BC_Write_Reg = &WriteHDLCPnP;
+		break;
+	  default:
+	  	printk(KERN_WARNING "AVM unknown subtype %d\n", cs->subtyp);
+		outb(0, cs->hw.avm.cfg_reg + 2);
+		release_region(cs->hw.avm.cfg_reg, 32);
+	  	return(0);
+	}
+	printk(KERN_INFO "HiSax: %s config irq:%d base:0x%X\n",
+		(cs->subtyp == AVM_FRITZ_PCI) ? "AVM Fritz!PCI" : "AVM Fritz!PnP",
+		cs->irq, cs->hw.avm.cfg_reg);
+
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Send_Data = &fill_hdlc;
+	cs->cardmsg = &AVM_card_msg;
+	ISACVersion(cs, (cs->subtyp == AVM_FRITZ_PCI) ? "AVM PCI:" : "AVM PnP:");
+	return (1);
+}
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/callc.c linux/drivers/isdn/hisax/callc.c
--- v2.3.3/linux/drivers/isdn/hisax/callc.c	Sun Dec 27 10:44:45 1998
+++ linux/drivers/isdn/hisax/callc.c	Sun May 23 10:03:41 1999
@@ -1,12 +1,61 @@
-/* $Id: callc.c,v 2.13 1998/02/12 23:07:16 keil Exp $
+/* $Id: callc.c,v 2.25 1999/01/02 11:17:20 keil Exp $
 
- * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author       Karsten Keil (keil@isdn4linux.de)
  *              based on the teles driver from Jan den Ouden
  *
+ *		This file is (c) under GNU PUBLIC LICENSE
+ *		For changes and modifications please read
+ *		../../../Documentation/isdn/HiSax.cert
+ *
  * Thanks to    Jan den Ouden
  *              Fritz Elfert
  *
  * $Log: callc.c,v $
+ * Revision 2.25  1999/01/02 11:17:20  keil
+ * Changes for 2.2
+ *
+ * Revision 2.24  1998/11/15 23:54:24  keil
+ * changes from 2.0
+ *
+ * Revision 2.23  1998/09/30 22:21:57  keil
+ * cosmetics
+ *
+ * Revision 2.22  1998/08/20 13:50:29  keil
+ * More support for hybrid modem (not working yet)
+ *
+ * Revision 2.21  1998/08/13 23:36:15  keil
+ * HiSax 3.1 - don't work stable with current LinkLevel
+ *
+ * Revision 2.20  1998/06/26 15:13:05  fritz
+ * Added handling of STAT_ICALL with incomplete CPN.
+ * Added AT&L for ttyI emulator.
+ * Added more locking stuff in tty_write.
+ *
+ * Revision 2.19  1998/05/25 14:08:06  keil
+ * HiSax 3.0
+ * fixed X.75 and leased line to work again
+ * Point2Point and fixed TEI are runtime options now:
+ *    hisaxctrl <id> 7 1  set PTP
+ *    hisaxctrl <id> 8 <TEIVALUE *2 >
+ *    set fixed TEI to TEIVALUE (0-63)
+ *
+ * Revision 2.18  1998/05/25 12:57:40  keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
+ * Revision 2.17  1998/04/15 16:46:06  keil
+ * RESUME support
+ *
+ * Revision 2.16  1998/04/10 10:35:17  paul
+ * fixed (silly?) warnings from egcs on Alpha.
+ *
+ * Revision 2.15  1998/03/19 13:18:37  keil
+ * Start of a CAPI like interface for supplementary Service
+ * first service: SUSPEND
+ *
+ * Revision 2.14  1998/03/07 22:56:54  tsbogend
+ * made HiSax working on Linux/Alpha
+ *
  * Revision 2.13  1998/02/12 23:07:16  keil
  * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
  *
@@ -60,12 +109,13 @@
 
 #define __NO_VERSION__
 #include "hisax.h"
+#include "../avmb1/capicmd.h"  /* this should be moved in a common place */
 
 #ifdef MODULE
 #define MOD_USE_COUNT ( GET_USE_COUNT (&__this_module))
 #endif				/* MODULE */
 
-const char *lli_revision = "$Revision: 2.13 $";
+const char *lli_revision = "$Revision: 2.25 $";
 
 extern struct IsdnCard cards[];
 extern int nrcards;
@@ -77,20 +127,18 @@
 
 static struct Fsm callcfsm =
 {NULL, 0, 0, NULL, NULL};
-static struct Fsm lcfsm =
-{NULL, 0, 0, NULL, NULL};
 
 static int chancount = 0;
 
-/* experimental REJECT after ALERTING for CALLBACK to beat the 4s delay */ 
-#define ALERT_REJECT 1
+/* experimental REJECT after ALERTING for CALLBACK to beat the 4s delay */
+#define ALERT_REJECT 0
 
 /* Value to delay the sending of the first B-channel paket after CONNECT
  * here is no value given by ITU, but experience shows that 300 ms will
  * work on many networks, if you or your other side is behind local exchanges
  * a greater value may be recommented. If the delay is to short the first paket
  * will be lost and autodetect on many comercial routers goes wrong !
- * You can adjust this value on runtime with 
+ * You can adjust this value on runtime with
  * hisaxctrl <id> 2 <value>
  * value is in milliseconds
  */
@@ -114,11 +162,12 @@
 #define  FLG_DO_HANGUP	13
 #define  FLG_DO_CONNECT	14
 #define  FLG_DO_ESTAB	15
+#define  FLG_RESUME	16
 
 /*
  * Because of callback it's a good idea to delay the shutdown of the d-channel
  */
-#define	DREL_TIMER_VALUE 10000
+#define	DREL_TIMER_VALUE 40000
 
 /*
  * Find card with given driverId
@@ -136,15 +185,30 @@
 	return (struct IsdnCardState *) 0;
 }
 
+int
+discard_queue(struct sk_buff_head *q)
+{
+	struct sk_buff *skb;
+	int ret=0;
+
+	while ((skb = skb_dequeue(q))) {
+		dev_kfree_skb(skb);
+		ret++;
+	}
+	return(ret);
+}
+
 static void
-link_debug(struct Channel *chanp, char *s, int direction)
+link_debug(struct Channel *chanp, int direction, char *fmt, ...)
 {
-	char tmp[100], tm[32];
+	va_list args;
+	char tmp[16];
 
-	jiftime(tm, jiffies);
-	sprintf(tmp, "%s Channel %d %s %s\n", tm, chanp->chan,
-		direction ? "LL->HL" : "HL->LL", s);
-	HiSax_putstatus(chanp->cs, tmp);
+	va_start(args, fmt);
+	sprintf(tmp, "Ch%d %s ", chanp->chan,
+		direction ? "LL->HL" : "HL->LL");
+	VHiSax_putstatus(chanp->cs, tmp, fmt, args);
+	va_end(args);
 }
 
 
@@ -244,72 +308,21 @@
 	"EV_RELEASE_ERR",
 };
 
-enum {
-	ST_LC_NULL,
-	ST_LC_ACTIVATE_WAIT,
-	ST_LC_DELAY,
-	ST_LC_ESTABLISH_WAIT,
-	ST_LC_CONNECTED,
-	ST_LC_FLUSH_WAIT,
-	ST_LC_RELEASE_WAIT,
-};
-
-#define LC_STATE_COUNT (ST_LC_RELEASE_WAIT+1)
-
-static char *strLcState[] =
-{
-	"ST_LC_NULL",
-	"ST_LC_ACTIVATE_WAIT",
-	"ST_LC_DELAY",
-	"ST_LC_ESTABLISH_WAIT",
-	"ST_LC_CONNECTED",
-	"ST_LC_FLUSH_WAIT",
-	"ST_LC_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 inline void
-lli_deliver_cause(struct Channel *chanp)
+lli_deliver_cause(struct Channel *chanp, isdn_ctrl *ic)
 {
-	isdn_ctrl ic;
-
 	if (chanp->proc->para.cause < 0)
 		return;
-	ic.driver = chanp->cs->myid;
-	ic.command = ISDN_STAT_CAUSE;
-	ic.arg = chanp->chan;
+	ic->driver = chanp->cs->myid;
+	ic->command = ISDN_STAT_CAUSE;
+	ic->arg = chanp->chan;
 	if (chanp->cs->protocol == ISDN_PTYPE_EURO)
-		sprintf(ic.parm.num, "E%02X%02X", chanp->proc->para.loc & 0x7f,
+		sprintf(ic->parm.num, "E%02X%02X", chanp->proc->para.loc & 0x7f,
 			chanp->proc->para.cause & 0x7f);
 	else
-		sprintf(ic.parm.num, "%02X%02X", chanp->proc->para.loc & 0x7f,
+		sprintf(ic->parm.num, "%02X%02X", chanp->proc->para.loc & 0x7f,
 			chanp->proc->para.cause & 0x7f);
-	chanp->cs->iif.statcallb(&ic);
+	chanp->cs->iif.statcallb(ic);
 }
 
 static void
@@ -321,13 +334,12 @@
 	if (chanp->leased) {
 		isdn_ctrl ic;
 		int ret;
-		char txt[32];
 
-		chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) chanp->chan);
+		chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan);
 		FsmChangeState(fi, ST_IN_WAIT_LL);
 		test_and_set_bit(FLG_CALL_REC, &chanp->Flags);
 		if (chanp->debug & 1)
-			link_debug(chanp, "STAT_ICALL_LEASED", 0);
+			link_debug(chanp, 0, "STAT_ICALL_LEASED");
 		ic.driver = chanp->cs->myid;
 		ic.command = ISDN_STAT_ICALL;
 		ic.arg = chanp->chan;
@@ -335,15 +347,13 @@
 		ic.parm.setup.si2 = 0;
 		ic.parm.setup.plan = 0;
 		ic.parm.setup.screen = 0;
-		sprintf(ic.parm.setup.eazmsn,"%d", chanp->chan + 1); 
+		sprintf(ic.parm.setup.eazmsn,"%d", chanp->chan + 1);
 		sprintf(ic.parm.setup.phone,"LEASED%d", chanp->cs->myid);
 		ret = chanp->cs->iif.statcallb(&ic);
-		if (chanp->debug & 1) {
-			sprintf(txt, "statcallb ret=%d", ret);
-			link_debug(chanp, txt, 1);
-		}
+		if (chanp->debug & 1)
+			link_debug(chanp, 1, "statcallb ret=%d", ret);
 		if (!ret) {
-			chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) chanp->chan);
+			chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan);
 			FsmChangeState(fi, ST_NULL);
 		}
 	} else if (fi->state == ST_WAIT_DSHUTDOWN)
@@ -371,28 +381,14 @@
 	FsmDelTimer(&chanp->dial_timer, 73);
 	chanp->l2_active_protocol = chanp->l2_protocol;
 	chanp->incoming = 0;
-	chanp->lc_b->l2_start = !0;
-	switch (chanp->l2_active_protocol) {
-		case (ISDN_PROTO_L2_X75I):
-			chanp->lc_b->l2_establish = !0;
-			break;
-		case (ISDN_PROTO_L2_HDLC):
-		case (ISDN_PROTO_L2_TRANS):
-			chanp->lc_b->l2_establish = 0;
-			break;
-		default:
-			printk(KERN_WARNING "lli_prep_dialout unknown protocol\n");
-			break;
-	}
 	if (test_bit(FLG_ESTAB_D, &chanp->Flags)) {
 		FsmEvent(fi, EV_DLEST, NULL);
 	} else {
 		chanp->Flags = 0;
+		if (EV_RESUME == event)
+			test_and_set_bit(FLG_RESUME, &chanp->Flags);
 		test_and_set_bit(FLG_START_D, &chanp->Flags);
-		if (chanp->leased) {
-			chanp->lc_d->l2_establish = 0;
-		}
-		FsmEvent(&chanp->lc_d->lcfi, EV_LC_ESTABLISH, NULL);
+		chanp->d_st->lli.l4l3(chanp->d_st, DL_ESTABLISH | REQUEST, NULL);
 	}
 }
 
@@ -400,14 +396,19 @@
 lli_do_dialout(struct FsmInst *fi, int event, void *arg)
 {
 	struct Channel *chanp = fi->userdata;
+	int ev;
 
 	FsmChangeState(fi, ST_OUT_DIAL);
-	chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) chanp->chan);
+	chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan);
+	if (test_and_clear_bit(FLG_RESUME, &chanp->Flags))
+		ev = CC_RESUME | REQUEST;
+	else
+		ev = CC_SETUP | REQUEST;
 	if (chanp->leased) {
 		FsmEvent(&chanp->fi, EV_SETUP_CNF, NULL);
 	} else {
 		test_and_set_bit(FLG_ESTAB_D, &chanp->Flags);
-		chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP_REQ, chanp);
+		chanp->d_st->lli.l4l3(chanp->d_st, ev, chanp);
 		test_and_set_bit(FLG_CALL_SEND, &chanp->Flags);
 	}
 }
@@ -421,14 +422,14 @@
 	FsmChangeState(fi, ST_WAIT_BCONN);
 	test_and_set_bit(FLG_LL_DCONN, &chanp->Flags);
 	if (chanp->debug & 1)
-		link_debug(chanp, "STAT_DCONN", 0);
+		link_debug(chanp, 0, "STAT_DCONN");
 	ic.driver = chanp->cs->myid;
 	ic.command = ISDN_STAT_DCONN;
 	ic.arg = chanp->chan;
 	chanp->cs->iif.statcallb(&ic);
 	init_b_st(chanp, 0);
 	test_and_set_bit(FLG_START_B, &chanp->Flags);
-	FsmEvent(&chanp->lc_b->lcfi, EV_LC_ESTABLISH, NULL);
+	chanp->b_st->lli.l4l3(chanp->b_st, DL_ESTABLISH | REQUEST, NULL);
 }
 
 static void
@@ -441,15 +442,19 @@
 	chanp->data_open = !0;
 	test_and_set_bit(FLG_CONNECT_B, &chanp->Flags);
 	if (chanp->debug & 1)
-		link_debug(chanp, "STAT_BCONN", 0);
+		link_debug(chanp, 0, "STAT_BCONN");
 	test_and_set_bit(FLG_LL_BCONN, &chanp->Flags);
 	ic.driver = chanp->cs->myid;
 	ic.command = ISDN_STAT_BCONN;
 	ic.arg = chanp->chan;
 	chanp->cs->iif.statcallb(&ic);
-	chanp->cs->cardmsg(chanp->cs, MDL_INFO_CONN, (void *) chanp->chan);
+	chanp->cs->cardmsg(chanp->cs, MDL_INFO_CONN, (void *) (long)chanp->chan);
 }
 
+/*
+ * RESUME
+ */
+
 /* incomming call */
 
 static void
@@ -463,14 +468,14 @@
 		test_and_set_bit(FLG_DO_CONNECT, &chanp->Flags);
 	else if (event == EV_HANGUP) {
 		test_and_set_bit(FLG_DO_HANGUP, &chanp->Flags);
-#ifdef ALERT_REJECT		
+#ifdef ALERT_REJECT
 		test_and_set_bit(FLG_DO_ALERT, &chanp->Flags);
 #endif
-	} 
+	}
 	if (test_bit(FLG_ESTAB_D, &chanp->Flags)) {
 		FsmEvent(fi, EV_DLEST, NULL);
 	} else if (!test_and_set_bit(FLG_START_D, &chanp->Flags))
-		FsmEvent(&chanp->lc_d->lcfi, EV_LC_ESTABLISH, NULL);
+		chanp->d_st->lli.l4l3(chanp->d_st, DL_ESTABLISH | REQUEST, NULL);
 }
 
 static void
@@ -479,9 +484,8 @@
 	struct Channel *chanp = fi->userdata;
 	isdn_ctrl ic;
 	int ret;
-	char txt[32];
 
-	chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) chanp->chan);
+	chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan);
 	/*
 	 * Report incoming calls only once to linklevel, use CallFlags
 	 * which is set to 3 with each broadcast message in isdnl1.c
@@ -491,7 +495,7 @@
 		FsmChangeState(fi, ST_IN_WAIT_LL);
 		test_and_set_bit(FLG_CALL_REC, &chanp->Flags);
 		if (chanp->debug & 1)
-			link_debug(chanp, "STAT_ICALL", 0);
+			link_debug(chanp, 0, "STAT_ICALL");
 		ic.driver = chanp->cs->myid;
 		ic.command = ISDN_STAT_ICALL;
 		ic.arg = chanp->chan;
@@ -501,22 +505,21 @@
 		 */
 		ic.parm.setup = chanp->proc->para.setup;
 		ret = chanp->cs->iif.statcallb(&ic);
-		if (chanp->debug & 1) {
-			sprintf(txt, "statcallb ret=%d", ret);
-			link_debug(chanp, txt, 1);
-		}
+		if (chanp->debug & 1)
+			link_debug(chanp, 1, "statcallb ret=%d", ret);
 		switch (ret) {
 			case 1:	/* OK, anybody likes this call */
 				FsmDelTimer(&chanp->drel_timer, 61);
 				if (test_bit(FLG_ESTAB_D, &chanp->Flags)) {
 					FsmChangeState(fi, ST_IN_ALERT_SEND);
 					test_and_set_bit(FLG_CALL_ALERT, &chanp->Flags);
-					chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING_REQ, chanp->proc);
+					chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc);
 				} else {
 					test_and_set_bit(FLG_DO_ALERT, &chanp->Flags);
 					FsmChangeState(fi, ST_IN_WAIT_D);
 					test_and_set_bit(FLG_START_D, &chanp->Flags);
-					FsmEvent(&chanp->lc_d->lcfi, EV_LC_ESTABLISH, NULL);
+					chanp->d_st->lli.l4l3(chanp->d_st,
+						DL_ESTABLISH | REQUEST, NULL);
 				}
 				break;
 			case 2:	/* Rejecting Call */
@@ -524,38 +527,36 @@
 				break;
 			case 0:	/* OK, nobody likes this call */
 			default:	/* statcallb problems */
-				chanp->d_st->lli.l4l3(chanp->d_st, CC_IGNORE, chanp->proc);
-				chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) chanp->chan);
+				chanp->d_st->lli.l4l3(chanp->d_st, CC_IGNORE | REQUEST, chanp->proc);
+				chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan);
 				FsmChangeState(fi, ST_NULL);
-#ifndef LAYER2_WATCHING
-				if (test_bit(FLG_ESTAB_D, &chanp->Flags))
+				if (test_bit(FLG_ESTAB_D, &chanp->Flags) &&
+					!test_bit(FLG_PTP, &chanp->d_st->l2.flag))
 					FsmRestartTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 61);
-#endif
 				break;
 		}
 	} else {
-		chanp->d_st->lli.l4l3(chanp->d_st, CC_IGNORE, chanp->proc);
-		chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) chanp->chan);
+		chanp->d_st->lli.l4l3(chanp->d_st, CC_IGNORE | REQUEST, chanp->proc);
+		chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan);
 		FsmChangeState(fi, ST_NULL);
-#ifndef LAYER2_WATCHING
-		if (test_bit(FLG_ESTAB_D, &chanp->Flags))
+		if (test_bit(FLG_ESTAB_D, &chanp->Flags) &&
+			!test_bit(FLG_PTP, &chanp->d_st->l2.flag))
 			FsmRestartTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 62);
-#endif
 	}
 }
 
 static void
 lli_establish_d(struct FsmInst *fi, int event, void *arg)
 {
-	/* This establish the D-channel for pending L3 messages 
-	 * without blocking th channel
+	/* This establish the D-channel for pending L3 messages
+	 * without blocking the channel
 	 */
 	struct Channel *chanp = fi->userdata;
 
 	test_and_set_bit(FLG_DO_ESTAB, &chanp->Flags);
 	FsmChangeState(fi, ST_IN_WAIT_D);
 	test_and_set_bit(FLG_START_D, &chanp->Flags);
-	FsmEvent(&chanp->lc_d->lcfi, EV_LC_ESTABLISH, NULL);
+	chanp->d_st->lli.l4l3(chanp->d_st, DL_ESTABLISH | REQUEST, NULL);
 }
 
 static void
@@ -573,27 +574,18 @@
 		!test_bit(FLG_DO_HANGUP, &chanp->Flags)) {
 		FsmChangeState(fi, ST_IN_WAIT_CONN_ACK);
 		test_and_clear_bit(FLG_DO_ALERT, &chanp->Flags);
-		chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP_RSP, chanp->proc);
+		chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | RESPONSE, chanp->proc);
 	} else if (test_and_clear_bit(FLG_DO_ALERT, &chanp->Flags)) {
 		if (test_bit(FLG_DO_HANGUP, &chanp->Flags))
 			FsmRestartTimer(&chanp->drel_timer, 40, EV_HANGUP, NULL, 63);
 		FsmChangeState(fi, ST_IN_ALERT_SEND);
 		test_and_set_bit(FLG_CALL_ALERT, &chanp->Flags);
-		chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING_REQ, chanp->proc);
+		chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc);
 	} else if (test_and_clear_bit(FLG_DO_HANGUP, &chanp->Flags)) {
 		FsmChangeState(fi, ST_WAIT_DRELEASE);
 		chanp->proc->para.cause = 0x15;		/* Call Rejected */
-		chanp->d_st->lli.l4l3(chanp->d_st, CC_REJECT_REQ, chanp->proc);
+		chanp->d_st->lli.l4l3(chanp->d_st, CC_REJECT | REQUEST, chanp->proc);
 		test_and_set_bit(FLG_DISC_SEND, &chanp->Flags);
-	} else if (test_and_clear_bit(FLG_DO_ESTAB, &chanp->Flags)) {
-		FsmChangeState(fi, ST_NULL);
-		chanp->Flags = 0;
-		test_and_set_bit(FLG_ESTAB_D, &chanp->Flags);
-		chanp->d_st->lli.l4l3(chanp->d_st, CC_ESTABLISH, chanp->proc);
-		chanp->proc = NULL;
-#ifndef LAYER2_WATCHING
-		FsmAddTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 60);
-#endif
 	}
 }
 
@@ -603,7 +595,7 @@
 	struct Channel *chanp = fi->userdata;
 
 	FsmChangeState(fi, ST_IN_WAIT_CONN_ACK);
-	chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP_RSP, chanp->proc);
+	chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | RESPONSE, chanp->proc);
 }
 
 static void
@@ -615,29 +607,26 @@
 	FsmChangeState(fi, ST_WAIT_BCONN);
 	test_and_set_bit(FLG_LL_DCONN, &chanp->Flags);
 	if (chanp->debug & 1)
-		link_debug(chanp, "STAT_DCONN", 0);
+		link_debug(chanp, 0, "STAT_DCONN");
 	ic.driver = chanp->cs->myid;
 	ic.command = ISDN_STAT_DCONN;
 	ic.arg = chanp->chan;
 	chanp->cs->iif.statcallb(&ic);
 	chanp->l2_active_protocol = chanp->l2_protocol;
 	chanp->incoming = !0;
-	chanp->lc_b->l2_start = 0;
-	switch (chanp->l2_active_protocol) {
-		case (ISDN_PROTO_L2_X75I):
-			chanp->lc_b->l2_establish = !0;
-			break;
-		case (ISDN_PROTO_L2_HDLC):
-		case (ISDN_PROTO_L2_TRANS):
-			chanp->lc_b->l2_establish = 0;
-			break;
-		default:
-			printk(KERN_WARNING "bchannel unknown protocol\n");
-			break;
-	}
 	init_b_st(chanp, !0);
 	test_and_set_bit(FLG_START_B, &chanp->Flags);
-	FsmEvent(&chanp->lc_b->lcfi, EV_LC_ESTABLISH, NULL);
+	chanp->b_st->lli.l4l3(chanp->b_st, DL_ESTABLISH | REQUEST, NULL);
+}
+
+/* Call suspend */
+
+static void
+lli_suspend(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	chanp->d_st->lli.l4l3(chanp->d_st, CC_SUSPEND | REQUEST, chanp->proc);
 }
 
 /* Call clearing */
@@ -651,7 +640,7 @@
 	FsmChangeState(fi, ST_WAIT_DRELEASE);
 	if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) {
 		if (chanp->debug & 1)
-			link_debug(chanp, "STAT_BHUP", 0);
+			link_debug(chanp, 0, "STAT_BHUP");
 		ic.driver = chanp->cs->myid;
 		ic.command = ISDN_STAT_BHUP;
 		ic.arg = chanp->chan;
@@ -660,7 +649,7 @@
 	if (test_and_clear_bit(FLG_START_B, &chanp->Flags))
 		release_b_st(chanp);
 	chanp->proc->para.cause = 0x10;		/* Normal Call Clearing */
-	chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT_REQ, chanp->proc);
+	chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT | REQUEST, chanp->proc);
 	test_and_set_bit(FLG_DISC_SEND, &chanp->Flags);
 }
 
@@ -670,22 +659,22 @@
 	struct Channel *chanp = fi->userdata;
 
 	FsmDelTimer(&chanp->drel_timer, 62);
-#ifdef LAYER2_WATCHING
-	FsmChangeState(fi, ST_NULL);
-#else
-	if (!test_bit(FLG_TWO_DCHAN, &chanp->cs->HW_Flags)) {
-		if (chanp->chan) {
-			if (chanp->cs->channel[0].fi.state != ST_NULL)
-				return;
-		} else {
-			if (chanp->cs->channel[1].fi.state != ST_NULL)
-				return;
+	if (test_bit(FLG_PTP, &chanp->d_st->l2.flag)) {
+		FsmChangeState(fi, ST_NULL);
+	} else {
+		if (!test_bit(FLG_TWO_DCHAN, &chanp->cs->HW_Flags)) {
+			if (chanp->chan) {
+				if (chanp->cs->channel[0].fi.state != ST_NULL)
+					return;
+			} else {
+				if (chanp->cs->channel[1].fi.state != ST_NULL)
+					return;
+			}
 		}
+		FsmChangeState(fi, ST_WAIT_DSHUTDOWN);
+		test_and_clear_bit(FLG_ESTAB_D, &chanp->Flags);
+		chanp->d_st->lli.l4l3(chanp->d_st, DL_RELEASE | REQUEST, NULL);
 	}
-	FsmChangeState(fi, ST_WAIT_DSHUTDOWN);
-	test_and_clear_bit(FLG_ESTAB_D, &chanp->Flags);
-	FsmEvent(&chanp->lc_d->lcfi, EV_LC_RELEASE, NULL);
-#endif
 }
 
 static void
@@ -694,22 +683,20 @@
 	struct Channel *chanp = fi->userdata;
 	isdn_ctrl ic;
 
-	if (test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags)) {
-		if (chanp->debug & 1)
-			link_debug(chanp, "STAT_DHUP", 0);
-		lli_deliver_cause(chanp);
-		ic.driver = chanp->cs->myid;
-		ic.command = ISDN_STAT_DHUP;
-		ic.arg = chanp->chan;
-		chanp->cs->iif.statcallb(&ic);
-	}
+	test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags);
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_DHUP");
+	lli_deliver_cause(chanp, &ic);
+	ic.driver = chanp->cs->myid;
+	ic.command = ISDN_STAT_DHUP;
+	ic.arg = chanp->chan;
+	chanp->cs->iif.statcallb(&ic);
 	FsmChangeState(fi, ST_NULL);
 	chanp->Flags = 0;
 	test_and_set_bit(FLG_ESTAB_D, &chanp->Flags);
-#ifndef LAYER2_WATCHING
-	FsmAddTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 60);
-#endif
-	chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) chanp->chan);
+	if (!test_bit(FLG_PTP, &chanp->d_st->l2.flag))
+		FsmAddTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 60);
+	chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan);
 }
 
 static void
@@ -720,7 +707,7 @@
 	FsmChangeState(fi, ST_NULL);
 	chanp->Flags = 0;
 	FsmDelTimer(&chanp->drel_timer, 63);
-	chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) chanp->chan);
+	chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan);
 }
 
 static void
@@ -731,7 +718,7 @@
 	chanp->data_open = 0;
 	FsmChangeState(fi, ST_WAIT_BRELEASE);
 	test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags);
-	FsmEvent(&chanp->lc_b->lcfi, EV_LC_RELEASE, NULL);
+	chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
 }
 
 static void
@@ -746,7 +733,7 @@
 	FsmChangeState(fi, ST_WAIT_DRELEASE);
 	if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) {
 		if (chanp->debug & 1)
-			link_debug(chanp, "STAT_BHUP", 0);
+			link_debug(chanp, 0, "STAT_BHUP");
 		ic.driver = chanp->cs->myid;
 		ic.command = ISDN_STAT_BHUP;
 		ic.arg = chanp->chan;
@@ -760,20 +747,20 @@
 		sprintf(ic.parm.num, "L0010");
 		chanp->cs->iif.statcallb(&ic);
 		if (chanp->debug & 1)
-			link_debug(chanp, "STAT_DHUP", 0);
+			link_debug(chanp, 0, "STAT_DHUP");
 		ic.driver = chanp->cs->myid;
 		ic.command = ISDN_STAT_DHUP;
 		ic.arg = chanp->chan;
 		chanp->cs->iif.statcallb(&ic);
 		FsmChangeState(fi, ST_WAIT_DSHUTDOWN);
 		test_and_clear_bit(FLG_ESTAB_D, &chanp->Flags);
-		FsmEvent(&chanp->lc_d->lcfi, EV_LC_RELEASE, NULL);
+		chanp->d_st->lli.l4l3(chanp->d_st, DL_RELEASE | REQUEST, NULL);
 	} else {
 		if (test_and_clear_bit(FLG_DO_HANGUP, &chanp->Flags))
 			chanp->proc->para.cause = 0x15;		/* Call Reject */
 		else
 			chanp->proc->para.cause = 0x10;		/* Normal Call Clearing */
-		chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT_REQ, chanp->proc);
+		chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT | REQUEST, chanp->proc);
 		test_and_set_bit(FLG_DISC_SEND, &chanp->Flags);
 	}
 }
@@ -788,7 +775,7 @@
 	chanp->data_open = 0;
 	if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) {
 		if (chanp->debug & 1)
-			link_debug(chanp, "STAT_BHUP", 0);
+			link_debug(chanp, 0, "STAT_BHUP");
 		ic.driver = chanp->cs->myid;
 		ic.command = ISDN_STAT_BHUP;
 		ic.arg = chanp->chan;
@@ -808,7 +795,7 @@
 	test_and_set_bit(FLG_DISC_REC, &chanp->Flags);
 	FsmChangeState(fi, ST_WAIT_BREL_DISC);
 	test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags);
-	FsmEvent(&chanp->lc_b->lcfi, EV_LC_RELEASE, NULL);
+	chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
 }
 
 static void
@@ -821,12 +808,11 @@
 	FsmChangeState(fi, ST_NULL);
 	test_and_set_bit(FLG_REL_REC, &chanp->Flags);
 	if (test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags)) {
-		chanp->lc_b->l2_establish = 0;	/* direct reset in lc_b->lcfi */
-		FsmEvent(&chanp->lc_b->lcfi, EV_LC_RELEASE, NULL);
+		chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
 	}
 	if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) {
 		if (chanp->debug & 1)
-			link_debug(chanp, "STAT_BHUP", 0);
+			link_debug(chanp, 0, "STAT_BHUP");
 		ic.driver = chanp->cs->myid;
 		ic.command = ISDN_STAT_BHUP;
 		ic.arg = chanp->chan;
@@ -834,17 +820,13 @@
 	}
 	if (test_and_clear_bit(FLG_START_B, &chanp->Flags))
 		release_b_st(chanp);
-	if (test_bit(FLG_LL_DCONN, &chanp->Flags) ||
-		test_bit(FLG_CALL_SEND, &chanp->Flags) ||
-		test_bit(FLG_CALL_ALERT, &chanp->Flags)) {
-		if (chanp->debug & 1)
-			link_debug(chanp, "STAT_DHUP", 0);
-		lli_deliver_cause(chanp);
-		ic.driver = chanp->cs->myid;
-		ic.command = ISDN_STAT_DHUP;
-		ic.arg = chanp->chan;
-		chanp->cs->iif.statcallb(&ic);
-	}
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_DHUP");
+	lli_deliver_cause(chanp, &ic);
+	ic.driver = chanp->cs->myid;
+	ic.command = ISDN_STAT_DHUP;
+	ic.arg = chanp->chan;
+	chanp->cs->iif.statcallb(&ic);
 	test_and_clear_bit(FLG_DISC_SEND, &chanp->Flags);
 	test_and_clear_bit(FLG_CALL_REC, &chanp->Flags);
 	test_and_clear_bit(FLG_CALL_ALERT, &chanp->Flags);
@@ -862,12 +844,11 @@
 	chanp->data_open = 0;
 	FsmChangeState(fi, ST_NULL);
 	if (test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags)) {
-		chanp->lc_b->l2_establish = 0;	/* direct reset in lc_b->lcfi */
-		FsmEvent(&chanp->lc_b->lcfi, EV_LC_RELEASE, NULL);
+		chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
 	}
 	if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) {
 		if (chanp->debug & 1)
-			link_debug(chanp, "STAT_BHUP", 0);
+			link_debug(chanp, 0, "STAT_BHUP");
 		ic.driver = chanp->cs->myid;
 		ic.command = ISDN_STAT_BHUP;
 		ic.arg = chanp->chan;
@@ -875,17 +856,13 @@
 	}
 	if (test_and_clear_bit(FLG_START_B, &chanp->Flags))
 		release_b_st(chanp);
-	if (test_bit(FLG_LL_DCONN, &chanp->Flags) ||
-		test_bit(FLG_CALL_SEND, &chanp->Flags) ||
-		test_bit(FLG_CALL_ALERT, &chanp->Flags)) {
-		if (chanp->debug & 1)
-			link_debug(chanp, "STAT_DHUP", 0);
-		lli_deliver_cause(chanp);
-		ic.driver = chanp->cs->myid;
-		ic.command = ISDN_STAT_DHUP;
-		ic.arg = chanp->chan;
-		chanp->cs->iif.statcallb(&ic);
-	}
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_DHUP");
+	lli_deliver_cause(chanp, &ic);
+	ic.driver = chanp->cs->myid;
+	ic.command = ISDN_STAT_DHUP;
+	ic.arg = chanp->chan;
+	chanp->cs->iif.statcallb(&ic);
 	test_and_clear_bit(FLG_DISC_SEND, &chanp->Flags);
 	test_and_clear_bit(FLG_CALL_REC, &chanp->Flags);
 	test_and_clear_bit(FLG_CALL_ALERT, &chanp->Flags);
@@ -905,7 +882,7 @@
 	test_and_set_bit(FLG_DISC_REC, &chanp->Flags);
 	if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) {
 		if (chanp->debug & 1)
-			link_debug(chanp, "STAT_BHUP", 0);
+			link_debug(chanp, 0, "STAT_BHUP");
 		ic.driver = chanp->cs->myid;
 		ic.command = ISDN_STAT_BHUP;
 		ic.arg = chanp->chan;
@@ -913,21 +890,17 @@
 	}
 	if (test_and_clear_bit(FLG_START_B, &chanp->Flags))
 		release_b_st(chanp);
-	if (test_bit(FLG_LL_DCONN, &chanp->Flags) ||
-		test_bit(FLG_CALL_SEND, &chanp->Flags) ||
-		test_bit(FLG_CALL_ALERT, &chanp->Flags)) {
-		if (chanp->debug & 1)
-			link_debug(chanp, "STAT_DHUP", 0);
-		lli_deliver_cause(chanp);
-		ic.driver = chanp->cs->myid;
-		ic.command = ISDN_STAT_DHUP;
-		ic.arg = chanp->chan;
-		chanp->cs->iif.statcallb(&ic);
-	}
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_DHUP");
+	lli_deliver_cause(chanp, &ic);
+	ic.driver = chanp->cs->myid;
+	ic.command = ISDN_STAT_DHUP;
+	ic.arg = chanp->chan;
+	chanp->cs->iif.statcallb(&ic);
 	test_and_clear_bit(FLG_CALL_ALERT, &chanp->Flags);
 	test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags);
 	test_and_clear_bit(FLG_CALL_SEND, &chanp->Flags);
-	chanp->d_st->lli.l4l3(chanp->d_st, CC_RELEASE_REQ, chanp->proc);
+	chanp->d_st->lli.l4l3(chanp->d_st, CC_RELEASE | REQUEST, chanp->proc);
 }
 
 /* processing charge info */
@@ -953,14 +926,14 @@
 	isdn_ctrl ic;
 
 	if (chanp->debug & 1)
-		link_debug(chanp, "STAT_NODCH", 0);
+		link_debug(chanp, 0, "STAT_NODCH");
 	ic.driver = chanp->cs->myid;
 	ic.command = ISDN_STAT_NODCH;
 	ic.arg = chanp->chan;
 	chanp->cs->iif.statcallb(&ic);
 	chanp->Flags = 0;
 	FsmChangeState(fi, ST_NULL);
-	FsmEvent(&chanp->lc_d->lcfi, EV_LC_RELEASE, NULL);
+	chanp->d_st->lli.l4l3(chanp->d_st, DL_RELEASE | REQUEST, NULL);
 }
 
 static void
@@ -970,7 +943,7 @@
 	isdn_ctrl ic;
 
 	if (chanp->debug & 1)
-		link_debug(chanp, "STAT_DHUP", 0);
+		link_debug(chanp, 0, "STAT_DHUP");
 	ic.driver = chanp->cs->myid;
 	ic.command = ISDN_STAT_DHUP;
 	ic.arg = chanp->chan;
@@ -984,15 +957,15 @@
 	isdn_ctrl ic;
 
 	if (chanp->debug & 1)
-		link_debug(chanp, "STAT_DHUP", 0);
+		link_debug(chanp, 0, "STAT_DHUP");
 	ic.driver = chanp->cs->myid;
 	ic.command = ISDN_STAT_DHUP;
 	ic.arg = chanp->chan;
 	chanp->cs->iif.statcallb(&ic);
-	chanp->d_st->lli.l4l3(chanp->d_st, CC_DLRL, chanp->proc);
+	chanp->d_st->lli.l4l3(chanp->d_st, CC_DLRL | REQUEST, chanp->proc);
 	chanp->Flags = 0;
 	FsmChangeState(fi, ST_NULL);
-	FsmEvent(&chanp->lc_d->lcfi, EV_LC_RELEASE, NULL);
+	chanp->d_st->lli.l4l3(chanp->d_st, DL_RELEASE | REQUEST, NULL);
 }
 
 static void
@@ -1004,7 +977,7 @@
 	FsmChangeState(fi, ST_NULL);
 	test_and_clear_bit(FLG_CALL_SEND, &chanp->Flags);
 	if (chanp->debug & 1)
-		link_debug(chanp, "STAT_DHUP", 0);
+		link_debug(chanp, 0, "STAT_DHUP");
 	ic.driver = chanp->cs->myid;
 	ic.command = ISDN_STAT_DHUP;
 	ic.arg = chanp->chan;
@@ -1019,15 +992,14 @@
 	isdn_ctrl ic;
 
 	FsmChangeState(fi, ST_WAIT_DRELEASE);
-	if (test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags)) {
-		if (chanp->debug & 1)
-			link_debug(chanp, "STAT_DHUP", 0);
-		lli_deliver_cause(chanp);
-		ic.driver = chanp->cs->myid;
-		ic.command = ISDN_STAT_DHUP;
-		ic.arg = chanp->chan;
-		chanp->cs->iif.statcallb(&ic);
-	}
+	test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags);
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_DHUP");
+	lli_deliver_cause(chanp, &ic);
+	ic.driver = chanp->cs->myid;
+	ic.command = ISDN_STAT_DHUP;
+	ic.arg = chanp->chan;
+	chanp->cs->iif.statcallb(&ic);
 	test_and_set_bit(FLG_DISC_SEND, &chanp->Flags);	/* DISCONN was sent from L3 */
 }
 
@@ -1038,15 +1010,14 @@
 	isdn_ctrl ic;
 
 	FsmChangeState(fi, ST_WAIT_DRELEASE);
-	if (test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags)) {
-		if (chanp->debug & 1)
-			link_debug(chanp, "STAT_DHUP", 0);
-		lli_deliver_cause(chanp);
-		ic.driver = chanp->cs->myid;
-		ic.command = ISDN_STAT_DHUP;
-		ic.arg = chanp->chan;
-		chanp->cs->iif.statcallb(&ic);
-	}
+	test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags);
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_DHUP");
+	lli_deliver_cause(chanp, &ic);
+	ic.driver = chanp->cs->myid;
+	ic.command = ISDN_STAT_DHUP;
+	ic.arg = chanp->chan;
+	chanp->cs->iif.statcallb(&ic);
 	test_and_set_bit(FLG_DISC_SEND, &chanp->Flags);	/* DISCONN was sent from L3 */
 }
 
@@ -1059,12 +1030,11 @@
 	chanp->data_open = 0;
 	FsmChangeState(fi, ST_NULL);
 	if (test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags)) {
-		chanp->lc_b->l2_establish = 0;	/* direct reset in lc_b->lcfi */
-		FsmEvent(&chanp->lc_b->lcfi, EV_LC_RELEASE, NULL);
+		chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
 	}
 	if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) {
 		if (chanp->debug & 1)
-			link_debug(chanp, "STAT_BHUP", 0);
+			link_debug(chanp, 0, "STAT_BHUP");
 		ic.driver = chanp->cs->myid;
 		ic.command = ISDN_STAT_BHUP;
 		ic.arg = chanp->chan;
@@ -1084,33 +1054,33 @@
 		chanp->cs->iif.statcallb(&ic);
 		chanp->Flags = 0;
 	} else {
-		if (test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags)) {
-			if (chanp->debug & 1)
-				link_debug(chanp, "STAT_DHUP", 0);
-			if (chanp->cs->protocol == ISDN_PTYPE_EURO) {
-				chanp->proc->para.cause = 0x2f;
-				chanp->proc->para.loc = 0;
-			} else {
-				chanp->proc->para.cause = 0x70;
-				chanp->proc->para.loc = 0;
-			}
-			lli_deliver_cause(chanp);
-			ic.driver = chanp->cs->myid;
-			ic.command = ISDN_STAT_DHUP;
-			ic.arg = chanp->chan;
-			chanp->cs->iif.statcallb(&ic);
+		test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags);
+		if (chanp->debug & 1)
+			link_debug(chanp, 0, "STAT_DHUP");
+		if (chanp->cs->protocol == ISDN_PTYPE_EURO) {
+			chanp->proc->para.cause = 0x2f;
+			chanp->proc->para.loc = 0;
+		} else {
+			chanp->proc->para.cause = 0x70;
+			chanp->proc->para.loc = 0;
 		}
-		chanp->d_st->lli.l4l3(chanp->d_st, CC_DLRL, chanp->proc);
+		lli_deliver_cause(chanp, &ic);
+		ic.driver = chanp->cs->myid;
+		ic.command = ISDN_STAT_DHUP;
+		ic.arg = chanp->chan;
+		chanp->cs->iif.statcallb(&ic);
+		chanp->d_st->lli.l4l3(chanp->d_st, CC_DLRL | REQUEST, chanp->proc);
 		chanp->Flags = 0;
-		FsmEvent(&chanp->lc_d->lcfi, EV_LC_RELEASE, NULL);
+		chanp->d_st->lli.l4l3(chanp->d_st, DL_RELEASE | REQUEST, NULL);
 	}
-	chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) chanp->chan);
+	chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan);
 }
 
 /* *INDENT-OFF* */
 static struct FsmNode fnlist[] HISAX_INITDATA =
 {
 	{ST_NULL,		EV_DIAL,		lli_prep_dialout},
+	{ST_NULL,		EV_RESUME,		lli_prep_dialout},
 	{ST_NULL,		EV_SETUP_IND,		lli_deliver_call},
 	{ST_NULL,		EV_SHUTDOWN_D,		lli_shutdown_d},
 	{ST_NULL,		EV_DLRL,		lli_go_null},
@@ -1162,6 +1132,7 @@
 	{ST_WAIT_BCONN,		EV_CINF,		lli_charge_info},
 	{ST_ACTIVE,		EV_CINF,		lli_charge_info},
 	{ST_ACTIVE,		EV_BC_REL,		lli_released_bchan},
+	{ST_ACTIVE,		EV_SUSPEND,		lli_suspend},
 	{ST_ACTIVE,		EV_HANGUP,		lli_disconn_bchan},
 	{ST_ACTIVE,		EV_DISCONNECT_IND,	lli_release_bchan},
 	{ST_ACTIVE,		EV_RELEASE_CNF,		lli_received_d_relcnf},
@@ -1195,6 +1166,7 @@
 	{ST_WAIT_DSHUTDOWN,	EV_DLRL,		lli_go_null},
 	{ST_WAIT_DSHUTDOWN,	EV_DLEST,		lli_d_established},
 	{ST_WAIT_DSHUTDOWN,	EV_DIAL,		lli_prep_dialout},
+	{ST_WAIT_DSHUTDOWN,	EV_RESUME,		lli_prep_dialout},
 	{ST_WAIT_DSHUTDOWN,	EV_SETUP_IND,		lli_deliver_call},
 };
 /* *INDENT-ON* */
@@ -1202,153 +1174,6 @@
 
 #define FNCOUNT (sizeof(fnlist)/sizeof(struct FsmNode))
 
-static void
-lc_activate_l1(struct FsmInst *fi, int event, void *arg)
-{
-	struct LcFsm *lf = fi->userdata;
-
-	FsmDelTimer(&lf->act_timer, 50);
-	FsmChangeState(fi, ST_LC_ACTIVATE_WAIT);
-	/* This timeout is to avoid a hang if no L1 activation is possible */
-	FsmAddTimer(&lf->act_timer, 30000, EV_LC_TIMER, NULL, 50);
-	lf->st->ma.manl1(lf->st, PH_ACTIVATE_REQ, NULL);
-}
-
-static void
-lc_activated_from_l1(struct FsmInst *fi, int event, void *arg)
-{
-	struct LcFsm *lf = fi->userdata;
-
-	if (lf->l2_establish)
-		FsmChangeState(fi, ST_LC_DELAY);
-	else {
-		FsmChangeState(fi, ST_LC_CONNECTED);
-		lf->lccall(lf, LC_ESTABLISH, NULL);
-	}
-}
-
-static void
-lc_l1_activated(struct FsmInst *fi, int event, void *arg)
-{
-	struct LcFsm *lf = fi->userdata;
-
-	FsmDelTimer(&lf->act_timer, 50);
-	FsmChangeState(fi, ST_LC_DELAY);
-	/* This timer is needed for delay the first paket on a channel
-	   to be shure that the other side is ready too */
-	if (lf->delay)
-		FsmAddTimer(&lf->act_timer, lf->delay, EV_LC_TIMER, NULL, 51);
-	else
-		FsmEvent(fi, EV_LC_TIMER, NULL);
-}
-
-static void
-lc_start_l2(struct FsmInst *fi, int event, void *arg)
-{
-	struct LcFsm *lf = fi->userdata;
-
-/*	if (!lf->st->l1.act_state)
-		lf->st->l1.act_state = 2;
-*/	if (lf->l2_establish) {
-		FsmChangeState(fi, ST_LC_ESTABLISH_WAIT);
-		if (lf->l2_start)
-			lf->st->ma.manl2(lf->st, DL_ESTABLISH, NULL);
-	} else {
-		FsmChangeState(fi, ST_LC_CONNECTED);
-		lf->lccall(lf, LC_ESTABLISH, NULL);
-	}
-}
-
-static void
-lc_connected(struct FsmInst *fi, int event, void *arg)
-{
-	struct LcFsm *lf = fi->userdata;
-
-	FsmDelTimer(&lf->act_timer, 50);
-	FsmChangeState(fi, ST_LC_CONNECTED);
-	lf->lccall(lf, LC_ESTABLISH, NULL);
-}
-
-static void
-lc_release_l2(struct FsmInst *fi, int event, void *arg)
-{
-	struct LcFsm *lf = fi->userdata;
-
-	if (lf->l2_establish) {
-		FsmChangeState(fi, ST_LC_RELEASE_WAIT);
-		lf->st->ma.manl2(lf->st, DL_RELEASE, NULL);
-	} else {
-		FsmChangeState(fi, ST_LC_NULL);
-		lf->st->ma.manl1(lf->st, PH_DEACTIVATE_REQ, NULL);
-		lf->lccall(lf, LC_RELEASE, NULL);
-	}
-}
-
-static void
-lc_l2_released(struct FsmInst *fi, int event, void *arg)
-{
-	struct LcFsm *lf = fi->userdata;
-
-	FsmChangeState(fi, ST_LC_RELEASE_WAIT);
-	FsmDelTimer(&lf->act_timer, 51);
-	/* This delay is needed for send out the UA frame before
-	 * PH_DEACTIVATE the interface
-	 */
-	FsmAddTimer(&lf->act_timer, 20, EV_LC_TIMER, NULL, 54);
-}
-
-static void
-lc_release_l1(struct FsmInst *fi, int event, void *arg)
-{
-	struct LcFsm *lf = fi->userdata;
-
-	FsmDelTimer(&lf->act_timer, 54);
-	FsmChangeState(fi, ST_LC_NULL);
-	lf->st->ma.manl1(lf->st, PH_DEACTIVATE_REQ, NULL);
-	lf->lccall(lf, LC_RELEASE, NULL);
-}
-
-static void
-lc_l1_deactivated(struct FsmInst *fi, int event, void *arg)
-{
-	struct LcFsm *lf = fi->userdata;
-
-	FsmDelTimer(&lf->act_timer, 54);
-	FsmChangeState(fi, ST_LC_NULL);
-	lf->lccall(lf, LC_RELEASE, NULL);
-}
-/* *INDENT-OFF* */
-static struct FsmNode LcFnList[] HISAX_INITDATA =
-{
-	{ST_LC_NULL,  		EV_LC_ESTABLISH,	lc_activate_l1},
-	{ST_LC_NULL,  		EV_LC_PH_ACTIVATE,	lc_activated_from_l1},
-	{ST_LC_NULL,  		EV_LC_DL_ESTABLISH,	lc_connected},
-	{ST_LC_ACTIVATE_WAIT,	EV_LC_PH_ACTIVATE,	lc_l1_activated},
-	{ST_LC_ACTIVATE_WAIT,	EV_LC_TIMER,		lc_release_l1},
-	{ST_LC_ACTIVATE_WAIT,  	EV_LC_PH_DEACTIVATE,	lc_l1_deactivated},
-	{ST_LC_DELAY,  		EV_LC_ESTABLISH,	lc_start_l2},
-	{ST_LC_DELAY,		EV_LC_TIMER,		lc_start_l2},
-	{ST_LC_DELAY,		EV_LC_DL_ESTABLISH,	lc_connected},
-	{ST_LC_DELAY,  		EV_LC_PH_DEACTIVATE,	lc_l1_deactivated},
-	{ST_LC_ESTABLISH_WAIT,	EV_LC_DL_ESTABLISH,	lc_connected},
-	{ST_LC_ESTABLISH_WAIT,	EV_LC_RELEASE,		lc_release_l1},
-	{ST_LC_ESTABLISH_WAIT,	EV_LC_DL_RELEASE,	lc_release_l1},
-	{ST_LC_ESTABLISH_WAIT,  EV_LC_PH_DEACTIVATE,	lc_l1_deactivated},
-	{ST_LC_CONNECTED,	EV_LC_ESTABLISH,	lc_connected},
-	{ST_LC_CONNECTED,	EV_LC_RELEASE,		lc_release_l2},
-	{ST_LC_CONNECTED,	EV_LC_DL_RELEASE,	lc_l2_released},
-	{ST_LC_CONNECTED,  	EV_LC_PH_DEACTIVATE,	lc_l1_deactivated},
-	{ST_LC_FLUSH_WAIT,	EV_LC_TIMER,		lc_release_l2},
-	{ST_LC_FLUSH_WAIT,  	EV_LC_PH_DEACTIVATE,	lc_l1_deactivated},
-	{ST_LC_RELEASE_WAIT,	EV_LC_DL_RELEASE,	lc_release_l1},
-	{ST_LC_RELEASE_WAIT,	EV_LC_TIMER,		lc_release_l1},
-	{ST_LC_FLUSH_WAIT,  	EV_LC_PH_DEACTIVATE,	lc_l1_deactivated},
-};
-/* *INDENT-ON* */
-
-
-#define LC_FN_COUNT (sizeof(LcFnList)/sizeof(struct FsmNode))
-
 HISAX_INITFUNC(void
 CallcNew(void))
 {
@@ -1357,18 +1182,11 @@
 	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);
 }
 
@@ -1384,75 +1202,10 @@
 			break;
 		case (ISDN_PROTO_L2_HDLC):
 		case (ISDN_PROTO_L2_TRANS):
+//		case (ISDN_PROTO_L2_MODEM):
 			releasestack_transl2(st);
 			break;
 	}
-	/* Reset B-Channel Statemachine */
-	FsmDelTimer(&chanp->lc_b->act_timer, 79);
-	FsmChangeState(&chanp->lc_b->lcfi, ST_LC_NULL);
-}
-
-static void
-dc_l1man(struct PStack *st, int pr, void *arg)
-{
-	struct Channel *chanp;
-
-	chanp = (struct Channel *) st->lli.userdata;
-	switch (pr) {
-		case (PH_ACTIVATE_CNF):
-		case (PH_ACTIVATE_IND):
-			FsmEvent(&chanp->lc_d->lcfi, EV_LC_PH_ACTIVATE, NULL);
-			break;
-		case (PH_DEACTIVATE_IND):
-			FsmEvent(&chanp->lc_d->lcfi, EV_LC_PH_DEACTIVATE, NULL);
-			break;
-	}
-}
-
-static void
-dc_l2man(struct PStack *st, int pr, void *arg)
-{
-	struct Channel *chanp = (struct Channel *) st->lli.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
-bc_l1man(struct PStack *st, int pr, void *arg)
-{
-	struct Channel *chanp = (struct Channel *) st->lli.userdata;
-
-	switch (pr) {
-		case (PH_ACTIVATE_IND):
-		case (PH_ACTIVATE_CNF):
-			FsmEvent(&chanp->lc_b->lcfi, EV_LC_PH_ACTIVATE, NULL);
-			break;
-		case (PH_DEACTIVATE_IND):
-			FsmEvent(&chanp->lc_b->lcfi, EV_LC_PH_DEACTIVATE, NULL);
-			break;
-	}
-}
-
-static void
-bc_l2man(struct PStack *st, int pr, void *arg)
-{
-	struct Channel *chanp = (struct Channel *) st->lli.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;
-	}
 }
 
 struct Channel
@@ -1471,7 +1224,7 @@
 			return (chanp);
 		chanp++;
 		i++;
-	}		
+	}
 	return (NULL);
 }
 
@@ -1491,62 +1244,85 @@
 			return (1);
 		chanp++;
 		i++;
-	}		
+	}
 	return (0);
 }
 
 static void
-ll_handler(struct l3_process *pc, int pr, void *arg)
+dchan_l3l4(struct PStack *st, int pr, void *arg)
 {
+	struct l3_process *pc = arg;
+	struct IsdnCardState *cs = st->l1.hardware;
 	struct Channel *chanp;
-	char tmp[64], tm[32];
+	int event;
+
+	switch (pr) {
+		case (DL_ESTABLISH | INDICATION):
+			event = EV_DLEST;
+			break;
+		case (DL_RELEASE | INDICATION):
+			event = EV_DLRL;
+			break;
+		default:
+			event = -1;
+			break;
+	}
+	if (event >= 0) {
+		int i;
 
-	if (pr == CC_SETUP_IND) {
+		chanp = st->lli.userdata;
+		if (test_bit(FLG_TWO_DCHAN, &cs->HW_Flags))
+			i = 1;
+		else
+			i = 0;
+		while (i < 2) {
+			FsmEvent(&chanp->fi, event, NULL);
+			chanp++;
+			i++;
+		}
+		return;
+	} else if (pr == (CC_SETUP | INDICATION)) {
 		if (!(chanp = selectfreechannel(pc->st))) {
-			pc->st->lli.l4l3(pc->st, CC_DLRL, pc);
-			return;
+			pc->st->lli.l4l3(pc->st, CC_DLRL | REQUEST, pc);
 		} else {
 			chanp->proc = pc;
 			pc->chan = chanp;
 			FsmEvent(&chanp->fi, EV_SETUP_IND, NULL);
-			return;
-		}
-	} else if (pr == CC_ESTABLISH) {
-		if (is_activ(pc->st)) {
-			pc->st->lli.l4l3(pc->st, CC_ESTABLISH, pc);
-			return;
-		} else if (!(chanp = selectfreechannel(pc->st))) {
-			pc->st->lli.l4l3(pc->st, CC_DLRL, pc);
-			return;
-		} else {
-			chanp->proc = pc;
-			FsmEvent(&chanp->fi, EV_ESTABLISH, NULL);
-			return;
 		}
-
-			
+		return;
 	}
-	chanp = pc->chan;
+	if (!(chanp = pc->chan))
+		return;
+
 	switch (pr) {
-		case (CC_DISCONNECT_IND):
+		case (CC_DISCONNECT | INDICATION):
 			FsmEvent(&chanp->fi, EV_DISCONNECT_IND, NULL);
 			break;
-		case (CC_RELEASE_CNF):
+		case (CC_RELEASE | CONFIRM):
+			FsmEvent(&chanp->fi, EV_RELEASE_CNF, NULL);
+			break;
+		case (CC_SUSPEND | CONFIRM):
+			FsmEvent(&chanp->fi, EV_RELEASE_CNF, NULL);
+			break;
+		case (CC_RESUME | CONFIRM):
+			FsmEvent(&chanp->fi, EV_SETUP_CNF, NULL);
+			break;
+		case (CC_RESUME_ERR):
 			FsmEvent(&chanp->fi, EV_RELEASE_CNF, NULL);
 			break;
-		case (CC_RELEASE_IND):
+		case (CC_RELEASE | INDICATION):
 			FsmEvent(&chanp->fi, EV_RELEASE_IND, NULL);
 			break;
-		case (CC_SETUP_COMPLETE_IND):
+		case (CC_SETUP_COMPL | INDICATION):
 			FsmEvent(&chanp->fi, EV_SETUP_CMPL_IND, NULL);
 			break;
-		case (CC_SETUP_CNF):
+		case (CC_SETUP | CONFIRM):
 			FsmEvent(&chanp->fi, EV_SETUP_CNF, NULL);
 			break;
-		case (CC_INFO_CHARGE):
+		case (CC_CHARGE | INDICATION):
 			FsmEvent(&chanp->fi, EV_CINF, NULL);
 			break;
-		case (CC_NOSETUP_RSP_ERR):
+		case (CC_NOSETUP_RSP):
 			FsmEvent(&chanp->fi, EV_NOSETUP_RSP, NULL);
 			break;
 		case (CC_SETUP_ERR):
@@ -1558,15 +1334,14 @@
 		case (CC_RELEASE_ERR):
 			FsmEvent(&chanp->fi, EV_RELEASE_ERR, NULL);
 			break;
-		case (CC_PROCEEDING_IND):
-		case (CC_ALERTING_IND):
+		case (CC_PROCEEDING | INDICATION):
+		case (CC_ALERTING | INDICATION):
 			break;
 		default:
 			if (chanp->debug & 0x800) {
-				jiftime(tm, jiffies);
-				sprintf(tmp, "%s Channel %d L3->L4 unknown primitiv %d\n",
-					tm, chanp->chan, pr);
-				HiSax_putstatus(chanp->cs, tmp);
+				HiSax_putstatus(chanp->cs, "Ch",
+					"%d L3->L4 unknown primitiv %x",
+					chanp->chan, pr);
 			}
 	}
 }
@@ -1576,7 +1351,7 @@
 {
 	struct PStack *st = chanp->d_st;
 	struct IsdnCardState *cs = chanp->cs;
-	char tmp[128];
+	char tmp[16];
 
 	HiSax_addlist(cs, st);
 	setstack_HiSax(st, cs);
@@ -1590,95 +1365,50 @@
 	st->l2.window = 1;
 	st->l2.T200 = 1000;	/* 1000 milliseconds  */
 	st->l2.N200 = 3;	/* try 3 times        */
-	if (st->protocol == ISDN_PTYPE_1TR6)
-		st->l2.T203 = 10000;	/* 10000 milliseconds */
+	st->l2.T203 = 10000;	/* 10000 milliseconds */
+	if (test_bit(FLG_TWO_DCHAN, &cs->HW_Flags))
+		sprintf(tmp, "DCh%d Q.921 ", chanp->chan);
 	else
-		st->l2.T203 = 10000;	/* 5000 milliseconds  */
-	
-	sprintf(tmp, "Channel %d q.921", chanp->chan);
+		sprintf(tmp, "DCh Q.921 ");
 	setstack_isdnl2(st, tmp);
-	setstack_isdnl3(st, chanp);
+	setstack_l3dc(st, chanp);
 	st->lli.userdata = chanp;
 	st->lli.l2writewakeup = NULL;
-	st->l3.l3l4 = ll_handler;
-	st->l1.l1man = dc_l1man;
-	st->l2.l2man = dc_l2man;
+	st->l3.l3l4 = dchan_l3l4;
 }
 
 static void
-callc_debug(struct FsmInst *fi, char *s)
+callc_debug(struct FsmInst *fi, char *fmt, ...)
 {
-	char str[80], tm[32];
+	va_list args;
 	struct Channel *chanp = fi->userdata;
+	char tmp[16];
 
-	jiftime(tm, jiffies);
-	sprintf(str, "%s Channel %d callc %s\n", tm, chanp->chan, s);
-	HiSax_putstatus(chanp->cs, 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 dc %s\n", tm, lf->ch->chan, s);
-	HiSax_putstatus(lf->ch->cs, 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 bc %s\n", tm, lf->ch->chan, s);
-	HiSax_putstatus(lf->ch->cs, str);
+	va_start(args, fmt);
+	sprintf(tmp, "Ch%d callc ", chanp->chan);
+	VHiSax_putstatus(chanp->cs, tmp, fmt, args);
+	va_end(args);
 }
 
 static void
-lccall_d(struct LcFsm *lf, int pr, void *arg)
-{
-	struct IsdnCardState *cs = lf->st->l1.hardware;
-	struct Channel *chanp;
-	int i;
-
-	if (test_bit(FLG_TWO_DCHAN, &cs->HW_Flags)) {
-		chanp = lf->ch;
-		i = 1;
-	} else {
-		chanp = cs->channel;
-		i = 0;
-	}
-	while (i < 2) {
-		switch (pr) {
-			case (LC_ESTABLISH):
-				FsmEvent(&chanp->fi, EV_DLEST, NULL);
-				break;
-			case (LC_RELEASE):
-				FsmEvent(&chanp->fi, EV_DLRL, NULL);
-				break;
-		}
-		chanp++;
-		i++;
-	}
+dummy_pstack(struct PStack *st, int pr, void *arg) {
+	printk(KERN_WARNING"call to dummy_pstack pr=%04x arg %lx\n", pr, (long)arg);
 }
 
 static void
-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;
-	}
+init_PStack(struct PStack **stp) {
+	*stp = kmalloc(sizeof(struct PStack), GFP_ATOMIC);
+	(*stp)->next = NULL;
+	(*stp)->l1.l1l2 = dummy_pstack;
+	(*stp)->l1.l1hw = dummy_pstack;
+	(*stp)->l1.l1tei = dummy_pstack;
+	(*stp)->l2.l2tei = dummy_pstack;
+	(*stp)->l2.l2l1 = dummy_pstack;
+	(*stp)->l2.l2l3 = dummy_pstack;
+	(*stp)->l3.l3l2 = dummy_pstack;
+	(*stp)->l3.l3l4 = dummy_pstack;
+	(*stp)->lli.l4l3 = dummy_pstack;
+	(*stp)->ma.layer = dummy_pstack;
 }
 
 static void
@@ -1693,9 +1423,8 @@
 	chanp->debug = 0;
 	chanp->Flags = 0;
 	chanp->leased = 0;
-	chanp->b_st = kmalloc(sizeof(struct PStack), GFP_ATOMIC);
-	chanp->b_st->next = NULL;
-
+	init_PStack(&chanp->b_st);
+	chanp->b_st->l1.delay = DEFAULT_B_DELAY;
 	chanp->fi.fsm = &callcfsm;
 	chanp->fi.state = ST_NULL;
 	chanp->fi.debug = 0;
@@ -1704,41 +1433,14 @@
 	FsmInitTimer(&chanp->fi, &chanp->dial_timer);
 	FsmInitTimer(&chanp->fi, &chanp->drel_timer);
 	if (!chan || test_bit(FLG_TWO_DCHAN, &csta->HW_Flags)) {
-		chanp->d_st = kmalloc(sizeof(struct PStack), GFP_ATOMIC);
+		init_PStack(&chanp->d_st);
+		if (chan)
+			csta->channel->d_st->next = chanp->d_st;
 		chanp->d_st->next = NULL;
 		init_d_st(chanp);
-		chanp->lc_d = kmalloc(sizeof(struct LcFsm), GFP_ATOMIC);
-		chanp->lc_d->lcfi.fsm = &lcfsm;
-		chanp->lc_d->lcfi.state = ST_LC_NULL;
-		chanp->lc_d->lcfi.debug = 0;
-		chanp->lc_d->lcfi.userdata = chanp->lc_d;
-		chanp->lc_d->lcfi.printdebug = lc_debug;
-		chanp->lc_d->type = LC_D;
-		chanp->lc_d->delay = 0;
-		chanp->lc_d->ch = chanp;
-		chanp->lc_d->st = chanp->d_st;
-		chanp->lc_d->l2_establish = !0;
-		chanp->lc_d->l2_start = !0;
-		chanp->lc_d->lccall = lccall_d;
-		FsmInitTimer(&chanp->lc_d->lcfi, &chanp->lc_d->act_timer);
 	} else {
 		chanp->d_st = csta->channel->d_st;
-		chanp->lc_d = csta->channel->lc_d;
 	}
-	chanp->lc_b = kmalloc(sizeof(struct LcFsm), GFP_ATOMIC);
-	chanp->lc_b->lcfi.fsm = &lcfsm;
-	chanp->lc_b->lcfi.state = ST_LC_NULL;
-	chanp->lc_b->lcfi.debug = 0;
-	chanp->lc_b->lcfi.userdata = chanp->lc_b;
-	chanp->lc_b->lcfi.printdebug = dlc_debug;
-	chanp->lc_b->type = LC_B;
-	chanp->lc_b->delay = DEFAULT_B_DELAY;
-	chanp->lc_b->ch = chanp;
-	chanp->lc_b->st = chanp->b_st;
-	chanp->lc_b->l2_establish = !0;
-	chanp->lc_b->l2_start = !0;
-	chanp->lc_b->lccall = lccall_b;
-	FsmInitTimer(&chanp->lc_b->lcfi, &chanp->lc_b->act_timer);
 	chanp->data_open = 0;
 }
 
@@ -1749,11 +1451,12 @@
 	init_chan(0, csta);
 	init_chan(1, csta);
 	printk(KERN_INFO "HiSax: 2 channels added\n");
-#ifdef LAYER2_WATCHING
-	printk(KERN_INFO "LAYER2 ESTABLISH\n");
-	test_and_set_bit(FLG_START_D, &csta->channel->Flags);
-	FsmEvent(&csta->channel->lc_d->lcfi, EV_LC_ESTABLISH, NULL);
-#endif
+	if (test_bit(FLG_PTP, &csta->channel->d_st->l2.flag)) {
+		printk(KERN_INFO "LAYER2 WATCHING ESTABLISH\n");
+		test_and_set_bit(FLG_START_D, &csta->channel->Flags);
+		csta->channel->d_st->lli.l4l3(csta->channel->d_st,
+			DL_ESTABLISH | REQUEST, NULL);
+	}
 	return (2);
 }
 
@@ -1779,8 +1482,6 @@
 	for (i = 0; i < 2; i++) {
 		FsmDelTimer(&csta->channel[i].drel_timer, 74);
 		FsmDelTimer(&csta->channel[i].dial_timer, 75);
-		FsmDelTimer(&csta->channel[i].lc_d->act_timer, 77);
-		FsmDelTimer(&csta->channel[i].lc_b->act_timer, 76);
 		if (i || test_bit(FLG_TWO_DCHAN, &csta->HW_Flags))
 			release_d_st(csta->channel + i);
 		if (csta->channel[i].b_st) {
@@ -1790,18 +1491,8 @@
 			csta->channel[i].b_st = NULL;
 		} else
 			printk(KERN_WARNING "CallcFreeChan b_st ch%d allready freed\n", i);
-		if (csta->channel[i].lc_b) {
-			kfree(csta->channel[i].lc_b);
-			csta->channel[i].b_st = NULL;
-		}
 		if (i || test_bit(FLG_TWO_DCHAN, &csta->HW_Flags)) {
 			release_d_st(csta->channel + i);
-			FsmDelTimer(&csta->channel[i].lc_d->act_timer, 77);
-			if (csta->channel[i].lc_d) {
-				kfree(csta->channel[i].lc_d);
-				csta->channel[i].d_st = NULL;
-			} else
-				printk(KERN_WARNING "CallcFreeChan lc_d ch%d allready freed\n", i);
 		} else
 			csta->channel[i].d_st = NULL;
 	}
@@ -1814,15 +1505,23 @@
 	struct sk_buff *skb = arg;
 
 	switch (pr) {
-		case (DL_DATA):
+		case (DL_DATA  | INDICATION):
 			if (chanp->data_open)
 				chanp->cs->iif.rcvcallb_skb(chanp->cs->myid, chanp->chan, skb);
 			else {
 				dev_kfree_skb(skb);
 			}
 			break;
+		case (DL_ESTABLISH | INDICATION):
+		case (DL_ESTABLISH | CONFIRM):
+			FsmEvent(&chanp->fi, EV_BC_EST, NULL);
+			break;
+		case (DL_RELEASE | INDICATION):
+		case (DL_RELEASE | CONFIRM):
+			FsmEvent(&chanp->fi, EV_BC_REL, NULL);
+			break;
 		default:
-			printk(KERN_WARNING "lldata_handler unknown primitive %d\n",
+			printk(KERN_WARNING "lldata_handler unknown primitive %x\n",
 			       pr);
 			break;
 	}
@@ -1835,22 +1534,24 @@
 	struct sk_buff *skb = arg;
 
 	switch (pr) {
-		case (PH_DATA_IND):
+		case (PH_DATA | INDICATION):
 			if (chanp->data_open)
 				chanp->cs->iif.rcvcallb_skb(chanp->cs->myid, chanp->chan, skb);
 			else {
-				if (chanp->lc_b->lcfi.state == ST_LC_DELAY)
-					FsmEvent(&chanp->lc_b->lcfi, EV_LC_DL_ESTABLISH, NULL);
-				if (chanp->data_open) {
-					link_debug(chanp, "channel now open", 0);
-					chanp->cs->iif.rcvcallb_skb(chanp->cs->myid,
-						chanp->chan, skb);
-				} else
-					dev_kfree_skb(skb);
+				link_debug(chanp, 0, "channel not open");
+				dev_kfree_skb(skb);
 			}
 			break;
+		case (PH_ACTIVATE | INDICATION):
+		case (PH_ACTIVATE | CONFIRM):
+			FsmEvent(&chanp->fi, EV_BC_EST, NULL);
+			break;
+		case (PH_DEACTIVATE | INDICATION):
+		case (PH_DEACTIVATE | CONFIRM):
+			FsmEvent(&chanp->fi, EV_BC_REL, NULL);
+			break;
 		default:
-			printk(KERN_WARNING "lltrans_handler unknown primitive %d\n",
+			printk(KERN_WARNING "lltrans_handler unknown primitive %x\n",
 			       pr);
 			break;
 	}
@@ -1865,7 +1566,7 @@
 	ic.driver = chanp->cs->myid;
 	ic.command = ISDN_STAT_BSENT;
 	ic.arg = chanp->chan;
-	ic.parm.length = len;
+//	ic.parm.length = len;
 	chanp->cs->iif.statcallb(&ic);
 }
 
@@ -1874,10 +1575,27 @@
 {
 	struct PStack *st = chanp->b_st;
 	struct IsdnCardState *cs = chanp->cs;
-	char tmp[128];
+	char tmp[16];
 
 	st->l1.hardware = cs;
-	chanp->bcs->mode = 2;
+	if (chanp->leased)
+		st->l1.bc = chanp->chan & 1;
+	else
+		st->l1.bc = chanp->proc->para.bchannel - 1;
+	switch (chanp->l2_active_protocol) {
+		case (ISDN_PROTO_L2_X75I):
+		case (ISDN_PROTO_L2_HDLC):
+			st->l1.mode = L1_MODE_HDLC;
+			break;
+		case (ISDN_PROTO_L2_TRANS):
+			st->l1.mode = L1_MODE_TRANS;
+			break;
+#if 0
+		case (ISDN_PROTO_L2_MODEM):
+			st->l1.mode = L1_MODE_MODEM;
+			break;
+#endif
+	}
 	if (chanp->bcs->BC_SetStack(st, chanp->bcs))
 		return (-1);
 	st->l2.flag = 0;
@@ -1892,50 +1610,87 @@
 	st->l3.debug = 0;
 	switch (chanp->l2_active_protocol) {
 		case (ISDN_PROTO_L2_X75I):
-			sprintf(tmp, "Channel %d x.75", chanp->chan);
+			sprintf(tmp, "Ch%d X.75", chanp->chan);
 			setstack_isdnl2(st, tmp);
+			setstack_l3bc(st, chanp);
 			st->l2.l2l3 = lldata_handler;
-			st->l1.l1man = bc_l1man;
-			st->l2.l2man = bc_l2man;
 			st->lli.userdata = chanp;
 			st->lli.l1writewakeup = NULL;
 			st->lli.l2writewakeup = ll_writewakeup;
 			st->l2.l2m.debug = chanp->debug & 16;
 			st->l2.debug = chanp->debug & 64;
-			st->ma.manl2(st, MDL_NOTEIPROC, NULL);
-			st->l1.mode = L1_MODE_HDLC;
-			if (chanp->leased)
-				st->l1.bc = chanp->chan & 1;
-			else
-				st->l1.bc = chanp->proc->para.bchannel - 1;
 			break;
 		case (ISDN_PROTO_L2_HDLC):
-			st->l1.l1l2 = lltrans_handler;
-			st->l1.l1man = bc_l1man;
-			st->lli.userdata = chanp;
-			st->lli.l1writewakeup = ll_writewakeup;
-			st->l1.mode = L1_MODE_HDLC;
-			if (chanp->leased)
-				st->l1.bc = chanp->chan & 1;
-			else
-				st->l1.bc = chanp->proc->para.bchannel - 1;
-			break;
 		case (ISDN_PROTO_L2_TRANS):
+//		case (ISDN_PROTO_L2_MODEM):
 			st->l1.l1l2 = lltrans_handler;
-			st->l1.l1man = bc_l1man;
 			st->lli.userdata = chanp;
 			st->lli.l1writewakeup = ll_writewakeup;
-			st->l1.mode = L1_MODE_TRANS;
-			if (chanp->leased)
-				st->l1.bc = chanp->chan & 1;
-			else
-				st->l1.bc = chanp->proc->para.bchannel - 1;
+			setstack_transl2(st);
+			setstack_l3bc(st, chanp);
 			break;
 	}
 	return (0);
 }
 
 static void
+leased_l4l3(struct PStack *st, int pr, void *arg)
+{
+	struct Channel *chanp = (struct Channel *) st->lli.userdata;
+	struct sk_buff *skb = arg;
+
+	switch (pr) {
+		case (DL_DATA | REQUEST):
+			link_debug(chanp, 0, "leased line d-channel DATA");
+			dev_kfree_skb(skb);
+			break;
+		case (DL_ESTABLISH | REQUEST):
+			st->l2.l2l1(st, PH_ACTIVATE | REQUEST, NULL);
+			break;
+		case (DL_RELEASE | REQUEST):
+			break;
+		default:
+			printk(KERN_WARNING "transd_l4l3 unknown primitive %x\n",
+			       pr);
+			break;
+	}
+}
+
+static void
+leased_l1l2(struct PStack *st, int pr, void *arg)
+{
+	struct Channel *chanp = (struct Channel *) st->lli.userdata;
+	struct sk_buff *skb = arg;
+	int i,event = EV_DLRL;
+
+	switch (pr) {
+		case (PH_DATA | INDICATION):
+			link_debug(chanp, 0, "leased line d-channel DATA");
+			dev_kfree_skb(skb);
+			break;
+		case (PH_ACTIVATE | INDICATION):
+		case (PH_ACTIVATE | CONFIRM):
+			event = EV_DLEST;
+		case (PH_DEACTIVATE | INDICATION):
+		case (PH_DEACTIVATE | CONFIRM):
+			if (test_bit(FLG_TWO_DCHAN, &chanp->cs->HW_Flags))
+				i = 1;
+			else
+				i = 0;
+			while (i < 2) {
+				FsmEvent(&chanp->fi, event, NULL);
+				chanp++;
+				i++;
+			}
+			break;
+		default:
+			printk(KERN_WARNING
+				"transd_l1l2 unknown primitive %x\n", pr);
+			break;
+	}
+}
+
+static void
 channel_report(struct Channel *chanp)
 {
 }
@@ -1953,13 +1708,70 @@
 		chanp[i].b_st->l2.l2m.debug = debugflags & 0x10;
 		chanp[i].d_st->l2.debug = debugflags & 0x20;
 		chanp[i].b_st->l2.debug = debugflags & 0x40;
-		chanp[i].lc_d->lcfi.debug = debugflags & 0x80;
-		chanp[i].lc_b->lcfi.debug = debugflags & 0x100;
+		chanp[i].d_st->l3.l3m.debug = debugflags & 0x80;
+		chanp[i].b_st->l3.l3m.debug = debugflags & 0x100;
 		chanp[i].b_st->ma.tei_m.debug = debugflags & 0x200;
 		chanp[i].b_st->ma.debug = debugflags & 0x200;
 		chanp[i].d_st->l1.l1m.debug = debugflags & 0x1000;
+		chanp[i].b_st->l1.l1m.debug = debugflags & 0x2000;
+	}
+	if (debugflags & 4)
+		csta->debug |= DEB_DLOG_HEX;
+	else
+		csta->debug &= ~DEB_DLOG_HEX;
+}
+
+static char tmpbuf[256];
+
+static void
+capi_debug(struct Channel *chanp, capi_msg *cm)
+{
+	char *t = tmpbuf;
+
+	t += sprintf(tmpbuf, "%d CAPIMSG", chanp->chan);
+	t += QuickHex(t, (u_char *)cm, (cm->Length>50)? 50: cm->Length);
+	t--;
+	*t= 0;
+	HiSax_putstatus(chanp->cs, "Ch", "%d CAPIMSG %s", chanp->chan, tmpbuf);
+}
+
+void
+lli_got_fac_req(struct Channel *chanp, capi_msg *cm) {
+	if ((cm->para[0] != 3) || (cm->para[1] != 0))
+		return;
+	if (cm->para[2]<3)
+		return;
+	if (cm->para[4] != 0)
+		return;
+	switch(cm->para[3]) {
+		case 4: /* Suspend */
+			if (cm->para[5]) {
+				strncpy(chanp->setup.phone, &cm->para[5], cm->para[5] +1);
+				FsmEvent(&chanp->fi, EV_SUSPEND, cm);
+			}
+			break;
+		case 5: /* Resume */
+			if (cm->para[5]) {
+				strncpy(chanp->setup.phone, &cm->para[5], cm->para[5] +1);
+				if (chanp->fi.state == ST_NULL) {
+					FsmEvent(&chanp->fi, EV_RESUME, cm);
+				} else {
+					FsmDelTimer(&chanp->dial_timer, 72);
+					FsmAddTimer(&chanp->dial_timer, 80, EV_RESUME, cm, 73);
+				}
+			}
+			break;
+	}
+}
+
+void
+lli_got_manufacturer(struct Channel *chanp, struct IsdnCardState *cs, capi_msg *cm) {
+	if ((cs->typ == ISDN_CTYPE_ELSA) || (cs->typ == ISDN_CTYPE_ELSA_PNP) ||
+		(cs->typ == ISDN_CTYPE_ELSA_PCI)) {
+		if (cs->hw.elsa.MFlag) {
+			cs->cardmsg(cs, CARD_AUX_IND, cm->para);
+		}
 	}
-	csta->dlogflag = debugflags & 4;
 }
 
 int
@@ -1967,9 +1779,9 @@
 {
 	struct IsdnCardState *csta = hisax_findcard(ic->driver);
 	struct Channel *chanp;
-	char tmp[128];
 	int i;
-	unsigned int num;
+	u_int num;
+	u_long adr;
 
 	if (!csta) {
 		printk(KERN_ERR
@@ -1977,32 +1789,32 @@
 		       ic->command, ic->driver);
 		return -ENODEV;
 	}
+
 	switch (ic->command) {
 		case (ISDN_CMD_SETEAZ):
 			chanp = csta->channel + ic->arg;
-			if (chanp->debug & 1) {
-				sprintf(tmp, "SETEAZ card %d %s", csta->cardnr + 1,
-					ic->parm.num);
-				link_debug(chanp, tmp, 1);
-			}
 			break;
+
 		case (ISDN_CMD_SETL2):
 			chanp = csta->channel + (ic->arg & 0xff);
-			if (chanp->debug & 1) {
-				sprintf(tmp, "SETL2 card %d %ld", csta->cardnr + 1,
-					ic->arg >> 8);
-				link_debug(chanp, tmp, 1);
-			}
+			if (chanp->debug & 1)
+				link_debug(chanp, 1, "SETL2 card %d %ld",
+					csta->cardnr + 1, ic->arg >> 8);
 			chanp->l2_protocol = ic->arg >> 8;
 			break;
+		case (ISDN_CMD_SETL3):
+			chanp = csta->channel + (ic->arg & 0xff);
+			if (chanp->debug & 1)
+				link_debug(chanp, 1, "SETL3 card %d %ld",
+					csta->cardnr + 1, ic->arg >> 8);
+			chanp->l3_protocol = ic->arg >> 8;
+			break;
 		case (ISDN_CMD_DIAL):
 			chanp = csta->channel + (ic->arg & 0xff);
-			if (chanp->debug & 1) {
-				sprintf(tmp, "DIAL %s -> %s (%d,%d)",
+			if (chanp->debug & 1)
+				link_debug(chanp, 1, "DIAL %s -> %s (%d,%d)",
 					ic->parm.setup.eazmsn, ic->parm.setup.phone,
-				 ic->parm.setup.si1, ic->parm.setup.si2);
-				link_debug(chanp, tmp, 1);
-			}
+					ic->parm.setup.si1, ic->parm.setup.si2);
 			chanp->setup = ic->parm.setup;
 			if (!strcmp(chanp->setup.eazmsn, "0"))
 				chanp->setup.eazmsn[0] = '\0';
@@ -2018,57 +1830,54 @@
 		case (ISDN_CMD_ACCEPTB):
 			chanp = csta->channel + ic->arg;
 			if (chanp->debug & 1)
-				link_debug(chanp, "ACCEPTB", 1);
+				link_debug(chanp, 1, "ACCEPTB");
 			FsmEvent(&chanp->fi, EV_ACCEPTB, NULL);
 			break;
 		case (ISDN_CMD_ACCEPTD):
 			chanp = csta->channel + ic->arg;
 			if (chanp->debug & 1)
-				link_debug(chanp, "ACCEPTD", 1);
+				link_debug(chanp, 1, "ACCEPTD");
 			FsmEvent(&chanp->fi, EV_ACCEPTD, NULL);
 			break;
 		case (ISDN_CMD_HANGUP):
 			chanp = csta->channel + ic->arg;
 			if (chanp->debug & 1)
-				link_debug(chanp, "HANGUP", 1);
+				link_debug(chanp, 1, "HANGUP");
 			FsmEvent(&chanp->fi, EV_HANGUP, NULL);
 			break;
-		case (ISDN_CMD_SUSPEND):
-			chanp = csta->channel + ic->arg;
-			if (chanp->debug & 1) {
-				sprintf(tmp, "SUSPEND %s", ic->parm.num);
-				link_debug(chanp, tmp, 1);
-			}
-			FsmEvent(&chanp->fi, EV_SUSPEND, ic);
-			break;
-		case (ISDN_CMD_RESUME):
+		case (CAPI_PUT_MESSAGE):
 			chanp = csta->channel + ic->arg;
-			if (chanp->debug & 1) {
-				sprintf(tmp, "RESUME %s", ic->parm.num);
-				link_debug(chanp, tmp, 1);
+			if (chanp->debug & 1)
+				capi_debug(chanp, &ic->parm.cmsg);
+			if (ic->parm.cmsg.Length < 8)
+				break;
+			switch(ic->parm.cmsg.Command) {
+				case CAPI_FACILITY:
+					if (ic->parm.cmsg.Subcommand == CAPI_REQ)
+						lli_got_fac_req(chanp, &ic->parm.cmsg);
+					break;
+				case CAPI_MANUFACTURER:
+					if (ic->parm.cmsg.Subcommand == CAPI_REQ)
+						lli_got_manufacturer(chanp, csta, &ic->parm.cmsg);
+					break;
+				default:
+					break;
 			}
-			FsmEvent(&chanp->fi, EV_RESUME, ic);
 			break;
 		case (ISDN_CMD_LOCK):
 			HiSax_mod_inc_use_count();
 #ifdef MODULE
-			if (csta->channel[0].debug & 0x400) {
-				jiftime(tmp, jiffies);
-				i = strlen(tmp);
-				sprintf(tmp + i, "   LOCK modcnt %d\n", MOD_USE_COUNT);
-				HiSax_putstatus(csta, tmp);
-			}
+			if (csta->channel[0].debug & 0x400)
+				HiSax_putstatus(csta, "   LOCK ", "modcnt %x",
+					MOD_USE_COUNT);
 #endif				/* MODULE */
 			break;
 		case (ISDN_CMD_UNLOCK):
 			HiSax_mod_dec_use_count();
 #ifdef MODULE
-			if (csta->channel[0].debug & 0x400) {
-				jiftime(tmp, jiffies);
-				i = strlen(tmp);
-				sprintf(tmp + i, " UNLOCK modcnt %d\n", MOD_USE_COUNT);
-				HiSax_putstatus(csta, tmp);
-			}
+			if (csta->channel[0].debug & 0x400)
+				HiSax_putstatus(csta, " UNLOCK ", "modcnt %x",
+					MOD_USE_COUNT);
 #endif				/* MODULE */
 			break;
 		case (ISDN_CMD_IOCTL):
@@ -2081,19 +1890,19 @@
 				case (1):
 					num = *(unsigned int *) ic->parm.num;
 					distr_debug(csta, num);
-					sprintf(tmp, "debugging flags card %d set to %x\n",
+					printk(KERN_DEBUG "HiSax: debugging flags card %d set to %x\n",
 						csta->cardnr + 1, num);
-					HiSax_putstatus(csta, tmp);
-					printk(KERN_DEBUG "HiSax: %s", tmp);
+					HiSax_putstatus(csta, "debugging flags ",
+						"card %d set to %x", csta->cardnr + 1, num);
 					break;
 				case (2):
-					num = *(unsigned int *) ic->parm.num; 
-					csta->channel[0].lc_b->delay = num;
-					csta->channel[1].lc_b->delay = num;
-					sprintf(tmp, "delay card %d set to %d ms\n",
+					num = *(unsigned int *) ic->parm.num;
+					csta->channel[0].b_st->l1.delay = num;
+					csta->channel[1].b_st->l1.delay = num;
+					HiSax_putstatus(csta, "delay ", "card %d set to %d ms",
+						csta->cardnr + 1, num);
+					printk(KERN_DEBUG "HiSax: delay card %d set to %d ms\n",
 						csta->cardnr + 1, num);
-					HiSax_putstatus(csta, tmp);
-					printk(KERN_DEBUG "HiSax: %s", tmp);
 					break;
 				case (3):
 					for (i = 0; i < *(unsigned int *) ic->parm.num; i++)
@@ -2106,47 +1915,91 @@
 				case (5):	/* set card in leased mode */
 					num = *(unsigned int *) ic->parm.num;
 					if ((num <1) || (num > 2)) {
-						sprintf(tmp, "Set LEASED wrong channel %d\n",
+						HiSax_putstatus(csta, "Set LEASED ",
+							"wrong channel %d", num);
+						printk(KERN_WARNING "HiSax: Set LEASED wrong channel %d\n",
 							num);
-						HiSax_putstatus(csta, tmp);
-						printk(KERN_WARNING "HiSax: %s", tmp);
 					} else {
 						num--;
-						csta->channel[num].leased = 1;
-						csta->channel[num].lc_d->l2_establish = 0;
-						sprintf(tmp, "card %d channel %d set leased mode\n",
+						chanp = csta->channel +num;
+						chanp->leased = 1;
+						HiSax_putstatus(csta, "Card",
+							"%d channel %d set leased mode\n",
 							csta->cardnr + 1, num + 1);
-						HiSax_putstatus(csta, tmp);
-						FsmEvent(&csta->channel[num].lc_d->lcfi, EV_LC_ESTABLISH, NULL);
+						chanp->d_st->l1.l1l2 = leased_l1l2;
+						chanp->d_st->lli.l4l3 = leased_l4l3;
+						chanp->d_st->lli.l4l3(chanp->d_st,
+							DL_ESTABLISH | REQUEST, NULL);
 					}
 					break;
 				case (6):	/* set B-channel test loop */
 					num = *(unsigned int *) ic->parm.num;
 					if (csta->stlist)
-						csta->stlist->ma.manl1(csta->stlist,
-							PH_TESTLOOP_REQ, (void *) num);
+						csta->stlist->l2.l2l1(csta->stlist,
+							PH_TESTLOOP | REQUEST, (void *) (long)num);
+					break;
+				case (7):	/* set card in PTP mode */
+					num = *(unsigned int *) ic->parm.num;
+					if (test_bit(FLG_TWO_DCHAN, &csta->HW_Flags)) {
+						printk(KERN_ERR "HiSax PTP mode only with one TEI possible\n");
+					} else if (num) {
+						test_and_set_bit(FLG_PTP, &csta->channel[0].d_st->l2.flag);
+						test_and_set_bit(FLG_FIXED_TEI, &csta->channel[0].d_st->l2.flag);
+						csta->channel[0].d_st->l2.tei = 0;
+						HiSax_putstatus(csta, "set card ", "in PTP mode");
+						printk(KERN_DEBUG "HiSax: set card in PTP mode\n");
+						printk(KERN_INFO "LAYER2 WATCHING ESTABLISH\n");
+						test_and_set_bit(FLG_START_D, &csta->channel[0].Flags);
+						test_and_set_bit(FLG_START_D, &csta->channel[1].Flags);
+						csta->channel[0].d_st->lli.l4l3(csta->channel[0].d_st,
+							DL_ESTABLISH | REQUEST, NULL);
+					} else {
+						test_and_clear_bit(FLG_PTP, &csta->channel[0].d_st->l2.flag);
+						test_and_clear_bit(FLG_FIXED_TEI, &csta->channel[0].d_st->l2.flag);
+						HiSax_putstatus(csta, "set card ", "in PTMP mode");
+						printk(KERN_DEBUG "HiSax: set card in PTMP mode\n");
+					}
+					break;
+				case (8):	/* set card in FIXED TEI mode */
+					num = *(unsigned int *) ic->parm.num;
+					chanp = csta->channel + (num & 1);
+					num = num >>1;
+					test_and_set_bit(FLG_FIXED_TEI, &chanp->d_st->l2.flag);
+					chanp->d_st->l2.tei = num;
+					HiSax_putstatus(csta, "set card ", "in FIXED TEI (%d) mode", num);
+					printk(KERN_DEBUG "HiSax: set card in FIXED TEI (%d) mode\n",
+						num);
+					break;
+				case (9): /* load firmware */
+					memcpy(&adr, ic->parm.num, sizeof(ulong));
+					csta->cardmsg(csta, CARD_LOAD_FIRM,
+						(void *) adr);
 					break;
 #ifdef MODULE
 				case (55):
 					while ( MOD_USE_COUNT > 0)
-                                           MOD_DEC_USE_COUNT;
+						MOD_DEC_USE_COUNT;
 					HiSax_mod_inc_use_count();
 					break;
 #endif				/* MODULE */
 				case (11):
+					num = csta->debug & DEB_DLOG_HEX;
 					csta->debug = *(unsigned int *) ic->parm.num;
-					sprintf(tmp, "l1 debugging flags card %d set to %x\n",
+					csta->debug |= num;
+					HiSax_putstatus(cards[0].cs, "l1 debugging ",
+						"flags card %d set to %x",
+						csta->cardnr + 1, csta->debug);
+					printk(KERN_DEBUG "HiSax: l1 debugging flags card %d set to %x\n",
 						csta->cardnr + 1, csta->debug);
-					HiSax_putstatus(cards[0].cs, tmp);
-					printk(KERN_DEBUG "HiSax: %s", tmp);
 					break;
 				case (13):
 					csta->channel[0].d_st->l3.debug = *(unsigned int *) ic->parm.num;
 					csta->channel[1].d_st->l3.debug = *(unsigned int *) ic->parm.num;
-					sprintf(tmp, "l3 debugging flags card %d set to %x\n",
+					HiSax_putstatus(cards[0].cs, "l3 debugging ",
+						"flags card %d set to %x\n", csta->cardnr + 1,
+						*(unsigned int *) ic->parm.num);
+					printk(KERN_DEBUG "HiSax: l3 debugging flags card %d set to %x\n",
 						csta->cardnr + 1, *(unsigned int *) ic->parm.num);
-					HiSax_putstatus(cards[0].cs, tmp);
-					printk(KERN_DEBUG "HiSax: %s", tmp);
 					break;
 				default:
 					printk(KERN_DEBUG "HiSax: invalid ioclt %d\n",
@@ -2170,7 +2023,6 @@
 	int len = skb->len;
 	unsigned long flags;
 	struct sk_buff *nskb;
-	char tmp[64];
 
 	if (!csta) {
 		printk(KERN_ERR
@@ -2180,13 +2032,13 @@
 	chanp = csta->channel + chan;
 	st = chanp->b_st;
 	if (!chanp->data_open) {
-		link_debug(chanp, "writebuf: channel not open", 1);
+		link_debug(chanp, 1, "writebuf: channel not open");
 		return -EIO;
 	}
 	if (len > MAX_DATA_SIZE) {
-		sprintf(tmp, "writebuf: packet too large (%d bytes)", len);
-		printk(KERN_WARNING "HiSax_%s !\n", tmp);
-		link_debug(chanp, tmp, 1);
+		link_debug(chanp, 1, "writebuf: packet too large (%d bytes)", len);
+		printk(KERN_WARNING "HiSax_writebuf: packet too large (%d bytes) !\n",
+			len);
 		return -EINVAL;
 	}
 	if (len) {
@@ -2194,10 +2046,8 @@
 			/* Must return 0 here, since this is not an error
 			 * but a temporary lack of resources.
 			 */
-			if (chanp->debug & 0x800) {
-				sprintf(tmp, "writebuf: no buffers for %d bytes", len);
-				link_debug(chanp, tmp, 1);
-			}
+			if (chanp->debug & 0x800)
+				link_debug(chanp, 1, "writebuf: no buffers for %d bytes", len);
 			return 0;
 		}
 		save_flags(flags);
@@ -2206,11 +2056,11 @@
 		if (nskb) {
 			if (!ack)
 				nskb->pkt_type = PACKET_NOACK;
-			if (chanp->lc_b->l2_establish)
-				st->l3.l3l2(st, DL_DATA, nskb);
+			if (chanp->l2_active_protocol == ISDN_PROTO_L2_X75I)
+				st->l3.l3l2(st, DL_DATA | REQUEST, nskb);
 			else {
 				chanp->bcs->tx_cnt += len;
-				st->l2.l2l1(st, PH_DATA_REQ, nskb);
+				st->l2.l2l1(st, PH_DATA | REQUEST, nskb);
 			}
 			dev_kfree_skb(skb);
 		} else
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/cert.c linux/drivers/isdn/hisax/cert.c
--- v2.3.3/linux/drivers/isdn/hisax/cert.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/cert.c	Sun May 23 10:03:41 1999
@@ -0,0 +1,55 @@
+/* $Id: cert.c,v 2.1 1998/11/15 23:51:15 keil Exp $
+
+ * Author       Karsten Keil (keil@isdn4linux.de)
+ *
+ *		This file is (c) under GNU PUBLIC LICENSE
+ *		For changes and modifications please read
+ *		../../../Documentation/isdn/HiSax.cert
+ *
+ * $Log: cert.c,v $
+ * Revision 2.1  1998/11/15 23:51:15  keil
+ * certification stuff
+ *
+ * Revision 1.2.2.1  1998/11/03 21:46:37  keil
+ * first version
+ *
+ *
+ */
+ 
+#include <linux/kernel.h>
+
+int
+certification_check(int output) {
+
+#ifdef CERTIFICATION
+#if CERTIFICATION == 0
+	if (output) {
+		printk(KERN_INFO "HiSax: Approval certification valid\n");
+		printk(KERN_INFO "HiSax: Approved with ELSA Quickstep series cards\n");
+		printk(KERN_INFO "HiSax: Approval registration numbers:\n");
+		printk(KERN_INFO "HiSax: German D133361J CETECOM ICT Services GmbH\n");
+		printk(KERN_INFO "HiSax: EU (D133362J) CETECOM ICT Services GmbH\n");
+	}
+	return(0);
+#endif
+#if CERTIFICATION == 1
+	if (output) {
+		printk(KERN_INFO "HiSax: Approval certification failed because of\n");
+		printk(KERN_INFO "HiSax: unauthorized source code changes\n");
+	}
+	return(1);
+#endif
+#if CERTIFICATION == 127
+	if (output) {
+		printk(KERN_INFO "HiSax: Approval certification not possible\n");
+		printk(KERN_INFO "HiSax: because \"md5sum\" is not available\n");
+	}
+	return(2);
+#endif
+#else
+	if (output) {
+		printk(KERN_INFO "HiSax: Certification not verified\n");
+	}
+	return(3);
+#endif
+}
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/config.c linux/drivers/isdn/hisax/config.c
--- v2.3.3/linux/drivers/isdn/hisax/config.c	Mon Mar 15 16:11:30 1999
+++ linux/drivers/isdn/hisax/config.c	Sun May 23 10:03:41 1999
@@ -1,10 +1,50 @@
-/* $Id: config.c,v 2.12 1998/02/11 17:28:02 keil Exp $
+/* $Id: config.c,v 2.23 1999/02/17 10:53:02 cpetig Exp $
 
- * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author       Karsten Keil (keil@isdn4linux.de)
  *              based on the teles driver from Jan den Ouden
  *
  *
  * $Log: config.c,v $
+ * Revision 2.23  1999/02/17 10:53:02  cpetig
+ * Added Hisax_closecard to exported symbols.
+ * As indicated by Oliver Schoett <os@sdm.de>.
+ *
+ * If anyone is annoyed by exporting symbols deep inside the code, please
+ * contact me.
+ *
+ * Revision 2.22  1999/02/04 21:41:53  keil
+ * Fix printk msg
+ *
+ * Revision 2.21  1999/02/04 10:48:52  keil
+ * Fix readstat bug
+ *
+ * Revision 2.20  1998/11/15 23:54:28  keil
+ * changes from 2.0
+ *
+ * Revision 2.19  1998/08/13 23:36:18  keil
+ * HiSax 3.1 - don't work stable with current LinkLevel
+ *
+ * Revision 2.18  1998/07/30 21:01:37  niemann
+ * Fixed Sedlbauer Speed Fax PCMCIA missing isdnl3new
+ *
+ * Revision 2.17  1998/07/15 15:01:26  calle
+ * Support for AVM passive PCMCIA cards:
+ *    A1 PCMCIA, FRITZ!Card PCMCIA and FRITZ!Card PCMCIA 2.0
+ *
+ * Revision 2.16  1998/05/25 14:10:03  keil
+ * HiSax 3.0
+ * X.75 and leased are working again.
+ *
+ * Revision 2.15  1998/05/25 12:57:43  keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
+ * Revision 2.14  1998/04/15 16:38:25  keil
+ * Add S0Box and Teles PCI support
+ *
+ * Revision 2.13  1998/03/09 23:19:23  keil
+ * Changes for PCMCIA
+ *
  * Revision 2.12  1998/02/11 17:28:02  keil
  * Niccy PnP/PCI support
  *
@@ -55,6 +95,12 @@
 #include <linux/timer.h>
 #include <linux/config.h>
 #include "hisax.h"
+#include <linux/module.h>
+#include <linux/kernel_stat.h>
+#include <linux/tqueue.h>
+#include <linux/interrupt.h>
+#define HISAX_STATUS_BUFSIZE 4096
+#define INCLUDE_INLINE_FUNCS
 
 /*
  * This structure array contains one entry per card. An entry looks
@@ -78,42 +124,78 @@
  *   13 Teleint          p0=irq p1=iobase
  *   14 Teles 16.3c      p0=irq p1=iobase
  *   15 Sedlbauer speed  p0=irq p1=iobase
+ *   15 Sedlbauer PC/104	p0=irq p1=iobase
+ *   15 Sedlbauer speed pci	no parameter
  *   16 USR Sportster internal  p0=irq  p1=iobase
  *   17 MIC card                p0=irq  p1=iobase
  *   18 ELSA Quickstep 1000PCI  no parameter
  *   19 Compaq ISDN S0 ISA card p0=irq  p1=IO0 (HSCX)  p2=IO1 (ISAC) p3=IO2
  *   20 Travers Technologies NETjet PCI card
- *   21 reserved TELES PCI
+ *   21 TELES PCI               no parameter
  *   22 Sedlbauer Speed Star    p0=irq p1=iobase
  *   23 reserved
  *   24 Dr Neuhaus Niccy PnP/PCI card p0=irq p1=IO0 p2=IO1 (PnP only)
- *
+ *   25 Teles S0Box             p0=irq p1=iobase (from isapnp setup)
+ *   26 AVM A1 PCMCIA (Fritz)   p0=irq p1=iobase
+ *   27 AVM PnP/PCI 		p0=irq p1=iobase (PCI no parameter)
+ *   28 Sedlbauer Speed Fax+ 	p0=irq p1=iobase (from isapnp setup)
  *
  * protocol can be either ISDN_PTYPE_EURO or ISDN_PTYPE_1TR6 or ISDN_PTYPE_NI1
  *
  *
  */
 
+const char *CardType[] =
+{"No Card", "Teles 16.0", "Teles 8.0", "Teles 16.3", "Creatix/Teles PnP",
+ "AVM A1", "Elsa ML", "Elsa Quickstep", "Teles PCMCIA", "ITK ix1-micro Rev.2",
+ "Elsa PCMCIA", "Eicon.Diehl Diva", "ISDNLink", "TeleInt", "Teles 16.3c",
+ "Sedlbauer Speed Card", "USR Sportster", "ith mic Linux", "Elsa PCI",
+ "Compaq ISA", "NETjet", "Teles PCI", "Sedlbauer Speed Star (PCMCIA)",
+ "AMD 7930", "NICCY", "S0Box", "AVM A1 (PCMCIA)", "AVM Fritz PnP/PCI",
+ "Sedlbauer Speed Fax +"
+};
+
 #ifdef CONFIG_HISAX_ELSA
 #define DEFAULT_CARD ISDN_CTYPE_ELSA
 #define DEFAULT_CFG {0,0,0,0}
-#ifdef MODULE
 int elsa_init_pcmcia(void*, int, int*, int);
 EXPORT_SYMBOL(elsa_init_pcmcia);
 #endif
-#endif
 #ifdef CONFIG_HISAX_AVM_A1
 #undef DEFAULT_CARD
 #undef DEFAULT_CFG
 #define DEFAULT_CARD ISDN_CTYPE_A1
 #define DEFAULT_CFG {10,0x340,0,0}
 #endif
+
+#ifdef CONFIG_HISAX_AVM_A1_PCMCIA
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_A1_PCMCIA
+#define DEFAULT_CFG {11,0x170,0,0}
+int avm_a1_init_pcmcia(void*, int, int*, int);
+EXPORT_SYMBOL(avm_a1_init_pcmcia);
+#endif
+
+#ifdef CONFIG_HISAX_FRITZPCI
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_FRITZPCI
+#define DEFAULT_CFG {0,0,0,0}
+#endif
+
 #ifdef CONFIG_HISAX_16_3
 #undef DEFAULT_CARD
 #undef DEFAULT_CFG
 #define DEFAULT_CARD ISDN_CTYPE_16_3
 #define DEFAULT_CFG {15,0x180,0,0}
 #endif
+#ifdef CONFIG_HISAX_S0BOX
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_S0BOX
+#define DEFAULT_CFG {7,0x378,0,0}
+#endif
 #ifdef CONFIG_HISAX_16_0
 #undef DEFAULT_CARD
 #undef DEFAULT_CFG
@@ -121,6 +203,13 @@
 #define DEFAULT_CFG {15,0xd0000,0xd80,0}
 #endif
 
+#ifdef CONFIG_HISAX_TELESPCI
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_TELESPCI
+#define DEFAULT_CFG {0,0,0,0}
+#endif
+
 #ifdef CONFIG_HISAX_IX1MICROR2
 #undef DEFAULT_CARD
 #undef DEFAULT_CFG
@@ -193,13 +282,6 @@
 #define DEFAULT_CFG {12,0x3e0,0,0}
 #endif
 
-#ifdef CONFIG_HISAX_DBRI
-#undef DEFAULT_CARD
-#undef DEFAULT_CFG
-#define DEFAULT_CARD ISDN_CTYPE_DBRI
-#define DEFAULT_CFG {0,0x0,0,0}
-#endif
-
 #ifdef CONFIG_HISAX_NICCY
 #undef DEFAULT_CARD
 #undef DEFAULT_CFG
@@ -232,10 +314,10 @@
 #endif
 
 #define FIRST_CARD { \
-  DEFAULT_CARD, \
-  DEFAULT_PROTO, \
-  DEFAULT_CFG, \
-  NULL, \
+	DEFAULT_CARD, \
+	DEFAULT_PROTO, \
+	DEFAULT_CFG, \
+	NULL, \
 }
 
 #define EMPTY_CARD	{0, DEFAULT_PROTO, {0, 0, 0, 0}, NULL}
@@ -250,29 +332,20 @@
 	EMPTY_CARD,
 	EMPTY_CARD,
 	EMPTY_CARD,
-	EMPTY_CARD,
-	EMPTY_CARD,
-	EMPTY_CARD,
-	EMPTY_CARD,
-	EMPTY_CARD,
-	EMPTY_CARD,
-	EMPTY_CARD,
-	EMPTY_CARD,
 };
 
-static char HiSaxID[96] HISAX_INITDATA = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \
-"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \
+static char HiSaxID[64] HISAX_INITDATA = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \
 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \
-"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
+"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
 char *HiSax_id HISAX_INITDATA = HiSaxID;
 #ifdef MODULE
 /* Variables for insmod */
 static int type[] HISAX_INITDATA =
-{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+{0, 0, 0, 0, 0, 0, 0, 0};
 static int protocol[] HISAX_INITDATA =
-{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+{0, 0, 0, 0, 0, 0, 0, 0};
 static int io[] HISAX_INITDATA =
-{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+{0, 0, 0, 0, 0, 0, 0, 0};
 #undef IO0_IO1
 #ifdef CONFIG_HISAX_16_3
 #define IO0_IO1
@@ -283,29 +356,29 @@
 #endif
 #ifdef IO0_IO1
 static int io0[] HISAX_INITDATA =
-{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+{0, 0, 0, 0, 0, 0, 0, 0};
 static int io1[] HISAX_INITDATA =
-{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+{0, 0, 0, 0, 0, 0, 0, 0};
 #endif
 static int irq[] HISAX_INITDATA =
-{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+{0, 0, 0, 0, 0, 0, 0, 0};
 static int mem[] HISAX_INITDATA =
-{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+{0, 0, 0, 0, 0, 0, 0, 0};
 static char *id HISAX_INITDATA = HiSaxID;
 
 MODULE_AUTHOR("Karsten Keil");
-MODULE_PARM(type, "1-3i");
-MODULE_PARM(protocol, "1-2i");
+MODULE_PARM(type, "1-8i");
+MODULE_PARM(protocol, "1-8i");
 MODULE_PARM(io, "1-8i");
-MODULE_PARM(irq, "1-2i");
-MODULE_PARM(mem, "1-12i");
+MODULE_PARM(irq, "1-8i");
+MODULE_PARM(mem, "1-8i");
 MODULE_PARM(id, "s");
 #ifdef CONFIG_HISAX_16_3	/* For Creatix/Teles PnP */
 MODULE_PARM(io0, "1-8i");
 MODULE_PARM(io1, "1-8i");
-#endif
+#endif /* CONFIG_HISAX_16_3 */
 
-#endif
+#endif /* MODULE */
 
 int nrcards;
 
@@ -333,23 +406,25 @@
 HISAX_INITFUNC(void
 HiSaxVersion(void))
 {
-	char tmp[64], rev[64];
-	char *r = rev;
+	char tmp[64];
 
+	printk(KERN_INFO "HiSax: Linux Driver for passive ISDN cards\n");
+#ifdef MODULE
+	printk(KERN_INFO "HiSax: Version 3.1a (module)\n");
+#else
+	printk(KERN_INFO "HiSax: Version 3.1a (kernel)\n");
+#endif
 	strcpy(tmp, l1_revision);
-	r += sprintf(r, "%s/", HiSax_getrev(tmp));
+	printk(KERN_INFO "HiSax: Layer1 Revision %s\n", HiSax_getrev(tmp));
 	strcpy(tmp, l2_revision);
-	r += sprintf(r, "%s/", HiSax_getrev(tmp));
+	printk(KERN_INFO "HiSax: Layer2 Revision %s\n", HiSax_getrev(tmp));
+	strcpy(tmp, tei_revision);
+	printk(KERN_INFO "HiSax: TeiMgr Revision %s\n", HiSax_getrev(tmp));
 	strcpy(tmp, l3_revision);
-	r += sprintf(r, "%s/", HiSax_getrev(tmp));
+	printk(KERN_INFO "HiSax: Layer3 Revision %s\n", HiSax_getrev(tmp));
 	strcpy(tmp, lli_revision);
-	r += sprintf(r, "%s/", HiSax_getrev(tmp));
-	strcpy(tmp, tei_revision);
-	r += sprintf(r, "%s", HiSax_getrev(tmp));
-
-	printk(KERN_INFO "HiSax: Driver for Siemens chip set ISDN cards\n");
-	printk(KERN_INFO "HiSax: Version 2.8\n");
-	printk(KERN_INFO "HiSax: Revisions %s\n", rev);
+	printk(KERN_INFO "HiSax: LinkLayer Revision %s\n", HiSax_getrev(tmp));
+	certification_check(1);
 }
 
 void
@@ -375,7 +450,7 @@
 	argc = ints[0];
 	i = 0;
 	j = 1;
-	while (argc && (i < 16)) {
+	while (argc && (i < HISAX_MAX_CARDS)) {
 		if (argc) {
 			cards[i].typ = ints[j];
 			j++;
@@ -413,11 +488,759 @@
 }
 #endif
 
+#if CARD_TELES0
+extern int setup_teles0(struct IsdnCard *card);
+#endif
+
+#if CARD_TELES3
+extern int setup_teles3(struct IsdnCard *card);
+#endif
+
+#if CARD_S0BOX
+extern int setup_s0box(struct IsdnCard *card);
+#endif
+
+#if CARD_TELESPCI
+extern int setup_telespci(struct IsdnCard *card);
+#endif
+
+#if CARD_AVM_A1
+extern int setup_avm_a1(struct IsdnCard *card);
+#endif
+
+#if CARD_AVM_A1_PCMCIA
+extern int setup_avm_a1_pcmcia(struct IsdnCard *card);
+#endif
+
+#if CARD_FRITZPCI
+extern int setup_avm_pcipnp(struct IsdnCard *card);
+#endif
+
+#if CARD_ELSA
+extern int setup_elsa(struct IsdnCard *card);
+#endif
+
+#if CARD_IX1MICROR2
+extern int setup_ix1micro(struct IsdnCard *card);
+#endif
+
+#if CARD_DIEHLDIVA
+extern	int  setup_diva(struct IsdnCard *card);
+#endif
+
+#if CARD_ASUSCOM
+extern int setup_asuscom(struct IsdnCard *card);
+#endif
+
+#if CARD_TELEINT
+extern int setup_TeleInt(struct IsdnCard *card);
+#endif
+
+#if CARD_SEDLBAUER
+extern int setup_sedlbauer(struct IsdnCard *card);
+#endif
+
+#if CARD_SPORTSTER
+extern int setup_sportster(struct IsdnCard *card);
+#endif
+
+#if CARD_MIC
+extern int setup_mic(struct IsdnCard *card);
+#endif
+
+#if CARD_NETJET
+extern int setup_netjet(struct IsdnCard *card);
+#endif
+
+#if CARD_TELES3C
+extern int setup_t163c(struct IsdnCard *card);
+#endif
+
+#if CARD_AMD7930
+extern int setup_amd7930(struct IsdnCard *card);
+#endif
+
+#if CARD_NICCY
+extern int setup_niccy(struct IsdnCard *card);
+#endif
+
+/*
+ * Find card with given driverId
+ */
+static inline struct IsdnCardState
+*hisax_findcard(int driverid)
+{
+	int i;
+
+	for (i = 0; i < nrcards; i++)
+		if (cards[i].cs)
+			if (cards[i].cs->myid == driverid)
+				return (cards[i].cs);
+	return (NULL);
+}
+
+int
+HiSax_readstatus(u_char * buf, int len, int user, int id, int channel)
+{
+	int count,cnt;
+	u_char *p = buf;
+	struct IsdnCardState *cs = hisax_findcard(id);
+
+	if (cs) {
+		if (len > HISAX_STATUS_BUFSIZE) {
+			printk(KERN_WARNING "HiSax: status overflow readstat %d/%d\n",
+				len, HISAX_STATUS_BUFSIZE);
+		}
+		count = cs->status_end - cs->status_read +1;
+		if (count >= len)
+			count = len;
+		if (user)
+			copy_to_user(p, cs->status_read, count);
+		else
+			memcpy(p, cs->status_read, count);
+		cs->status_read += count;
+		if (cs->status_read > cs->status_end)
+			cs->status_read = cs->status_buf;
+		p += count;
+		count = len - count;
+		while (count) {
+			if (count > HISAX_STATUS_BUFSIZE)
+				cnt = HISAX_STATUS_BUFSIZE;
+			else
+				cnt = count;
+			if (user)
+				copy_to_user(p, cs->status_read, cnt);
+			else
+				memcpy(p, cs->status_read, cnt);
+			p += cnt;
+			cs->status_read += cnt % HISAX_STATUS_BUFSIZE;
+			count -= cnt;
+		}
+		return len;
+	} else {
+		printk(KERN_ERR
+		 "HiSax: if_readstatus called with invalid driverId!\n");
+		return -ENODEV;
+	}
+}
+
+inline int
+jiftime(char *s, long mark)
+{
+	s += 8;
+
+	*s-- = '\0';
+	*s-- = mark % 10 + '0';
+	mark /= 10;
+	*s-- = mark % 10 + '0';
+	mark /= 10;
+	*s-- = '.';
+	*s-- = mark % 10 + '0';
+	mark /= 10;
+	*s-- = mark % 6 + '0';
+	mark /= 6;
+	*s-- = ':';
+	*s-- = mark % 10 + '0';
+	mark /= 10;
+	*s-- = mark % 10 + '0';
+	return(8);
+}
+
+static u_char tmpbuf[HISAX_STATUS_BUFSIZE];
+
+void
+VHiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt, va_list args)
+{
+/* if head == NULL the fmt contains the full info */
+
+	long flags;
+	int count, i;
+	u_char *p;
+	isdn_ctrl ic;
+	int len;
+
+	save_flags(flags);
+	cli();
+	p = tmpbuf;
+	if (head) {
+		p += jiftime(p, jiffies);
+		p += sprintf(p, " %s", head);
+		p += vsprintf(p, fmt, args);
+		*p++ = '\n';
+		*p = 0;
+		len = p - tmpbuf;
+		p = tmpbuf;
+	} else {
+		p = fmt;
+		len = strlen(fmt);
+	}
+	if (!cs) {
+		printk(KERN_WARNING "HiSax: No CardStatus for message %s", p);
+		restore_flags(flags);
+		return;
+	}
+	if (len > HISAX_STATUS_BUFSIZE) {
+		printk(KERN_WARNING "HiSax: status overflow %d/%d\n",
+			len, HISAX_STATUS_BUFSIZE);
+		restore_flags(flags);
+		return;
+	}
+	count = len;
+	i = cs->status_end - cs->status_write +1;
+	if (i >= len)
+		i = len;
+	len -= i;
+	memcpy(cs->status_write, p, i);
+	cs->status_write += i;
+	if (cs->status_write > cs->status_end)
+		cs->status_write = cs->status_buf;
+	p += i;
+	if (len) {
+		memcpy(cs->status_write, p, len);
+		cs->status_write += len;
+	}
+#ifdef KERNELSTACK_DEBUG
+	i = (ulong)&len - current->kernel_stack_page;
+	sprintf(tmpbuf, "kstack %s %lx use %ld\n", current->comm,
+		current->kernel_stack_page, i);
+	len = strlen(tmpbuf);
+	for (p = tmpbuf, i = len; i > 0; i--, p++) {
+		*cs->status_write++ = *p;
+		if (cs->status_write > cs->status_end)
+			cs->status_write = cs->status_buf;
+		count++;
+	}
+#endif
+	restore_flags(flags);
+	if (count) {
+		ic.command = ISDN_STAT_STAVAIL;
+		ic.driver = cs->myid;
+		ic.arg = count;
+		cs->iif.statcallb(&ic);
+	}
+}
+
+void
+HiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt, ...)
+{
+	va_list args;
+
+	va_start(args, fmt);
+	VHiSax_putstatus(cs, head, fmt, args);
+	va_end(args);
+}
+
+int
+ll_run(struct IsdnCardState *cs)
+{
+	long flags;
+	isdn_ctrl ic;
+
+	save_flags(flags);
+	cli();
+	ic.driver = cs->myid;
+	ic.command = ISDN_STAT_RUN;
+	cs->iif.statcallb(&ic);
+	restore_flags(flags);
+	return 0;
+}
+
+void
+ll_stop(struct IsdnCardState *cs)
+{
+	isdn_ctrl ic;
+
+	ic.command = ISDN_STAT_STOP;
+	ic.driver = cs->myid;
+	cs->iif.statcallb(&ic);
+	CallcFreeChan(cs);
+}
+
+static void
+ll_unload(struct IsdnCardState *cs)
+{
+	isdn_ctrl ic;
+
+	ic.command = ISDN_STAT_UNLOAD;
+	ic.driver = cs->myid;
+	cs->iif.statcallb(&ic);
+	if (cs->status_buf)
+		kfree(cs->status_buf);
+	cs->status_read = NULL;
+	cs->status_write = NULL;
+	cs->status_end = NULL;
+	kfree(cs->dlog);
+}
+
+static void
+closecard(int cardnr)
+{
+	struct IsdnCardState *csta = cards[cardnr].cs;
+
+	if (csta->bcs->BC_Close != NULL) {
+		csta->bcs->BC_Close(csta->bcs + 1);
+		csta->bcs->BC_Close(csta->bcs);
+	}
+
+	if (csta->rcvbuf) {
+		kfree(csta->rcvbuf);
+		csta->rcvbuf = NULL;
+	}
+	discard_queue(&csta->rq);
+	discard_queue(&csta->sq);
+	if (csta->tx_skb) {
+		dev_kfree_skb(csta->tx_skb);
+		csta->tx_skb = NULL;
+	}
+	if (csta->mon_rx) {
+		kfree(csta->mon_rx);
+		csta->mon_rx = NULL;
+	}
+	if (csta->mon_tx) {
+		kfree(csta->mon_tx);
+		csta->mon_tx = NULL;
+	}
+	csta->cardmsg(csta, CARD_RELEASE, NULL);
+	if (csta->dbusytimer.function != NULL)
+		del_timer(&csta->dbusytimer);
+	ll_unload(csta);
+}
+
+HISAX_INITFUNC(static int init_card(struct IsdnCardState *cs))
+{
+	int irq_cnt, cnt = 3;
+	long flags;
+
+	save_flags(flags);
+	cli();
+	irq_cnt = kstat_irqs(cs->irq);
+	printk(KERN_INFO "%s: IRQ %d count %d\n", CardType[cs->typ], cs->irq,
+		irq_cnt);
+	if (cs->cardmsg(cs, CARD_SETIRQ, NULL)) {
+		printk(KERN_WARNING "HiSax: couldn't get interrupt %d\n",
+			cs->irq);
+		restore_flags(flags);
+		return(1);
+	}
+	while (cnt) {
+		cs->cardmsg(cs, CARD_INIT, NULL);
+		sti();
+		current->state = TASK_INTERRUPTIBLE;
+		/* Timeout 10ms */
+		schedule_timeout((10 * HZ) / 1000);
+		restore_flags(flags);
+		printk(KERN_INFO "%s: IRQ %d count %d\n", CardType[cs->typ],
+			cs->irq, kstat_irqs(cs->irq));
+		if (kstat_irqs(cs->irq) == irq_cnt) {
+			printk(KERN_WARNING
+			       "%s: IRQ(%d) getting no interrupts during init %d\n",
+			       CardType[cs->typ], cs->irq, 4 - cnt);
+			if (cnt == 1) {
+				free_irq(cs->irq, cs);
+				return (2);
+			} else {
+				cs->cardmsg(cs, CARD_RESET, NULL);
+				cnt--;
+			}
+		} else {
+			cs->cardmsg(cs, CARD_TEST, NULL);
+			return(0);
+		}
+	}
+	restore_flags(flags);
+	return(3);
+}
+
+HISAX_INITFUNC(static int
+checkcard(int cardnr, char *id, int *busy_flag))
+{
+	long flags;
+	int ret = 0;
+	struct IsdnCard *card = cards + cardnr;
+	struct IsdnCardState *cs;
+
+	save_flags(flags);
+	cli();
+	if (!(cs = (struct IsdnCardState *)
+		kmalloc(sizeof(struct IsdnCardState), GFP_ATOMIC))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for IsdnCardState(card %d)\n",
+		       cardnr + 1);
+		restore_flags(flags);
+		return (0);
+	}
+	memset(cs, 0, sizeof(struct IsdnCardState));
+	card->cs = cs;
+	cs->cardnr = cardnr;
+	cs->debug = L1_DEB_WARN;
+	cs->HW_Flags = 0;
+	cs->busy_flag = busy_flag;
+#if TEI_PER_CARD
+#else
+	test_and_set_bit(FLG_TWO_DCHAN, &cs->HW_Flags);
+#endif
+	cs->protocol = card->protocol;
+
+	if ((card->typ > 0) && (card->typ < 31)) {
+		if (!((1 << card->typ) & SUPORTED_CARDS)) {
+			printk(KERN_WARNING
+			     "HiSax: Support for %s Card not selected\n",
+			       CardType[card->typ]);
+			restore_flags(flags);
+			return (0);
+		}
+	} else {
+		printk(KERN_WARNING
+		       "HiSax: Card Type %d out of range\n",
+		       card->typ);
+		restore_flags(flags);
+		return (0);
+	}
+	if (!(cs->dlog = kmalloc(MAX_DLOG_SPACE, GFP_ATOMIC))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for dlog(card %d)\n",
+		       cardnr + 1);
+		restore_flags(flags);
+		return (0);
+	}
+	if (!(cs->status_buf = kmalloc(HISAX_STATUS_BUFSIZE, GFP_ATOMIC))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for status_buf(card %d)\n",
+		       cardnr + 1);
+		kfree(cs->dlog);
+		restore_flags(flags);
+		return (0);
+	}
+	cs->stlist = NULL;
+	cs->mon_tx = NULL;
+	cs->mon_rx = NULL;
+	cs->status_read = cs->status_buf;
+	cs->status_write = cs->status_buf;
+	cs->status_end = cs->status_buf + HISAX_STATUS_BUFSIZE - 1;
+	cs->typ = card->typ;
+	strcpy(cs->iif.id, id);
+	cs->iif.channels = 2;
+	cs->iif.maxbufsize = MAX_DATA_SIZE;
+	cs->iif.hl_hdrlen = MAX_HEADER_LEN;
+	cs->iif.features =
+	    ISDN_FEATURE_L2_X75I |
+	    ISDN_FEATURE_L2_HDLC |
+	    ISDN_FEATURE_L2_MODEM |
+	    ISDN_FEATURE_L2_TRANS |
+	    ISDN_FEATURE_L3_TRANS |
+#ifdef	CONFIG_HISAX_1TR6
+	    ISDN_FEATURE_P_1TR6 |
+#endif
+#ifdef	CONFIG_HISAX_EURO
+	    ISDN_FEATURE_P_EURO |
+#endif
+#ifdef        CONFIG_HISAX_NI1
+	    ISDN_FEATURE_P_NI1 |
+#endif
+	    0;
+
+	cs->iif.command = HiSax_command;
+	cs->iif.writecmd = NULL;
+	cs->iif.writebuf_skb = HiSax_writebuf_skb;
+	cs->iif.readstat = HiSax_readstatus;
+	register_isdn(&cs->iif);
+	cs->myid = cs->iif.channels;
+	printk(KERN_INFO
+	       "HiSax: Card %d Protocol %s Id=%s (%d)\n", cardnr + 1,
+	       (card->protocol == ISDN_PTYPE_1TR6) ? "1TR6" :
+	       (card->protocol == ISDN_PTYPE_EURO) ? "EDSS1" :
+	       (card->protocol == ISDN_PTYPE_LEASED) ? "LEASED" :
+	       (card->protocol == ISDN_PTYPE_NI1) ? "NI1" :
+	       "NONE", cs->iif.id, cs->myid);
+	switch (card->typ) {
+#if CARD_TELES0
+		case ISDN_CTYPE_16_0:
+		case ISDN_CTYPE_8_0:
+			ret = setup_teles0(card);
+			break;
+#endif
+#if CARD_TELES3
+		case ISDN_CTYPE_16_3:
+		case ISDN_CTYPE_PNP:
+		case ISDN_CTYPE_TELESPCMCIA:
+		case ISDN_CTYPE_COMPAQ_ISA:
+			ret = setup_teles3(card);
+			break;
+#endif
+#if CARD_S0BOX
+		case ISDN_CTYPE_S0BOX:
+			ret = setup_s0box(card);
+			break;
+#endif
+#if CARD_TELESPCI
+		case ISDN_CTYPE_TELESPCI:
+			ret = setup_telespci(card);
+			break;
+#endif
+#if CARD_AVM_A1
+		case ISDN_CTYPE_A1:
+			ret = setup_avm_a1(card);
+			break;
+#endif
+#if CARD_AVM_A1_PCMCIA
+		case ISDN_CTYPE_A1_PCMCIA:
+			ret = setup_avm_a1_pcmcia(card);
+			break;
+#endif
+#if CARD_FRITZPCI
+		case ISDN_CTYPE_FRITZPCI:
+			ret = setup_avm_pcipnp(card);
+			break;
+#endif
+#if CARD_ELSA
+		case ISDN_CTYPE_ELSA:
+		case ISDN_CTYPE_ELSA_PNP:
+		case ISDN_CTYPE_ELSA_PCMCIA:
+		case ISDN_CTYPE_ELSA_PCI:
+			ret = setup_elsa(card);
+			break;
+#endif
+#if CARD_IX1MICROR2
+		case ISDN_CTYPE_IX1MICROR2:
+			ret = setup_ix1micro(card);
+			break;
+#endif
+#if CARD_DIEHLDIVA
+		case ISDN_CTYPE_DIEHLDIVA:
+			ret = setup_diva(card);
+			break;
+#endif
+#if CARD_ASUSCOM
+		case ISDN_CTYPE_ASUSCOM:
+			ret = setup_asuscom(card);
+			break;
+#endif
+#if CARD_TELEINT
+		case ISDN_CTYPE_TELEINT:
+			ret = setup_TeleInt(card);
+			break;
+#endif
+#if CARD_SEDLBAUER
+		case ISDN_CTYPE_SEDLBAUER:
+		case ISDN_CTYPE_SEDLBAUER_PCMCIA:
+		case ISDN_CTYPE_SEDLBAUER_FAX:
+			ret = setup_sedlbauer(card);
+			break;
+#endif
+#if CARD_SPORTSTER
+		case ISDN_CTYPE_SPORTSTER:
+			ret = setup_sportster(card);
+			break;
+#endif
+#if CARD_MIC
+		case ISDN_CTYPE_MIC:
+			ret = setup_mic(card);
+			break;
+#endif
+#if CARD_NETJET
+		case ISDN_CTYPE_NETJET:
+			ret = setup_netjet(card);
+			break;
+#endif
+#if CARD_TELES3C
+		case ISDN_CTYPE_TELES3C:
+			ret = setup_t163c(card);
+			break;
+#endif
+#if CARD_NICCY
+		case ISDN_CTYPE_NICCY:
+			ret = setup_niccy(card);
+			break;
+#endif
+#if CARD_AMD7930
+		case ISDN_CTYPE_AMD7930:
+			ret = setup_amd7930(card);
+			break;
+#endif
+		default:
+			printk(KERN_WARNING "HiSax: Unknown Card Typ %d\n",
+			       card->typ);
+			ll_unload(cs);
+			restore_flags(flags);
+			return (0);
+	}
+	if (!ret) {
+		ll_unload(cs);
+		restore_flags(flags);
+		return (0);
+	}
+	if (!(cs->rcvbuf = kmalloc(MAX_DFRAME_LEN_L1, GFP_ATOMIC))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for isac rcvbuf\n");
+		return (1);
+	}
+	cs->rcvidx = 0;
+	cs->tx_skb = NULL;
+	cs->tx_cnt = 0;
+	cs->event = 0;
+	cs->tqueue.next = 0;
+	cs->tqueue.sync = 0;
+	cs->tqueue.data = cs;
+
+	skb_queue_head_init(&cs->rq);
+	skb_queue_head_init(&cs->sq);
+
+	init_bcstate(cs, 0);
+	init_bcstate(cs, 1);
+	ret = init_card(cs);
+	if (ret) {
+		closecard(cardnr);
+		restore_flags(flags);
+		return (0);
+	}
+	init_tei(cs, cs->protocol);
+	CallcNewChan(cs);
+	/* ISAR needs firmware download first */
+	if (!test_bit(HW_ISAR, &cs->HW_Flags))
+		ll_run(cs);
+	restore_flags(flags);
+	return (1);
+}
+
+HISAX_INITFUNC(void
+HiSax_shiftcards(int idx))
+{
+	int i;
+
+	for (i = idx; i < (HISAX_MAX_CARDS - 1); i++)
+		memcpy(&cards[i], &cards[i + 1], sizeof(cards[i]));
+}
+
+HISAX_INITFUNC(int
+HiSax_inithardware(int *busy_flag))
+{
+	int foundcards = 0;
+	int i = 0;
+	int t = ',';
+	int flg = 0;
+	char *id;
+	char *next_id = HiSax_id;
+	char ids[20];
+
+	if (strchr(HiSax_id, ','))
+		t = ',';
+	else if (strchr(HiSax_id, '%'))
+		t = '%';
+
+	while (i < nrcards) {
+		if (cards[i].typ < 1)
+			break;
+		id = next_id;
+		if ((next_id = strchr(id, t))) {
+			*next_id++ = 0;
+			strcpy(ids, id);
+			flg = i + 1;
+		} else {
+			next_id = id;
+			if (flg >= i)
+				strcpy(ids, id);
+			else
+				sprintf(ids, "%s%d", id, i);
+		}
+		if (checkcard(i, ids, busy_flag)) {
+			foundcards++;
+			i++;
+		} else {
+			printk(KERN_WARNING "HiSax: Card %s not installed !\n",
+			       CardType[cards[i].typ]);
+			if (cards[i].cs)
+				kfree((void *) cards[i].cs);
+			cards[i].cs = NULL;
+			HiSax_shiftcards(i);
+		}
+	}
+	return foundcards;
+}
+
+void
+HiSax_closecard(int cardnr)
+{
+	int 	i,last=nrcards - 1;
+
+	if (cardnr>last)
+		return;
+	if (cards[cardnr].cs) {
+		ll_stop(cards[cardnr].cs);
+		release_tei(cards[cardnr].cs);
+		closecard(cardnr);
+		free_irq(cards[cardnr].cs->irq, cards[cardnr].cs);
+		kfree((void *) cards[cardnr].cs);
+		cards[cardnr].cs = NULL;
+	}
+	i = cardnr;
+	while (i!=last) {
+		cards[i] = cards[i+1];
+		i++;
+	}
+	nrcards--;
+}
+
+EXPORT_SYMBOL(HiSax_closecard);
+
+void
+HiSax_reportcard(int cardnr)
+{
+	struct IsdnCardState *cs = cards[cardnr].cs;
+	struct PStack *stptr;
+	struct l3_process *pc;
+	int j, i = 1;
+
+	printk(KERN_DEBUG "HiSax: reportcard No %d\n", cardnr + 1);
+	printk(KERN_DEBUG "HiSax: Type %s\n", CardType[cs->typ]);
+	printk(KERN_DEBUG "HiSax: debuglevel %x\n", cs->debug);
+	printk(KERN_DEBUG "HiSax: HiSax_reportcard address 0x%lX\n",
+		(ulong) & HiSax_reportcard);
+	printk(KERN_DEBUG "HiSax: cs 0x%lX\n", (ulong) cs);
+	printk(KERN_DEBUG "HiSax: HW_Flags %x bc0 flg %x bc0 flg %x\n",
+		cs->HW_Flags, cs->bcs[0].Flag, cs->bcs[1].Flag);
+	printk(KERN_DEBUG "HiSax: bcs 0 mode %d ch%d\n",
+		cs->bcs[0].mode, cs->bcs[0].channel);
+	printk(KERN_DEBUG "HiSax: bcs 1 mode %d ch%d\n",
+		cs->bcs[1].mode, cs->bcs[1].channel);
+	printk(KERN_DEBUG "HiSax: cs stl 0x%lX\n", (ulong) & (cs->stlist));
+	stptr = cs->stlist;
+	while (stptr != NULL) {
+		printk(KERN_DEBUG "HiSax: dst%d 0x%lX\n", i, (ulong) stptr);
+		printk(KERN_DEBUG "HiSax: dst%d stp 0x%lX\n", i, (ulong) stptr->l1.stlistp);
+		printk(KERN_DEBUG "HiSax:   tei %d sapi %d\n",
+		       stptr->l2.tei, stptr->l2.sap);
+		printk(KERN_DEBUG "HiSax:      man 0x%lX\n", (ulong) stptr->ma.layer);
+		pc = stptr->l3.proc;
+		while (pc) {
+			printk(KERN_DEBUG "HiSax: l3proc %x 0x%lX\n", pc->callref,
+			       (ulong) pc);
+			printk(KERN_DEBUG "HiSax:    state %d  st 0x%lX chan 0x%lX\n",
+			    pc->state, (ulong) pc->st, (ulong) pc->chan);
+			pc = pc->next;
+		}
+		stptr = stptr->next;
+		i++;
+	}
+	for (j = 0; j < 2; j++) {
+		printk(KERN_DEBUG "HiSax: ch%d 0x%lX\n", j,
+		       (ulong) & cs->channel[j]);
+		stptr = cs->channel[j].b_st;
+		i = 1;
+		while (stptr != NULL) {
+			printk(KERN_DEBUG "HiSax:  b_st%d 0x%lX\n", i, (ulong) stptr);
+			printk(KERN_DEBUG "HiSax:    man 0x%lX\n", (ulong) stptr->ma.layer);
+			stptr = stptr->next;
+			i++;
+		}
+	}
+}
+
+
 __initfunc(int
 HiSax_init(void))
 {
 	int i;
-	
+
 #ifdef MODULE
 	int nzproto = 0;
 #ifdef CONFIG_HISAX_ELSA
@@ -432,13 +1255,19 @@
 		return 0;
 	}
 #endif
+#ifdef CONFIG_HISAX_AVM_A1_PCMCIA
+	if (type[0] == ISDN_CTYPE_A1_PCMCIA) {
+		/* we have to export  and return in this case */
+		return 0;
+	}
+#endif
 #endif
-	HiSaxVersion();
 	nrcards = 0;
+	HiSaxVersion();
 #ifdef MODULE
 	if (id)			/* If id= string used */
 		HiSax_id = id;
-	for (i = 0; i < 16; i++) {
+	for (i = 0; i < HISAX_MAX_CARDS; i++) {
 		cards[i].typ = type[i];
 		if (protocol[i]) {
 			cards[i].protocol = protocol[i];
@@ -476,6 +1305,7 @@
 			case ISDN_CTYPE_16_3:
 			case ISDN_CTYPE_TELESPCMCIA:
 			case ISDN_CTYPE_A1:
+			case ISDN_CTYPE_A1_PCMCIA:
 			case ISDN_CTYPE_ELSA_PNP:
 			case ISDN_CTYPE_ELSA_PCMCIA:
 			case ISDN_CTYPE_IX1MICROR2:
@@ -484,16 +1314,19 @@
 			case ISDN_CTYPE_TELEINT:
 			case ISDN_CTYPE_SEDLBAUER:
 			case ISDN_CTYPE_SEDLBAUER_PCMCIA:
+			case ISDN_CTYPE_SEDLBAUER_FAX:
 			case ISDN_CTYPE_SPORTSTER:
 			case ISDN_CTYPE_MIC:
 			case ISDN_CTYPE_TELES3C:
+			case ISDN_CTYPE_S0BOX:
+			case ISDN_CTYPE_FRITZPCI:
 				cards[i].para[0] = irq[i];
 				cards[i].para[1] = io[i];
 				break;
 			case ISDN_CTYPE_ELSA_PCI:
 			case ISDN_CTYPE_NETJET:
 			case ISDN_CTYPE_AMD7930:
-			case ISDN_CTYPE_DBRI:
+			case ISDN_CTYPE_TELESPCI:
 				break;
 		}
 	}
@@ -507,13 +1340,14 @@
 		HiSax_id = HiSaxID;
 	if (!HiSaxID[0])
 		strcpy(HiSaxID, "HiSax");
-	for (i = 0; i < 16; i++)
+	for (i = 0; i < HISAX_MAX_CARDS; i++)
 		if (cards[i].typ > 0)
 			nrcards++;
 	printk(KERN_DEBUG "HiSax: Total %d card%s defined\n",
 	       nrcards, (nrcards > 1) ? "s" : "");
 
 	CallcNew();
+	Isdnl3New();
 	Isdnl2New();
 	TeiNew();
 	Isdnl1New();
@@ -522,7 +1356,6 @@
 		/* No symbols to export, hide all symbols */
 
 #ifdef MODULE
-		EXPORT_NO_SYMBOLS;
 		printk(KERN_INFO "HiSax: module installed\n");
 #endif
 		return (0);
@@ -530,6 +1363,7 @@
 		Isdnl1Free();
 		TeiFree();
 		Isdnl2Free();
+		Isdnl3Free();
 		CallcFree();
 		return -EIO;
 	}
@@ -539,13 +1373,27 @@
 void
 cleanup_module(void)
 {
-	HiSax_closehardware();
+	int cardnr = nrcards -1;
+	long flags;
+
+	save_flags(flags);
+	cli();
+	while(cardnr>=0)
+		HiSax_closecard(cardnr--);
+	Isdnl1Free();
+	TeiFree();
+	Isdnl2Free();
+	Isdnl3Free();
+	CallcFree();
+	restore_flags(flags);
 	printk(KERN_INFO "HiSax module removed\n");
 }
+#endif
 
 #ifdef CONFIG_HISAX_ELSA
 int elsa_init_pcmcia(void *pcm_iob, int pcm_irq, int *busy_flag, int prot)
 {
+#ifdef MODULE
 	int i;
 	int nzproto = 0;
 
@@ -553,10 +1401,10 @@
 	HiSaxVersion();
 	if (id)			/* If id= string used */
 		HiSax_id = id;
-	/* Initialize all 16 structs, even though we only accept
+	/* Initialize all 8 structs, even though we only accept
 	   two pcmcia cards
 	   */
-	for (i = 0; i < 16; i++) {
+	for (i = 0; i < HISAX_MAX_CARDS; i++) {
 		cards[i].para[0] = irq[i];
 		cards[i].para[1] = io[i];
 		cards[i].typ = type[i];
@@ -575,7 +1423,7 @@
 		HiSax_id = HiSaxID;
 	if (!HiSaxID[0])
 		strcpy(HiSaxID, "HiSax");
-	for (i = 0; i < 16; i++)
+	for (i = 0; i < HISAX_MAX_CARDS; i++)
 		if (cards[i].typ > 0)
 			nrcards++;
 	printk(KERN_DEBUG "HiSax: Total %d card%s defined\n",
@@ -583,16 +1431,71 @@
 
 	Isdnl1New();
 	CallcNew();
+	Isdnl3New();
 	Isdnl2New();
 	TeiNew();
 	HiSax_inithardware(busy_flag);
 	printk(KERN_NOTICE "HiSax: module installed\n");
+#endif
 	return (0);
 }
 #endif
+
 #ifdef CONFIG_HISAX_SEDLBAUER
 int sedl_init_pcmcia(void *pcm_iob, int pcm_irq, int *busy_flag, int prot)
 {
+#ifdef MODULE
+	int i;
+	int nzproto = 0;
+
+	nrcards = 0;
+	HiSaxVersion();
+	if (id)			/* If id= string used */
+		HiSax_id = id;
+	/* Initialize all 8 structs, even though we only accept
+	   two pcmcia cards
+	   */
+	for (i = 0; i < HISAX_MAX_CARDS; i++) {
+		cards[i].para[0] = irq[i];
+		cards[i].para[1] = io[i];
+		cards[i].typ = type[i];
+		if (protocol[i]) {
+			cards[i].protocol = protocol[i];
+			nzproto++;
+		}
+	}
+	cards[0].para[0] = pcm_irq;
+	cards[0].para[1] = (int)pcm_iob;
+	cards[0].protocol = prot;
+	cards[0].typ = ISDN_CTYPE_SEDLBAUER_PCMCIA;
+	nzproto = 1;
+
+	if (!HiSax_id)
+		HiSax_id = HiSaxID;
+	if (!HiSaxID[0])
+		strcpy(HiSaxID, "HiSax");
+	for (i = 0; i < HISAX_MAX_CARDS; i++)
+		if (cards[i].typ > 0)
+			nrcards++;
+	printk(KERN_DEBUG "HiSax: Total %d card%s defined\n",
+	       nrcards, (nrcards > 1) ? "s" : "");
+
+	CallcNew();
+	Isdnl3New();
+	Isdnl2New();
+	Isdnl1New();
+	TeiNew();
+	HiSax_inithardware(busy_flag);
+	printk(KERN_NOTICE "HiSax: module installed\n");
+#endif
+	return (0);
+}
+#endif
+
+#ifdef CONFIG_HISAX_AVM_A1_PCMCIA
+int avm_a1_init_pcmcia(void *pcm_iob, int pcm_irq, int *busy_flag, int prot)
+{
+#ifdef MODULE
 	int i;
 	int nzproto = 0;
 
@@ -615,14 +1518,14 @@
 	cards[0].para[0] = pcm_irq;
 	cards[0].para[1] = (int)pcm_iob;
 	cards[0].protocol = prot;
-	cards[0].typ = ISDN_CTYPE_SEDLBAUER_PCMCIA;
+	cards[0].typ = ISDN_CTYPE_A1_PCMCIA;
 	nzproto = 1;
 
 	if (!HiSax_id)
 		HiSax_id = HiSaxID;
 	if (!HiSaxID[0])
 		strcpy(HiSaxID, "HiSax");
-	for (i = 0; i < 16; i++)
+	for (i = 0; i < HISAX_MAX_CARDS; i++)
 		if (cards[i].typ > 0)
 			nrcards++;
 	printk(KERN_DEBUG "HiSax: Total %d card%s defined\n",
@@ -630,11 +1533,12 @@
 
 	Isdnl1New();
 	CallcNew();
+	Isdnl3New();
 	Isdnl2New();
 	TeiNew();
 	HiSax_inithardware(busy_flag);
 	printk(KERN_NOTICE "HiSax: module installed\n");
+#endif
 	return (0);
 }
 #endif
-#endif 
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/diva.c linux/drivers/isdn/hisax/diva.c
--- v2.3.3/linux/drivers/isdn/hisax/diva.c	Thu Nov  5 09:58:43 1998
+++ linux/drivers/isdn/hisax/diva.c	Sun May 23 10:03:41 1999
@@ -1,13 +1,30 @@
-/* $Id: diva.c,v 1.5 1998/02/02 13:29:38 keil Exp $
+/* $Id: diva.c,v 1.10 1998/11/15 23:54:31 keil Exp $
 
  * diva.c     low level stuff for Eicon.Diehl Diva Family ISDN cards
  *
- * Author     Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author     Karsten Keil (keil@isdn4linux.de)
  *
  * Thanks to Eicon Technology Diehl GmbH & Co. oHG for documents and informations
  *
  *
  * $Log: diva.c,v $
+ * Revision 1.10  1998/11/15 23:54:31  keil
+ * changes from 2.0
+ *
+ * Revision 1.9  1998/06/27 22:52:03  keil
+ * support for Diva 2.01
+ *
+ * Revision 1.8  1998/05/25 12:57:46  keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
+ * Revision 1.7  1998/04/15 16:42:36  keil
+ * new init code
+ * new PCI init (2.1.94)
+ *
+ * Revision 1.6  1998/03/07 22:56:57  tsbogend
+ * made HiSax working on Linux/Alpha
+ *
  * Revision 1.5  1998/02/02 13:29:38  keil
  * fast io
  *
@@ -31,13 +48,13 @@
 #include "hisax.h"
 #include "isac.h"
 #include "hscx.h"
+#include "ipac.h"
 #include "isdnl1.h"
 #include <linux/pci.h>
-#include <linux/bios32.h>
 
 extern const char *CardType[];
 
-const char *Diva_revision = "$Revision: 1.5 $";
+const char *Diva_revision = "$Revision: 1.10 $";
 
 #define byteout(addr,val) outb(val,addr)
 #define bytein(addr) inb(addr)
@@ -47,6 +64,8 @@
 #define DIVA_ISA_ISAC_DATA	2
 #define DIVA_ISA_ISAC_ADR	6
 #define DIVA_ISA_CTRL		7
+#define DIVA_IPAC_ADR		0
+#define DIVA_IPAC_DATA		1
 
 #define DIVA_PCI_ISAC_DATA	8
 #define DIVA_PCI_ISAC_ADR	0xc
@@ -55,6 +74,7 @@
 /* SUB Types */
 #define DIVA_ISA	1
 #define DIVA_PCI	2
+#define DIVA_IPAC_ISA	3
 
 /* PCI stuff */
 #define PCI_VENDOR_EICON_DIEHL	0x1133
@@ -140,13 +160,37 @@
 	readfifo(cs->hw.diva.isac_adr, cs->hw.diva.isac, 0, data, size);
 }
 
-static void 
+static void
 WriteISACfifo(struct IsdnCardState *cs, u_char *data, int size)
 {
 	writefifo(cs->hw.diva.isac_adr, cs->hw.diva.isac, 0, data, size);
 }
 
 static u_char
+ReadISAC_IPAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, offset+0x80));
+}
+
+static void
+WriteISAC_IPAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, offset|0x80, value);
+}
+
+static void
+ReadISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+	readfifo(cs->hw.diva.isac_adr, cs->hw.diva.isac, 0x80, data, size);
+}
+
+static void
+WriteISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+	writefifo(cs->hw.diva.isac_adr, cs->hw.diva.isac, 0x80, data, size);
+}
+
+static u_char
 ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
 {
 	return(readreg(cs->hw.diva.hscx_adr,
@@ -168,13 +212,13 @@
 		cs->hw.diva.hscx, reg + (nr ? 0x40 : 0))
 #define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.diva.hscx_adr, \
                 cs->hw.diva.hscx, reg + (nr ? 0x40 : 0), data)
-                
+
 #define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.diva.hscx_adr, \
 		cs->hw.diva.hscx, (nr ? 0x40 : 0), ptr, cnt)
-		
+
 #define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.diva.hscx_adr, \
 		cs->hw.diva.hscx, (nr ? 0x40 : 0), ptr, cnt)
-		
+
 #include "hscx_irq.c"
 
 static void
@@ -215,18 +259,69 @@
 	}
 }
 
+static void
+diva_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char ista,val;
+	int icnt=20;
+
+	if (!cs) {
+		printk(KERN_WARNING "Diva: Spurious interrupt!\n");
+		return;
+	}
+	ista = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_ISTA);
+Start_IPAC:
+	if (cs->debug & L1_DEB_IPAC)
+		debugl1(cs, "IPAC ISTA %02X", ista);
+	if (ista & 0x0f) {
+		val = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, HSCX_ISTA + 0x40);
+		if (ista & 0x01)
+			val |= 0x01;
+		if (ista & 0x04)
+			val |= 0x02;
+		if (ista & 0x08)
+			val |= 0x04;
+		if (val)
+			hscx_int_main(cs, val);
+	}
+	if (ista & 0x20) {
+		val = 0xfe & readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_ISTA + 0x80);
+		if (val) {
+			isac_interrupt(cs, val);
+		}
+	}
+	if (ista & 0x10) {
+		val = 0x01;
+		isac_interrupt(cs, val);
+	}
+	ista  = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_ISTA);
+	if ((ista & 0x3f) && icnt) {
+		icnt--;
+		goto Start_IPAC;
+	}
+	if (!icnt)
+		printk(KERN_WARNING "DIVA IPAC IRQ LOOP\n");
+	writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_MASK, 0xFF);
+	writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_MASK, 0xC0);
+}
+
+
 void
 release_io_diva(struct IsdnCardState *cs)
 {
 	int bytecnt;
-	
-	del_timer(&cs->hw.diva.tl);
-	if (cs->subtyp == DIVA_ISA)
+
+	if (cs->subtyp != DIVA_IPAC_ISA) {
+		del_timer(&cs->hw.diva.tl);
+		if (cs->hw.diva.cfg_reg)
+			byteout(cs->hw.diva.ctrl, 0); /* LED off, Reset */
+	}
+	if ((cs->subtyp == DIVA_ISA) || (cs->subtyp == DIVA_IPAC_ISA))
 		bytecnt = 8;
 	else
 		bytecnt = 32;
 	if (cs->hw.diva.cfg_reg) {
-		byteout(cs->hw.diva.ctrl, 0); /* LED off, Reset */
 		release_region(cs->hw.diva.cfg_reg, bytecnt);
 	}
 }
@@ -238,19 +333,30 @@
 
 	save_flags(flags);
 	sti();
-	cs->hw.diva.ctrl_reg = 0;        /* Reset On */
-	byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg);
-	current->state = TASK_INTERRUPTIBLE;
-	schedule_timeout((10*HZ)/1000);	/* Timeout 10ms */
-	cs->hw.diva.ctrl_reg |= DIVA_RESET;  /* Reset Off */
-	byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg);
-	current->state = TASK_INTERRUPTIBLE;
-	schedule_timeout((10*HZ)/1000);	/* Timeout 10ms */
-	if (cs->subtyp == DIVA_ISA)
-		cs->hw.diva.ctrl_reg |= DIVA_ISA_LED_A;
-	else
-		cs->hw.diva.ctrl_reg |= DIVA_PCI_LED_A;
-	byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg);
+	if (cs->subtyp == DIVA_IPAC_ISA) {
+		writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_POTA2, 0x20);
+		current->state = TASK_INTERRUPTIBLE;
+		schedule_timeout((10*HZ)/1000); /* Timeout 10ms */
+		writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_POTA2, 0x00);
+		current->state = TASK_INTERRUPTIBLE;
+		schedule_timeout((10*HZ)/1000); /* Timeout 10ms */
+		writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_MASK, 0xc0);
+	} else {
+		cs->hw.diva.ctrl_reg = 0;        /* Reset On */
+		byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg);
+		current->state = TASK_INTERRUPTIBLE;
+		schedule_timeout((10*HZ)/1000); /* Timeout 10ms */
+		cs->hw.diva.ctrl_reg |= DIVA_RESET;  /* Reset Off */
+		byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg);
+		current->state = TASK_INTERRUPTIBLE;
+		schedule_timeout((10*HZ)/1000); /* Timeout 10ms */
+		if (cs->subtyp == DIVA_ISA)
+			cs->hw.diva.ctrl_reg |= DIVA_ISA_LED_A;
+		else
+			cs->hw.diva.ctrl_reg |= DIVA_PCI_LED_A;
+		byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg);
+	}
+	restore_flags(flags);
 }
 
 #define DIVA_ASSIGN 1
@@ -260,6 +366,8 @@
 {
 	int blink = 0;
 
+	if (cs->subtyp == DIVA_IPAC_ISA)
+		return;
 	del_timer(&cs->hw.diva.tl);
 	if (cs->hw.diva.status & DIVA_ASSIGN)
 		cs->hw.diva.ctrl_reg |= (DIVA_ISA == cs->subtyp) ?
@@ -272,14 +380,14 @@
 	if (cs->hw.diva.status & 0xf000)
 		cs->hw.diva.ctrl_reg |= (DIVA_ISA == cs->subtyp) ?
 			DIVA_ISA_LED_B : DIVA_PCI_LED_B;
-	else if (cs->hw.diva.status & 0x0f00) { 
+	else if (cs->hw.diva.status & 0x0f00) {
 		cs->hw.diva.ctrl_reg ^= (DIVA_ISA == cs->subtyp) ?
 			DIVA_ISA_LED_B : DIVA_PCI_LED_B;
 		blink = 500;
 	} else
 		cs->hw.diva.ctrl_reg &= ~((DIVA_ISA == cs->subtyp) ?
 			DIVA_ISA_LED_B : DIVA_PCI_LED_B);
-	
+
 	byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg);
 	if (blink) {
 		init_timer(&cs->hw.diva.tl);
@@ -291,6 +399,8 @@
 static int
 Diva_card_msg(struct IsdnCardState *cs, int mt, void *arg)
 {
+	u_int irq_flag = I4L_IRQ_FLAG;
+
 	switch (mt) {
 		case CARD_RESET:
 			reset_diva(cs);
@@ -299,36 +409,40 @@
 			release_io_diva(cs);
 			return(0);
 		case CARD_SETIRQ:
-			return(request_irq(cs->irq, &diva_interrupt,
-					I4L_IRQ_FLAG, "HiSax", cs));
+			if (cs->subtyp == DIVA_PCI)
+				irq_flag |= SA_SHIRQ;
+			if (cs->subtyp == DIVA_IPAC_ISA) {
+				return(request_irq(cs->irq, &diva_interrupt_ipac,
+						irq_flag, "HiSax", cs));
+			} else {
+				return(request_irq(cs->irq, &diva_interrupt,
+						irq_flag, "HiSax", cs));
+			}
 		case CARD_INIT:
-			clear_pending_isac_ints(cs);
-			clear_pending_hscx_ints(cs);
-			initisac(cs);
-			inithscx(cs);
+			inithscxisac(cs, 3);
 			return(0);
 		case CARD_TEST:
 			return(0);
-		case MDL_REMOVE_REQ:
+		case (MDL_REMOVE | REQUEST):
 			cs->hw.diva.status = 0;
 			break;
-		case MDL_ASSIGN_REQ:
+		case (MDL_ASSIGN | REQUEST):
 			cs->hw.diva.status |= DIVA_ASSIGN;
 			break;
 		case MDL_INFO_SETUP:
-			if ((int)arg) 
+			if ((long)arg)
 				cs->hw.diva.status |=  0x0200;
 			else
 				cs->hw.diva.status |=  0x0100;
 			break;
 		case MDL_INFO_CONN:
-			if ((int)arg) 
+			if ((long)arg)
 				cs->hw.diva.status |=  0x2000;
 			else
 				cs->hw.diva.status |=  0x1000;
 			break;
 		case MDL_INFO_REL:
-			if ((int)arg) {
+			if ((long)arg) {
 				cs->hw.diva.status &=  ~0x2000;
 				cs->hw.diva.status &=  ~0x0200;
 			} else {
@@ -337,18 +451,19 @@
 			}
 			break;
 	}
-	diva_led_handler(cs);
+	if (cs->subtyp != DIVA_IPAC_ISA)
+		diva_led_handler(cs);
 	return(0);
 }
 
-
-
-static 	int pci_index __initdata = 0;
+static 	struct pci_dev *dev_diva __initdata = NULL;
+static 	struct pci_dev *dev_diva_u __initdata = NULL;
 
 __initfunc(int
 setup_diva(struct IsdnCard *card))
 {
 	int bytecnt;
+	u_char val;
 	struct IsdnCardState *cs = card->cs;
 	char tmp[64];
 
@@ -358,64 +473,72 @@
 		return(0);
 	cs->hw.diva.status = 0;
 	if (card->para[1]) {
-		cs->subtyp = DIVA_ISA;
 		cs->hw.diva.ctrl_reg = 0;
 		cs->hw.diva.cfg_reg = card->para[1];
-		cs->hw.diva.ctrl = card->para[1] + DIVA_ISA_CTRL;
-		cs->hw.diva.isac = card->para[1] + DIVA_ISA_ISAC_DATA;
-		cs->hw.diva.hscx = card->para[1] + DIVA_HSCX_DATA;
-		cs->hw.diva.isac_adr = card->para[1] + DIVA_ISA_ISAC_ADR;
-		cs->hw.diva.hscx_adr = card->para[1] + DIVA_HSCX_ADR;
+		val = readreg(cs->hw.diva.cfg_reg + DIVA_IPAC_ADR,
+			cs->hw.diva.cfg_reg + DIVA_IPAC_DATA, IPAC_ID);
+		printk(KERN_INFO "Diva: IPAC version %x\n", val);
+		if (val == 1) {
+			cs->subtyp = DIVA_IPAC_ISA;
+			cs->hw.diva.ctrl = 0;
+			cs->hw.diva.isac = card->para[1] + DIVA_IPAC_DATA;
+			cs->hw.diva.hscx = card->para[1] + DIVA_IPAC_DATA;
+			cs->hw.diva.isac_adr = card->para[1] + DIVA_IPAC_ADR;
+			cs->hw.diva.hscx_adr = card->para[1] + DIVA_IPAC_ADR;
+			test_and_set_bit(HW_IPAC, &cs->HW_Flags);
+		} else {
+			cs->subtyp = DIVA_ISA;
+			cs->hw.diva.ctrl = card->para[1] + DIVA_ISA_CTRL;
+			cs->hw.diva.isac = card->para[1] + DIVA_ISA_ISAC_DATA;
+			cs->hw.diva.hscx = card->para[1] + DIVA_HSCX_DATA;
+			cs->hw.diva.isac_adr = card->para[1] + DIVA_ISA_ISAC_ADR;
+			cs->hw.diva.hscx_adr = card->para[1] + DIVA_HSCX_ADR;
+		}
 		cs->irq = card->para[0];
 		bytecnt = 8;
 	} else {
 #if CONFIG_PCI
-		u_char pci_bus, pci_device_fn, pci_irq;
-		u_int pci_ioaddr;
+		if (!pci_present()) {
+			printk(KERN_ERR "Diva: no PCI bus present\n");
+			return(0);
+		}
 
 		cs->subtyp = 0;
-		for (; pci_index < 0xff; pci_index++) {
-			if (pcibios_find_device(PCI_VENDOR_EICON_DIEHL,
-			   PCI_DIVA20_ID, pci_index, &pci_bus, &pci_device_fn)
-			   == PCIBIOS_SUCCESSFUL)
+		if ((dev_diva = pci_find_device(PCI_VENDOR_EICON_DIEHL,
+			PCI_DIVA20_ID, dev_diva))) {
 				cs->subtyp = DIVA_PCI;
-			else if (pcibios_find_device(PCI_VENDOR_EICON_DIEHL,
-			   PCI_DIVA20_ID, pci_index, &pci_bus, &pci_device_fn)
-			   == PCIBIOS_SUCCESSFUL)
+			/* get IRQ */
+			cs->irq = dev_diva->irq;
+			/* get IO address */
+			cs->hw.diva.cfg_reg = dev_diva->base_address[2]
+				& PCI_BASE_ADDRESS_IO_MASK;
+		} else if ((dev_diva_u = pci_find_device(PCI_VENDOR_EICON_DIEHL,
+			PCI_DIVA20_U_ID, dev_diva_u))) {
 			   	cs->subtyp = DIVA_PCI;
-			else
-				break;
 			/* get IRQ */
-			pcibios_read_config_byte(pci_bus, pci_device_fn,
-				PCI_INTERRUPT_LINE, &pci_irq);
-
+			cs->irq = dev_diva_u->irq;
 			/* get IO address */
-			pcibios_read_config_dword(pci_bus, pci_device_fn,
-				PCI_BASE_ADDRESS_2, &pci_ioaddr);
-			if (cs->subtyp)
-				break;
-		}
-		if (!cs->subtyp) {
+			cs->hw.diva.cfg_reg = dev_diva_u->base_address[2]
+				& PCI_BASE_ADDRESS_IO_MASK;
+		} else {
 			printk(KERN_WARNING "Diva: No PCI card found\n");
 			return(0);
 		}
-		if (!pci_irq) {
+
+		if (!cs->irq) {
 			printk(KERN_WARNING "Diva: No IRQ for PCI card found\n");
 			return(0);
 		}
 
-		if (!pci_ioaddr) {
+		if (!cs->hw.diva.cfg_reg) {
 			printk(KERN_WARNING "Diva: No IO-Adr for PCI card found\n");
 			return(0);
 		}
-		pci_ioaddr &= ~3; /* remove io/mem flag */
-		cs->hw.diva.cfg_reg = pci_ioaddr; 
-		cs->hw.diva.ctrl = pci_ioaddr + DIVA_PCI_CTRL;
-		cs->hw.diva.isac = pci_ioaddr + DIVA_PCI_ISAC_DATA;
-		cs->hw.diva.hscx = pci_ioaddr + DIVA_HSCX_DATA;
-		cs->hw.diva.isac_adr = pci_ioaddr + DIVA_PCI_ISAC_ADR;
-		cs->hw.diva.hscx_adr = pci_ioaddr + DIVA_HSCX_ADR;
-		cs->irq = pci_irq;
+		cs->hw.diva.ctrl = cs->hw.diva.cfg_reg + DIVA_PCI_CTRL;
+		cs->hw.diva.isac = cs->hw.diva.cfg_reg + DIVA_PCI_ISAC_DATA;
+		cs->hw.diva.hscx = cs->hw.diva.cfg_reg + DIVA_HSCX_DATA;
+		cs->hw.diva.isac_adr = cs->hw.diva.cfg_reg + DIVA_PCI_ISAC_ADR;
+		cs->hw.diva.hscx_adr = cs->hw.diva.cfg_reg + DIVA_HSCX_ADR;
 		bytecnt = 32;
 #else
 		printk(KERN_WARNING "Diva: cfgreg 0 and NO_PCI_BIOS\n");
@@ -426,7 +549,8 @@
 
 	printk(KERN_INFO
 		"Diva: %s card configured at 0x%x IRQ %d\n",
-		(cs->subtyp == DIVA_ISA) ? "ISA" : "PCI",
+		(cs->subtyp == DIVA_PCI) ? "PCI" :
+		(cs->subtyp == DIVA_ISA) ? "ISA" : "IPAC",
 		cs->hw.diva.cfg_reg, cs->irq);
 	if (check_region(cs->hw.diva.cfg_reg, bytecnt)) {
 		printk(KERN_WARNING
@@ -440,24 +564,32 @@
 	}
 
 	reset_diva(cs);
-	cs->hw.diva.tl.function = (void *) diva_led_handler;
-	cs->hw.diva.tl.data = (long) cs;
-	init_timer(&cs->hw.diva.tl);
-	cs->readisac  = &ReadISAC;
-	cs->writeisac = &WriteISAC;
-	cs->readisacfifo  = &ReadISACfifo;
-	cs->writeisacfifo = &WriteISACfifo;
 	cs->BC_Read_Reg  = &ReadHSCX;
 	cs->BC_Write_Reg = &WriteHSCX;
 	cs->BC_Send_Data = &hscx_fill_fifo;
 	cs->cardmsg = &Diva_card_msg;
-
-	ISACVersion(cs, "Diva:");
-	if (HscxVersion(cs, "Diva:")) {
-		printk(KERN_WARNING
+	if (cs->subtyp == DIVA_IPAC_ISA) {
+		cs->readisac  = &ReadISAC_IPAC;
+		cs->writeisac = &WriteISAC_IPAC;
+		cs->readisacfifo  = &ReadISACfifo_IPAC;
+		cs->writeisacfifo = &WriteISACfifo_IPAC;
+		val = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_ID);
+		printk(KERN_INFO "Diva: IPAC version %x\n", val);
+	} else {
+		cs->hw.diva.tl.function = (void *) diva_led_handler;
+		cs->hw.diva.tl.data = (long) cs;
+		init_timer(&cs->hw.diva.tl);
+		cs->readisac  = &ReadISAC;
+		cs->writeisac = &WriteISAC;
+		cs->readisacfifo  = &ReadISACfifo;
+		cs->writeisacfifo = &WriteISACfifo;
+		ISACVersion(cs, "Diva:");
+		if (HscxVersion(cs, "Diva:")) {
+			printk(KERN_WARNING
 		       "Diva: wrong HSCX versions check IO address\n");
-		release_io_diva(cs);
-		return (0);
+			release_io_diva(cs);
+			return (0);
+		}
 	}
 	return (1);
 }
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/elsa.c linux/drivers/isdn/hisax/elsa.c
--- v2.3.3/linux/drivers/isdn/hisax/elsa.c	Thu Nov  5 09:58:44 1998
+++ linux/drivers/isdn/hisax/elsa.c	Sun May 23 10:03:41 1999
@@ -1,13 +1,41 @@
-/* $Id: elsa.c,v 2.6 1998/02/02 13:29:40 keil Exp $
+/* $Id: elsa.c,v 2.12 1998/11/15 23:54:35 keil Exp $
 
  * elsa.c     low level stuff for Elsa isdn cards
  *
- * Author     Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author     Karsten Keil (keil@isdn4linux.de)
+ *
+ *		This file is (c) under GNU PUBLIC LICENSE
+ *		For changes and modifications please read
+ *		../../../Documentation/isdn/HiSax.cert
  *
  * Thanks to    Elsa GmbH for documents and informations
  *
+ *              Klaus Lichtenwalder (Klaus.Lichtenwalder@WebForum.DE)
+ *              for ELSA PCMCIA support
+ *
  *
  * $Log: elsa.c,v $
+ * Revision 2.12  1998/11/15 23:54:35  keil
+ * changes from 2.0
+ *
+ * Revision 2.11  1998/08/20 13:50:34  keil
+ * More support for hybrid modem (not working yet)
+ *
+ * Revision 2.10  1998/08/13 23:36:22  keil
+ * HiSax 3.1 - don't work stable with current LinkLevel
+ *
+ * Revision 2.9  1998/05/25 12:57:48  keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
+ * Revision 2.8  1998/04/15 16:41:42  keil
+ * QS3000 PCI support
+ * new init code
+ * new PCI init (2.1.94)
+ *
+ * Revision 2.7  1998/03/07 22:56:58  tsbogend
+ * made HiSax working on Linux/Alpha
+ *
  * Revision 2.6  1998/02/02 13:29:40  keil
  * fast io
  *
@@ -29,15 +57,6 @@
  * Revision 2.0  1997/06/26 11:02:40  keil
  * New Layer and card interface
  *
- * Revision 1.14  1997/04/13 19:53:25  keil
- * Fixed QS1000 init, change in IRQ check delay for SMP
- *
- * Revision 1.13  1997/04/07 22:58:07  keil
- * need include config.h
- *
- * Revision 1.12  1997/04/06 22:54:14  keil
- * Using SKB's
- *
  * old changes removed KKe
  *
  */
@@ -51,14 +70,16 @@
 #include "hscx.h"
 #include "isdnl1.h"
 #include <linux/pci.h>
-#include <linux/bios32.h>
+
+//#define KDEBUG_DEF
+//#include "../kdebug.h"
 
 extern const char *CardType[];
 
-const char *Elsa_revision = "$Revision: 2.6 $";
+static const char *Elsa_revision = "$Revision: 2.12 $";
 const char *Elsa_Types[] =
 {"None", "PC", "PCC-8", "PCC-16", "PCF", "PCF-Pro",
- "PCMCIA", "QS 1000", "QS 3000", "QS 1000 PCI"};
+ "PCMCIA", "QS 1000", "QS 3000", "QS 1000 PCI", "QS 3000 PCI"};
 
 const char *ITACVer[] =
 {"?0?", "?1?", "?2?", "?3?", "?4?", "V2.2",
@@ -87,11 +108,13 @@
 #define ELSA_QS1000  7
 #define ELSA_QS3000  8
 #define ELSA_QS1000PCI 9
+#define ELSA_QS3000PCI 10
 
 /* PCI stuff */
 #define PCI_VENDOR_ELSA	0x1048
 #define PCI_QS1000_ID	0x1000
-
+#define PCI_QS3000_ID	0x3000
+#define ELSA_PCI_IRQ_MASK	0x04
 
 /* ITAC Registeradressen (only Microlink PC) */
 #define ITAC_SYS	0x34
@@ -128,6 +151,30 @@
 #define ELSA_BAD_PWR     2
 #define ELSA_ASSIGN      4
 
+#define RS_ISR_PASS_LIMIT 256
+#define _INLINE_ inline
+#define FLG_MODEM_ACTIVE 1
+/* IPAC AUX */
+#define ELSA_IPAC_LINE_LED	0x40	/* Bit 6 Gelbe LED */
+#define ELSA_IPAC_STAT_LED	0x80	/* Bit 7 Gruene LED */
+
+const u_char ARCOFI_VERSION[] = {2,0xa0,0};
+const u_char ARCOFI_COP_5[] = {4,0xa1,0x25,0xbb,0x4a}; /* GTX */
+const u_char ARCOFI_COP_6[] = {6,0xa1,0x26,0,0,0x82,0x7c}; /* GRL GRH */
+const u_char ARCOFI_COP_7[] = {4,0xa1,0x27,0x80,0x80}; /* GZ */
+const u_char ARCOFI_COP_8[] = {10,0xa1,0x28,0x49,0x31,0x8,0x13,0x6e,0x88,0x2a,0x61}; /* TX */
+const u_char ARCOFI_COP_9[] = {10,0xa1,0x29,0x80,0xcb,0xe9,0x88,0x00,0xc8,0xd8,0x80}; /* RX */
+const u_char ARCOFI_XOP_0[] = {2,0xa1,0x30}; /* PWR Down */
+const u_char ARCOFI_XOP_1[] = {2,0xa1,0x31}; /* PWR UP */
+const u_char ARCOFI_XOP_F[] = {2,0xa1,0x3f}; /* Normal OP */
+const u_char ARCOFI_SOP_F[] = {10,0xa1,0x1f,0x00,0x50,0x10,0x00,0x00,0x80,0x02,0x12};
+
+static void set_arcofi(struct IsdnCardState *cs, int bc);
+
+#if ARCOFI_USE
+#include "elsa_ser.c"
+#endif
+
 static inline u_char
 readreg(unsigned int ale, unsigned int adr, u_char off)
 {
@@ -302,6 +349,21 @@
 		printk(KERN_WARNING "Elsa: Spurious interrupt!\n");
 		return;
 	}
+	if ((cs->typ == ISDN_CTYPE_ELSA_PCMCIA) && (*cs->busy_flag == 1)) {
+	/* The card tends to generate interrupts while being removed
+	   causing us to just crash the kernel. bad. */
+		printk(KERN_WARNING "Elsa: card not available!\n");
+		return;
+	}
+#if ARCOFI_USE
+	if (cs->hw.elsa.MFlag) {
+		val = serial_inp(cs, UART_IIR);
+		if (!(val & UART_IIR_NO_INT)) {
+			debugl1(cs,"IIR %02x", val);
+			rs_interrupt_elsa(intno, cs);
+		}
+	}
+#endif
 	val = readreg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_ISTA + 0x40);
       Start_HSCX:
 	if (val) {
@@ -338,6 +400,14 @@
 			cs->hw.elsa.counter++;
 		}
 	}
+	if (cs->hw.elsa.MFlag) {
+		val = serial_inp(cs, UART_MCR);
+		val ^= 0x8;
+		serial_outp(cs, UART_MCR, val);
+		val = serial_inp(cs, UART_MCR);
+		val ^= 0x8;
+		serial_outp(cs, UART_MCR, val);
+	}
 	if (cs->hw.elsa.trig)
 		byteout(cs->hw.elsa.trig, 0x00);
 	writereg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_MASK, 0x0);
@@ -350,25 +420,28 @@
 {
 	struct IsdnCardState *cs = dev_id;
 	u_char ista,val;
-	char   tmp[64];
 	int icnt=20;
 
 	if (!cs) {
 		printk(KERN_WARNING "Elsa: Spurious interrupt!\n");
 		return;
 	}
-	if ((cs->typ == ISDN_CTYPE_ELSA_PCMCIA) && (*cs->busy_flag == 1)) {
-	  /* The card tends to generate interrupts while being removed
-	     causing us to just crash the kernel. bad. */
-	  printk(KERN_WARNING "Elsa: card not available!\n");
-	  return;
+	val = bytein(cs->hw.elsa.cfg + 0x4c); /* PCI IRQ */
+	if (!(val & ELSA_PCI_IRQ_MASK))
+		return;
+#if ARCOFI_USE
+	if (cs->hw.elsa.MFlag) {
+		val = serial_inp(cs, UART_IIR);
+		if (!(val & UART_IIR_NO_INT)) {
+			debugl1(cs,"IIR %02x", val);
+			rs_interrupt_elsa(intno, cs);
+		}
 	}
+#endif
 	ista = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ISTA);
 Start_IPAC:
-	if (cs->debug & L1_DEB_IPAC) {
-		sprintf(tmp, "IPAC ISTA %02X", ista);
-		debugl1(cs, tmp);
-	}
+	if (cs->debug & L1_DEB_IPAC)
+		debugl1(cs, "IPAC ISTA %02X", ista);
 	if (ista & 0x0f) {
 		val = readreg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_ISTA + 0x40);
 		if (ista & 0x01)
@@ -409,15 +482,24 @@
 	del_timer(&cs->hw.elsa.tl);
 	if (cs->hw.elsa.ctrl)
 		byteout(cs->hw.elsa.ctrl, 0);	/* LEDs Out */
-	if ((cs->subtyp == ELSA_PCFPRO) || 
-		(cs->subtyp == ELSA_QS3000) ||
-		(cs->subtyp == ELSA_PCF))
-		bytecnt = 16;
 	if (cs->subtyp == ELSA_QS1000PCI) {
 		byteout(cs->hw.elsa.cfg + 0x4c, 0x01);  /* disable IRQ */
+		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ATX, 0xff);
 		bytecnt = 2;
 		release_region(cs->hw.elsa.cfg, 0x80);
 	}
+	if (cs->subtyp == ELSA_QS3000PCI) {
+		byteout(cs->hw.elsa.cfg + 0x4c, 0x03); /* disable ELSA PCI IRQ */
+		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ATX, 0xff);
+		release_region(cs->hw.elsa.cfg, 0x80);
+	}
+	if ((cs->subtyp == ELSA_PCFPRO) ||
+		(cs->subtyp == ELSA_QS3000) ||
+		(cs->subtyp == ELSA_PCF) ||
+		(cs->subtyp == ELSA_QS3000PCI)) {
+		bytecnt = 16;
+		release_modem(cs);
+	}
 	if (cs->hw.elsa.base)
 		release_region(cs->hw.elsa.base, bytecnt);
 }
@@ -445,7 +527,7 @@
 		if (cs->hw.elsa.trig)
 			byteout(cs->hw.elsa.trig, 0xff);
 	}
-	if (cs->subtyp == ELSA_QS1000PCI) {
+	if ((cs->subtyp == ELSA_QS1000PCI) || (cs->subtyp == ELSA_QS3000PCI)) {
 		save_flags(flags);
 		sti();
 		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_POTA2, 0x20);
@@ -455,35 +537,55 @@
 		current->state = TASK_INTERRUPTIBLE;
 		schedule_timeout((10*HZ)/1000);	/* Timeout 10ms */
 		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_MASK, 0xc0);
-		schedule();
 		restore_flags(flags);
-		byteout(cs->hw.elsa.cfg + 0x4c, 0x41); /* enable ELSA PCI IRQ */
+		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ACFG, 0x0);
+		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_AOE, 0x3c);
+		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ATX, 0xff);
+		if (cs->subtyp == ELSA_QS1000PCI)
+			byteout(cs->hw.elsa.cfg + 0x4c, 0x41); /* enable ELSA PCI IRQ */
+		else if (cs->subtyp == ELSA_QS3000PCI)
+			byteout(cs->hw.elsa.cfg + 0x4c, 0x43); /* enable ELSA PCI IRQ */
 	}
 }
 
-const u_char ARCOFI_VERSION[] = {2,0xa0,0};
-const u_char ARCOFI_COP_5[] = {4,0xa1,0x25,0xbb,0x4a}; /* GTX */
-const u_char ARCOFI_COP_6[] = {6,0xa1,0x26,0,0,0x82,0x7c}; /* GRL GRH */
-const u_char ARCOFI_COP_7[] = {4,0xa1,0x27,0x80,0x80}; /* GZ */
-const u_char ARCOFI_COP_8[] = {10,0xa1,0x28,0x49,0x31,0x8,0x13,0x6e,0x88,0x2a,0x61}; /* TX */
-const u_char ARCOFI_COP_9[] = {10,0xa1,0x29,0x80,0xcb,0x9e,0x88,0x00,0xc8,0xd8,0x80}; /* RX */
-const u_char ARCOFI_XOP_0[] = {2,0xa1,0x30}; /* PWR Down */
-const u_char ARCOFI_XOP_1[] = {2,0xa1,0x31}; /* PWR Down */
-const u_char ARCOFI_XOP_F[] = {2,0xa1,0x3f}; /* PWR Down */
-const u_char ARCOFI_SOP_F[] = {10,0xa1,0x1f,0x00,0x50,0x10,0x00,0x00,0x80,0x02,0x12};
- 
 static void
 init_arcofi(struct IsdnCardState *cs) {
-	send_arcofi(cs, ARCOFI_COP_5);
-	send_arcofi(cs, ARCOFI_COP_6);
-	send_arcofi(cs, ARCOFI_COP_7);
-	send_arcofi(cs, ARCOFI_COP_8);
-	send_arcofi(cs, ARCOFI_COP_9);
-	send_arcofi(cs, ARCOFI_SOP_F);
-	send_arcofi(cs, ARCOFI_XOP_F);
+	send_arcofi(cs, ARCOFI_XOP_0, 1, 0);
+/*	send_arcofi(cs, ARCOFI_XOP_F, 1);
+*/
 }
 
+#define ARCDEL 500
+
 static void
+set_arcofi(struct IsdnCardState *cs, int bc) {
+	long flags;
+
+	debugl1(cs,"set_arcofi bc=%d", bc);
+	save_flags(flags);
+	sti();
+	send_arcofi(cs, ARCOFI_XOP_0, bc, 0);
+	udelay(ARCDEL);
+	send_arcofi(cs, ARCOFI_COP_5, bc, 0);
+	udelay(ARCDEL);
+	send_arcofi(cs, ARCOFI_COP_6, bc, 0);
+	udelay(ARCDEL);
+	send_arcofi(cs, ARCOFI_COP_7, bc, 0);
+	udelay(ARCDEL);
+	send_arcofi(cs, ARCOFI_COP_8, bc, 0);
+	udelay(ARCDEL);
+	send_arcofi(cs, ARCOFI_COP_9, bc, 0);
+	udelay(ARCDEL);
+	send_arcofi(cs, ARCOFI_SOP_F, bc, 0);
+	udelay(ARCDEL);
+	send_arcofi(cs, ARCOFI_XOP_1, bc, 0);
+	udelay(ARCDEL);
+	send_arcofi(cs, ARCOFI_XOP_F, bc, 0);
+	restore_flags(flags);
+	debugl1(cs,"end set_arcofi bc=%d", bc);
+}
+
+static int
 check_arcofi(struct IsdnCardState *cs)
 {
 #if ARCOFI_USE
@@ -496,13 +598,12 @@
 		if (!(cs->mon_tx=kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) {
 			if (cs->debug & L1_DEB_WARN)
 				debugl1(cs, "ISAC MON TX out of buffers!");
-			return;
+			return(0);
 		}
-	send_arcofi(cs, ARCOFI_VERSION);
+	send_arcofi(cs, ARCOFI_VERSION, 0, 1);
 	if (test_and_clear_bit(HW_MON1_TX_END, &cs->HW_Flags)) {
 		if (test_and_clear_bit(HW_MON1_RX_END, &cs->HW_Flags)) {
-			sprintf(tmp, "Arcofi response received %d bytes", cs->mon_rxp);
-			debugl1(cs, tmp);
+			debugl1(cs, "Arcofi response received %d bytes", cs->mon_rxp);
 			p = cs->mon_rx;
 			t = tmp;
 			t += sprintf(tmp, "Arcofi data");
@@ -531,8 +632,7 @@
 			cs->mon_rxp = 0;
 		}
 	} else if (cs->mon_tx) {
-		sprintf(tmp, "Arcofi not detected");
-		debugl1(cs, tmp);
+		debugl1(cs, "Arcofi not detected");
 	}
 	if (arcofi_present) {
 		if (cs->subtyp==ELSA_QS1000) {
@@ -573,8 +673,10 @@
 				Elsa_Types[cs->subtyp],
 				cs->hw.elsa.base+8);
 		init_arcofi(cs);
+		return(1);
 	}
 #endif
+	return(0);
 }
 
 static void
@@ -582,8 +684,7 @@
 {
 	int blink = 0;
 
-	if ((cs->subtyp == ELSA_PCMCIA) &&
-		(cs->subtyp == ELSA_QS1000PCI))
+	if (cs->subtyp == ELSA_PCMCIA)
 		return;
 	del_timer(&cs->hw.elsa.tl);
 	if (cs->hw.elsa.status & ELSA_ASSIGN)
@@ -602,7 +703,16 @@
 	} else
 		cs->hw.elsa.ctrl_reg &= ~ELSA_LINE_LED;
 
-	byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg);
+	if ((cs->subtyp == ELSA_QS1000PCI) ||
+		(cs->subtyp == ELSA_QS3000PCI)) {
+		u_char led = 0xff;
+		if (cs->hw.elsa.ctrl_reg & ELSA_LINE_LED)
+			led ^= ELSA_IPAC_LINE_LED;
+		if (cs->hw.elsa.ctrl_reg & ELSA_STAT_LED)
+			led ^= ELSA_IPAC_STAT_LED;
+		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ATX, led);
+	} else
+		byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg);
 	if (blink) {
 		init_timer(&cs->hw.elsa.tl);
 		cs->hw.elsa.tl.expires = jiffies + ((blink * HZ) / 1000);
@@ -613,8 +723,9 @@
 static int
 Elsa_card_msg(struct IsdnCardState *cs, int mt, void *arg)
 {
-	int pwr, ret = 0;
-	long flags;	
+	int len, ret = 0;
+	u_char *msg;
+	long flags;
 
 	switch (mt) {
 		case CARD_RESET:
@@ -624,28 +735,33 @@
 			release_io_elsa(cs);
 			return(0);
 		case CARD_SETIRQ:
-			if (cs->subtyp == ELSA_QS1000PCI)
+			if ((cs->subtyp == ELSA_QS1000PCI) ||
+				(cs->subtyp == ELSA_QS3000PCI))
 				ret = request_irq(cs->irq, &elsa_interrupt_ipac,
-					I4L_IRQ_FLAG, "HiSax", cs);
+					I4L_IRQ_FLAG | SA_SHIRQ, "HiSax", cs);
 			else
 				ret = request_irq(cs->irq, &elsa_interrupt,
 					I4L_IRQ_FLAG, "HiSax", cs);
 			return(ret);
 		case CARD_INIT:
-			if (cs->hw.elsa.trig)
-				byteout(cs->hw.elsa.trig, 0xff);
-			clear_pending_isac_ints(cs);
-			clear_pending_hscx_ints(cs);
-			initisac(cs);
-			inithscx(cs);
-			if (cs->subtyp == ELSA_QS1000) {
+			cs->debug |= L1_DEB_IPAC;
+			inithscxisac(cs, 1);
+			if ((cs->subtyp == ELSA_QS1000) ||
+			    (cs->subtyp == ELSA_QS3000))
+			{
 				byteout(cs->hw.elsa.timer, 0);
-				byteout(cs->hw.elsa.trig, 0xff);
 			}
+			if (cs->hw.elsa.trig)
+				byteout(cs->hw.elsa.trig, 0xff);
+			inithscxisac(cs, 2);
 			return(0);
 		case CARD_TEST:
-			if ((cs->subtyp != ELSA_PCMCIA) &&
-				(cs->subtyp != ELSA_QS1000PCI)) {
+			if ((cs->subtyp == ELSA_PCMCIA) ||
+				(cs->subtyp == ELSA_QS1000PCI)) {
+				return(0);
+			} else if (cs->subtyp == ELSA_QS3000PCI) {
+				ret = 0;
+			} else {
 				save_flags(flags);
 				cs->hw.elsa.counter = 0;
 				sti();
@@ -653,48 +769,52 @@
 				cs->hw.elsa.status |= ELSA_TIMER_AKTIV;
 				byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg);
 				byteout(cs->hw.elsa.timer, 0);
-			} else
-				return(0);
-			current->state = TASK_INTERRUPTIBLE;
-			schedule_timeout((110*HZ)/1000);		/* Timeout 110ms */
-			restore_flags(flags);
-			cs->hw.elsa.ctrl_reg &= ~ELSA_ENA_TIMER_INT;
-			byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg);
-			cs->hw.elsa.status &= ~ELSA_TIMER_AKTIV;
-			printk(KERN_INFO "Elsa: %d timer tics in 110 msek\n",
-			       cs->hw.elsa.counter);
-			if (abs(cs->hw.elsa.counter - 13) < 3) {
-				printk(KERN_INFO "Elsa: timer and irq OK\n");
-				ret = 0;
-			} else {
-				printk(KERN_WARNING
-				       "Elsa: timer tic problem (%d/12) maybe an IRQ(%d) conflict\n",
-				       cs->hw.elsa.counter, cs->irq);
-				ret = 1;
+				current->state = TASK_INTERRUPTIBLE;
+				/* Timeout 110ms */
+				schedule_timeout((110*HZ)/1000);
+				restore_flags(flags);
+				cs->hw.elsa.ctrl_reg &= ~ELSA_ENA_TIMER_INT;
+				byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg);
+				cs->hw.elsa.status &= ~ELSA_TIMER_AKTIV;
+				printk(KERN_INFO "Elsa: %d timer tics in 110 msek\n",
+				       cs->hw.elsa.counter);
+				if (abs(cs->hw.elsa.counter - 13) < 3) {
+					printk(KERN_INFO "Elsa: timer and irq OK\n");
+					ret = 0;
+				} else {
+					printk(KERN_WARNING
+					       "Elsa: timer tic problem (%d/12) maybe an IRQ(%d) conflict\n",
+					       cs->hw.elsa.counter, cs->irq);
+					ret = 1;
+				}
 			}
-			check_arcofi(cs);
+#if ARCOFI_USE
+			if (check_arcofi(cs)) {
+				init_modem(cs);
+			}
+#endif
 			elsa_led_handler(cs);
 			return(ret);
-		case MDL_REMOVE_REQ:
+		case (MDL_REMOVE | REQUEST):
 			cs->hw.elsa.status &= 0;
 			break;
-		case MDL_ASSIGN_REQ:
+		case (MDL_ASSIGN | REQUEST):
 			cs->hw.elsa.status |= ELSA_ASSIGN;
 			break;
 		case MDL_INFO_SETUP:
-			if ((int) arg)
+			if ((long) arg)
 				cs->hw.elsa.status |= 0x0200;
 			else
 				cs->hw.elsa.status |= 0x0100;
 			break;
 		case MDL_INFO_CONN:
-			if ((int) arg)
+			if ((long) arg)
 				cs->hw.elsa.status |= 0x2000;
 			else
 				cs->hw.elsa.status |= 0x1000;
 			break;
 		case MDL_INFO_REL:
-			if ((int) arg) {
+			if ((long) arg) {
 				cs->hw.elsa.status &= ~0x2000;
 				cs->hw.elsa.status &= ~0x0200;
 			} else {
@@ -703,13 +823,23 @@
 			}
 			break;
 		case CARD_AUX_IND:
+			if (cs->hw.elsa.MFlag) {
+				if (!arg)
+					return(0);
+				msg = arg;
+				len = *msg;
+				msg++;
+				modem_write_cmd(cs, msg, len);
+			}
 			break;
 	}
-	pwr = bytein(cs->hw.elsa.ale);
-	if (pwr & 0x08)
-		cs->hw.elsa.status |= ELSA_BAD_PWR;
-	else
-		cs->hw.elsa.status &= ~ELSA_BAD_PWR;
+	if (cs->typ == ISDN_CTYPE_ELSA) {
+		int pwr = bytein(cs->hw.elsa.ale);
+		if (pwr & 0x08)
+			cs->hw.elsa.status |= ELSA_BAD_PWR;
+		else
+			cs->hw.elsa.status &= ~ELSA_BAD_PWR;
+	}
 	elsa_led_handler(cs);
 	return(ret);
 }
@@ -778,7 +908,8 @@
 	return (CARD_portlist[i]);
 }
 
-static 	int pci_index __initdata = 0;
+static 	struct pci_dev *dev_qs1000 __initdata = NULL;
+static 	struct pci_dev *dev_qs3000 __initdata = NULL;
 
 int
 setup_elsa(struct IsdnCard *card)
@@ -793,6 +924,7 @@
 	printk(KERN_INFO "HiSax: Elsa driver Rev. %s\n", HiSax_getrev(tmp));
 	cs->hw.elsa.ctrl_reg = 0;
 	cs->hw.elsa.status = 0;
+	cs->hw.elsa.MFlag = 0;
 	if (cs->typ == ISDN_CTYPE_ELSA) {
 		cs->hw.elsa.base = card->para[0];
 		printk(KERN_INFO "Elsa: Microlink IO probing\n");
@@ -886,59 +1018,62 @@
 		       cs->irq);
 	} else if (cs->typ == ISDN_CTYPE_ELSA_PCI) {
 #if CONFIG_PCI
-		u_char pci_bus, pci_device_fn, pci_irq;
-		u_int pci_ioaddr;
-
+		if (!pci_present()) {
+			printk(KERN_ERR "Elsa: no PCI bus present\n");
+			return(0);
+		}
 		cs->subtyp = 0;
-		for (; pci_index < 0xff; pci_index++) {
-			if (pcibios_find_device(PCI_VENDOR_ELSA,
-			   PCI_QS1000_ID, pci_index, &pci_bus, &pci_device_fn)
-			   == PCIBIOS_SUCCESSFUL)
+		if ((dev_qs1000 = pci_find_device(PCI_VENDOR_ELSA, PCI_QS1000_ID,
+			 dev_qs1000))) {
 				cs->subtyp = ELSA_QS1000PCI;
-			else
-				break;
-			/* get IRQ */
-			pcibios_read_config_byte(pci_bus, pci_device_fn,
-				PCI_INTERRUPT_LINE, &pci_irq);
-
-			/* get IO address */
-			pcibios_read_config_dword(pci_bus, pci_device_fn,
-				PCI_BASE_ADDRESS_1, &pci_ioaddr);
-			pci_ioaddr &= ~3; /* remove io/mem flag */
-			cs->hw.elsa.cfg = pci_ioaddr;
-			pcibios_read_config_dword(pci_bus, pci_device_fn,
-				PCI_BASE_ADDRESS_3, &pci_ioaddr);
-			if (cs->subtyp)
-				break;
-		}
-		if (!cs->subtyp) {
+			cs->irq = dev_qs1000->irq;
+			cs->hw.elsa.cfg = dev_qs1000->base_address[1] & 
+				PCI_BASE_ADDRESS_IO_MASK;
+			cs->hw.elsa.base = dev_qs1000->base_address[3] & 
+				PCI_BASE_ADDRESS_IO_MASK;
+		} else if ((dev_qs3000 = pci_find_device(PCI_VENDOR_ELSA,
+			PCI_QS3000_ID, dev_qs3000))) {
+			cs->subtyp = ELSA_QS3000PCI;
+			cs->irq = dev_qs3000->irq;
+			cs->hw.elsa.cfg = dev_qs3000->base_address[1] & 
+				PCI_BASE_ADDRESS_IO_MASK;
+			cs->hw.elsa.base = dev_qs3000->base_address[3] & 
+				PCI_BASE_ADDRESS_IO_MASK;
+		} else {
 			printk(KERN_WARNING "Elsa: No PCI card found\n");
 			return(0);
 		}
-		if (!pci_irq) {
+		if (!cs->irq) {
 			printk(KERN_WARNING "Elsa: No IRQ for PCI card found\n");
 			return(0);
 		}
 
-		if (!pci_ioaddr) {
+		if (!(cs->hw.elsa.base && cs->hw.elsa.cfg)) {
 			printk(KERN_WARNING "Elsa: No IO-Adr for PCI card found\n");
 			return(0);
 		}
-		pci_ioaddr &= ~3; /* remove io/mem flag */
-		cs->hw.elsa.base = pci_ioaddr;
-		cs->hw.elsa.ale  = pci_ioaddr;
-		cs->hw.elsa.isac = pci_ioaddr +1;
-		cs->hw.elsa.hscx = pci_ioaddr +1; 
-		cs->irq = pci_irq;
+		cs->hw.elsa.ale  = cs->hw.elsa.base;
+		cs->hw.elsa.isac = cs->hw.elsa.base +1;
+		cs->hw.elsa.hscx = cs->hw.elsa.base +1; 
 		test_and_set_bit(HW_IPAC, &cs->HW_Flags);
 		cs->hw.elsa.timer = 0;
 		cs->hw.elsa.trig  = 0;
 		printk(KERN_INFO
-		       "Elsa: %s defined at 0x%x/0x%x IRQ %d\n",
-		       Elsa_Types[cs->subtyp],
-		       cs->hw.elsa.base,
-		       cs->hw.elsa.cfg,
-		       cs->irq);
+			"Elsa: %s defined at 0x%x/0x%x IRQ %d\n",
+			Elsa_Types[cs->subtyp],
+			cs->hw.elsa.base,
+			cs->hw.elsa.cfg,
+			cs->irq);
+		if ((cs->hw.elsa.cfg & 0xff) || (cs->hw.elsa.base & 0xf)) {
+			printk(KERN_WARNING "Elsa: You may have a wrong PCI bios\n");
+			printk(KERN_WARNING "Elsa: If your system hangs now, read\n");
+			printk(KERN_WARNING "Elsa: Documentation/isdn/README.HiSax\n");
+			printk(KERN_WARNING "Elsa: Waiting 5 sec to sync discs\n");
+			save_flags(flags);
+			sti();
+			HZDELAY(500);	/* wait 500*10 ms */
+			restore_flags(flags);
+		}
 #else
 		printk(KERN_WARNING "Elsa: Elsa PCI and NO_PCI_BIOS\n");
 		printk(KERN_WARNING "Elsa: unable to config Elsa PCI\n");
@@ -957,6 +1092,7 @@
 			break;
 		case ELSA_PCFPRO:
 		case ELSA_PCF:
+		case ELSA_QS3000PCI:
 			bytecnt = 16;
 			break;
 		case ELSA_QS1000PCI:
@@ -980,7 +1116,7 @@
 	} else {
 		request_region(cs->hw.elsa.base, bytecnt, "elsa isdn");
 	}
-	if (cs->subtyp == ELSA_QS1000PCI) {
+	if ((cs->subtyp == ELSA_QS1000PCI) || (cs->subtyp == ELSA_QS3000PCI)) {
 		if (check_region(cs->hw.elsa.cfg, 0x80)) {
 			printk(KERN_WARNING
 			       "HiSax: %s pci port %x-%x already in use\n",
@@ -1020,12 +1156,12 @@
 		}
 		printk(KERN_INFO "Elsa: timer OK; resetting card\n");
 	}
-	reset_elsa(cs);
 	cs->BC_Read_Reg = &ReadHSCX;
 	cs->BC_Write_Reg = &WriteHSCX;
 	cs->BC_Send_Data = &hscx_fill_fifo;
 	cs->cardmsg = &Elsa_card_msg;
-	if (cs->subtyp == ELSA_QS1000PCI) {
+	reset_elsa(cs);
+	if ((cs->subtyp == ELSA_QS1000PCI) || (cs->subtyp == ELSA_QS3000PCI)) {
 		cs->readisac = &ReadISAC_IPAC;
 		cs->writeisac = &WriteISAC_IPAC;
 		cs->readisacfifo = &ReadISACfifo_IPAC;
@@ -1056,4 +1192,3 @@
 	}
 	return (1);
 }
-
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/elsa_ser.c linux/drivers/isdn/hisax/elsa_ser.c
--- v2.3.3/linux/drivers/isdn/hisax/elsa_ser.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/elsa_ser.c	Wed May 26 16:55:40 1999
@@ -0,0 +1,749 @@
+#include <linux/config.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+
+#define MAX_MODEM_BUF	256
+#define WAKEUP_CHARS	(MAX_MODEM_BUF/2)
+#define RS_ISR_PASS_LIMIT 256
+#define BASE_BAUD ( 1843200 / 16 )
+
+#ifndef MIN
+#define MIN(a,b)	((a) < (b) ? (a) : (b))
+#endif
+
+//#define SERIAL_DEBUG_OPEN 1
+//#define SERIAL_DEBUG_INTR 1
+//#define SERIAL_DEBUG_FLOW 1
+#undef SERIAL_DEBUG_OPEN
+#undef SERIAL_DEBUG_INTR
+#undef SERIAL_DEBUG_FLOW
+#undef SERIAL_DEBUG_REG
+//#define SERIAL_DEBUG_REG 1
+
+#ifdef SERIAL_DEBUG_REG
+static u_char deb[32];
+const char *ModemIn[] = {"RBR","IER","IIR","LCR","MCR","LSR","MSR","SCR"};
+const char *ModemOut[] = {"THR","IER","FCR","LCR","MCR","LSR","MSR","SCR"};
+#endif
+
+static char *MInit_1 = "AT&F&C1E0&D2\r\0";
+static char *MInit_2 = "ATL2M1S64=13\r\0";
+static char *MInit_3 = "AT+FCLASS=0\r\0";
+static char *MInit_4 = "ATV1S2=128X1\r\0";
+static char *MInit_5 = "AT\\V8\\N3\r\0";
+static char *MInit_6 = "ATL0M0&G0%E1\r\0";
+static char *MInit_7 = "AT%L1%M0%C3\r\0";
+
+static char *MInit_speed28800 = "AT%G0%B28800\r\0";
+
+static char *MInit_dialout = "ATs7=60 x1 d\r\0";
+static char *MInit_dialin = "ATs7=60 x1 a\r\0";
+
+
+static inline unsigned int serial_in(struct IsdnCardState *cs, int offset)
+{
+#ifdef SERIAL_DEBUG_REG
+	u_int val = inb(cs->hw.elsa.base + 8 + offset);
+	debugl1(cs,"in   %s %02x",ModemIn[offset], val);
+	return(val);
+#else
+	return inb(cs->hw.elsa.base + 8 + offset);
+#endif
+}
+
+static inline unsigned int serial_inp(struct IsdnCardState *cs, int offset)
+{
+#ifdef SERIAL_DEBUG_REG
+#ifdef CONFIG_SERIAL_NOPAUSE_IO
+	u_int val = inb(cs->hw.elsa.base + 8 + offset);
+	debugl1(cs,"inp  %s %02x",ModemIn[offset], val);
+#else
+	u_int val = inb_p(cs->hw.elsa.base + 8 + offset);
+	debugl1(cs,"inP  %s %02x",ModemIn[offset], val);
+#endif
+	return(val);
+#else
+#ifdef CONFIG_SERIAL_NOPAUSE_IO
+	return inb(cs->hw.elsa.base + 8 + offset);
+#else
+	return inb_p(cs->hw.elsa.base + 8 + offset);
+#endif
+#endif
+}
+
+static inline void serial_out(struct IsdnCardState *cs, int offset, int value)
+{
+#ifdef SERIAL_DEBUG_REG
+	debugl1(cs,"out  %s %02x",ModemOut[offset], value);
+#endif
+	outb(value, cs->hw.elsa.base + 8 + offset);
+}
+
+static inline void serial_outp(struct IsdnCardState *cs, int offset,
+			       int value)
+{
+#ifdef SERIAL_DEBUG_REG
+#ifdef CONFIG_SERIAL_NOPAUSE_IO
+	debugl1(cs,"outp %s %02x",ModemOut[offset], value);
+#else
+	debugl1(cs,"outP %s %02x",ModemOut[offset], value);
+#endif
+#endif
+#ifdef CONFIG_SERIAL_NOPAUSE_IO
+	outb(value, cs->hw.elsa.base + 8 + offset);
+#else
+    	outb_p(value, cs->hw.elsa.base + 8 + offset);
+#endif
+}
+
+/*
+ * This routine is called to set the UART divisor registers to match
+ * the specified baud rate for a serial port.
+ */
+static void change_speed(struct IsdnCardState *cs, int baud)
+{
+	int	quot = 0, baud_base;
+	unsigned cval, fcr = 0;
+	int	bits;
+	unsigned long	flags;
+
+
+	/* byte size and parity */
+	cval = 0x03; bits = 10;
+	/* Determine divisor based on baud rate */
+	baud_base = BASE_BAUD;
+	quot = baud_base / baud;
+	/* If the quotient is ever zero, default to 9600 bps */
+	if (!quot)
+		quot = baud_base / 9600;
+
+	/* Set up FIFO's */
+	if ((baud_base / quot) < 2400)
+		fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
+	else
+		fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8;
+	serial_outp(cs, UART_FCR, fcr);
+	/* CTS flow control flag and modem status interrupts */
+	cs->hw.elsa.IER &= ~UART_IER_MSI;
+	cs->hw.elsa.IER |= UART_IER_MSI;
+	serial_outp(cs, UART_IER, cs->hw.elsa.IER);
+
+	debugl1(cs,"modem quot=0x%x", quot);
+	save_flags(flags);
+	cli();
+	serial_outp(cs, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */
+	serial_outp(cs, UART_DLL, quot & 0xff);		/* LS of divisor */
+	serial_outp(cs, UART_DLM, quot >> 8);		/* MS of divisor */
+	serial_outp(cs, UART_LCR, cval);		/* reset DLAB */
+	serial_inp(cs, UART_RX);
+	restore_flags(flags);
+}
+
+static int mstartup(struct IsdnCardState *cs)
+{
+	unsigned long flags;
+	int	retval=0;
+
+
+	save_flags(flags); cli();
+
+	/*
+	 * Clear the FIFO buffers and disable them
+	 * (they will be reenabled in change_speed())
+	 */
+	serial_outp(cs, UART_FCR, (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT));
+
+	/*
+	 * At this point there's no way the LSR could still be 0xFF;
+	 * if it is, then bail out, because there's likely no UART
+	 * here.
+	 */
+	if (serial_inp(cs, UART_LSR) == 0xff) {
+		retval = -ENODEV;
+		goto errout;
+	}
+	
+	/*
+	 * Clear the interrupt registers.
+	 */
+	(void) serial_inp(cs, UART_RX);
+	(void) serial_inp(cs, UART_IIR);
+	(void) serial_inp(cs, UART_MSR);
+
+	/*
+	 * Now, initialize the UART 
+	 */
+	serial_outp(cs, UART_LCR, UART_LCR_WLEN8);	/* reset DLAB */
+
+	cs->hw.elsa.MCR = 0;
+	cs->hw.elsa.MCR = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2;
+	serial_outp(cs, UART_MCR, cs->hw.elsa.MCR);
+	
+	/*
+	 * Finally, enable interrupts
+	 */
+	cs->hw.elsa.IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI;
+	serial_outp(cs, UART_IER, cs->hw.elsa.IER);	/* enable interrupts */
+	
+	/*
+	 * And clear the interrupt registers again for luck.
+	 */
+	(void)serial_inp(cs, UART_LSR);
+	(void)serial_inp(cs, UART_RX);
+	(void)serial_inp(cs, UART_IIR);
+	(void)serial_inp(cs, UART_MSR);
+
+	cs->hw.elsa.transcnt = cs->hw.elsa.transp = 0;
+	cs->hw.elsa.rcvcnt = cs->hw.elsa.rcvp =0;
+
+	/*
+	 * and set the speed of the serial port
+	 */
+	change_speed(cs, BASE_BAUD);
+	cs->hw.elsa.MFlag = 1;
+errout:
+	restore_flags(flags);
+	return retval;
+}
+
+/*
+ * This routine will shutdown a serial port; interrupts are disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.
+ */
+static void mshutdown(struct IsdnCardState *cs)
+{
+	unsigned long	flags;
+
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk(KERN_DEBUG"Shutting down serial ....");
+#endif
+	
+	save_flags(flags); cli(); /* Disable interrupts */
+
+	/*
+	 * clear delta_msr_wait queue to avoid mem leaks: we may free the irq
+	 * here so the queue might never be waken up
+	 */
+
+	cs->hw.elsa.IER = 0;
+	serial_outp(cs, UART_IER, 0x00);	/* disable all intrs */
+	cs->hw.elsa.MCR &= ~UART_MCR_OUT2;
+	
+	/* disable break condition */
+	serial_outp(cs, UART_LCR, serial_inp(cs, UART_LCR) & ~UART_LCR_SBC);
+	
+	cs->hw.elsa.MCR &= ~(UART_MCR_DTR|UART_MCR_RTS);
+	serial_outp(cs, UART_MCR, cs->hw.elsa.MCR);
+
+	/* disable FIFO's */	
+	serial_outp(cs, UART_FCR, (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT));
+	serial_inp(cs, UART_RX);    /* read data port to reset things */
+	
+	restore_flags(flags);
+#ifdef SERIAL_DEBUG_OPEN
+	printk(" done\n");
+#endif
+}
+
+inline int
+write_modem(struct BCState *bcs) {
+	int ret=0;
+	struct IsdnCardState *cs = bcs->cs;
+	int count, len, fp, buflen;
+	long flags;
+	
+	if (!bcs->tx_skb)
+		return 0;
+	if (bcs->tx_skb->len <= 0)
+		return 0;
+	save_flags(flags);
+	cli();
+	buflen = MAX_MODEM_BUF - cs->hw.elsa.transcnt;
+	len = MIN(buflen, bcs->tx_skb->len);		
+	fp = cs->hw.elsa.transcnt + cs->hw.elsa.transp;
+	fp &= (MAX_MODEM_BUF -1);
+	count = MIN(len, MAX_MODEM_BUF - fp);
+	if (count < len) {
+		memcpy(cs->hw.elsa.transbuf + fp, bcs->tx_skb->data, count);
+		skb_pull(bcs->tx_skb, count);
+		cs->hw.elsa.transcnt += count;
+		ret = count;
+		count = len - count;
+		fp = 0;
+	}
+	memcpy((cs->hw.elsa.transbuf + fp), bcs->tx_skb->data, count);
+	skb_pull(bcs->tx_skb, count);
+	cs->hw.elsa.transcnt += count;
+	ret += count;
+	
+	if (cs->hw.elsa.transcnt && 
+	    !(cs->hw.elsa.IER & UART_IER_THRI)) {
+			cs->hw.elsa.IER |= UART_IER_THRI;
+		serial_outp(cs, UART_IER, cs->hw.elsa.IER);
+	}
+	restore_flags(flags);
+	return(ret);
+}
+
+inline void
+modem_fill(struct BCState *bcs) {
+		
+	if (bcs->tx_skb) {
+		if (bcs->tx_skb->len) {
+			write_modem(bcs);
+			return;
+		} else {
+			if (bcs->st->lli.l1writewakeup &&
+				(PACKET_NOACK != bcs->tx_skb->pkt_type))
+					bcs->st->lli.l1writewakeup(bcs->st,
+						bcs->hw.hscx.count);
+			dev_kfree_skb(bcs->tx_skb);
+			bcs->tx_skb = NULL;
+		}
+	}
+	if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+		bcs->hw.hscx.count = 0;
+		test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+		write_modem(bcs);
+	} else {
+		test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		hscx_sched_event(bcs, B_XMTBUFREADY);
+	}
+}
+
+static inline void receive_chars(struct IsdnCardState *cs,
+				 int *status)
+{
+	unsigned char ch;
+	struct sk_buff *skb;
+
+	do {
+		ch = serial_in(cs, UART_RX);
+		if (cs->hw.elsa.rcvcnt >= MAX_MODEM_BUF)
+			break;
+		cs->hw.elsa.rcvbuf[cs->hw.elsa.rcvcnt++] = ch;
+#ifdef SERIAL_DEBUG_INTR
+		printk("DR%02x:%02x...", ch, *status);
+#endif
+		if (*status & (UART_LSR_BI | UART_LSR_PE |
+			       UART_LSR_FE | UART_LSR_OE)) {
+					
+#ifdef SERIAL_DEBUG_INTR
+			printk("handling exept....");
+#endif
+		}
+		*status = serial_inp(cs, UART_LSR);
+	} while (*status & UART_LSR_DR);
+	if (cs->hw.elsa.MFlag == 2) {
+		if (!(skb = dev_alloc_skb(cs->hw.elsa.rcvcnt)))
+			printk(KERN_WARNING "ElsaSER: receive out of memory\n");
+		else {
+			memcpy(skb_put(skb, cs->hw.elsa.rcvcnt), cs->hw.elsa.rcvbuf, 
+				cs->hw.elsa.rcvcnt);
+			skb_queue_tail(& cs->hw.elsa.bcs->rqueue, skb);
+		}
+		hscx_sched_event(cs->hw.elsa.bcs, B_RCVBUFREADY);
+	} else {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "modem read cnt %d", cs->hw.elsa.rcvcnt);
+		QuickHex(t, cs->hw.elsa.rcvbuf, cs->hw.elsa.rcvcnt);
+		debugl1(cs, tmp);
+	}
+	cs->hw.elsa.rcvcnt = 0;
+}
+
+static inline void transmit_chars(struct IsdnCardState *cs, int *intr_done)
+{
+	int count;
+	
+	debugl1(cs, "transmit_chars: p(%x) cnt(%x)", cs->hw.elsa.transp, 
+		cs->hw.elsa.transcnt);
+	
+	if (cs->hw.elsa.transcnt <= 0) {
+		cs->hw.elsa.IER &= ~UART_IER_THRI;
+		serial_out(cs, UART_IER, cs->hw.elsa.IER);
+		return;
+	}
+	count = 16;
+	do {
+		serial_outp(cs, UART_TX, cs->hw.elsa.transbuf[cs->hw.elsa.transp++]);
+		if (cs->hw.elsa.transp >= MAX_MODEM_BUF)
+			cs->hw.elsa.transp=0;
+		if (--cs->hw.elsa.transcnt <= 0)
+			break;
+	} while (--count > 0);
+	if ((cs->hw.elsa.transcnt < WAKEUP_CHARS) && (cs->hw.elsa.MFlag==2))
+		modem_fill(cs->hw.elsa.bcs);
+
+#ifdef SERIAL_DEBUG_INTR
+	printk("THRE...");
+#endif
+	if (intr_done)
+		*intr_done = 0;
+	if (cs->hw.elsa.transcnt <= 0) {
+		cs->hw.elsa.IER &= ~UART_IER_THRI;
+		serial_outp(cs, UART_IER, cs->hw.elsa.IER);
+	}
+}
+
+#if 0
+static inline void check_modem_status(struct IsdnCardState *cs)
+{
+	int	status;
+	struct async_struct *info = cs->hw.elsa.info;
+	struct	async_icount *icount;
+	
+	status = serial_inp(info, UART_MSR);
+
+	if (status & UART_MSR_ANY_DELTA) {
+		icount = &info->state->icount;
+		/* update input line counters */
+		if (status & UART_MSR_TERI)
+			icount->rng++;
+		if (status & UART_MSR_DDSR)
+			icount->dsr++;
+		if (status & UART_MSR_DDCD) {
+			icount->dcd++;
+		}
+		if (status & UART_MSR_DCTS)
+			icount->cts++;
+//		wake_up_interruptible(&info->delta_msr_wait);
+	}
+
+	if ((info->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) {
+#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR))
+		printk("ttys%d CD now %s...", info->line,
+		       (status & UART_MSR_DCD) ? "on" : "off");
+#endif		
+		if (status & UART_MSR_DCD)
+//			wake_up_interruptible(&info->open_wait);
+;
+		else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+			   (info->flags & ASYNC_CALLOUT_NOHUP))) {
+#ifdef SERIAL_DEBUG_OPEN
+			printk("doing serial hangup...");
+#endif
+			if (info->tty)
+				tty_hangup(info->tty);
+		}
+	}
+#if 0
+	if (info->flags & ASYNC_CTS_FLOW) {
+		if (info->tty->hw_stopped) {
+			if (status & UART_MSR_CTS) {
+#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
+				printk("CTS tx start...");
+#endif
+				info->tty->hw_stopped = 0;
+				info->IER |= UART_IER_THRI;
+				serial_outp(info, UART_IER, info->IER);
+//				rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+				return;
+			}
+		} else {
+			if (!(status & UART_MSR_CTS)) {
+#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
+				printk("CTS tx stop...");
+#endif
+				info->tty->hw_stopped = 1;
+				info->IER &= ~UART_IER_THRI;
+				serial_outp(info, UART_IER, info->IER);
+			}
+		}
+	}
+#endif 0
+}
+#endif
+
+static void rs_interrupt_elsa(int irq, struct IsdnCardState *cs)
+{
+	int status, iir, msr;
+	int pass_counter = 0;
+	
+#ifdef SERIAL_DEBUG_INTR
+	printk("rs_interrupt_single(%d)...", irq);
+#endif
+
+	do {
+		status = serial_inp(cs, UART_LSR);
+		debugl1(cs,"rs LSR %02x", status);
+#ifdef SERIAL_DEBUG_INTR
+		printk("status = %x...", status);
+#endif
+		if (status & UART_LSR_DR)
+			receive_chars(cs, &status);
+		if (status & UART_LSR_THRE)
+			transmit_chars(cs, 0);
+		if (pass_counter++ > RS_ISR_PASS_LIMIT) {
+			printk("rs_single loop break.\n");
+			break;
+		}
+		iir = serial_inp(cs, UART_IIR);
+		debugl1(cs,"rs IIR %02x", iir);
+		if ((iir & 0xf) == 0) {
+			msr = serial_inp(cs, UART_MSR);
+			debugl1(cs,"rs MSR %02x", msr);
+		}
+	} while (!(iir & UART_IIR_NO_INT));
+#ifdef SERIAL_DEBUG_INTR
+	printk("end.\n");
+#endif
+}
+
+extern int open_hscxstate(struct IsdnCardState *cs, struct BCState *bcs);
+extern void modehscx(struct BCState *bcs, int mode, int bc);
+extern void hscx_l2l1(struct PStack *st, int pr, void *arg);
+
+void
+close_elsastate(struct BCState *bcs)
+{
+	struct sk_buff *skb;
+
+	modehscx(bcs, 0, bcs->channel);
+	if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (bcs->hw.hscx.rcvbuf) {
+			if (bcs->mode != L1_MODE_MODEM)
+				kfree(bcs->hw.hscx.rcvbuf);
+			bcs->hw.hscx.rcvbuf = NULL;
+		}
+		while ((skb = skb_dequeue(&bcs->rqueue))) {
+			dev_kfree_skb(skb);
+		}
+		while ((skb = skb_dequeue(&bcs->squeue))) {
+			dev_kfree_skb(skb);
+		}
+		if (bcs->tx_skb) {
+			dev_kfree_skb(bcs->tx_skb);
+			bcs->tx_skb = NULL;
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		}
+	}
+}
+
+void
+modem_write_cmd(struct IsdnCardState *cs, u_char *buf, int len) {
+	int count, fp;
+	u_char *msg = buf;
+	long flags;
+	
+	if (!len)
+		return;
+	save_flags(flags);
+	cli();		
+	if (len > (MAX_MODEM_BUF - cs->hw.elsa.transcnt)) {
+		restore_flags(flags);
+		return;
+	}
+	fp = cs->hw.elsa.transcnt + cs->hw.elsa.transp;
+	fp &= (MAX_MODEM_BUF -1);
+	count = MIN(len, MAX_MODEM_BUF - fp);
+	if (count < len) {
+		memcpy(cs->hw.elsa.transbuf + fp, msg, count);
+		cs->hw.elsa.transcnt += count;
+		msg += count;
+		count = len - count;
+		fp = 0;
+	}
+	memcpy(cs->hw.elsa.transbuf + fp, msg, count);
+	cs->hw.elsa.transcnt += count;
+	if (cs->hw.elsa.transcnt && 
+	    !(cs->hw.elsa.IER & UART_IER_THRI)) {
+		cs->hw.elsa.IER |= UART_IER_THRI;
+		serial_outp(cs, UART_IER, cs->hw.elsa.IER);
+	}
+	restore_flags(flags);
+}
+
+void
+modem_set_init(struct IsdnCardState *cs) {
+	long flags;
+	int timeout;
+
+#define RCV_DELAY 20000	
+	save_flags(flags);
+	sti();
+	modem_write_cmd(cs, MInit_1, strlen(MInit_1));
+	timeout = 1000;
+	while(timeout-- && cs->hw.elsa.transcnt)
+		udelay(1000);
+	debugl1(cs, "msi tout=%d", timeout);
+	udelay(RCV_DELAY);
+	modem_write_cmd(cs, MInit_2, strlen(MInit_2));
+	timeout = 1000;
+	while(timeout-- && cs->hw.elsa.transcnt)
+		udelay(1000);
+	debugl1(cs, "msi tout=%d", timeout);
+	udelay(RCV_DELAY);
+	modem_write_cmd(cs, MInit_3, strlen(MInit_3));
+	timeout = 1000;
+	while(timeout-- && cs->hw.elsa.transcnt)
+		udelay(1000);
+	debugl1(cs, "msi tout=%d", timeout);
+	udelay(RCV_DELAY);
+	modem_write_cmd(cs, MInit_4, strlen(MInit_4));
+	timeout = 1000;
+	while(timeout-- && cs->hw.elsa.transcnt)
+		udelay(1000);
+	debugl1(cs, "msi tout=%d", timeout);
+	udelay(RCV_DELAY );
+	modem_write_cmd(cs, MInit_5, strlen(MInit_5));
+	timeout = 1000;
+	while(timeout-- && cs->hw.elsa.transcnt)
+		udelay(1000);
+	debugl1(cs, "msi tout=%d", timeout);
+	udelay(RCV_DELAY);
+	modem_write_cmd(cs, MInit_6, strlen(MInit_6));
+	timeout = 1000;
+	while(timeout-- && cs->hw.elsa.transcnt)
+		udelay(1000);
+	debugl1(cs, "msi tout=%d", timeout);
+	udelay(RCV_DELAY);
+	modem_write_cmd(cs, MInit_7, strlen(MInit_7));
+	timeout = 1000;
+	while(timeout-- && cs->hw.elsa.transcnt)
+		udelay(1000);
+	debugl1(cs, "msi tout=%d", timeout);
+	udelay(RCV_DELAY);
+	restore_flags(flags);
+}
+
+void
+modem_set_dial(struct IsdnCardState *cs, int outgoing) {
+	long flags;
+	int timeout;
+#define RCV_DELAY 20000	
+
+	save_flags(flags);
+	sti();
+	modem_write_cmd(cs, MInit_speed28800, strlen(MInit_speed28800));
+	timeout = 1000;
+	while(timeout-- && cs->hw.elsa.transcnt)
+		udelay(1000);
+	debugl1(cs, "msi tout=%d", timeout);
+	udelay(RCV_DELAY);
+	if (outgoing)
+		modem_write_cmd(cs, MInit_dialout, strlen(MInit_dialout));
+	else
+		modem_write_cmd(cs, MInit_dialin, strlen(MInit_dialin));
+	timeout = 1000;
+	while(timeout-- && cs->hw.elsa.transcnt)
+		udelay(1000);
+	debugl1(cs, "msi tout=%d", timeout);
+	udelay(RCV_DELAY);
+	restore_flags(flags);
+}
+
+void
+modem_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	long flags;
+
+	if (pr == (PH_DATA | REQUEST)) {
+		save_flags(flags);
+		cli();
+		if (st->l1.bcs->tx_skb) {
+			skb_queue_tail(&st->l1.bcs->squeue, skb);
+			restore_flags(flags);
+		} else {
+			st->l1.bcs->tx_skb = skb;
+			test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+			st->l1.bcs->hw.hscx.count = 0;
+			restore_flags(flags);
+			write_modem(st->l1.bcs);
+		}
+	} else if (pr == (PH_ACTIVATE | REQUEST)) {
+		test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+		st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
+		set_arcofi(st->l1.bcs->cs, st->l1.bc);
+		mstartup(st->l1.bcs->cs);
+		modem_set_dial(st->l1.bcs->cs, test_bit(FLG_ORIG, &st->l2.flag));
+		st->l1.bcs->cs->hw.elsa.MFlag=2;
+	} else if (pr == (PH_DEACTIVATE | REQUEST)) {
+		test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+		send_arcofi(st->l1.bcs->cs, ARCOFI_XOP_0, st->l1.bc, 0);
+		st->l1.bcs->cs->hw.elsa.MFlag=1;
+	} else {
+		printk(KERN_WARNING"ElsaSer: unknown pr %x\n", pr);
+	}
+}
+
+int
+setstack_elsa(struct PStack *st, struct BCState *bcs)
+{
+
+	bcs->channel = st->l1.bc;
+	switch (st->l1.mode) {
+		case L1_MODE_HDLC:
+		case L1_MODE_TRANS:
+			if (open_hscxstate(st->l1.hardware, bcs))
+				return (-1);
+			st->l2.l2l1 = hscx_l2l1;
+			break;
+		case L1_MODE_MODEM:
+			bcs->mode = L1_MODE_MODEM;
+			if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+				bcs->hw.hscx.rcvbuf = bcs->cs->hw.elsa.rcvbuf;
+				skb_queue_head_init(&bcs->rqueue);
+				skb_queue_head_init(&bcs->squeue);
+			}
+			bcs->tx_skb = NULL;
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			bcs->event = 0;
+			bcs->hw.hscx.rcvidx = 0;
+			bcs->tx_cnt = 0;
+			bcs->cs->hw.elsa.bcs = bcs;
+			st->l2.l2l1 = modem_l2l1;
+			break;
+	}
+	st->l1.bcs = bcs;
+	setstack_manager(st);
+	bcs->st = st;
+	setstack_l1_B(st);
+	return (0);
+}
+
+void
+init_modem(struct IsdnCardState *cs) {
+
+	cs->bcs[0].BC_SetStack = setstack_elsa;
+	cs->bcs[1].BC_SetStack = setstack_elsa;
+	cs->bcs[0].BC_Close = close_elsastate;
+	cs->bcs[1].BC_Close = close_elsastate;
+	if (!(cs->hw.elsa.rcvbuf = kmalloc(MAX_MODEM_BUF,
+		GFP_ATOMIC))) {
+		printk(KERN_WARNING
+			"Elsa: No modem mem hw.elsa.rcvbuf\n");
+		return;
+	}
+	if (!(cs->hw.elsa.transbuf = kmalloc(MAX_MODEM_BUF,
+		GFP_ATOMIC))) {
+		printk(KERN_WARNING
+			"Elsa: No modem mem hw.elsa.transbuf\n");
+		kfree(cs->hw.elsa.rcvbuf);
+		cs->hw.elsa.rcvbuf = NULL;
+		return;
+	}
+	if (mstartup(cs)) {
+		printk(KERN_WARNING "Elsa: problem startup modem\n");
+	}
+	modem_set_init(cs);
+}
+
+void
+release_modem(struct IsdnCardState *cs) {
+
+	cs->hw.elsa.MFlag = 0;
+	if (cs->hw.elsa.transbuf) {
+		if (cs->hw.elsa.rcvbuf) {
+			mshutdown(cs);
+			kfree(cs->hw.elsa.rcvbuf);
+			cs->hw.elsa.rcvbuf = NULL;
+		}
+		kfree(cs->hw.elsa.transbuf);
+		cs->hw.elsa.transbuf = NULL;
+	}
+}
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/fsm.c linux/drivers/isdn/hisax/fsm.c
--- v2.3.3/linux/drivers/isdn/hisax/fsm.c	Wed Apr  1 16:20:58 1998
+++ linux/drivers/isdn/hisax/fsm.c	Sun May 23 10:03:41 1999
@@ -1,4 +1,4 @@
-/* $Id: fsm.c,v 1.7 1997/11/06 17:09:13 keil Exp $
+/* $Id: fsm.c,v 1.10 1998/11/15 23:54:39 keil Exp $
 
  * Author       Karsten Keil (keil@temic-ech.spacenet.de)
  *              based on the teles driver from Jan den Ouden
@@ -7,6 +7,18 @@
  *              Fritz Elfert
  *
  * $Log: fsm.c,v $
+ * Revision 1.10  1998/11/15 23:54:39  keil
+ * changes from 2.0
+ *
+ * Revision 1.9  1998/03/26 07:10:02  paul
+ * The jumpmatrix table in struct Fsm was an array of "int". This is not
+ * large enough for pointers to functions on Linux/Alpha (instant crash
+ * on "insmod hisax). Now there is a typedef for the pointer to function.
+ * This also prevents warnings about "incompatible pointer types".
+ *
+ * Revision 1.8  1998/03/07 22:56:59  tsbogend
+ * made HiSax working on Linux/Alpha
+ *
  * Revision 1.7  1997/11/06 17:09:13  keil
  * New 2.1 init code
  *
@@ -41,18 +53,18 @@
 {
 	int i;
 
-	fsm->jumpmatrix = (int *)
-	    kmalloc(4L * fsm->state_count * fsm->event_count, GFP_KERNEL);
-	memset(fsm->jumpmatrix, 0, 4L * fsm->state_count * fsm->event_count);
+	fsm->jumpmatrix = (FSMFNPTR *)
+		kmalloc(sizeof (FSMFNPTR) * fsm->state_count * fsm->event_count, GFP_KERNEL);
+	memset(fsm->jumpmatrix, 0, sizeof (FSMFNPTR) * fsm->state_count * fsm->event_count);
 
 	for (i = 0; i < fncount; i++) 
 		if ((fnlist[i].state>=fsm->state_count) || (fnlist[i].event>=fsm->event_count)) {
-			printk(KERN_ERR "FsmNew Error line %d st(%d/%d) ev(%d/%d)\n",
-				i,fnlist[i].state,fsm->state_count,
-				fnlist[i].event,fsm->event_count);
+			printk(KERN_ERR "FsmNew Error line %d st(%ld/%ld) ev(%ld/%ld)\n",
+				i,(long)fnlist[i].state,(long)fsm->state_count,
+				(long)fnlist[i].event,(long)fsm->event_count);
 		} else		
 			fsm->jumpmatrix[fsm->state_count * fnlist[i].event +
-				fnlist[i].state] = (int) fnlist[i].routine;
+				fnlist[i].state] = (FSMFNPTR) fnlist[i].routine;
 }
 
 void
@@ -64,31 +76,26 @@
 int
 FsmEvent(struct FsmInst *fi, int event, void *arg)
 {
-	void (*r) (struct FsmInst *, int, void *);
-	char str[80];
+	FSMFNPTR r;
 
 	if ((fi->state>=fi->fsm->state_count) || (event >= fi->fsm->event_count)) {
-		printk(KERN_ERR "FsmEvent Error st(%d/%d) ev(%d/%d)\n",
-			fi->state,fi->fsm->state_count,event,fi->fsm->event_count);
+		printk(KERN_ERR "FsmEvent Error st(%ld/%ld) ev(%d/%ld)\n",
+			(long)fi->state,(long)fi->fsm->state_count,event,(long)fi->fsm->event_count);
 		return(1);
 	}
-	r = (void (*)) fi->fsm->jumpmatrix[fi->fsm->state_count * event + fi->state];
+	r = fi->fsm->jumpmatrix[fi->fsm->state_count * event + fi->state];
 	if (r) {
-		if (fi->debug) {
-			sprintf(str, "State %s Event %s",
+		if (fi->debug)
+			fi->printdebug(fi, "State %s Event %s",
 				fi->fsm->strState[fi->state],
 				fi->fsm->strEvent[event]);
-			fi->printdebug(fi, str);
-		}
 		r(fi, event, arg);
 		return (0);
 	} else {
-		if (fi->debug) {
-			sprintf(str, "State %s Event %s no routine",
+		if (fi->debug)
+			fi->printdebug(fi, "State %s Event %s no routine",
 				fi->fsm->strState[fi->state],
 				fi->fsm->strEvent[event]);
-			fi->printdebug(fi, str);
-		}
 		return (!0);
 	}
 }
@@ -96,25 +103,18 @@
 void
 FsmChangeState(struct FsmInst *fi, int newstate)
 {
-	char str[80];
-
 	fi->state = newstate;
-	if (fi->debug) {
-		sprintf(str, "ChangeState %s",
+	if (fi->debug)
+		fi->printdebug(fi, "ChangeState %s",
 			fi->fsm->strState[newstate]);
-		fi->printdebug(fi, str);
-	}
 }
 
 static void
 FsmExpireTimer(struct FsmTimer *ft)
 {
 #if FSM_TIMER_DEBUG
-	if (ft->fi->debug) {
-		char str[40];
-		sprintf(str, "FsmExpireTimer %lx", (long) ft);
-		ft->fi->printdebug(ft->fi, str);
-	}
+	if (ft->fi->debug)
+		ft->fi->printdebug(ft->fi, "FsmExpireTimer %lx", (long) ft);
 #endif
 	FsmEvent(ft->fi, ft->event, ft->arg);
 }
@@ -126,11 +126,8 @@
 	ft->tl.function = (void *) FsmExpireTimer;
 	ft->tl.data = (long) ft;
 #if FSM_TIMER_DEBUG
-	if (ft->fi->debug) {
-		char str[40];
-		sprintf(str, "FsmInitTimer %lx", (long) ft);
-		ft->fi->printdebug(ft->fi, str);
-	}
+	if (ft->fi->debug)
+		ft->fi->printdebug(ft->fi, "FsmInitTimer %lx", (long) ft);
 #endif
 	init_timer(&ft->tl);
 }
@@ -139,11 +136,8 @@
 FsmDelTimer(struct FsmTimer *ft, int where)
 {
 #if FSM_TIMER_DEBUG
-	if (ft->fi->debug) {
-		char str[40];
-		sprintf(str, "FsmDelTimer %lx %d", (long) ft, where);
-		ft->fi->printdebug(ft->fi, str);
-	}
+	if (ft->fi->debug)
+		ft->fi->printdebug(ft->fi, "FsmDelTimer %lx %d", (long) ft, where);
 #endif
 	del_timer(&ft->tl);
 }
@@ -154,11 +148,9 @@
 {
 
 #if FSM_TIMER_DEBUG
-	if (ft->fi->debug) {
-		char str[40];
-		sprintf(str, "FsmAddTimer %lx %d %d", (long) ft, millisec, where);
-		ft->fi->printdebug(ft->fi, str);
-	}
+	if (ft->fi->debug)
+		ft->fi->printdebug(ft->fi, "FsmAddTimer %lx %d %d",
+			(long) ft, millisec, where);
 #endif
 
 	if (ft->tl.next || ft->tl.prev) {
@@ -180,11 +172,9 @@
 {
 
 #if FSM_TIMER_DEBUG
-	if (ft->fi->debug) {
-		char str[40];
-		sprintf(str, "FsmRestartTimer %lx %d %d", (long) ft, millisec, where);
-		ft->fi->printdebug(ft->fi, str);
-	}
+	if (ft->fi->debug)
+		ft->fi->printdebug(ft->fi, "FsmRestartTimer %lx %d %d",
+			(long) ft, millisec, where);
 #endif
 
 	if (ft->tl.next || ft->tl.prev)
@@ -194,25 +184,4 @@
 	ft->arg = arg;
 	ft->tl.expires = jiffies + (millisec * HZ) / 1000;
 	add_timer(&ft->tl);
-}
-
-void
-jiftime(char *s, long mark)
-{
-	s += 8;
-
-	*s-- = '\0';
-	*s-- = mark % 10 + '0';
-	mark /= 10;
-	*s-- = mark % 10 + '0';
-	mark /= 10;
-	*s-- = '.';
-	*s-- = mark % 10 + '0';
-	mark /= 10;
-	*s-- = mark % 6 + '0';
-	mark /= 6;
-	*s-- = ':';
-	*s-- = mark % 10 + '0';
-	mark /= 10;
-	*s-- = mark % 10 + '0';
 }
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/hfc_2bds0.c linux/drivers/isdn/hisax/hfc_2bds0.c
--- v2.3.3/linux/drivers/isdn/hisax/hfc_2bds0.c	Thu May 14 18:42:23 1998
+++ linux/drivers/isdn/hisax/hfc_2bds0.c	Sun May 23 10:03:41 1999
@@ -1,4 +1,4 @@
-/* $Id: hfc_2bds0.c,v 1.3 1998/02/12 23:07:22 keil Exp $
+/* $Id: hfc_2bds0.c,v 1.8 1998/11/15 23:54:40 keil Exp $
  *
  *  specific routines for CCD's HFC 2BDS0
  *
@@ -6,6 +6,22 @@
  *
  *
  * $Log: hfc_2bds0.c,v $
+ * Revision 1.8  1998/11/15 23:54:40  keil
+ * changes from 2.0
+ *
+ * Revision 1.7  1998/09/30 22:24:45  keil
+ * Fix missing line in setstack*
+ *
+ * Revision 1.6  1998/08/13 23:36:26  keil
+ * HiSax 3.1 - don't work stable with current LinkLevel
+ *
+ * Revision 1.5  1998/06/27 22:52:58  keil
+ * make 16.3c working with 3.0
+ *
+ * Revision 1.4  1998/05/25 12:57:52  keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
  * Revision 1.3  1998/02/12 23:07:22  keil
  * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
  *
@@ -46,11 +62,8 @@
 		}
 		ret = bytein(cs->hw.hfcD.addr);
 #if HFC_REG_DEBUG
-		if (cs->debug & L1_DEB_HSCX_FIFO && (data != 2)) {
-			char tmp[32];
-			sprintf(tmp, "t3c RD %02x %02x", reg, ret);
-			debugl1(cs, tmp);
-		}
+		if (cs->debug & L1_DEB_HSCX_FIFO && (data != 2))
+			debugl1(cs, "t3c RD %02x %02x", reg, ret);
 #endif
 	} else
 		ret = bytein(cs->hw.hfcD.addr | 1);
@@ -67,11 +80,8 @@
 	if (data)
 		byteout(cs->hw.hfcD.addr, value);
 #if HFC_REG_DEBUG
-	if (cs->debug & L1_DEB_HSCX_FIFO && (data != HFCD_DATA_NODEB)) {
-		char tmp[16];
-		sprintf(tmp, "t3c W%c %02x %02x", data ? 'D' : 'C', reg, value);
-		debugl1(cs, tmp);
-	}
+	if (cs->debug & L1_DEB_HSCX_FIFO && (data != HFCD_DATA_NODEB))
+		debugl1(cs, "t3c W%c %02x %02x", data ? 'D' : 'C', reg, value);
 #endif
 }
 
@@ -227,7 +237,6 @@
 	int chksum;
 	long flags;
 	u_char stat, cip;
-	char tmp[64];
 	
 	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
 		debugl1(cs, "hfc_empty_fifo");
@@ -283,11 +292,9 @@
 			WaitNoBusy(cs);
 			stat = ReadReg(cs, HFCD_DATA, cip);
 			sti();
-			if (cs->debug & L1_DEB_HSCX) {
-				sprintf(tmp, "hfc_empty_fifo %d chksum %x stat %x",
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "hfc_empty_fifo %d chksum %x stat %x",
 					bcs->channel, chksum, stat);
-				debugl1(cs, tmp);
-			}
 			if (stat) {
 				debugl1(cs, "FIFO CRC error");
 				dev_kfree_skb(skb);
@@ -315,14 +322,11 @@
 	int idx, fcnt;
 	int count;
 	u_char cip;
-	char tmp[64];
-	
 
-	if (!bcs->hw.hfc.tx_skb)
+	if (!bcs->tx_skb)
 		return;
-	if (bcs->hw.hfc.tx_skb->len <= 0)
+	if (bcs->tx_skb->len <= 0)
 		return;
-
 	save_flags(flags);
 	cli();
 	SelFiFo(cs, HFCB_SEND | HFCB_CHANNEL(bcs->channel)); 
@@ -335,12 +339,10 @@
 	bcs->hw.hfc.f2 = ReadReg(cs, HFCD_DATA, cip);
 	bcs->hw.hfc.send[bcs->hw.hfc.f1] = ReadZReg(cs, HFCB_FIFO | HFCB_Z1 | HFCB_SEND | HFCB_CHANNEL(bcs->channel));
 	sti();
- 	if (cs->debug & L1_DEB_HSCX) {
-		sprintf(tmp, "hfc_fill_fifo %d f1(%d) f2(%d) z1(%x)",
+ 	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "hfc_fill_fifo %d f1(%d) f2(%d) z1(%x)",
 			bcs->channel, bcs->hw.hfc.f1, bcs->hw.hfc.f2,
 			bcs->hw.hfc.send[bcs->hw.hfc.f1]);
-		debugl1(cs, tmp);
-	}
 	fcnt = bcs->hw.hfc.f1 - bcs->hw.hfc.f2;
 	if (fcnt < 0)
 		fcnt += 32;
@@ -351,13 +353,11 @@
 		return;
 	}
 	count = GetFreeFifoBytes_B(bcs);
-	if (cs->debug & L1_DEB_HSCX) {
-		sprintf(tmp, "hfc_fill_fifo %d count(%d/%d),%lx",
-			bcs->channel, bcs->hw.hfc.tx_skb->len,
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "hfc_fill_fifo %d count(%ld/%d),%lx",
+			bcs->channel, bcs->tx_skb->len,
 			count, current->state);
-		debugl1(cs, tmp);
-	}
-	if (count < bcs->hw.hfc.tx_skb->len) {
+	if (count < bcs->tx_skb->len) {
 		if (cs->debug & L1_DEB_HSCX)
 			debugl1(cs, "hfc_fill_fifo no fifo mem");
 		restore_flags(flags);
@@ -368,26 +368,26 @@
 	cli();
 	WaitForBusy(cs);
 	WaitNoBusy(cs);
-	WriteReg(cs, HFCD_DATA_NODEB, cip, bcs->hw.hfc.tx_skb->data[idx++]);
-	while (idx < bcs->hw.hfc.tx_skb->len) {
+	WriteReg(cs, HFCD_DATA_NODEB, cip, bcs->tx_skb->data[idx++]);
+	while (idx < bcs->tx_skb->len) {
 		cli();
 		if (!WaitNoBusy(cs))
 			break;
-		WriteReg(cs, HFCD_DATA_NODEB, cip, bcs->hw.hfc.tx_skb->data[idx]);
+		WriteReg(cs, HFCD_DATA_NODEB, cip, bcs->tx_skb->data[idx]);
 		sti();
 		idx++;
 	}
-	if (idx != bcs->hw.hfc.tx_skb->len) {
+	if (idx != bcs->tx_skb->len) {
 		sti();
 		debugl1(cs, "FIFO Send BUSY error");
 		printk(KERN_WARNING "HFC S FIFO channel %d BUSY Error\n", bcs->channel);
 	} else {
-		bcs->tx_cnt -= bcs->hw.hfc.tx_skb->len;
+		bcs->tx_cnt -= bcs->tx_skb->len;
 		if (bcs->st->lli.l1writewakeup &&
-			(PACKET_NOACK != bcs->hw.hfc.tx_skb->pkt_type))
-			bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.hfc.tx_skb->len);
-		dev_kfree_skb(bcs->hw.hfc.tx_skb);
-		bcs->hw.hfc.tx_skb = NULL;
+			(PACKET_NOACK != bcs->tx_skb->pkt_type))
+			bcs->st->lli.l1writewakeup(bcs->st, bcs->tx_skb->len);
+		dev_kfree_skb(bcs->tx_skb);
+		bcs->tx_skb = NULL;
 	}
 	WaitForBusy(cs);
 	cli();
@@ -404,15 +404,12 @@
 hfc_send_data(struct BCState *bcs)
 {
 	struct IsdnCardState *cs = bcs->cs;
-	char tmp[32];
 	
 	if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
 		hfc_fill_fifo(bcs);
 		test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
-	} else {
-		sprintf(tmp,"send_data %d blocked", bcs->channel);
-		debugl1(cs, tmp);
-	}
+	} else
+		debugl1(cs,"send_data %d blocked", bcs->channel);
 }
 
 void
@@ -424,15 +421,13 @@
 	u_char f1, f2, cip;
 	int receive, count = 5;
 	struct sk_buff *skb;
-	char tmp[64];
 
 	save_flags(flags);
     Begin:
 	count--;
 	cli();
 	if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
-		sprintf(tmp,"rec_data %d blocked", bcs->channel);
-		debugl1(cs, tmp);
+		debugl1(cs,"rec_data %d blocked", bcs->channel);
 		restore_flags(flags);
 		return;
 	}
@@ -445,11 +440,9 @@
 	f2 = ReadReg(cs, HFCD_DATA, cip);
 	sti();
 	if (f1 != f2) {
-		if (cs->debug & L1_DEB_HSCX) {
-			sprintf(tmp, "hfc rec %d f1(%d) f2(%d)",
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfc rec %d f1(%d) f2(%d)",
 				bcs->channel, f1, f2);
-			debugl1(cs, tmp);
-		}
 		cli();
 		z1 = ReadZReg(cs, HFCB_FIFO | HFCB_Z1 | HFCB_REC | HFCB_CHANNEL(bcs->channel));
 		z2 = ReadZReg(cs, HFCB_FIFO | HFCB_Z2 | HFCB_REC | HFCB_CHANNEL(bcs->channel));
@@ -458,11 +451,9 @@
 		if (rcnt < 0)
 			rcnt += cs->hw.hfcD.bfifosize;
 		rcnt++;
-		if (cs->debug & L1_DEB_HSCX) {
-			sprintf(tmp, "hfc rec %d z1(%x) z2(%x) cnt(%d)",
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfc rec %d z1(%x) z2(%x) cnt(%d)",
 				bcs->channel, z1, z2, rcnt);
-			debugl1(cs, tmp);
-		}
 		if ((skb = hfc_empty_fifo(bcs, rcnt))) {
 			cli();
 			skb_queue_tail(&bcs->rqueue, skb);
@@ -490,12 +481,9 @@
 {
 	struct IsdnCardState *cs = bcs->cs;
 
-	if (cs->debug & L1_DEB_HSCX) {
-		char tmp[40];
-		sprintf(tmp, "HFCD bchannel mode %d bchan %d/%d",
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "HFCD bchannel mode %d bchan %d/%d",
 			mode, bc, bcs->channel);
-		debugl1(cs, tmp);
-	}
 	bcs->mode = mode;
 	bcs->channel = bc;
 	switch (mode) {
@@ -543,122 +531,99 @@
 	long flags;
 
 	switch (pr) {
-		case (PH_DATA_REQ):
+		case (PH_DATA | REQUEST):
 			save_flags(flags);
 			cli();
-			if (st->l1.bcs->hw.hfc.tx_skb) {
+			if (st->l1.bcs->tx_skb) {
 				skb_queue_tail(&st->l1.bcs->squeue, skb);
 				restore_flags(flags);
 			} else {
-				st->l1.bcs->hw.hfc.tx_skb = skb;
+				st->l1.bcs->tx_skb = skb;
 /*				test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
 */				st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
 				restore_flags(flags);
 			}
 			break;
-		case (PH_PULL_IND):
-			if (st->l1.bcs->hw.hfc.tx_skb) {
+		case (PH_PULL | INDICATION):
+			if (st->l1.bcs->tx_skb) {
 				printk(KERN_WARNING "hfc_l2l1: this shouldn't happen\n");
 				break;
 			}
 			save_flags(flags);
 			cli();
 /*			test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
-*/			st->l1.bcs->hw.hfc.tx_skb = skb;
+*/			st->l1.bcs->tx_skb = skb;
 			st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
 			restore_flags(flags);
 			break;
-		case (PH_PULL_REQ):
-			if (!st->l1.bcs->hw.hfc.tx_skb) {
+		case (PH_PULL | REQUEST):
+			if (!st->l1.bcs->tx_skb) {
 				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
-				st->l1.l1l2(st, PH_PULL_CNF, NULL);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
 			} else
 				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
 			break;
+		case (PH_ACTIVATE | REQUEST):
+			test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+			mode_2bs0(st->l1.bcs, st->l1.mode, st->l1.bc);
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | REQUEST):
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | CONFIRM):
+			test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+			test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+			mode_2bs0(st->l1.bcs, 0, st->l1.bc);
+			st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+			break;
 	}
 }
 
 void
 close_2bs0(struct BCState *bcs)
 {
-	struct sk_buff *skb;
-
-	mode_2bs0(bcs, 0, 0);
+	mode_2bs0(bcs, 0, bcs->channel);
 	if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
-		while ((skb = skb_dequeue(&bcs->rqueue))) {
-			dev_kfree_skb(skb);
-		}
-		while ((skb = skb_dequeue(&bcs->squeue))) {
-			dev_kfree_skb(skb);
-		}
-		if (bcs->hw.hfc.tx_skb) {
-			dev_kfree_skb(bcs->hw.hfc.tx_skb);
-			bcs->hw.hfc.tx_skb = NULL;
+		discard_queue(&bcs->rqueue);
+		discard_queue(&bcs->squeue);
+		if (bcs->tx_skb) {
+			dev_kfree_skb(bcs->tx_skb);
+			bcs->tx_skb = NULL;
 			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
 		}
 	}
 }
 
 static int
-open_hfcstate(struct IsdnCardState *cs,
-	      int bc)
+open_hfcstate(struct IsdnCardState *cs, struct BCState *bcs)
 {
-	struct BCState *bcs = cs->bcs + bc;
-
 	if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
 		skb_queue_head_init(&bcs->rqueue);
 		skb_queue_head_init(&bcs->squeue);
 	}
-	bcs->hw.hfc.tx_skb = NULL;
+	bcs->tx_skb = NULL;
 	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
 	bcs->event = 0;
 	bcs->tx_cnt = 0;
 	return (0);
 }
 
-static void
-hfc_manl1(struct PStack *st, int pr,
-	  void *arg)
-{
-	switch (pr) {
-		case (PH_ACTIVATE_REQ):
-			test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
-			mode_2bs0(st->l1.bcs, st->l1.mode, st->l1.bc);
-			st->l1.l1man(st, PH_ACTIVATE_CNF, NULL);
-			break;
-		case (PH_DEACTIVATE_REQ):
-			if (!test_bit(BC_FLG_BUSY, &st->l1.bcs->Flag))
-				mode_2bs0(st->l1.bcs, 0, 0);
-			test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
-			break;
-	}
-}
-
 int
 setstack_2b(struct PStack *st, struct BCState *bcs)
 {
-	if (open_hfcstate(st->l1.hardware, bcs->channel))
+	bcs->channel = st->l1.bc;
+	if (open_hfcstate(st->l1.hardware, bcs))
 		return (-1);
 	st->l1.bcs = bcs;
 	st->l2.l2l1 = hfc_l2l1;
-	st->ma.manl1 = hfc_manl1;
 	setstack_manager(st);
 	bcs->st = st;
+	setstack_l1_B(st);
 	return (0);
 }
 
 static void
-manl1_msg(struct IsdnCardState *cs, int msg, void *arg) {
-	struct PStack *st;
-
-	st = cs->stlist;
-	while (st) {
-		st->ma.manl1(st, msg, arg);
-		st = st->next;
-	}
-}
-
-static void
 hfcd_bh(struct IsdnCardState *cs)
 {
 /*	struct PStack *stptr;
@@ -671,7 +636,7 @@
 			debugl1(cs, "D-Channel Busy cleared");
 		stptr = cs->stlist;
 		while (stptr != NULL) {
-			stptr->l1.l1l2(stptr, PH_PAUSE_CNF, NULL);
+			stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL);
 			stptr = stptr->next;
 		}
 	}
@@ -679,19 +644,19 @@
 	if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) {
 		switch (cs->ph_state) {
 			case (0):
-				manl1_msg(cs, PH_RESET_IND, NULL);
+				l1_msg(cs, HW_RESET | INDICATION, NULL);
 				break;
 			case (3):
-				manl1_msg(cs, PH_DEACT_IND, NULL);
+				l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL);
 				break;
 			case (8):
-				manl1_msg(cs, PH_RSYNC_IND, NULL);
+				l1_msg(cs, HW_RSYNC | INDICATION, NULL);
 				break;
 			case (6):
-				manl1_msg(cs, PH_INFO2_IND, NULL);
+				l1_msg(cs, HW_INFO2 | INDICATION, NULL);
 				break;
 			case (7):
-				manl1_msg(cs, PH_I4_P8_IND, NULL);
+				l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL);
 				break;
 			default:
 				break;
@@ -722,7 +687,6 @@
 	int chksum;
 	int count=5;
 	u_char *ptr;
-	char tmp[64];
 
 	save_flags(flags);
 	cli();
@@ -745,11 +709,9 @@
 		if (rcnt < 0)
 			rcnt += cs->hw.hfcD.dfifosize;
 		rcnt++;
-		if (cs->debug & L1_DEB_ISAC) {
-			sprintf(tmp, "hfcd recd f1(%d) f2(%d) z1(%x) z2(%x) cnt(%d)",
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "hfcd recd f1(%d) f2(%d) z1(%x) z2(%x) cnt(%d)",
 				f1, f2, z1, z2, rcnt);
-			debugl1(cs, tmp);
-		}
 		sti();
 		idx = 0;
 		cip = HFCD_FIFO | HFCD_FIFO_OUT | HFCD_REC;
@@ -796,11 +758,9 @@
 				WaitNoBusy(cs);
 				stat = ReadReg(cs, HFCD_DATA, cip);
 				sti();
-				if (cs->debug & L1_DEB_ISAC) {
-					sprintf(tmp, "empty_dfifo chksum %x stat %x",
+				if (cs->debug & L1_DEB_ISAC)
+					debugl1(cs, "empty_dfifo chksum %x stat %x",
 						chksum, stat);
-					debugl1(cs, tmp);
-				}
 				if (stat) {
 					debugl1(cs, "FIFO CRC error");
 					dev_kfree_skb(skb);
@@ -837,7 +797,6 @@
 	int idx, fcnt;
 	int count;
 	u_char cip;
-	char tmp[64];
 
 	if (!cs->tx_skb)
 		return;
@@ -855,12 +814,10 @@
 	cs->hw.hfcD.f2 = ReadReg(cs, HFCD_DATA, cip) & 0xf;
 	cs->hw.hfcD.send[cs->hw.hfcD.f1] = ReadZReg(cs, HFCD_FIFO | HFCD_Z1 | HFCD_SEND);
 	sti();
-	if (cs->debug & L1_DEB_ISAC) {
-		sprintf(tmp, "hfc_fill_Dfifo f1(%d) f2(%d) z1(%x)",
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "hfc_fill_Dfifo f1(%d) f2(%d) z1(%x)",
 			cs->hw.hfcD.f1, cs->hw.hfcD.f2,
 			cs->hw.hfcD.send[cs->hw.hfcD.f1]);
-		debugl1(cs, tmp);
-	}
 	fcnt = cs->hw.hfcD.f1 - cs->hw.hfcD.f2;
 	if (fcnt < 0)
 		fcnt += 16;
@@ -871,11 +828,9 @@
 		return;
 	}
 	count = GetFreeFifoBytes_D(cs);
-	if (cs->debug & L1_DEB_ISAC) {
-		sprintf(tmp, "hfc_fill_Dfifo count(%d/%d)",
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "hfc_fill_Dfifo count(%ld/%d)",
 			cs->tx_skb->len, count);
-		debugl1(cs, tmp);
-	}
 	if (count < cs->tx_skb->len) {
 		if (cs->debug & L1_DEB_ISAC)
 			debugl1(cs, "hfc_fill_Dfifo no fifo mem");
@@ -929,24 +884,19 @@
 {
        	u_char exval;
        	struct BCState *bcs;
-	char tmp[32];
 	int count=15;
 	long flags;
 
-	if (cs->debug & L1_DEB_ISAC) {
-		sprintf(tmp, "HFCD irq %x %s", val,
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "HFCD irq %x %s", val,
 			test_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags) ?
 			"locked" : "unlocked");
-		debugl1(cs, tmp);
-	}
 	val &= cs->hw.hfcD.int_m1;
 	if (val & 0x40) { /* TE state machine irq */
 		exval = cs->readisac(cs, HFCD_STATES) & 0xf;
-		if (cs->debug & L1_DEB_ISAC) {
-			sprintf(tmp, "ph_state chg %d->%d", cs->ph_state,
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ph_state chg %d->%d", cs->ph_state,
 				exval);
-			debugl1(cs, tmp);
-		}
 		cs->ph_state = exval;
 		sched_event_D(cs, D_L1STATECHANGE);
 		val &= ~0x40;
@@ -983,23 +933,19 @@
 				if (cs->debug)
 					debugl1(cs, "hfcd spurious 0x01 IRQ");
 			} else {
-				if (bcs->hw.hfc.tx_skb) {
+				if (bcs->tx_skb) {
 					if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
 						hfc_fill_fifo(bcs);
 						test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
-					} else {
-						sprintf(tmp,"fill_data %d blocked", bcs->channel);
-						debugl1(cs, tmp);
-					}
+					} else
+						debugl1(cs,"fill_data %d blocked", bcs->channel);
 				} else {
-					if ((bcs->hw.hfc.tx_skb = skb_dequeue(&bcs->squeue))) {
+					if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
 						if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
 							hfc_fill_fifo(bcs);
 							test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
-						} else {
-							sprintf(tmp,"fill_data %d blocked", bcs->channel);
-							debugl1(cs, tmp);
-						}
+						} else
+							debugl1(cs,"fill_data %d blocked", bcs->channel);
 					} else {
 						hfc_sched_event(bcs, B_XMTBUFREADY);
 					}
@@ -1011,23 +957,19 @@
 				if (cs->debug)
 					debugl1(cs, "hfcd spurious 0x02 IRQ");
 			} else {
-				if (bcs->hw.hfc.tx_skb) {
+				if (bcs->tx_skb) {
 					if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
 						hfc_fill_fifo(bcs);
 						test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
-					} else {
-						sprintf(tmp,"fill_data %d blocked", bcs->channel);
-						debugl1(cs, tmp);
-					}
+					} else
+						debugl1(cs,"fill_data %d blocked", bcs->channel);
 				} else {
-					if ((bcs->hw.hfc.tx_skb = skb_dequeue(&bcs->squeue))) {
+					if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
 						if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
 							hfc_fill_fifo(bcs);
 							test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
-						} else {
-							sprintf(tmp,"fill_data %d blocked", bcs->channel);
-							debugl1(cs, tmp);
-						}
+						} else
+							debugl1(cs,"fill_data %d blocked", bcs->channel);
 					} else {
 						hfc_sched_event(bcs, B_XMTBUFREADY);
 					}
@@ -1042,7 +984,7 @@
 				del_timer(&cs->dbusytimer);
 			if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
 				sched_event_D(cs, D_CLEARBUSY);
-			if (cs->tx_skb) {
+			if (cs->tx_skb)
 				if (cs->tx_skb->len) {
 					if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
 						hfc_fill_dfifo(cs);
@@ -1056,7 +998,6 @@
 					cs->tx_cnt = 0;
 					cs->tx_skb = NULL;
 				}
-			}
 			if ((cs->tx_skb = skb_dequeue(&cs->sq))) {
 				cs->tx_cnt = 0;
 				if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
@@ -1072,10 +1013,8 @@
 		if (cs->hw.hfcD.int_s1 && count--) {
 			val = cs->hw.hfcD.int_s1;
 			cs->hw.hfcD.int_s1 = 0;
-			if (cs->debug & L1_DEB_ISAC) {
-				sprintf(tmp, "HFCD irq %x loop %d", val, 15-count);
-				debugl1(cs, tmp);
-			}
+			if (cs->debug & L1_DEB_ISAC)
+				debugl1(cs, "HFCD irq %x loop %d", val, 15-count);
 		} else
 			val = 0;
 		restore_flags(flags);
@@ -1083,13 +1022,17 @@
 }
 
 static void
-HFCD_l2l1(struct PStack *st, int pr, void *arg)
+HFCD_l1hw(struct PStack *st, int pr, void *arg)
 {
 	struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
 	struct sk_buff *skb = arg;
-	char str[64];
+	
 	switch (pr) {
-		case (PH_DATA_REQ):
+		case (PH_DATA | REQUEST):
+			if (cs->debug & DEB_DLOG_HEX)
+				LogFrame(cs, skb->data, skb->len);
+			if (cs->debug & DEB_DLOG_VERBOSE)
+				dlogframe(cs, skb, 0);
 			if (cs->tx_skb) {
 				skb_queue_tail(&cs->sq, skb);
 #ifdef L2FRAME_DEBUG		/* psa */
@@ -1097,12 +1040,6 @@
 					Logl2Frame(cs, skb, "PH_DATA Queued", 0);
 #endif
 			} else {
-				if ((cs->dlogflag) && (!(skb->data[2] & 1))) {	/* I-FRAME */
-					LogFrame(cs, skb->data, skb->len);
-					sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei);
-					dlogframe(cs, skb->data + 4, skb->len - 4,
-						  str);
-				}
 				cs->tx_skb = skb;
 				cs->tx_cnt = 0;
 #ifdef L2FRAME_DEBUG		/* psa */
@@ -1117,19 +1054,17 @@
 
 			}
 			break;
-		case (PH_PULL_IND):
+		case (PH_PULL | INDICATION):
 			if (cs->tx_skb) {
 				if (cs->debug & L1_DEB_WARN)
 					debugl1(cs, " l2l1 tx_skb exist this shouldn't happen");
 				skb_queue_tail(&cs->sq, skb);
 				break;
 			}
-			if ((cs->dlogflag) && (!(skb->data[2] & 1))) {	/* I-FRAME */
+			if (cs->debug & DEB_DLOG_HEX)
 				LogFrame(cs, skb->data, skb->len);
-				sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei);
-				dlogframe(cs, skb->data + 4, skb->len - 4,
-					  str);
-			}
+			if (cs->debug & DEB_DLOG_VERBOSE)
+				dlogframe(cs, skb, 0);
 			cs->tx_skb = skb;
 			cs->tx_cnt = 0;
 #ifdef L2FRAME_DEBUG		/* psa */
@@ -1142,47 +1077,39 @@
 			} else
 				debugl1(cs, "hfc_fill_dfifo blocked");
 			break;
-		case (PH_PULL_REQ):
+		case (PH_PULL | REQUEST):
 #ifdef L2FRAME_DEBUG		/* psa */
 			if (cs->debug & L1_DEB_LAPD)
 				debugl1(cs, "-> PH_REQUEST_PULL");
 #endif
 			if (!cs->tx_skb) {
 				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
-				st->l1.l1l2(st, PH_PULL_CNF, NULL);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
 			} else
 				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
 			break;
-	}
-}
-
-void
-hfcd_l1cmd(struct IsdnCardState *cs, int msg, void *arg)
-{
-	char tmp[32];
-	switch(msg) {
-		case PH_RESET_REQ:
+		case (HW_RESET | REQUEST):
 			cs->writeisac(cs, HFCD_STATES, HFCD_LOAD_STATE | 3); /* HFC ST 3 */
 			udelay(6);
 			cs->writeisac(cs, HFCD_STATES, 3); /* HFC ST 2 */
 			cs->hw.hfcD.mst_m |= HFCD_MASTER;
 			cs->writeisac(cs, HFCD_MST_MODE, cs->hw.hfcD.mst_m);
 			cs->writeisac(cs, HFCD_STATES, HFCD_ACTIVATE | HFCD_DO_ACTION);
-			manl1_msg(cs, PH_POWERUP_CNF, NULL);
+			l1_msg(cs, HW_POWERUP | CONFIRM, NULL);
 			break;
-		case PH_ENABLE_REQ:
+		case (HW_ENABLE | REQUEST):
 			cs->writeisac(cs, HFCD_STATES, HFCD_ACTIVATE | HFCD_DO_ACTION);
 			break;
-		case PH_DEACT_ACK:
+		case (HW_DEACTIVATE | REQUEST):
 			cs->hw.hfcD.mst_m &= ~HFCD_MASTER;
 			cs->writeisac(cs, HFCD_MST_MODE, cs->hw.hfcD.mst_m);
 			break;
-		case PH_INFO3_REQ:
+		case (HW_INFO3 | REQUEST):
 			cs->hw.hfcD.mst_m |= HFCD_MASTER;
 			cs->writeisac(cs, HFCD_MST_MODE, cs->hw.hfcD.mst_m);
 			break;
 #if 0
-		case PH_TESTLOOP_REQ:
+		case (HW_TESTLOOP | REQUEST):
 			u_char val = 0;
 			if (1 & (int) arg)
 				val |= 0x0c;
@@ -1208,10 +1135,8 @@
 			break;
 #endif
 		default:
-			if (cs->debug & L1_DEB_WARN) {
-				sprintf(tmp, "hfcd_l1cmd unknown %4x", msg);
-				debugl1(cs, tmp);
-			}
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "hfcd_l1hw unknown pr %4x", pr);
 			break;
 	}
 }
@@ -1219,7 +1144,7 @@
 void
 setstack_hfcd(struct PStack *st, struct IsdnCardState *cs)
 {
-	st->l2.l2l1 = HFCD_l2l1;
+	st->l1.l1hw = HFCD_l1hw;
 }
 
 static void
@@ -1234,7 +1159,7 @@
 		stptr = cs->stlist;
 		
 		while (stptr != NULL) {
-			stptr->l1.l1l2(stptr, PH_PAUSE_IND, NULL);
+			stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL);
 			stptr = stptr->next;
 		}
 	}
@@ -1260,7 +1185,6 @@
 init2bds0(struct IsdnCardState *cs))
 {
 	cs->setstack_d = setstack_hfcd;
-	cs->l1cmd = hfcd_l1cmd;
 	cs->dbusytimer.function = (void *) hfc_dbusy_timer;
 	cs->dbusytimer.data = (long) cs;
 	init_timer(&cs->dbusytimer);
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/hfc_2bs0.c linux/drivers/isdn/hisax/hfc_2bs0.c
--- v2.3.3/linux/drivers/isdn/hisax/hfc_2bs0.c	Wed Apr  1 16:20:58 1998
+++ linux/drivers/isdn/hisax/hfc_2bs0.c	Sun May 23 10:03:41 1999
@@ -1,11 +1,24 @@
-/* $Id: hfc_2bs0.c,v 1.4 1998/02/12 23:07:29 keil Exp $
+/* $Id: hfc_2bs0.c,v 1.8 1998/11/15 23:54:43 keil Exp $
 
  *  specific routines for CCD's HFC 2BS0
  *
- * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author       Karsten Keil (keil@isdn4linux.de)
  *
  *
  * $Log: hfc_2bs0.c,v $
+ * Revision 1.8  1998/11/15 23:54:43  keil
+ * changes from 2.0
+ *
+ * Revision 1.7  1998/09/30 22:24:46  keil
+ * Fix missing line in setstack*
+ *
+ * Revision 1.6  1998/08/13 23:36:28  keil
+ * HiSax 3.1 - don't work stable with current LinkLevel
+ *
+ * Revision 1.5  1998/05/25 12:57:54  keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
  * Revision 1.4  1998/02/12 23:07:29  keil
  * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
  *
@@ -109,7 +122,6 @@
 	int idx, cnt;
 	int rcnt, z1, z2;
 	u_char cip, f1, f2;
-	char tmp[64];
 
 	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
 		debugl1(cs, "hfc_clear_fifo");
@@ -129,21 +141,17 @@
 	z2 = ReadZReg(bcs, HFC_Z2 | HFC_REC | HFC_CHANNEL(bcs->channel));
 	cnt = 32;
 	while (((f1 != f2) || (z1 != z2)) && cnt--) {
-		if (cs->debug & L1_DEB_HSCX) {
-			sprintf(tmp, "hfc clear %d f1(%d) f2(%d)",
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfc clear %d f1(%d) f2(%d)",
 				bcs->channel, f1, f2);
-			debugl1(cs, tmp);
-		}
 		rcnt = z1 - z2;
 		if (rcnt < 0)
 			rcnt += cs->hw.hfc.fifosize;
 		if (rcnt)
 			rcnt++;
-		if (cs->debug & L1_DEB_HSCX) {
-			sprintf(tmp, "hfc clear %d z1(%x) z2(%x) cnt(%d)",
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfc clear %d z1(%x) z2(%x) cnt(%d)",
 				bcs->channel, z1, z2, rcnt);
-			debugl1(cs, tmp);
-		}
 		cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel);
 		idx = 0;
 		while ((idx < rcnt) && WaitNoBusy(cs)) {
@@ -180,7 +188,6 @@
 	int idx;
 	int chksum;
 	u_char stat, cip;
-	char tmp[64];
 
 	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
 		debugl1(cs, "hfc_empty_fifo");
@@ -235,11 +242,9 @@
 		chksum += cs->BC_Read_Reg(cs, HFC_DATA, cip);
 		WaitNoBusy(cs);
 		stat = cs->BC_Read_Reg(cs, HFC_DATA, cip);
-		if (cs->debug & L1_DEB_HSCX) {
-			sprintf(tmp, "hfc_empty_fifo %d chksum %x stat %x",
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfc_empty_fifo %d chksum %x stat %x",
 				bcs->channel, chksum, stat);
-			debugl1(cs, tmp);
-		}
 		if (stat) {
 			debugl1(cs, "FIFO CRC error");
 			dev_kfree_skb(skb);
@@ -261,11 +266,10 @@
 	int idx, fcnt;
 	int count;
 	u_char cip;
-	char tmp[64];
 
-	if (!bcs->hw.hfc.tx_skb)
+	if (!bcs->tx_skb)
 		return;
-	if (bcs->hw.hfc.tx_skb->len <= 0)
+	if (bcs->tx_skb->len <= 0)
 		return;
 
 	save_flags(flags);
@@ -281,12 +285,10 @@
 	WaitNoBusy(cs);
 	bcs->hw.hfc.f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
 	bcs->hw.hfc.send[bcs->hw.hfc.f1] = ReadZReg(bcs, HFC_Z1 | HFC_SEND | HFC_CHANNEL(bcs->channel));
-	if (cs->debug & L1_DEB_HSCX) {
-		sprintf(tmp, "hfc_fill_fifo %d f1(%d) f2(%d) z1(%x)",
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "hfc_fill_fifo %d f1(%d) f2(%d) z1(%x)",
 			bcs->channel, bcs->hw.hfc.f1, bcs->hw.hfc.f2,
 			bcs->hw.hfc.send[bcs->hw.hfc.f1]);
-		debugl1(cs, tmp);
-	}
 	fcnt = bcs->hw.hfc.f1 - bcs->hw.hfc.f2;
 	if (fcnt < 0)
 		fcnt += 32;
@@ -297,13 +299,11 @@
 		return;
 	}
 	count = GetFreeFifoBytes(bcs);
-	if (cs->debug & L1_DEB_HSCX) {
-		sprintf(tmp, "hfc_fill_fifo %d count(%d/%d)",
-			bcs->channel, bcs->hw.hfc.tx_skb->len,
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "hfc_fill_fifo %d count(%ld/%d)",
+			bcs->channel, bcs->tx_skb->len,
 			count);
-		debugl1(cs, tmp);
-	}
-	if (count < bcs->hw.hfc.tx_skb->len) {
+	if (count < bcs->tx_skb->len) {
 		if (cs->debug & L1_DEB_HSCX)
 			debugl1(cs, "hfc_fill_fifo no fifo mem");
 		restore_flags(flags);
@@ -311,18 +311,18 @@
 	}
 	cip = HFC_CIP | HFC_FIFO_IN | HFC_SEND | HFC_CHANNEL(bcs->channel);
 	idx = 0;
-	while ((idx < bcs->hw.hfc.tx_skb->len) && WaitNoBusy(cs))
-		cs->BC_Write_Reg(cs, HFC_DATA_NODEB, cip, bcs->hw.hfc.tx_skb->data[idx++]);
-	if (idx != bcs->hw.hfc.tx_skb->len) {
+	while ((idx < bcs->tx_skb->len) && WaitNoBusy(cs))
+		cs->BC_Write_Reg(cs, HFC_DATA_NODEB, cip, bcs->tx_skb->data[idx++]);
+	if (idx != bcs->tx_skb->len) {
 		debugl1(cs, "FIFO Send BUSY error");
 		printk(KERN_WARNING "HFC S FIFO channel %d BUSY Error\n", bcs->channel);
 	} else {
-		count =  bcs->hw.hfc.tx_skb->len;
+		count =  bcs->tx_skb->len;
 		bcs->tx_cnt -= count;
-		if (PACKET_NOACK == bcs->hw.hfc.tx_skb->pkt_type)
+		if (PACKET_NOACK == bcs->tx_skb->pkt_type)
 			count = -1;
-		dev_kfree_skb(bcs->hw.hfc.tx_skb);
-		bcs->hw.hfc.tx_skb = NULL;
+		dev_kfree_skb(bcs->tx_skb);
+		bcs->tx_skb = NULL;
 		WaitForBusy(cs);
 		WaitNoBusy(cs);
 		cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F1_INC | HFC_SEND | HFC_CHANNEL(bcs->channel));
@@ -343,7 +343,6 @@
 	u_char f1, f2, cip;
 	int receive, transmit, count = 5;
 	struct sk_buff *skb;
-	char tmp[64];
 
 	save_flags(flags);
       Begin:
@@ -360,11 +359,9 @@
 	WaitNoBusy(cs);
 	f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
 	if (f1 != f2) {
-		if (cs->debug & L1_DEB_HSCX) {
-			sprintf(tmp, "hfc rec %d f1(%d) f2(%d)",
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfc rec %d f1(%d) f2(%d)",
 				bcs->channel, f1, f2);
-			debugl1(cs, tmp);
-		}
 		WaitForBusy(cs);
 		z1 = ReadZReg(bcs, HFC_Z1 | HFC_REC | HFC_CHANNEL(bcs->channel));
 		z2 = ReadZReg(bcs, HFC_Z2 | HFC_REC | HFC_CHANNEL(bcs->channel));
@@ -372,11 +369,9 @@
 		if (rcnt < 0)
 			rcnt += cs->hw.hfc.fifosize;
 		rcnt++;
-		if (cs->debug & L1_DEB_HSCX) {
-			sprintf(tmp, "hfc rec %d z1(%x) z2(%x) cnt(%d)",
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfc rec %d z1(%x) z2(%x) cnt(%d)",
 				bcs->channel, z1, z2, rcnt);
-			debugl1(cs, tmp);
-		}
 /*              sti(); */
 		if ((skb = hfc_empty_fifo(bcs, rcnt))) {
 			skb_queue_tail(&bcs->rqueue, skb);
@@ -388,14 +383,14 @@
 	restore_flags(flags);
 	udelay(1);
 	cli();
-	if (bcs->hw.hfc.tx_skb) {
+	if (bcs->tx_skb) {
 		transmit = 1;
 		test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
 		hfc_fill_fifo(bcs);
 		if (test_bit(BC_FLG_BUSY, &bcs->Flag))
 			transmit = 0;
 	} else {
-		if ((bcs->hw.hfc.tx_skb = skb_dequeue(&bcs->squeue))) {
+		if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
 			transmit = 1;
 			test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
 			hfc_fill_fifo(bcs);
@@ -417,13 +412,11 @@
 {
 	struct IsdnCardState *cs = bcs->cs;
 
-	if (cs->debug & L1_DEB_HSCX) {
-		char tmp[40];
-		sprintf(tmp, "HFC 2BS0 mode %d bchan %d/%d",
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "HFC 2BS0 mode %d bchan %d/%d",
 			mode, bc, bcs->channel);
-		debugl1(cs, tmp);
-	}
 	bcs->mode = mode;
+	bcs->channel = bc;
 
 	switch (mode) {
 		case (L1_MODE_NULL):
@@ -468,57 +461,66 @@
 	long flags;
 
 	switch (pr) {
-		case (PH_DATA_REQ):
+		case (PH_DATA | REQUEST):
 			save_flags(flags);
 			cli();
-			if (st->l1.bcs->hw.hfc.tx_skb) {
+			if (st->l1.bcs->tx_skb) {
 				skb_queue_tail(&st->l1.bcs->squeue, skb);
 				restore_flags(flags);
 			} else {
-				st->l1.bcs->hw.hfc.tx_skb = skb;
+				st->l1.bcs->tx_skb = skb;
 				test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
 				st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
 				restore_flags(flags);
 			}
 			break;
-		case (PH_PULL_IND):
-			if (st->l1.bcs->hw.hfc.tx_skb) {
+		case (PH_PULL | INDICATION):
+			if (st->l1.bcs->tx_skb) {
 				printk(KERN_WARNING "hfc_l2l1: this shouldn't happen\n");
 				break;
 			}
 			save_flags(flags);
 			cli();
 			test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
-			st->l1.bcs->hw.hfc.tx_skb = skb;
+			st->l1.bcs->tx_skb = skb;
 			st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
 			restore_flags(flags);
 			break;
-		case (PH_PULL_REQ):
-			if (!st->l1.bcs->hw.hfc.tx_skb) {
+		case (PH_PULL | REQUEST):
+			if (!st->l1.bcs->tx_skb) {
 				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
-				st->l1.l1l2(st, PH_PULL_CNF, NULL);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
 			} else
 				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
 			break;
+		case (PH_ACTIVATE | REQUEST):
+			test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+			mode_hfc(st->l1.bcs, st->l1.mode, st->l1.bc);
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | REQUEST):
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | CONFIRM):
+			test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+			test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+			mode_hfc(st->l1.bcs, 0, st->l1.bc);
+			st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+			break;
 	}
 }
 
+
 void
 close_hfcstate(struct BCState *bcs)
 {
-	struct sk_buff *skb;
-
-	mode_hfc(bcs, 0, 0);
+	mode_hfc(bcs, 0, bcs->channel);
 	if (test_bit(BC_FLG_INIT, &bcs->Flag)) {
-		while ((skb = skb_dequeue(&bcs->rqueue))) {
-			dev_kfree_skb(skb);
-		}
-		while ((skb = skb_dequeue(&bcs->squeue))) {
-			dev_kfree_skb(skb);
-		}
-		if (bcs->hw.hfc.tx_skb) {
-			dev_kfree_skb(bcs->hw.hfc.tx_skb);
-			bcs->hw.hfc.tx_skb = NULL;
+		discard_queue(&bcs->rqueue);
+		discard_queue(&bcs->squeue);
+		if (bcs->tx_skb) {
+			dev_kfree_skb(bcs->tx_skb);
+			bcs->tx_skb = NULL;
 			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
 		}
 	}
@@ -526,50 +528,30 @@
 }
 
 static int
-open_hfcstate(struct IsdnCardState *cs,
-	      int bc)
+open_hfcstate(struct IsdnCardState *cs, struct BCState *bcs)
 {
-	struct BCState *bcs = cs->bcs + bc;
-
 	if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
 		skb_queue_head_init(&bcs->rqueue);
 		skb_queue_head_init(&bcs->squeue);
 	}
-	bcs->hw.hfc.tx_skb = NULL;
+	bcs->tx_skb = NULL;
 	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
 	bcs->event = 0;
 	bcs->tx_cnt = 0;
 	return (0);
 }
 
-static void
-hfc_manl1(struct PStack *st, int pr,
-	  void *arg)
-{
-	switch (pr) {
-		case (PH_ACTIVATE_REQ):
-			test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
-			mode_hfc(st->l1.bcs, st->l1.mode, st->l1.bc);
-			st->l1.l1man(st, PH_ACTIVATE_CNF, NULL);
-			break;
-		case (PH_DEACTIVATE_REQ):
-			if (!test_bit(BC_FLG_BUSY, &st->l1.bcs->Flag))
-				mode_hfc(st->l1.bcs, 0, 0);
-			test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
-			break;
-	}
-}
-
 int
 setstack_hfc(struct PStack *st, struct BCState *bcs)
 {
-	if (open_hfcstate(st->l1.hardware, bcs->channel))
+	bcs->channel = st->l1.bc;
+	if (open_hfcstate(st->l1.hardware, bcs))
 		return (-1);
 	st->l1.bcs = bcs;
 	st->l2.l2l1 = hfc_l2l1;
-	st->ma.manl1 = hfc_manl1;
 	setstack_manager(st);
 	bcs->st = st;
+	setstack_l1_B(st);
 	return (0);
 }
 
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/hisax.h linux/drivers/isdn/hisax/hisax.h
--- v2.3.3/linux/drivers/isdn/hisax/hisax.h	Mon Mar 15 16:11:30 1999
+++ linux/drivers/isdn/hisax/hisax.h	Sun May 23 10:03:41 1999
@@ -1,8 +1,48 @@
-/* $Id: hisax.h,v 2.14 1998/02/11 17:28:04 keil Exp $
+/* $Id: hisax.h,v 2.26 1998/11/15 23:54:45 keil Exp $
 
  *   Basic declarations, defines and prototypes
  *
  * $Log: hisax.h,v $
+ * Revision 2.26  1998/11/15 23:54:45  keil
+ * changes from 2.0
+ *
+ * Revision 2.25  1998/09/30 22:28:42  keil
+ * More work for ISAR support
+ *
+ * Revision 2.24  1998/08/20 13:50:39  keil
+ * More support for hybrid modem (not working yet)
+ *
+ * Revision 2.23  1998/08/13 23:36:31  keil
+ * HiSax 3.1 - don't work stable with current LinkLevel
+ *
+ * Revision 2.22  1998/07/15 15:01:28  calle
+ * Support for AVM passive PCMCIA cards:
+ *    A1 PCMCIA, FRITZ!Card PCMCIA and FRITZ!Card PCMCIA 2.0
+ *
+ * Revision 2.21  1998/05/25 14:10:05  keil
+ * HiSax 3.0
+ * X.75 and leased are working again.
+ *
+ * Revision 2.20  1998/05/25 12:57:57  keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
+ * Revision 2.19  1998/04/15 16:39:15  keil
+ * Add S0Box and Teles PCI support
+ *
+ * Revision 2.18  1998/03/26 07:10:04  paul
+ * The jumpmatrix table in struct Fsm was an array of "int". This is not
+ * large enough for pointers to functions on Linux/Alpha (instant crash
+ * on "insmod hisax). Now there is a typedef for the pointer to function.
+ * This also prevents warnings about "incompatible pointer types".
+ *
+ * Revision 2.17  1998/03/19 13:18:43  keil
+ * Start of a CAPI like interface for supplementary Service
+ * first service: SUSPEND
+ *
+ * Revision 2.16  1998/03/09 23:19:25  keil
+ * Changes for PCMCIA
+ *
  * Revision 2.14  1998/02/11 17:28:04  keil
  * Niccy PnP/PCI support
  *
@@ -72,120 +112,90 @@
 #include <linux/isdnif.h>
 #include <linux/tty.h>
 #include <linux/init.h>
+#include <linux/serialP.h>
+
+#define REQUEST		0
+#define CONFIRM		1
+#define INDICATION	2
+#define RESPONSE	3
+
+#define HW_ENABLE	0x0000
+#define HW_RESET	0x0004
+#define HW_POWERUP	0x0008
+#define HW_ACTIVATE	0x0010
+#define HW_DEACTIVATE	0x0018
+#define HW_INFO2	0x0020
+#define HW_INFO3	0x0030
+#define HW_INFO4_P8	0x0040
+#define HW_INFO4_P10	0x0048
+#define HW_RSYNC	0x0060
+#define HW_TESTLOOP	0x0070
+#define CARD_RESET	0x00F0
+#define CARD_SETIRQ	0x00F1
+#define CARD_INIT	0x00F2
+#define CARD_RELEASE	0x00F3
+#define CARD_TEST	0x00F4
+#define CARD_AUX_IND	0x00F5
+#define CARD_LOAD_FIRM	0x00F6
+
+#define PH_ACTIVATE	0x0100
+#define PH_DEACTIVATE	0x0110
+#define PH_DATA		0x0120
+#define PH_PULL		0x0130
+#define PH_TESTLOOP	0x0140
+#define PH_PAUSE	0x0150
+#define MPH_ACTIVATE	0x0180
+#define MPH_DEACTIVATE	0x0190
+#define MPH_INFORMATION	0x01A0
+
+#define DL_ESTABLISH	0x0200
+#define DL_RELEASE	0x0210
+#define DL_DATA		0x0220
+#define DL_FLUSH	0x0224
+#define DL_UNIT_DATA	0x0230
+#define MDL_ASSIGN	0x0280
+#define MDL_REMOVE	0x0284
+#define MDL_ERROR	0x0288
+#define MDL_INFO_SETUP	0x02E0
+#define MDL_INFO_CONN	0x02E4
+#define MDL_INFO_REL	0x02E8
+
+#define CC_SETUP	0x0300
+#define CC_RESUME	0x0304
+#define CC_MORE_INFO	0x0310
+#define CC_IGNORE	0x0320
+#define CC_REJECT	0x0324
+#define CC_SETUP_COMPL	0x0330
+#define CC_PROCEEDING	0x0340
+#define CC_ALERTING	0x0344
+#define CC_CONNECT	0x0350
+#define CC_CHARGE	0x0354
+#define CC_DISCONNECT	0x0360
+#define CC_RELEASE	0x0368
+#define CC_SUSPEND	0x0370
+#define CC_T303		0x0383
+#define CC_T304		0x0384
+#define CC_T305		0x0385
+#define CC_T308_1	0x0388
+#define CC_T308_2	0x0389
+#define CC_T310		0x0390
+#define CC_T313		0x0393
+#define CC_T318		0x0398
+#define CC_T319		0x0399
+#define CC_NOSETUP_RSP	0x03E0
+#define CC_SETUP_ERR	0x03E1
+#define CC_SUSPEND_ERR	0x03E2
+#define CC_RESUME_ERR	0x03E3
+#define CC_CONNECT_ERR	0x03E4
+#define CC_RELEASE_ERR	0x03E5
+#define CC_DLRL		0x03F0
+#define CC_RESTART	0x03F4
 
-#define PH_ACTIVATE_REQ	0x0010
-#define PH_ACTIVATE_CNF	0x0011
-#define PH_ACTIVATE_IND	0x0012
-#define PH_DEACTIVATE_REQ 0x0020
-#define PH_DEACTIVATE_CNF 0x0021
-#define PH_DEACTIVATE_IND 0x0022
-#define PH_DEACT_REQ	0x0024
-#define PH_DEACT_CNF	0x0025
-#define PH_DEACT_IND	0x0026
-#define PH_DEACT_ACK	0x0027
-#define PH_TESTLOOP_REQ	0x0030
-#define PH_PAUSE_CNF	0x0035
-#define PH_PAUSE_IND	0x0036
-#define PH_PULL_REQ	0x0038
-#define PH_PULL_CNF	0x0039
-#define	PH_PULL_IND	0x003A
-#define PH_DATA_REQ	0x0040
-#define PH_DATA_IND	0x0042
-
-#define PH_INFO3_REQ	0x0008
-#define PH_INFO2_IND	0x000A
-#define PH_ENABLE_REQ	0x0004
-#define PH_RSYNC_IND	0x0006
-#define PH_RESET_REQ	0x0000
-#define PH_RESET_IND	0x0002
-#define PH_POWERUP_CNF	0x0003
-#define PH_ACTIV_REQ	0x000C
-#define PH_I4_P8_IND	0x000D
-#define PH_I4_P10_IND	0x000F
-
-#define MDL_ASSIGN_REQ	0x0050
-#define MDL_ASSIGN_IND	0x0052
-#define MDL_REMOVE_REQ	0x0054
-#define MDL_ERROR_REQ	0x0058
-#define MDL_ERROR_IND	0x005A
-#define CARD_AUX_IND	0x005E
-
-#define DL_UNIT_DATA	6
-#define CC_ESTABLISH	7
-#define DL_ESTABLISH	8
-#define DL_DATA		9
-
-#define CC_CONNECT	15
-#define DL_RELEASE	20
-#define DL_FLUSH	21
-
-#define CC_REJECT	23
-
-#define CC_SETUP_REQ	24
-#define CC_SETUP_CNF	25
-#define CC_SETUP_IND	26
-#define CC_SETUP_RSP	27
-#define CC_SETUP_COMPLETE_IND	28
-
-#define CC_DISCONNECT_REQ	29
-#define CC_DISCONNECT_IND	30
-
-#define CC_RELEASE_CNF	31
-#define CC_RELEASE_IND	32
-#define CC_RELEASE_REQ	33
-
-#define CC_REJECT_REQ	34
-
-#define CC_PROCEEDING_IND	35
-
-#define CC_DLRL		36
-#define CC_DLEST	37
-
-#define CC_ALERTING_REQ	38
-#define CC_ALERTING_IND	39
-
-#define DL_STOP		40
-#define DL_START	41
-
-#define MDL_INFO_SETUP	42
-#define MDL_INFO_CONN	43
-#define MDL_INFO_REL	44
-#define MDL_NOTEIPROC	46
-
-#define LC_ESTABLISH	47
-#define LC_RELEASE	48
-
-#define CC_INFO_CHARGE	52
-
-#define CC_MORE_INFO	53
-#define CC_IGNORE	54
-#define CC_RESTART	55
-
-
-#define CC_T303		60
-#define CC_T304		61
-#define CC_T305		62
-#define CC_T308_1	64
-#define CC_T308_2	65
-#define CC_T310		66
-#define CC_T313		67
-#define CC_T318		68
-#define CC_T319		69
-
-#define CC_NOSETUP_RSP_ERR	70
-#define CC_SETUP_ERR		71
-#define CC_CONNECT_ERR		72
-#define CC_RELEASE_ERR		73
-
-#define CARD_RESET	0x1001
-#define CARD_SETIRQ	0x1002
-#define CARD_INIT	0x1003
-#define CARD_RELEASE	0x1004
-#define CARD_TEST	0x1005
 
 #ifdef __KERNEL__
 
 #define MAX_DFRAME_LEN	260
+#define MAX_DFRAME_LEN_L1	300
 #define HSCX_BUFMAX	4096
 #define MAX_DATA_SIZE	(HSCX_BUFMAX - 4)
 #define MAX_DATA_MEM	(HSCX_BUFMAX + 64)
@@ -193,6 +203,8 @@
 #define MAX_HEADER_LEN	4
 #define MAX_WINDOW	8
 #define MAX_MON_FRAME	32
+#define MAX_DLOG_SPACE	2048
+#define MAX_BLOG_SPACE	256
 
 /* #define I4L_IRQ_FLAG SA_INTERRUPT */
 #define I4L_IRQ_FLAG    0
@@ -201,8 +213,12 @@
  * Statemachine
  */
 
+struct FsmInst;
+
+typedef void (* FSMFNPTR)(struct FsmInst *, int, void *);
+
 struct Fsm {
-	int *jumpmatrix;
+	FSMFNPTR *jumpmatrix;
 	int state_count, event_count;
 	char **strEvent, **strState;
 };
@@ -213,7 +229,7 @@
 	int debug;
 	void *userdata;
 	int userint;
-	void (*printdebug) (struct FsmInst *, char *);
+	void (*printdebug) (struct FsmInst *, char *, ...);
 };
 
 struct FsmNode {
@@ -249,9 +265,10 @@
 	struct FsmInst l1m;
 	struct FsmTimer	timer;
 	void (*l1l2) (struct PStack *, int, void *);
-	void (*l1man) (struct PStack *, int, void *);
+	void (*l1hw) (struct PStack *, int, void *);
 	void (*l1tei) (struct PStack *, int, void *);
 	int mode, bc;
+	int delay;
 };
 
 #define GROUP_TEI	127
@@ -266,17 +283,20 @@
 #define FLG_ORIG	2
 #define FLG_MOD128	3
 #define FLG_PEND_REL	4
-#define FLG_L3_INIT	5 
-#define FLG_T200_RUN	6 
+#define FLG_L3_INIT	5
+#define FLG_T200_RUN	6
 #define FLG_ACK_PEND	7
 #define FLG_REJEXC	8
 #define FLG_OWN_BUSY	9
 #define FLG_PEER_BUSY	10
 #define FLG_DCHAN_BUSY	11
+#define FLG_L1_ACTIV	12
+#define FLG_ESTAB_PEND	13
+#define FLG_PTP		14
+#define FLG_FIXED_TEI	15
 
 struct Layer2 {
 	int tei;
-	int tei_wanted;
 	int sap;
 	int maxlen;
 	unsigned int flag;
@@ -288,23 +308,25 @@
 	struct sk_buff_head i_queue;
 	struct sk_buff_head ui_queue;
 	void (*l2l1) (struct PStack *, int, void *);
-	void (*l2man) (struct PStack *, int, void *);
 	void (*l2l3) (struct PStack *, int, void *);
 	void (*l2tei) (struct PStack *, int, void *);
 	struct FsmInst l2m;
 	struct FsmTimer t200, t203;
 	int T200, N200, T203;
 	int debug;
-	char debug_id[32];
+	char debug_id[16];
 };
 
 struct Layer3 {
-	void (*l3l4) (struct l3_process *, int, void *);
+	void (*l3l4) (struct PStack *, int, void *);
 	void (*l3l2) (struct PStack *, int, void *);
+	struct FsmInst l3m;
+	struct sk_buff_head squeue;
 	struct l3_process *proc;
 	struct l3_process *global;
 	int N303;
 	int debug;
+	char debug_id[8];
 };
 
 struct LLInterface {
@@ -321,8 +343,6 @@
 	struct FsmTimer t202;
 	int T202, N202, debug;
 	void (*layer) (struct PStack *, int, void *);
-	void (*manl1) (struct PStack *, int, void *);
-	void (*manl2) (struct PStack *, int, void *);
 };
 
 
@@ -343,7 +363,7 @@
 	struct Layer1 l1;
 	struct Layer2 l2;
 	struct Layer3 l3;
-	struct LLInterface lli; 
+	struct LLInterface lli;
 	struct Management ma;
 	int protocol;		/* EDSS1 or 1TR6 */
 };
@@ -361,21 +381,55 @@
 };
 
 struct hscx_hw {
+	int hscx;
+	int rcvidx;
+	int count;              /* Current skb sent count */
+	u_char *rcvbuf;         /* B-Channel receive Buffer */
+};
+
+struct isar_reg {
+	unsigned int Flags;
+	volatile u_char bstat;
+	volatile u_char iis;
+	volatile u_char cmsb;
+	volatile u_char clsb;
+	volatile u_char par[8];
+};
+
+struct isar_hw {
+	int dpath;
+	int rcvidx;
+	int txcnt;
+	int mml;
+	u_char *rcvbuf;         /* B-Channel receive Buffer */
+	struct isar_reg *reg;
+};
+
+struct hdlc_stat_reg {
+	u_char cmd  __attribute__((packed));
+	u_char xml  __attribute__((packed));
+	u_char mode __attribute__((packed));
+	u_char fill __attribute__((packed));
+};
+
+struct hdlc_hw {
+	union {
+		u_int ctrl;
+		struct hdlc_stat_reg sr;
+	} ctrl;
+	u_int stat;
 	int rcvidx;
 	int count;              /* Current skb sent count */
 	u_char *rcvbuf;         /* B-Channel receive Buffer */
-	struct sk_buff *tx_skb; /* B-Channel transmit Buffer */
 };
 
 struct hfcB_hw {
 	unsigned int *send;
 	int f1;
 	int f2;
-	struct sk_buff *tx_skb; /* B-Channel transmit Buffer */
 };
 
 struct tiger_hw {
-	struct sk_buff *tx_skb; /* B-Channel transmit Buffer */
 	u_int *send;
 	u_int *s_irq;
 	u_int *s_end;
@@ -397,8 +451,7 @@
 	u_char s_state;
 };
 
-struct foreign_hw {
-	int doHDLCprocessing;
+struct amd7930_hw {
 	u_char *tx_buff;
 	u_char *rv_buff;
 	int rv_buff_in;
@@ -407,9 +460,9 @@
 	struct hdlc_state *hdlc_state;
 	struct tq_struct tq_rcv;
 	struct tq_struct tq_xmt;
-	struct sk_buff *tx_skb; /* B-Channel transmit Buffer */
 };
 
+
 #define BC_FLG_INIT	1
 #define BC_FLG_ACTIV	2
 #define BC_FLG_BUSY	3
@@ -420,6 +473,7 @@
 #define L1_MODE_NULL	0
 #define L1_MODE_TRANS	1
 #define L1_MODE_HDLC	2
+#define L1_MODE_MODEM	7
 
 struct BCState {
 	int channel;
@@ -427,34 +481,26 @@
 	int Flag;
 	struct IsdnCardState *cs;
 	int tx_cnt;		/* B-Channel transmit counter */
+	struct sk_buff *tx_skb; /* B-Channel transmit Buffer */
 	struct sk_buff_head rqueue;	/* B-Channel receive Queue */
 	struct sk_buff_head squeue;	/* B-Channel send Queue */
 	struct PStack *st;
+	u_char *blog;
+	struct timer_list transbusy;
 	struct tq_struct tqueue;
 	int event;
 	int  (*BC_SetStack) (struct PStack *, struct BCState *);
 	void (*BC_Close) (struct BCState *);
 	union {
 		struct hscx_hw hscx;
+		struct hdlc_hw hdlc;
+		struct isar_hw isar;
 		struct hfcB_hw hfc;
 		struct tiger_hw tiger;
-		struct foreign_hw foreign;
+		struct amd7930_hw  amd7930;
 	} hw;
 };
 
-struct LcFsm {
-	int type;
-	int delay;
-	struct FsmInst lcfi;
-	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 *b_st, *d_st;
 	struct IsdnCardState *cs;
@@ -462,11 +508,10 @@
 	int chan;
 	int incoming;
 	struct FsmInst fi;
-	struct LcFsm *lc_d;
-	struct LcFsm *lc_b;
 	struct FsmTimer drel_timer, dial_timer;
 	int debug;
 	int l2_protocol, l2_active_protocol;
+	int l3_protocol;
 	int data_open;
 	struct l3_process *proc;
 	setup_parm setup;	/* from isdnif.h numbers and Serviceindicator */
@@ -487,21 +532,33 @@
 	unsigned int counter;
 	unsigned int status;
 	struct timer_list tl;
+	unsigned int MFlag;
+	struct BCState *bcs;
+	u_char *transbuf;
+	u_char *rcvbuf;
+	unsigned int transp;
+	unsigned int rcvp;
+	unsigned int transcnt;
+	unsigned int rcvcnt;
+	u_char IER;
+	u_char FCR;
+	u_char LCR;
+	u_char MCR;
 	u_char ctrl_reg;
-};	
+};
 
 struct teles3_hw {
 	unsigned int cfg_reg;
-	unsigned int isac;
-	unsigned int hscx[2];
-	unsigned int isacfifo;
-	unsigned int hscxfifo[2];
-};	
+	signed   int isac;
+	signed   int hscx[2];
+	signed   int isacfifo;
+	signed   int hscxfifo[2];
+};
 
 struct teles0_hw {
 	unsigned int cfg_reg;
 	unsigned int membase;
-};	
+};
 
 struct avm_hw {
 	unsigned int cfg_reg;
@@ -510,7 +567,7 @@
 	unsigned int isacfifo;
 	unsigned int hscxfifo[2];
 	unsigned int counter;
-};	
+};
 
 struct ix1_hw {
 	unsigned int cfg_reg;
@@ -530,7 +587,7 @@
 	unsigned int status;
 	struct timer_list tl;
 	u_char ctrl_reg;
-};	
+};
 
 struct asus_hw {
 	unsigned int cfg_reg;
@@ -559,6 +616,9 @@
 	unsigned int hscx;
 	unsigned int reset_on;
 	unsigned int reset_off;
+	struct isar_reg isar;
+	unsigned int chip;
+	unsigned int bus;
 };
 
 struct spt_hw {
@@ -566,7 +626,7 @@
 	unsigned int isac;
 	unsigned int hscx[2];
 	unsigned char res_irq;
-};	
+};
 
 struct mic_hw {
 	unsigned int cfg_reg;
@@ -610,6 +670,7 @@
 
 #define HW_IOM1		0
 #define HW_IPAC		1
+#define HW_ISAR		2
 #define FLG_TWO_DCHAN	4
 #define FLG_L1_DBUSY	5
 #define FLG_DBUSY_TIMER 6
@@ -624,7 +685,7 @@
 	unsigned char subtyp;
 	int protocol;
 	unsigned int irq;
-	int HW_Flags; 
+	int HW_Flags;
 	int *busy_flag;
 	union {
 		struct elsa_hw elsa;
@@ -641,7 +702,6 @@
 		struct njet_hw njet;
 		struct hfcD_hw hfcD;
 		struct ix1_hw niccy;
-                struct foreign_interface *foreign;
 	} hw;
 	int myid;
 	isdn_if iif;
@@ -657,7 +717,6 @@
 	void   (*BC_Write_Reg) (struct IsdnCardState *, int, u_char, u_char);
 	void   (*BC_Send_Data) (struct BCState *);
 	int    (*cardmsg) (struct IsdnCardState *, int, void *);
-	void   (*l1cmd) (struct IsdnCardState *, int, void *);
 	struct Channel channel[2];
 	struct BCState bcs[2];
 	struct PStack *stlist;
@@ -671,8 +730,7 @@
 	struct sk_buff_head rq, sq; /* D-channel queues */
 	int ph_state;
 	int cardnr;
-	int dlogflag;
-	char *dlogspace;
+	char *dlog;
 	int debug;
 	u_char *mon_tx;
 	u_char *mon_rx;
@@ -688,6 +746,8 @@
 #define  MON0_TX	4
 #define  MON1_TX	8
 
+#define	 HISAX_MAX_CARDS	8
+
 #define  ISDN_CTYPE_16_0	1
 #define  ISDN_CTYPE_8_0		2
 #define  ISDN_CTYPE_16_3	3
@@ -712,9 +772,12 @@
 #define  ISDN_CTYPE_SEDLBAUER_PCMCIA	22
 #define  ISDN_CTYPE_AMD7930	23
 #define  ISDN_CTYPE_NICCY	24
-#define  ISDN_CTYPE_DBRI	25
+#define  ISDN_CTYPE_S0BOX	25
+#define  ISDN_CTYPE_A1_PCMCIA	26
+#define  ISDN_CTYPE_FRITZPCI	27
+#define  ISDN_CTYPE_SEDLBAUER_FAX     28
 
-#define  ISDN_CTYPE_COUNT	25
+#define  ISDN_CTYPE_COUNT	28
 
 #ifdef ISDN_CHIP_ISAC
 #undef ISDN_CHIP_ISAC
@@ -742,15 +805,42 @@
 #define  CARD_TELES3  0
 #endif
 
+#ifdef	CONFIG_HISAX_TELESPCI
+#define  CARD_TELESPCI (1<< ISDN_CTYPE_TELESPCI)
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define  CARD_TELESPCI  0
+#endif
+
 #ifdef	CONFIG_HISAX_AVM_A1
 #define  CARD_AVM_A1 (1<< ISDN_CTYPE_A1)
-#ifndef ISDN_CHIP_ISAC 
+#ifndef ISDN_CHIP_ISAC
 #define ISDN_CHIP_ISAC 1
 #endif
 #else
 #define  CARD_AVM_A1  0
 #endif
 
+#ifdef	CONFIG_HISAX_AVM_A1_PCMCIA
+#define  CARD_AVM_A1_PCMCIA (1<< ISDN_CTYPE_A1_PCMCIA)
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define  CARD_AVM_A1_PCMCIA  0
+#endif
+
+#ifdef	CONFIG_HISAX_FRITZPCI
+#define  CARD_FRITZPCI (1<< ISDN_CTYPE_FRITZPCI)
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define  CARD_FRITZPCI  0
+#endif
+
 #ifdef	CONFIG_HISAX_ELSA
 #define  CARD_ELSA (1<< ISDN_CTYPE_ELSA) | (1<< ISDN_CTYPE_ELSA_PNP) | \
 		   (1<< ISDN_CTYPE_ELSA_PCMCIA) | (1<< ISDN_CTYPE_ELSA_PCI)
@@ -803,7 +893,7 @@
 #endif
 
 #ifdef  CONFIG_HISAX_SEDLBAUER
-#define CARD_SEDLBAUER (1 << ISDN_CTYPE_SEDLBAUER) | (1 << ISDN_CTYPE_SEDLBAUER_PCMCIA)
+#define CARD_SEDLBAUER (1 << ISDN_CTYPE_SEDLBAUER) | (1 << ISDN_CTYPE_SEDLBAUER_PCMCIA) | ( 1 << ISDN_CTYPE_SEDLBAUER_FAX)
 #ifndef ISDN_CHIP_ISAC
 #define ISDN_CHIP_ISAC 1
 #endif
@@ -859,18 +949,21 @@
 #define CARD_NICCY 0
 #endif
 
-#ifdef  CONFIG_HISAX_DBRI
-#define CARD_DBRI (1 << ISDN_CTYPE_DBRI)
+#ifdef	CONFIG_HISAX_S0BOX
+#define	CARD_S0BOX (1 << ISDN_CTYPE_S0BOX)
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
 #else
-#define CARD_DBRI 0
+#define CARD_S0BOX 0
 #endif
 
-
 #define  SUPORTED_CARDS  (CARD_TELES0 | CARD_TELES3 | CARD_AVM_A1 | CARD_ELSA \
 			 | CARD_IX1MICROR2 | CARD_DIEHLDIVA | CARD_ASUSCOM \
 			 | CARD_TELEINT | CARD_SEDLBAUER | CARD_SPORTSTER \
 			 | CARD_MIC | CARD_NETJET | CARD_TELES3C | CARD_AMD7930 \
-			 | CARD_NICCY | CARD_DBRI)
+			 | CARD_AVM_A1_PCMCIA | CARD_FRITZPCI\
+			 | CARD_NICCY | CARD_S0BOX | CARD_TELESPCI)
 
 #define TEI_PER_CARD 0
 
@@ -883,25 +976,38 @@
 #undef TEI_PER_CARD
 #define TEI_PER_CARD 1
 #define HISAX_EURO_SENDCOMPLETE 1
-#ifdef	CONFIG_HISAX_ML
+#define EXT_BEARER_CAPS 1
+#define HISAX_SEND_STD_LLC_IE 1
+#ifdef	CONFIG_HISAX_NO_SENDCOMPLETE
 #undef HISAX_EURO_SENDCOMPLETE
 #endif
+#ifdef	CONFIG_HISAX_NO_LLC
+#undef HISAX_SEND_STD_LLC_IE
+#endif
 #undef HISAX_DE_AOC
 #ifdef CONFIG_DE_AOC
 #define HISAX_DE_AOC 1
 #endif
 #endif
 
-#if TEI_PER_CARD
-#undef TEI_FIXED
-#endif
+/* L1 Debug */
+#define	L1_DEB_WARN		0x01
+#define	L1_DEB_INTSTAT		0x02
+#define	L1_DEB_ISAC		0x04
+#define	L1_DEB_ISAC_FIFO	0x08
+#define	L1_DEB_HSCX		0x10
+#define	L1_DEB_HSCX_FIFO	0x20
+#define	L1_DEB_LAPD	        0x40
+#define	L1_DEB_IPAC	        0x80
+#define	L1_DEB_RECEIVE_FRAME    0x100
+#define L1_DEB_MONITOR		0x200
+#define DEB_DLOG_HEX		0x400
+#define DEB_DLOG_VERBOSE	0x800
 
-#undef PTP_DATA_LINK
+#define L2FRAME_DEBUG
 
-#ifdef PTP_DATA_LINK
-#undef TEI_FIXED
-#define TEI_FIXED 0
-#define LAYER2_WATCHING
+#ifdef L2FRAME_DEBUG
+extern void Logl2Frame(struct IsdnCardState *cs, struct sk_buff *skb, char *buf, int dir);
 #endif
 
 struct IsdnCard {
@@ -911,17 +1017,26 @@
 	struct IsdnCardState *cs;
 };
 
-void setstack_isdnl2(struct PStack *st, char *debug_id);
-int HiSax_inithardware(int *);
-void HiSax_closehardware(void);
+void init_bcstate(struct IsdnCardState *cs, int bc);
 
 void setstack_HiSax(struct PStack *st, struct IsdnCardState *cs);
 unsigned int random_ri(void);
-void setstack_isdnl3(struct PStack *st, struct Channel *chanp);
 void HiSax_addlist(struct IsdnCardState *sp, struct PStack *st);
+void HiSax_rmlist(struct IsdnCardState *sp, struct PStack *st);
+
+void setstack_l1_B(struct PStack *st);
+
+void setstack_tei(struct PStack *st);
+void setstack_manager(struct PStack *st);
+
+void setstack_isdnl2(struct PStack *st, char *debug_id);
 void releasestack_isdnl2(struct PStack *st);
+void setstack_transl2(struct PStack *st);
+void releasestack_transl2(struct PStack *st);
+
+void setstack_l3dc(struct PStack *st, struct Channel *chanp);
+void setstack_l3bc(struct PStack *st, struct Channel *chanp);
 void releasestack_isdnl3(struct PStack *st);
-void HiSax_rmlist(struct IsdnCardState *sp, struct PStack *st);
 
 u_char *findie(u_char * p, int size, u_char ie, int wanted_set);
 int getcallref(u_char * p);
@@ -937,20 +1052,18 @@
 void FsmRestartTimer(struct FsmTimer *ft, int millisec, int event,
 	void *arg, int where);
 void FsmDelTimer(struct FsmTimer *ft, int where);
-void jiftime(char *s, long mark);
+int jiftime(char *s, long mark);
 
 int HiSax_command(isdn_ctrl * ic);
 int HiSax_writebuf_skb(int id, int chan, int ack, struct sk_buff *skb);
-void HiSax_putstatus(struct IsdnCardState *csta, char *buf);
+void HiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt, ...);
+void VHiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt, va_list args);
 void HiSax_reportcard(int cardnr);
 int QuickHex(char *txt, u_char * p, int cnt);
-void LogFrame(struct IsdnCardState *sp, u_char * p, int size);
-void dlogframe(struct IsdnCardState *sp, u_char * p, int size, char *comment);
+void LogFrame(struct IsdnCardState *cs, u_char * p, int size);
+void dlogframe(struct IsdnCardState *cs, struct sk_buff *skb, int dir);
 void iecpy(u_char * dest, u_char * iestart, int ieoffset);
-void setstack_transl2(struct PStack *st);
-void releasestack_transl2(struct PStack *st);
-void setstack_tei(struct PStack *st);
-void setstack_manager(struct PStack *st);
+int discard_queue(struct sk_buff_head *q);
 #ifdef ISDN_CHIP_ISAC
 void setstack_isac(struct PStack *st, struct IsdnCardState *cs);
 #endif	/* ISDN_CHIP_ISAC */
@@ -958,18 +1071,21 @@
 
 #define HZDELAY(jiffs) {int tout = jiffs; while (tout--) udelay(1000000/HZ);}
 
-int ll_run(struct IsdnCardState *csta);
-void ll_stop(struct IsdnCardState *csta);
+int ll_run(struct IsdnCardState *cs);
+void ll_stop(struct IsdnCardState *cs);
 void CallcNew(void);
 void CallcFree(void);
-int CallcNewChan(struct IsdnCardState *csta);
-void CallcFreeChan(struct IsdnCardState *csta);
+int CallcNewChan(struct IsdnCardState *cs);
+void CallcFreeChan(struct IsdnCardState *cs);
 void Isdnl1New(void);
 void Isdnl1Free(void);
 void Isdnl2New(void);
 void Isdnl2Free(void);
-void init_tei(struct IsdnCardState *sp, int protocol);
-void release_tei(struct IsdnCardState *sp);
+void Isdnl3New(void);
+void Isdnl3Free(void);
+void init_tei(struct IsdnCardState *cs, int protocol);
+void release_tei(struct IsdnCardState *cs);
 char *HiSax_getrev(const char *revision);
 void TeiNew(void);
 void TeiFree(void);
+int certification_check(int output);
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/hscx.c linux/drivers/isdn/hisax/hscx.c
--- v2.3.3/linux/drivers/isdn/hisax/hscx.c	Wed Apr  1 16:20:58 1998
+++ linux/drivers/isdn/hisax/hscx.c	Sun May 23 10:03:41 1999
@@ -1,11 +1,40 @@
-/* $Id: hscx.c,v 1.7 1998/02/12 23:07:36 keil Exp $
+/* $Id: hscx.c,v 1.16 1998/11/15 23:54:48 keil Exp $
 
  * hscx.c   HSCX specific routines
  *
- * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author       Karsten Keil (keil@isdn4linux.de)
  *
  *
  * $Log: hscx.c,v $
+ * Revision 1.16  1998/11/15 23:54:48  keil
+ * changes from 2.0
+ *
+ * Revision 1.15  1998/08/20 13:50:42  keil
+ * More support for hybrid modem (not working yet)
+ *
+ * Revision 1.14  1998/08/13 23:36:33  keil
+ * HiSax 3.1 - don't work stable with current LinkLevel
+ *
+ * Revision 1.13  1998/06/26 22:03:28  keil
+ * send flags between hdlc frames
+ *
+ * Revision 1.12  1998/06/09 18:26:01  keil
+ * PH_DEACTIVATE B-channel every time signaled to higher layer
+ *
+ * Revision 1.11  1998/05/25 14:10:07  keil
+ * HiSax 3.0
+ * X.75 and leased are working again.
+ *
+ * Revision 1.10  1998/05/25 12:57:59  keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
+ * Revision 1.9  1998/04/15 16:45:33  keil
+ * new init code
+ *
+ * Revision 1.8  1998/03/19 13:16:24  keil
+ * fix the correct release of the hscx
+ *
  * Revision 1.7  1998/02/12 23:07:36  keil
  * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
  *
@@ -30,6 +59,7 @@
 #define __NO_VERSION__
 #include "hisax.h"
 #include "hscx.h"
+#include "isac.h"
 #include "isdnl1.h"
 #include <linux/interrupt.h>
 
@@ -56,21 +86,20 @@
 modehscx(struct BCState *bcs, int mode, int bc)
 {
 	struct IsdnCardState *cs = bcs->cs;
-	int hscx = bcs->channel;
+	int hscx = bcs->hw.hscx.hscx;
 
-	if (cs->debug & L1_DEB_HSCX) {
-		char tmp[40];
-		sprintf(tmp, "hscx %c mode %d ichan %d",
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "hscx %c mode %d ichan %d",
 			'A' + hscx, mode, bc);
-		debugl1(cs, tmp);
-	}
 	bcs->mode = mode;
-	cs->BC_Write_Reg(cs, hscx, HSCX_CCR1, 0x85);
+	bcs->channel = bc;
 	cs->BC_Write_Reg(cs, hscx, HSCX_XAD1, 0xFF);
 	cs->BC_Write_Reg(cs, hscx, HSCX_XAD2, 0xFF);
 	cs->BC_Write_Reg(cs, hscx, HSCX_RAH2, 0xFF);
 	cs->BC_Write_Reg(cs, hscx, HSCX_XBCH, 0x0);
 	cs->BC_Write_Reg(cs, hscx, HSCX_RLCR, 0x0);
+	cs->BC_Write_Reg(cs, hscx, HSCX_CCR1,
+		test_bit(HW_IPAC, &cs->HW_Flags) ? 0x82 : 0x85);
 	cs->BC_Write_Reg(cs, hscx, HSCX_CCR2, 0x30);
 	cs->BC_Write_Reg(cs, hscx, HSCX_XCCR, 7);
 	cs->BC_Write_Reg(cs, hscx, HSCX_RCCR, 7);
@@ -90,14 +119,16 @@
 	}
 	switch (mode) {
 		case (L1_MODE_NULL):
-			cs->BC_Write_Reg(cs, hscx, HSCX_TSAX, 0xff);
-			cs->BC_Write_Reg(cs, hscx, HSCX_TSAR, 0xff);
+			cs->BC_Write_Reg(cs, hscx, HSCX_TSAX, 0x1f);
+			cs->BC_Write_Reg(cs, hscx, HSCX_TSAR, 0x1f);
 			cs->BC_Write_Reg(cs, hscx, HSCX_MODE, 0x84);
 			break;
 		case (L1_MODE_TRANS):
 			cs->BC_Write_Reg(cs, hscx, HSCX_MODE, 0xe4);
 			break;
 		case (L1_MODE_HDLC):
+			cs->BC_Write_Reg(cs, hscx, HSCX_CCR1,
+				test_bit(HW_IPAC, &cs->HW_Flags) ? 0x8a : 0x8d);
 			cs->BC_Write_Reg(cs, hscx, HSCX_MODE, 0x8c);
 			break;
 	}
@@ -114,89 +145,106 @@
 	mark_bh(IMMEDIATE_BH);
 }
 
-static void
+void
 hscx_l2l1(struct PStack *st, int pr, void *arg)
 {
 	struct sk_buff *skb = arg;
 	long flags;
 
 	switch (pr) {
-		case (PH_DATA_REQ):
+		case (PH_DATA | REQUEST):
 			save_flags(flags);
 			cli();
-			if (st->l1.bcs->hw.hscx.tx_skb) {
+			if (st->l1.bcs->tx_skb) {
 				skb_queue_tail(&st->l1.bcs->squeue, skb);
 				restore_flags(flags);
 			} else {
-				st->l1.bcs->hw.hscx.tx_skb = skb;
+				st->l1.bcs->tx_skb = skb;
 				test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
 				st->l1.bcs->hw.hscx.count = 0;
 				restore_flags(flags);
 				st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
 			}
 			break;
-		case (PH_PULL_IND):
-			if (st->l1.bcs->hw.hscx.tx_skb) {
+		case (PH_PULL | INDICATION):
+			if (st->l1.bcs->tx_skb) {
 				printk(KERN_WARNING "hscx_l2l1: this shouldn't happen\n");
 				break;
 			}
 			test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
-			st->l1.bcs->hw.hscx.tx_skb = skb;
+			st->l1.bcs->tx_skb = skb;
 			st->l1.bcs->hw.hscx.count = 0;
 			st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
 			break;
-		case (PH_PULL_REQ):
-			if (!st->l1.bcs->hw.hscx.tx_skb) {
+		case (PH_PULL | REQUEST):
+			if (!st->l1.bcs->tx_skb) {
 				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
-				st->l1.l1l2(st, PH_PULL_CNF, NULL);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
 			} else
 				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
 			break;
+		case (PH_ACTIVATE | REQUEST):
+			test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+			modehscx(st->l1.bcs, st->l1.mode, st->l1.bc);
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | REQUEST):
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | CONFIRM):
+			test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+			test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+			modehscx(st->l1.bcs, 0, st->l1.bc);
+			st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+			break;
 	}
-
 }
 
 void
 close_hscxstate(struct BCState *bcs)
 {
-	struct sk_buff *skb;
-
-	modehscx(bcs, 0, 0);
+	modehscx(bcs, 0, bcs->channel);
 	if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
 		if (bcs->hw.hscx.rcvbuf) {
 			kfree(bcs->hw.hscx.rcvbuf);
 			bcs->hw.hscx.rcvbuf = NULL;
 		}
-		while ((skb = skb_dequeue(&bcs->rqueue))) {
-			dev_kfree_skb(skb);
-		}
-		while ((skb = skb_dequeue(&bcs->squeue))) {
-			dev_kfree_skb(skb);
+		if (bcs->blog) {
+			kfree(bcs->blog);
+			bcs->blog = NULL;
 		}
-		if (bcs->hw.hscx.tx_skb) {
-			dev_kfree_skb(bcs->hw.hscx.tx_skb);
-			bcs->hw.hscx.tx_skb = NULL;
+		discard_queue(&bcs->rqueue);
+		discard_queue(&bcs->squeue);
+		if (bcs->tx_skb) {
+			dev_kfree_skb(bcs->tx_skb);
+			bcs->tx_skb = NULL;
 			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
 		}
 	}
 }
 
-static int
-open_hscxstate(struct IsdnCardState *cs,
-	       int bc)
+int
+open_hscxstate(struct IsdnCardState *cs, struct BCState *bcs)
 {
-	struct BCState *bcs = cs->bcs + bc;
-
 	if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
 		if (!(bcs->hw.hscx.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) {
 			printk(KERN_WARNING
-			       "HiSax: No memory for hscx.rcvbuf\n");
+				"HiSax: No memory for hscx.rcvbuf\n");
+			test_and_clear_bit(BC_FLG_INIT, &bcs->Flag);
 			return (1);
 		}
+		if (!(bcs->blog = kmalloc(MAX_BLOG_SPACE, GFP_ATOMIC))) {
+			printk(KERN_WARNING
+				"HiSax: No memory for bcs->blog\n");
+			test_and_clear_bit(BC_FLG_INIT, &bcs->Flag);
+			kfree(bcs->hw.hscx.rcvbuf);
+			bcs->hw.hscx.rcvbuf = NULL;
+			return (2);
+		}
 		skb_queue_head_init(&bcs->rqueue);
 		skb_queue_head_init(&bcs->squeue);
 	}
-	bcs->hw.hscx.tx_skb = NULL;
+	bcs->tx_skb = NULL;
 	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
 	bcs->event = 0;
 	bcs->hw.hscx.rcvidx = 0;
@@ -204,77 +252,74 @@
 	return (0);
 }
 
-static void
-hscx_manl1(struct PStack *st, int pr,
-	   void *arg)
-{
-	switch (pr) {
-		case (PH_ACTIVATE_REQ):
-			test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
-			modehscx(st->l1.bcs, st->l1.mode, st->l1.bc);
-			st->l1.l1man(st, PH_ACTIVATE_CNF, NULL);
-			break;
-		case (PH_DEACTIVATE_REQ):
-			if (!test_bit(BC_FLG_BUSY, &st->l1.bcs->Flag))
-				modehscx(st->l1.bcs, 0, 0);
-			test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
-			break;
-	}
-}
-
 int
 setstack_hscx(struct PStack *st, struct BCState *bcs)
 {
-	if (open_hscxstate(st->l1.hardware, bcs->channel))
+	bcs->channel = st->l1.bc;
+	if (open_hscxstate(st->l1.hardware, bcs))
 		return (-1);
 	st->l1.bcs = bcs;
 	st->l2.l2l1 = hscx_l2l1;
-	st->ma.manl1 = hscx_manl1;
 	setstack_manager(st);
 	bcs->st = st;
+	setstack_l1_B(st);
 	return (0);
 }
 
 HISAX_INITFUNC(void
 clear_pending_hscx_ints(struct IsdnCardState *cs))
 {
-	int val;
-	char tmp[64];
+	int val, eval;
 
 	val = cs->BC_Read_Reg(cs, 1, HSCX_ISTA);
-	sprintf(tmp, "HSCX B ISTA %x", val);
-	debugl1(cs, tmp);
+	debugl1(cs, "HSCX B ISTA %x", val);
 	if (val & 0x01) {
-		val = cs->BC_Read_Reg(cs, 1, HSCX_EXIR);
-		sprintf(tmp, "HSCX B EXIR %x", val);
-		debugl1(cs, tmp);
-	} else if (val & 0x02) {
-		val = cs->BC_Read_Reg(cs, 0, HSCX_EXIR);
-		sprintf(tmp, "HSCX A EXIR %x", val);
-		debugl1(cs, tmp);
+		eval = cs->BC_Read_Reg(cs, 1, HSCX_EXIR);
+		debugl1(cs, "HSCX B EXIR %x", eval);
+	}
+	if (val & 0x02) {
+		eval = cs->BC_Read_Reg(cs, 0, HSCX_EXIR);
+		debugl1(cs, "HSCX A EXIR %x", eval);
 	}
 	val = cs->BC_Read_Reg(cs, 0, HSCX_ISTA);
-	sprintf(tmp, "HSCX A ISTA %x", val);
-	debugl1(cs, tmp);
+	debugl1(cs, "HSCX A ISTA %x", val);
 	val = cs->BC_Read_Reg(cs, 1, HSCX_STAR);
-	sprintf(tmp, "HSCX B STAR %x", val);
-	debugl1(cs, tmp);
+	debugl1(cs, "HSCX B STAR %x", val);
 	val = cs->BC_Read_Reg(cs, 0, HSCX_STAR);
-	sprintf(tmp, "HSCX A STAR %x", val);
-	debugl1(cs, tmp);
+	debugl1(cs, "HSCX A STAR %x", val);
+	/* disable all IRQ */
 	cs->BC_Write_Reg(cs, 0, HSCX_MASK, 0xFF);
 	cs->BC_Write_Reg(cs, 1, HSCX_MASK, 0xFF);
-	cs->BC_Write_Reg(cs, 0, HSCX_MASK, 0);
-	cs->BC_Write_Reg(cs, 1, HSCX_MASK, 0);
 }
 
-HISAX_INITFUNC(void 
+HISAX_INITFUNC(void
 inithscx(struct IsdnCardState *cs))
 {
 	cs->bcs[0].BC_SetStack = setstack_hscx;
 	cs->bcs[1].BC_SetStack = setstack_hscx;
 	cs->bcs[0].BC_Close = close_hscxstate;
 	cs->bcs[1].BC_Close = close_hscxstate;
+	cs->bcs[0].hw.hscx.hscx = 0;
+	cs->bcs[1].hw.hscx.hscx = 1;
 	modehscx(cs->bcs, 0, 0);
 	modehscx(cs->bcs + 1, 0, 0);
+}
+
+HISAX_INITFUNC(void
+inithscxisac(struct IsdnCardState *cs, int part))
+{
+	if (part & 1) {
+		clear_pending_isac_ints(cs);
+		clear_pending_hscx_ints(cs);
+		initisac(cs);
+		inithscx(cs);
+	}
+	if (part & 2) {
+		/* Reenable all IRQ */
+		cs->writeisac(cs, ISAC_MASK, 0);
+		cs->BC_Write_Reg(cs, 0, HSCX_MASK, 0);
+		cs->BC_Write_Reg(cs, 1, HSCX_MASK, 0);
+		/* RESET Receiver and Transmitter */
+		cs->writeisac(cs, ISAC_CMDR, 0x41);
+	}
 }
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/hscx.h linux/drivers/isdn/hisax/hscx.h
--- v2.3.3/linux/drivers/isdn/hisax/hscx.h	Wed Apr  1 16:20:58 1998
+++ linux/drivers/isdn/hisax/hscx.h	Sun May 23 10:03:41 1999
@@ -1,4 +1,4 @@
-/* $Id: hscx.h,v 1.3 1997/07/27 21:38:35 keil Exp $
+/* $Id: hscx.h,v 1.4 1998/04/15 16:45:34 keil Exp $
 
  * hscx.h   HSCX specific defines
  *
@@ -6,6 +6,9 @@
  *
  *
  * $Log: hscx.h,v $
+ * Revision 1.4  1998/04/15 16:45:34  keil
+ * new init code
+ *
  * Revision 1.3  1997/07/27 21:38:35  keil
  * new B-channel interface
  *
@@ -44,3 +47,4 @@
 extern void modehscx(struct BCState *bcs, int mode, int bc);
 extern void clear_pending_hscx_ints(struct IsdnCardState *cs);
 extern void inithscx(struct IsdnCardState *cs);
+extern void inithscxisac(struct IsdnCardState *cs, int part);
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/hscx_irq.c linux/drivers/isdn/hisax/hscx_irq.c
--- v2.3.3/linux/drivers/isdn/hisax/hscx_irq.c	Thu May 14 18:42:40 1998
+++ linux/drivers/isdn/hisax/hscx_irq.c	Sun May 23 10:03:41 1999
@@ -1,12 +1,24 @@
-/* $Id: hscx_irq.c,v 1.7 1998/02/12 23:07:37 keil Exp $
+/* $Id: hscx_irq.c,v 1.11 1998/11/15 23:54:49 keil Exp $
 
  * hscx_irq.c     low level b-channel stuff for Siemens HSCX
  *
- * Author     Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author     Karsten Keil (keil@isdn4linux.de)
  *
  * This is an include file for fast inline IRQ stuff
  *
  * $Log: hscx_irq.c,v $
+ * Revision 1.11  1998/11/15 23:54:49  keil
+ * changes from 2.0
+ *
+ * Revision 1.10  1998/08/13 23:36:35  keil
+ * HiSax 3.1 - don't work stable with current LinkLevel
+ *
+ * Revision 1.9  1998/06/24 14:44:51  keil
+ * Fix recovery of TX IRQ loss
+ *
+ * Revision 1.8  1998/04/10 10:35:22  paul
+ * fixed (silly?) warnings from egcs on Alpha.
+ *
  * Revision 1.7  1998/02/12 23:07:37  keil
  * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
  *
@@ -85,7 +97,7 @@
 	if (bcs->hw.hscx.rcvidx + count > HSCX_BUFMAX) {
 		if (cs->debug & L1_DEB_WARN)
 			debugl1(cs, "hscx_empty_fifo: incoming packet too large");
-		WriteHSCXCMDR(cs, bcs->channel, 0x80);
+		WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x80);
 		bcs->hw.hscx.rcvidx = 0;
 		return;
 	}
@@ -93,17 +105,16 @@
 	bcs->hw.hscx.rcvidx += count;
 	save_flags(flags);
 	cli();
-	READHSCXFIFO(cs, bcs->channel, ptr, count);
-	WriteHSCXCMDR(cs, bcs->channel, 0x80);
+	READHSCXFIFO(cs, bcs->hw.hscx.hscx, ptr, count);
+	WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x80);
 	restore_flags(flags);
 	if (cs->debug & L1_DEB_HSCX_FIFO) {
-		char tmp[256];
-		char *t = tmp;
+		char *t = bcs->blog;
 
 		t += sprintf(t, "hscx_empty_fifo %c cnt %d",
-			     bcs->channel ? 'B' : 'A', count);
+			     bcs->hw.hscx.hscx ? 'B' : 'A', count);
 		QuickHex(t, ptr, count);
-		debugl1(cs, tmp);
+		debugl1(cs, bcs->blog);
 	}
 }
 
@@ -120,36 +131,35 @@
 	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
 		debugl1(cs, "hscx_fill_fifo");
 
-	if (!bcs->hw.hscx.tx_skb)
+	if (!bcs->tx_skb)
 		return;
-	if (bcs->hw.hscx.tx_skb->len <= 0)
+	if (bcs->tx_skb->len <= 0)
 		return;
 
 	more = (bcs->mode == L1_MODE_TRANS) ? 1 : 0;
-	if (bcs->hw.hscx.tx_skb->len > fifo_size) {
+	if (bcs->tx_skb->len > fifo_size) {
 		more = !0;
 		count = fifo_size;
 	} else
-		count = bcs->hw.hscx.tx_skb->len;
+		count = bcs->tx_skb->len;
 
-	waitforXFW(cs, bcs->channel);
+	waitforXFW(cs, bcs->hw.hscx.hscx);
 	save_flags(flags);
 	cli();
-	ptr = bcs->hw.hscx.tx_skb->data;
-	skb_pull(bcs->hw.hscx.tx_skb, count);
+	ptr = bcs->tx_skb->data;
+	skb_pull(bcs->tx_skb, count);
 	bcs->tx_cnt -= count;
 	bcs->hw.hscx.count += count;
-	WRITEHSCXFIFO(cs, bcs->channel, ptr, count);
-	WriteHSCXCMDR(cs, bcs->channel, more ? 0x8 : 0xa);
+	WRITEHSCXFIFO(cs, bcs->hw.hscx.hscx, ptr, count);
+	WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, more ? 0x8 : 0xa);
 	restore_flags(flags);
 	if (cs->debug & L1_DEB_HSCX_FIFO) {
-		char tmp[256];
-		char *t = tmp;
+		char *t = bcs->blog;
 
 		t += sprintf(t, "hscx_fill_fifo %c cnt %d",
-			     bcs->channel ? 'B' : 'A', count);
+			     bcs->hw.hscx.hscx ? 'B' : 'A', count);
 		QuickHex(t, ptr, count);
-		debugl1(cs, tmp);
+		debugl1(cs, bcs->blog);
 	}
 }
 
@@ -161,7 +171,6 @@
 	struct sk_buff *skb;
 	int fifo_size = test_bit(HW_IPAC, &cs->HW_Flags)? 64: 32;
 	int count;
-	char tmp[32];
 
 	if (!test_bit(BC_FLG_INIT, &bcs->Flag))
 		return;
@@ -173,11 +182,9 @@
 				if (cs->debug & L1_DEB_WARN)
 					debugl1(cs, "HSCX invalid frame");
 			if ((r & 0x40) && bcs->mode)
-				if (cs->debug & L1_DEB_WARN) {
-					sprintf(tmp, "HSCX RDO mode=%d",
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "HSCX RDO mode=%d",
 						bcs->mode);
-					debugl1(cs, tmp);
-				}
 			if (!(r & 0x20))
 				if (cs->debug & L1_DEB_WARN)
 					debugl1(cs, "HSCX CRC error");
@@ -189,10 +196,8 @@
 				count = fifo_size;
 			hscx_empty_fifo(bcs, count);
 			if ((count = bcs->hw.hscx.rcvidx - 1) > 0) {
-				if (cs->debug & L1_DEB_HSCX_FIFO) {
-					sprintf(tmp, "HX Frame %d", count);
-					debugl1(cs, tmp);
-				}
+				if (cs->debug & L1_DEB_HSCX_FIFO)
+					debugl1(cs, "HX Frame %d", count);
 				if (!(skb = dev_alloc_skb(count)))
 					printk(KERN_WARNING "HSCX: receive out of memory\n");
 				else {
@@ -219,20 +224,20 @@
 		}
 	}
 	if (val & 0x10) {	/* XPR */
-		if (bcs->hw.hscx.tx_skb) {
-			if (bcs->hw.hscx.tx_skb->len) {
+		if (bcs->tx_skb) {
+			if (bcs->tx_skb->len) {
 				hscx_fill_fifo(bcs);
 				return;
 			} else {
 				if (bcs->st->lli.l1writewakeup &&
-					(PACKET_NOACK != bcs->hw.hscx.tx_skb->pkt_type))
+					(PACKET_NOACK != bcs->tx_skb->pkt_type))
 					bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.hscx.count);
-				dev_kfree_skb(bcs->hw.hscx.tx_skb);
+				dev_kfree_skb(bcs->tx_skb);
 				bcs->hw.hscx.count = 0; 
-				bcs->hw.hscx.tx_skb = NULL;
+				bcs->tx_skb = NULL;
 			}
 		}
-		if ((bcs->hw.hscx.tx_skb = skb_dequeue(&bcs->squeue))) {
+		if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
 			bcs->hw.hscx.count = 0;
 			test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
 			hscx_fill_fifo(bcs);
@@ -249,73 +254,60 @@
 
 	u_char exval;
 	struct BCState *bcs;
-	char tmp[32];
 
 	if (val & 0x01) {
 		bcs = cs->bcs + 1;
 		exval = READHSCX(cs, 1, HSCX_EXIR);
-		if (exval == 0x40) {
+		if (exval & 0x40) {
 			if (bcs->mode == 1)
 				hscx_fill_fifo(bcs);
 			else {
 				/* Here we lost an TX interrupt, so
 				   * restart transmitting the whole frame.
 				 */
-				if (bcs->hw.hscx.tx_skb) {
-					skb_push(bcs->hw.hscx.tx_skb, bcs->hw.hscx.count);
+				if (bcs->tx_skb) {
+					skb_push(bcs->tx_skb, bcs->hw.hscx.count);
 					bcs->tx_cnt += bcs->hw.hscx.count;
 					bcs->hw.hscx.count = 0;
 				}
-				WriteHSCXCMDR(cs, bcs->channel, 0x01);
-				if (cs->debug & L1_DEB_WARN) {
-					sprintf(tmp, "HSCX B EXIR %x Lost TX", exval);
-					debugl1(cs, tmp);
-				}
+				WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x01);
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "HSCX B EXIR %x Lost TX", exval);
 			}
-		} else if (cs->debug & L1_DEB_HSCX) {
-			sprintf(tmp, "HSCX B EXIR %x", exval);
-			debugl1(cs, tmp);
-		}
+		} else if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX B EXIR %x", exval);
 	}
 	if (val & 0xf8) {
-		if (cs->debug & L1_DEB_HSCX) {
-			sprintf(tmp, "HSCX B interrupt %x", val);
-			debugl1(cs, tmp);
-		}
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX B interrupt %x", val);
 		hscx_interrupt(cs, val, 1);
 	}
 	if (val & 0x02) {
 		bcs = cs->bcs;
 		exval = READHSCX(cs, 0, HSCX_EXIR);
-		if (exval == 0x40) {
+		if (exval & 0x40) {
 			if (bcs->mode == L1_MODE_TRANS)
 				hscx_fill_fifo(bcs);
 			else {
 				/* Here we lost an TX interrupt, so
 				   * restart transmitting the whole frame.
 				 */
-				if (bcs->hw.hscx.tx_skb) {
-					skb_push(bcs->hw.hscx.tx_skb, bcs->hw.hscx.count);
+				if (bcs->tx_skb) {
+					skb_push(bcs->tx_skb, bcs->hw.hscx.count);
 					bcs->tx_cnt += bcs->hw.hscx.count;
 					bcs->hw.hscx.count = 0;
 				}
-				WriteHSCXCMDR(cs, bcs->channel, 0x01);
-				if (cs->debug & L1_DEB_WARN) {
-					sprintf(tmp, "HSCX A EXIR %x Lost TX", exval);
-					debugl1(cs, tmp);
-				}
+				WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x01);
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "HSCX A EXIR %x Lost TX", exval);
 			}
-		} else if (cs->debug & L1_DEB_HSCX) {
-			sprintf(tmp, "HSCX A EXIR %x", exval);
-			debugl1(cs, tmp);
-		}
+		} else if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX A EXIR %x", exval);
 	}
 	if (val & 0x04) {
 		exval = READHSCX(cs, 0, HSCX_ISTA);
-		if (cs->debug & L1_DEB_HSCX) {
-			sprintf(tmp, "HSCX A interrupt %x", exval);
-			debugl1(cs, tmp);
-		}
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX A interrupt %x", exval);
 		hscx_interrupt(cs, exval, 0);
 	}
 }
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/ipac.h linux/drivers/isdn/hisax/ipac.h
--- v2.3.3/linux/drivers/isdn/hisax/ipac.h	Wed Apr  1 16:20:58 1998
+++ linux/drivers/isdn/hisax/ipac.h	Sun May 23 10:03:41 1999
@@ -1,4 +1,4 @@
-/* $Id: ipac.h,v 1.2 1997/10/29 18:51:21 keil Exp $
+/* $Id: ipac.h,v 1.3 1998/04/15 16:48:09 keil Exp $
 
  * ipac.h   IPAC specific defines
  *
@@ -6,6 +6,9 @@
  *
  *
  * $Log: ipac.h,v $
+ * Revision 1.3  1998/04/15 16:48:09  keil
+ * IPAC_ATX added
+ *
  * Revision 1.2  1997/10/29 18:51:21  keil
  * New files
  *
@@ -26,6 +29,7 @@
 #define IPAC_ACFG	0xC3
 #define IPAC_AOE	0xC4
 #define IPAC_ARX	0xC5
+#define IPAC_ATX	0xC5
 #define IPAC_PITA1	0xC6
 #define IPAC_PITA2	0xC7
 #define IPAC_POTA1	0xC8
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/isac.c linux/drivers/isdn/hisax/isac.c
--- v2.3.3/linux/drivers/isdn/hisax/isac.c	Thu May 14 18:43:27 1998
+++ linux/drivers/isdn/hisax/isac.c	Sun May 23 10:03:41 1999
@@ -1,11 +1,33 @@
-/* $Id: isac.c,v 1.12 1998/02/12 23:07:40 keil Exp $
+/* $Id: isac.c,v 1.18 1998/11/15 23:54:51 keil Exp $
 
  * isac.c   ISAC specific routines
  *
- * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author       Karsten Keil (keil@isdn4linux.de)
  *
+ *		This file is (c) under GNU PUBLIC LICENSE
+ *		For changes and modifications please read
+ *		../../../Documentation/isdn/HiSax.cert
  *
  * $Log: isac.c,v $
+ * Revision 1.18  1998/11/15 23:54:51  keil
+ * changes from 2.0
+ *
+ * Revision 1.17  1998/08/13 23:36:37  keil
+ * HiSax 3.1 - don't work stable with current LinkLevel
+ *
+ * Revision 1.16  1998/05/25 12:58:01  keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
+ * Revision 1.15  1998/04/15 16:45:32  keil
+ * new init code
+ *
+ * Revision 1.14  1998/04/10 10:35:26  paul
+ * fixed (silly?) warnings from egcs on Alpha.
+ *
+ * Revision 1.13  1998/03/07 22:57:01  tsbogend
+ * made HiSax working on Linux/Alpha
+ *
  * Revision 1.12  1998/02/12 23:07:40  keil
  * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
  *
@@ -63,30 +85,17 @@
 	int val;
 
 	val = cs->readisac(cs, ISAC_RBCH);
-	printk(KERN_INFO "%s ISAC version : %s\n", s, ISACVer[(val >> 5) & 3]);
+	printk(KERN_INFO "%s ISAC version (%x): %s\n", s, val, ISACVer[(val >> 5) & 3]);
 }
 
 static void
 ph_command(struct IsdnCardState *cs, unsigned int command)
 {
-	if (cs->debug & L1_DEB_ISAC) {
-		char tmp[32];
-		sprintf(tmp, "ph_command %x", command);
-		debugl1(cs, tmp);
-	}
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "ph_command %x", command);
 	cs->writeisac(cs, ISAC_CIX0, (command << 2) | 3);
 }
 
-static void
-manl1_msg(struct IsdnCardState *cs, int msg, void *arg) {
-	struct PStack *st;
-
-	st = cs->stlist;
-	while (st) {
-		st->ma.manl1(st, msg, arg);
-		st = st->next;
-	}
-}
 
 static void
 isac_new_ph(struct IsdnCardState *cs)
@@ -95,28 +104,28 @@
 		case (ISAC_IND_RS):
 		case (ISAC_IND_EI):
 			ph_command(cs, ISAC_CMD_DUI);
-			manl1_msg(cs, PH_RESET_IND, NULL);
+			l1_msg(cs, HW_RESET | INDICATION, NULL);
 			break;
 		case (ISAC_IND_DID):
-			manl1_msg(cs, PH_DEACT_CNF, NULL);
+			l1_msg(cs, HW_DEACTIVATE | CONFIRM, NULL);
 			break;
 		case (ISAC_IND_DR):
-			manl1_msg(cs, PH_DEACT_IND, NULL);
+			l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL);
 			break;
 		case (ISAC_IND_PU):
-			manl1_msg(cs, PH_POWERUP_CNF, NULL);
+			l1_msg(cs, HW_POWERUP | CONFIRM, NULL);
 			break;
 		case (ISAC_IND_RSY):
-			manl1_msg(cs, PH_RSYNC_IND, NULL);
+			l1_msg(cs, HW_RSYNC | INDICATION, NULL);
 			break;
 		case (ISAC_IND_ARD):
-			manl1_msg(cs, PH_INFO2_IND, NULL);
+			l1_msg(cs, HW_INFO2 | INDICATION, NULL);
 			break;
 		case (ISAC_IND_AI8):
-			manl1_msg(cs, PH_I4_P8_IND, NULL);
+			l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL);
 			break;
 		case (ISAC_IND_AI10):
-			manl1_msg(cs, PH_I4_P10_IND, NULL);
+			l1_msg(cs, HW_INFO4_P10 | INDICATION, NULL);
 			break;
 		default:
 			break;
@@ -130,13 +139,12 @@
 	
 	if (!cs)
 		return;
-
 	if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) {
 		if (cs->debug)
 			debugl1(cs, "D-Channel Busy cleared");
 		stptr = cs->stlist;
 		while (stptr != NULL) {
-			stptr->l1.l1l2(stptr, PH_PAUSE_CNF, NULL);
+			stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL);
 			stptr = stptr->next;
 		}
 	}
@@ -147,13 +155,13 @@
 	if (test_and_clear_bit(D_XMTBUFREADY, &cs->event))
 		DChannel_proc_xmt(cs);
 	if (test_and_clear_bit(D_RX_MON0, &cs->event))
-		test_and_set_bit(HW_MON0_TX_END, &cs->HW_Flags);
+		test_and_set_bit(HW_MON0_RX_END, &cs->HW_Flags);
 	if (test_and_clear_bit(D_RX_MON1, &cs->event))
-		test_and_set_bit(HW_MON1_TX_END, &cs->HW_Flags);
+		test_and_set_bit(HW_MON1_RX_END, &cs->HW_Flags);
 	if (test_and_clear_bit(D_TX_MON0, &cs->event))
-		test_and_set_bit(HW_MON0_RX_END, &cs->HW_Flags);
+		test_and_set_bit(HW_MON0_TX_END, &cs->HW_Flags);
 	if (test_and_clear_bit(D_TX_MON1, &cs->event))
-		test_and_set_bit(HW_MON1_RX_END, &cs->HW_Flags);
+		test_and_set_bit(HW_MON1_TX_END, &cs->HW_Flags);
 }
 
 void
@@ -165,13 +173,10 @@
 	if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO))
 		debugl1(cs, "isac_empty_fifo");
 
-	if ((cs->rcvidx + count) >= MAX_DFRAME_LEN) {
-		if (cs->debug & L1_DEB_WARN) {
-			char tmp[40];
-			sprintf(tmp, "isac_empty_fifo overrun %d",
+	if ((cs->rcvidx + count) >= MAX_DFRAME_LEN_L1) {
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "isac_empty_fifo overrun %d",
 				cs->rcvidx + count);
-			debugl1(cs, tmp);
-		}
 		cs->writeisac(cs, ISAC_CMDR, 0x80);
 		cs->rcvidx = 0;
 		return;
@@ -184,12 +189,11 @@
 	cs->writeisac(cs, ISAC_CMDR, 0x80);
 	restore_flags(flags);
 	if (cs->debug & L1_DEB_ISAC_FIFO) {
-		char tmp[128];
-		char *t = tmp;
+		char *t = cs->dlog;
 
 		t += sprintf(t, "isac_empty_fifo cnt %d", count);
 		QuickHex(t, ptr, count);
-		debugl1(cs, tmp);
+		debugl1(cs, cs->dlog);
 	}
 }
 
@@ -231,12 +235,11 @@
 	cs->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000);
 	add_timer(&cs->dbusytimer);
 	if (cs->debug & L1_DEB_ISAC_FIFO) {
-		char tmp[128];
-		char *t = tmp;
+		char *t = cs->dlog;
 
 		t += sprintf(t, "isac_fill_fifo cnt %d", count);
 		QuickHex(t, ptr, count);
-		debugl1(cs, tmp);
+		debugl1(cs, cs->dlog);
 	}
 }
 
@@ -255,12 +258,9 @@
 	struct sk_buff *skb;
 	unsigned int count;
 	long flags;
-	char tmp[32];
 
-	if (cs->debug & L1_DEB_ISAC) {
-		sprintf(tmp, "ISAC interrupt %x", val);
-		debugl1(cs, tmp);
-	}
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "ISAC interrupt %x", val);
 	if (val & 0x80) {	/* RME */
 		exval = cs->readisac(cs, ISAC_RSTA);
 		if ((exval & 0x70) != 0x20) {
@@ -323,12 +323,20 @@
 	}
       afterXPR:
 	if (val & 0x04) {	/* CISQ */
-		cs->ph_state = (cs->readisac(cs, ISAC_CIX0) >> 2) & 0xf;
-		if (cs->debug & L1_DEB_ISAC) {
-			sprintf(tmp, "ph_state change %x", cs->ph_state);
-			debugl1(cs, tmp);
+		exval = cs->readisac(cs, ISAC_CIR0);
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC CIR0 %02X", exval );
+		if (exval & 2) {
+			cs->ph_state = (exval >> 2) & 0xf;
+			if (cs->debug & L1_DEB_ISAC)
+				debugl1(cs, "ph_state change %x", cs->ph_state);
+			isac_sched_event(cs, D_L1STATECHANGE);
+		}
+		if (exval & 1) {
+			exval = cs->readisac(cs, ISAC_CIR1);
+			if (cs->debug & L1_DEB_ISAC)
+				debugl1(cs, "ISAC CIR1 %02X", exval );
 		}
-		isac_sched_event(cs, D_L1STATECHANGE);
 	}
 	if (val & 0x02) {	/* SIN */
 		/* never */
@@ -337,16 +345,12 @@
 	}
 	if (val & 0x01) {	/* EXI */
 		exval = cs->readisac(cs, ISAC_EXIR);
-		if (cs->debug & L1_DEB_WARN) {
-			sprintf(tmp, "ISAC EXIR %02x", exval);
-			debugl1(cs, tmp);
-		}
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "ISAC EXIR %02x", exval);
 		if (exval & 0x04) {
 			v1 = cs->readisac(cs, ISAC_MOSR);
-			if (cs->debug & L1_DEB_WARN) {
-				sprintf(tmp, "ISAC MOSR %02x", v1);
-				debugl1(cs, tmp);
-			}
+			if (cs->debug & L1_DEB_MONITOR)
+				debugl1(cs, "ISAC MOSR %02x", v1);
 #if ARCOFI_USE
 			if (v1 & 0x08) {
 				if (!cs->mon_rx) {
@@ -370,10 +374,8 @@
 					goto afterMONR0;
 				}
 				cs->mon_rx[cs->mon_rxp++] = cs->readisac(cs, ISAC_MOR0);
-				if (cs->debug & L1_DEB_WARN) {
-					sprintf(tmp, "ISAC MOR0 %02x", cs->mon_rx[cs->mon_rxp -1]);
-					debugl1(cs, tmp);
-				}
+				if (cs->debug & L1_DEB_MONITOR)
+					debugl1(cs, "ISAC MOR0 %02x", cs->mon_rx[cs->mon_rxp -1]);
 				if (cs->mon_rxp == 1) {
 					cs->mocr |= 0x04;
 					cs->writeisac(cs, ISAC_MOCR, cs->mocr);
@@ -402,66 +404,70 @@
 					goto afterMONR1;
 				}
 				cs->mon_rx[cs->mon_rxp++] = cs->readisac(cs, ISAC_MOR1);
-				if (cs->debug & L1_DEB_WARN) {
-					sprintf(tmp, "ISAC MOR1 %02x", cs->mon_rx[cs->mon_rxp -1]);
-					debugl1(cs, tmp);
-				}
-				if (cs->mon_rxp == 1) {
-					cs->mocr |= 0x40;
-					cs->writeisac(cs, ISAC_MOCR, cs->mocr);
-				}
+				if (cs->debug & L1_DEB_MONITOR)
+					debugl1(cs, "ISAC MOR1 %02x", cs->mon_rx[cs->mon_rxp -1]);
+				cs->mocr |= 0x40;
+				cs->writeisac(cs, ISAC_MOCR, cs->mocr);
 			}
 		      afterMONR1:
 			if (v1 & 0x04) {
 				cs->mocr &= 0xf0;
+				cs->writeisac(cs, ISAC_MOCR, cs->mocr);
 				cs->mocr |= 0x0a;
 				cs->writeisac(cs, ISAC_MOCR, cs->mocr);
-				isac_sched_event(cs, D_RX_MON0);
+				test_and_set_bit(HW_MON0_RX_END, &cs->HW_Flags);
 			}
 			if (v1 & 0x40) {
 				cs->mocr &= 0x0f;
+				cs->writeisac(cs, ISAC_MOCR, cs->mocr);
 				cs->mocr |= 0xa0;
 				cs->writeisac(cs, ISAC_MOCR, cs->mocr);
-				isac_sched_event(cs, D_RX_MON1);
+				test_and_set_bit(HW_MON1_RX_END, &cs->HW_Flags);
 			}
 			if (v1 & 0x02) {
-				if (!cs->mon_tx) {
+				if ((!cs->mon_tx) || (cs->mon_txc && 
+					(cs->mon_txp >= cs->mon_txc) && 
+					!(v1 & 0x08))) {
 					cs->mocr &= 0xf0;
+					cs->writeisac(cs, ISAC_MOCR, cs->mocr);
 					cs->mocr |= 0x0a;
 					cs->writeisac(cs, ISAC_MOCR, cs->mocr);
+					if (cs->mon_txc &&
+						(cs->mon_txp >= cs->mon_txc))
+						test_and_set_bit(HW_MON0_TX_END, &cs->HW_Flags);
 					goto AfterMOX0;
 				}
-				if (cs->mon_txp >= cs->mon_txc) {
-					if (cs->mon_txc)
-						isac_sched_event(cs, D_TX_MON0);
+				if (cs->mon_txc && (cs->mon_txp >= cs->mon_txc)) {
+					test_and_set_bit(HW_MON0_TX_END, &cs->HW_Flags);
 					goto AfterMOX0;
 				}
 				cs->writeisac(cs, ISAC_MOX0,
 					cs->mon_tx[cs->mon_txp++]);
-				if (cs->debug & L1_DEB_WARN) {
-					sprintf(tmp, "ISAC %02x -> MOX0", cs->mon_tx[cs->mon_txp -1]);
-					debugl1(cs, tmp);
-				}
+				if (cs->debug & L1_DEB_MONITOR)
+					debugl1(cs, "ISAC %02x -> MOX0", cs->mon_tx[cs->mon_txp -1]);
 			}
 		      AfterMOX0:
 			if (v1 & 0x20) {
-				if (!cs->mon_tx) {
+				if ((!cs->mon_tx) || (cs->mon_txc && 
+					(cs->mon_txp >= cs->mon_txc) && 
+					!(v1 & 0x80))) {
 					cs->mocr &= 0x0f;
+					cs->writeisac(cs, ISAC_MOCR, cs->mocr);
 					cs->mocr |= 0xa0;
 					cs->writeisac(cs, ISAC_MOCR, cs->mocr);
+					if (cs->mon_txc &&
+						(cs->mon_txp >= cs->mon_txc))
+						test_and_set_bit(HW_MON1_TX_END, &cs->HW_Flags);
 					goto AfterMOX1;
 				}
-				if (cs->mon_txp >= cs->mon_txc) {
-					if (cs->mon_txc)
-						isac_sched_event(cs, D_TX_MON1);
+				if (cs->mon_txc && (cs->mon_txp >= cs->mon_txc)) {
+					test_and_set_bit(HW_MON1_TX_END, &cs->HW_Flags);
 					goto AfterMOX1;
 				}
 				cs->writeisac(cs, ISAC_MOX1,
 					cs->mon_tx[cs->mon_txp++]);
-				if (cs->debug & L1_DEB_WARN) {
-					sprintf(tmp, "ISAC %02x -> MOX1", cs->mon_tx[cs->mon_txp -1]);
-					debugl1(cs, tmp);
-				}
+				if (cs->debug & L1_DEB_MONITOR)
+					debugl1(cs, "ISAC %02x -> MOX1", cs->mon_tx[cs->mon_txp -1]);
 			}
 		      AfterMOX1:
 #endif
@@ -470,14 +476,18 @@
 }
 
 static void
-ISAC_l2l1(struct PStack *st, int pr, void *arg)
+ISAC_l1hw(struct PStack *st, int pr, void *arg)
 {
 	struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
 	struct sk_buff *skb = arg;
-	char str[64];
+	int  val;
 
 	switch (pr) {
-		case (PH_DATA_REQ):
+		case (PH_DATA |REQUEST):
+			if (cs->debug & DEB_DLOG_HEX)
+				LogFrame(cs, skb->data, skb->len);
+			if (cs->debug & DEB_DLOG_VERBOSE)
+				dlogframe(cs, skb, 0);
 			if (cs->tx_skb) {
 				skb_queue_tail(&cs->sq, skb);
 #ifdef L2FRAME_DEBUG		/* psa */
@@ -485,12 +495,6 @@
 					Logl2Frame(cs, skb, "PH_DATA Queued", 0);
 #endif
 			} else {
-				if ((cs->dlogflag) && (!(skb->data[2] & 1))) {	/* I-FRAME */
-					LogFrame(cs, skb->data, skb->len);
-					sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei);
-					dlogframe(cs, skb->data + 4, skb->len - 4,
-						  str);
-				}
 				cs->tx_skb = skb;
 				cs->tx_cnt = 0;
 #ifdef L2FRAME_DEBUG		/* psa */
@@ -500,19 +504,17 @@
 				isac_fill_fifo(cs);
 			}
 			break;
-		case (PH_PULL_IND):
+		case (PH_PULL |INDICATION):
 			if (cs->tx_skb) {
 				if (cs->debug & L1_DEB_WARN)
 					debugl1(cs, " l2l1 tx_skb exist this shouldn't happen");
 				skb_queue_tail(&cs->sq, skb);
 				break;
 			}
-			if ((cs->dlogflag) && (!(skb->data[2] & 1))) {	/* I-FRAME */
+			if (cs->debug & DEB_DLOG_HEX)
 				LogFrame(cs, skb->data, skb->len);
-				sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei);
-				dlogframe(cs, skb->data + 4, skb->len - 4,
-					  str);
-			}
+			if (cs->debug & DEB_DLOG_VERBOSE)
+				dlogframe(cs, skb, 0);
 			cs->tx_skb = skb;
 			cs->tx_cnt = 0;
 #ifdef L2FRAME_DEBUG		/* psa */
@@ -521,28 +523,18 @@
 #endif
 			isac_fill_fifo(cs);
 			break;
-		case (PH_PULL_REQ):
+		case (PH_PULL | REQUEST):
 #ifdef L2FRAME_DEBUG		/* psa */
 			if (cs->debug & L1_DEB_LAPD)
 				debugl1(cs, "-> PH_REQUEST_PULL");
 #endif
 			if (!cs->tx_skb) {
 				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
-				st->l1.l1l2(st, PH_PULL_CNF, NULL);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
 			} else
 				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
 			break;
-	}
-}
-
-void
-isac_l1cmd(struct IsdnCardState *cs, int msg, void *arg)
-{
-	u_char val;
-	char tmp[32];
-	
-	switch(msg) {
-		case PH_RESET_REQ:
+		case (HW_RESET | REQUEST):
 			if ((cs->ph_state == ISAC_IND_EI) ||
 				(cs->ph_state == ISAC_IND_DR) ||
 				(cs->ph_state == ISAC_IND_RS))
@@ -550,17 +542,17 @@
 			else
 				ph_command(cs, ISAC_CMD_RS);
 			break;
-		case PH_ENABLE_REQ:
+		case (HW_ENABLE | REQUEST):
 			ph_command(cs, ISAC_CMD_TIM);
 			break;
-		case PH_INFO3_REQ:
+		case (HW_INFO3 | REQUEST):
 			ph_command(cs, ISAC_CMD_AR8);
 			break;
-		case PH_TESTLOOP_REQ:
+		case (HW_TESTLOOP | REQUEST):
 			val = 0;
-			if (1 & (int) arg)
+			if (1 & (long) arg)
 				val |= 0x0c;
-			if (2 & (int) arg)
+			if (2 & (long) arg)
 				val |= 0x3;
 			if (test_bit(HW_IOM1, &cs->HW_Flags)) {
 				/* IOM 1 Mode */
@@ -580,11 +572,21 @@
 					cs->writeisac(cs, ISAC_ADF1, 0x0);
 			}
 			break;
-		default:
-			if (cs->debug & L1_DEB_WARN) {
-				sprintf(tmp, "isac_l1cmd unknown %4x", msg);
-				debugl1(cs, tmp);
+		case (HW_DEACTIVATE | RESPONSE):
+			discard_queue(&cs->rq);
+			discard_queue(&cs->sq);
+			if (cs->tx_skb) {
+				dev_kfree_skb(cs->tx_skb);
+				cs->tx_skb = NULL;
 			}
+			if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+				del_timer(&cs->dbusytimer);
+			if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+				isac_sched_event(cs, D_CLEARBUSY);
+			break;
+		default:
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "isac_l1hw unknown %04x", pr);
 			break;
 	}
 }
@@ -592,22 +594,29 @@
 void
 setstack_isac(struct PStack *st, struct IsdnCardState *cs)
 {
-	st->l2.l2l1 = ISAC_l2l1;
+	st->l1.l1hw = ISAC_l1hw;
 }
 
 static void
 dbusy_timer_handler(struct IsdnCardState *cs)
 {
 	struct PStack *stptr;
+	int	val;
 
 	if (test_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
-		if (cs->debug)
+		if (cs->debug) {
 			debugl1(cs, "D-Channel Busy");
+			val = cs->readisac(cs, ISAC_RBCH);
+			if (val & ISAC_RBCH_XAC)
+				debugl1(cs, "ISAC XAC");
+			else
+				debugl1(cs, "ISAC No XAC");
+		}
 		test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags);
 		stptr = cs->stlist;
 		
 		while (stptr != NULL) {
-			stptr->l1.l1l2(stptr, PH_PAUSE_IND, NULL);
+			stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL);
 			stptr = stptr->next;
 		}
 	}
@@ -617,7 +626,6 @@
 initisac(struct IsdnCardState *cs))
 {
 	cs->tqueue.routine = (void *) (void *) isac_bh;
-	cs->l1cmd = isac_l1cmd;
 	cs->setstack_d = setstack_isac;
 	cs->dbusytimer.function = (void *) dbusy_timer_handler;
 	cs->dbusytimer.data = (long) cs;
@@ -648,35 +656,24 @@
 HISAX_INITFUNC(void
 clear_pending_isac_ints(struct IsdnCardState *cs))
 {
-	int val;
-	char tmp[64];
+	int val, eval;
 
 	val = cs->readisac(cs, ISAC_STAR);
-	sprintf(tmp, "ISAC STAR %x", val);
-	debugl1(cs, tmp);
+	debugl1(cs, "ISAC STAR %x", val);
 	val = cs->readisac(cs, ISAC_MODE);
-	sprintf(tmp, "ISAC MODE %x", val);
-	debugl1(cs, tmp);
+	debugl1(cs, "ISAC MODE %x", val);
 	val = cs->readisac(cs, ISAC_ADF2);
-	sprintf(tmp, "ISAC ADF2 %x", val);
-	debugl1(cs, tmp);
+	debugl1(cs, "ISAC ADF2 %x", val);
 	val = cs->readisac(cs, ISAC_ISTA);
-	sprintf(tmp, "ISAC ISTA %x", val);
-	debugl1(cs, tmp);
+	debugl1(cs, "ISAC ISTA %x", val);
 	if (val & 0x01) {
-		val = cs->readisac(cs, ISAC_EXIR);
-		sprintf(tmp, "ISAC EXIR %x", val);
-		debugl1(cs, tmp);
-	} else if (val & 0x04) {
-		val = cs->readisac(cs, ISAC_CIR0);
-		sprintf(tmp, "ISAC CIR0 %x", val);
-		debugl1(cs, tmp);
-		cs->ph_state = (val >> 2) & 0xf;
-	} else {
-		cs->ph_state = (cs->readisac(cs, ISAC_CIX0) >> 2) & 0xf;
+		eval = cs->readisac(cs, ISAC_EXIR);
+		debugl1(cs, "ISAC EXIR %x", eval);
 	}
+	val = cs->readisac(cs, ISAC_CIR0);
+	debugl1(cs, "ISAC CIR0 %x", val);
+	cs->ph_state = (val >> 2) & 0xf;
 	isac_sched_event(cs, D_L1STATECHANGE);
+	/* Disable all IRQ */
 	cs->writeisac(cs, ISAC_MASK, 0xFF);
-	cs->writeisac(cs, ISAC_MASK, 0);
-	cs->writeisac(cs, ISAC_CMDR, 0x41);
 }
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/isac.h linux/drivers/isdn/hisax/isac.h
--- v2.3.3/linux/drivers/isdn/hisax/isac.h	Wed Apr  1 16:20:58 1998
+++ linux/drivers/isdn/hisax/isac.h	Sun May 23 10:03:41 1999
@@ -1,4 +1,4 @@
-/* $Id: isac.h,v 1.4 1997/10/29 19:09:34 keil Exp $
+/* $Id: isac.h,v 1.5 1998/05/25 12:58:03 keil Exp $
 
  * isac.h   ISAC specific defines
  *
@@ -6,6 +6,10 @@
  *
  *
  * $Log: isac.h,v $
+ * Revision 1.5  1998/05/25 12:58:03  keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
  * Revision 1.4  1997/10/29 19:09:34  keil
  * new L1
  *
@@ -26,16 +30,18 @@
 #define ISAC_STAR 0x21
 #define ISAC_CMDR 0x21
 #define ISAC_EXIR 0x24
-#define ISAC_RBCH 0x2a
 #define ISAC_ADF2 0x39
 #define ISAC_SPCR 0x30
 #define ISAC_ADF1 0x38
 #define ISAC_CIR0 0x31
 #define ISAC_CIX0 0x31
+#define ISAC_CIR1 0x33
+#define ISAC_CIX1 0x33
 #define ISAC_STCR 0x37
 #define ISAC_MODE 0x22
 #define ISAC_RSTA 0x27
 #define ISAC_RBCL 0x25
+#define ISAC_RBCH 0x2A
 #define ISAC_TIMR 0x23
 #define ISAC_SQXR 0x3b
 #define ISAC_MOSR 0x3a
@@ -44,6 +50,8 @@
 #define ISAC_MOX0 0x32
 #define ISAC_MOR1 0x34
 #define ISAC_MOX1 0x34
+
+#define ISAC_RBCH_XAC 0x80
 
 #define ISAC_CMD_TIM	0x0
 #define ISAC_CMD_RS	0x1
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/isar.c linux/drivers/isdn/hisax/isar.c
--- v2.3.3/linux/drivers/isdn/hisax/isar.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/isar.c	Sun May 23 10:03:41 1999
@@ -0,0 +1,937 @@
+/* $Id: isar.c,v 1.2 1998/11/15 23:54:53 keil Exp $
+
+ * isar.c   ISAR (Siemens PSB 7110) specific routines
+ *
+ * Author       Karsten Keil (keil@isdn4linux.de)
+ *
+ *
+ * $Log: isar.c,v $
+ * Revision 1.2  1998/11/15 23:54:53  keil
+ * changes from 2.0
+ *
+ * Revision 1.1  1998/08/13 23:33:47  keil
+ * First version, only init
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include "hisax.h"
+#include "isar.h"
+#include "isdnl1.h"
+#include <linux/interrupt.h>
+
+#define DBG_LOADFIRM	0
+#define DUMP_MBOXFRAME	2
+
+#define MIN(a,b) ((a<b)?a:b)
+
+void isar_setup(struct IsdnCardState *cs);
+
+static inline int
+waitforHIA(struct IsdnCardState *cs, int timeout)
+{
+
+	while ((cs->BC_Read_Reg(cs, 0, ISAR_HIA) & 1) && timeout) {
+		udelay(1);
+		timeout--;
+	}
+	if (!timeout)
+		printk(KERN_WARNING "HiSax: ISAR waitforHIA timeout\n");
+	return(timeout);
+}
+
+
+int
+sendmsg(struct IsdnCardState *cs, u_char his, u_char creg, u_char len,
+	u_char *msg)
+{
+	long flags;
+	int i;
+	
+	if (!waitforHIA(cs, 4000))
+		return(0);
+#if DUMP_MBOXFRAME
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "sendmsg(%02x,%02x,%d)", his, creg, len);
+#endif
+	save_flags(flags);
+	cli();
+	cs->BC_Write_Reg(cs, 0, ISAR_CTRL_H, creg);
+	cs->BC_Write_Reg(cs, 0, ISAR_CTRL_L, len);
+	cs->BC_Write_Reg(cs, 0, ISAR_WADR, 0);
+	if (msg && len) {
+		cs->BC_Write_Reg(cs, 1, ISAR_MBOX, msg[0]);
+		for (i=1; i<len; i++)
+			cs->BC_Write_Reg(cs, 2, ISAR_MBOX, msg[i]);
+#if DUMP_MBOXFRAME>1
+		if (cs->debug & L1_DEB_HSCX_FIFO) {
+			char tmp[256], *t;
+			
+			i = len;
+			while (i>0) {
+				t = tmp;
+				t += sprintf(t, "sendmbox cnt %d", len);
+				QuickHex(t, &msg[len-i], (i>64) ? 64:i);
+				debugl1(cs, tmp);
+				i -= 64;
+			}
+		}
+#endif
+	}
+	cs->BC_Write_Reg(cs, 1, ISAR_HIS, his);
+	restore_flags(flags);
+	waitforHIA(cs, 10000);
+	return(1);
+}
+
+/* Call only with IRQ disabled !!! */
+inline void
+rcv_mbox(struct IsdnCardState *cs, struct isar_reg *ireg, u_char *msg)
+{
+	int i;
+
+	cs->BC_Write_Reg(cs, 1, ISAR_RADR, 0);
+	if (msg && ireg->clsb) {
+		msg[0] = cs->BC_Read_Reg(cs, 1, ISAR_MBOX);
+		for (i=1; i < ireg->clsb; i++)
+			 msg[i] = cs->BC_Read_Reg(cs, 2, ISAR_MBOX);
+#if DUMP_MBOXFRAME>1
+		if (cs->debug & L1_DEB_HSCX_FIFO) {
+			char tmp[256], *t;
+			
+			i = ireg->clsb;
+			while (i>0) {
+				t = tmp;
+				t += sprintf(t, "rcv_mbox cnt %d", ireg->clsb);
+				QuickHex(t, &msg[ireg->clsb-i], (i>64) ? 64:i);
+				debugl1(cs, tmp);
+				i -= 64;
+			}
+		}
+#endif
+	}
+	cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+}
+
+/* Call only with IRQ disabled !!! */
+inline void
+get_irq_infos(struct IsdnCardState *cs, struct isar_reg *ireg)
+{
+	ireg->iis = cs->BC_Read_Reg(cs, 1, ISAR_IIS);
+	ireg->cmsb = cs->BC_Read_Reg(cs, 1, ISAR_CTRL_H);
+	ireg->clsb = cs->BC_Read_Reg(cs, 1, ISAR_CTRL_L);
+#if DUMP_MBOXFRAME
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "rcv_mbox(%02x,%02x,%d)", ireg->iis, ireg->cmsb,
+			ireg->clsb);
+#endif
+}
+
+int
+waitrecmsg(struct IsdnCardState *cs, u_char *len,
+	u_char *msg, int maxdelay)
+{
+	int timeout = 0;
+	long flags;
+	struct isar_reg *ir = cs->bcs[0].hw.isar.reg;
+	
+	
+	while((!(cs->BC_Read_Reg(cs, 0, ISAR_IRQBIT) & ISAR_IRQSTA)) &&
+		(timeout++ < maxdelay))
+		udelay(1);
+	if (timeout >= maxdelay) {
+		printk(KERN_WARNING"isar recmsg IRQSTA timeout\n");
+		return(0);
+	}
+	save_flags(flags);
+	cli();
+	get_irq_infos(cs, ir);
+	rcv_mbox(cs, ir, msg);
+	*len = ir->clsb;
+	restore_flags(flags);
+	return(1);
+}
+
+int
+ISARVersion(struct IsdnCardState *cs, char *s)
+{
+	int ver;
+	u_char msg[] = ISAR_MSG_HWVER;
+	u_char tmp[64];
+	u_char len;
+	int debug;
+
+	cs->cardmsg(cs, CARD_RESET,  NULL);
+	/* disable ISAR IRQ */
+	cs->BC_Write_Reg(cs, 0, ISAR_IRQBIT, 0);
+	debug = cs->debug;
+	cs->debug &= ~(L1_DEB_HSCX | L1_DEB_HSCX_FIFO);
+	if (!sendmsg(cs, ISAR_HIS_VNR, 0, 3, msg))
+		return(-1);
+	if (!waitrecmsg(cs, &len, tmp, 100000))
+		 return(-2);
+	cs->debug = debug;
+	if (cs->bcs[0].hw.isar.reg->iis == ISAR_IIS_VNR) {
+		if (len == 1) {
+			ver = tmp[0] & 0xf;
+			printk(KERN_INFO "%s ISAR version %d\n", s, ver);
+			return(ver);
+		}
+		return(-3);
+	}
+	return(-4);
+}
+
+int
+isar_load_firmware(struct IsdnCardState *cs, u_char *buf)
+{
+	int ret, size, cnt, debug;
+	u_char len, nom, noc;
+	u_short sadr, left, *sp;
+	u_char *p = buf;
+	u_char *msg, *tmpmsg, *mp, tmp[64];
+	long flags;
+	struct isar_reg *ireg = cs->bcs[0].hw.isar.reg;
+	
+	struct {u_short sadr;
+		u_short len;
+		u_short d_key;
+	} blk_head;
+		
+#define	BLK_HEAD_SIZE 6
+	if (1 != (ret = ISARVersion(cs, "Testing"))) {
+		printk(KERN_ERR"isar_load_firmware wrong isar version %d\n", ret);
+		return(1);
+	}
+	debug = cs->debug;
+#if DBG_LOADFIRM<2
+	cs->debug &= ~(L1_DEB_HSCX | L1_DEB_HSCX_FIFO);
+#endif
+	printk(KERN_DEBUG"isar_load_firmware buf %#lx\n", (u_long)buf);
+	if ((ret = verify_area(VERIFY_READ, (void *) p, sizeof(int)))) {
+		printk(KERN_ERR"isar_load_firmware verify_area ret %d\n", ret);
+		return ret;
+	}
+	if ((ret = copy_from_user(&size, p, sizeof(int)))) {
+		printk(KERN_ERR"isar_load_firmware copy_from_user ret %d\n", ret);
+		return ret;
+	}
+	p += sizeof(int);
+	printk(KERN_DEBUG"isar_load_firmware size: %d\n", size);
+	if ((ret = verify_area(VERIFY_READ, (void *) p, size))) {
+		printk(KERN_ERR"isar_load_firmware verify_area ret %d\n", ret);
+		return ret;
+	}
+	cnt = 0;
+	/* disable ISAR IRQ */
+	cs->BC_Write_Reg(cs, 0, ISAR_IRQBIT, 0);
+	if (!(msg = kmalloc(256, GFP_KERNEL))) {
+		printk(KERN_ERR"isar_load_firmware no buffer\n");
+		return (1);
+	}
+	if (!(tmpmsg = kmalloc(256, GFP_KERNEL))) {
+		printk(KERN_ERR"isar_load_firmware no tmp buffer\n");
+		kfree(msg);
+		return (1);
+	}
+	while (cnt < size) {
+		if ((ret = copy_from_user(&blk_head, p, BLK_HEAD_SIZE))) {
+			printk(KERN_ERR"isar_load_firmware copy_from_user ret %d\n", ret);
+			goto reterror;
+		}
+		cnt += BLK_HEAD_SIZE;
+		p += BLK_HEAD_SIZE;
+		printk(KERN_DEBUG"isar firmware block (%#x,%5d,%#x)\n",
+			blk_head.sadr, blk_head.len, blk_head.d_key & 0xff);
+		sadr = blk_head.sadr;
+		left = blk_head.len;
+		if (!sendmsg(cs, ISAR_HIS_DKEY, blk_head.d_key & 0xff, 0, NULL)) {
+			printk(KERN_ERR"isar sendmsg dkey failed\n");
+			ret = 1;goto reterror;
+		}
+		if (!waitrecmsg(cs, &len, tmp, 100000)) {
+			printk(KERN_ERR"isar waitrecmsg dkey failed\n");
+			ret = 1;goto reterror;
+		}
+		if ((ireg->iis != ISAR_IIS_DKEY) || ireg->cmsb || len) {
+			printk(KERN_ERR"isar wrong dkey response (%x,%x,%x)\n",
+				ireg->iis, ireg->cmsb, len);
+			ret = 1;goto reterror;
+		}
+		while (left>0) {
+			noc = MIN(126, left);
+			nom = 2*noc;
+			mp  = msg;
+			*mp++ = sadr / 256;
+			*mp++ = sadr % 256;
+			left -= noc;
+			*mp++ = noc;
+			if ((ret = copy_from_user(tmpmsg, p, nom))) {
+				printk(KERN_ERR"isar_load_firmware copy_from_user ret %d\n", ret);
+				goto reterror;
+			}
+			p += nom;
+			cnt += nom;
+			nom += 3;
+			sp = (u_short *)tmpmsg;
+#if DBG_LOADFIRM
+			printk(KERN_DEBUG"isar: load %3d words at %04x\n",
+				 noc, sadr);
+#endif
+			sadr += noc;
+			while(noc) {
+				*mp++ = *sp / 256;
+				*mp++ = *sp % 256;
+				sp++;
+				noc--;
+			}
+			if (!sendmsg(cs, ISAR_HIS_FIRM, 0, nom, msg)) {
+				printk(KERN_ERR"isar sendmsg prog failed\n");
+				ret = 1;goto reterror;
+			}
+			if (!waitrecmsg(cs, &len, tmp, 100000)) {
+				printk(KERN_ERR"isar waitrecmsg prog failed\n");
+				ret = 1;goto reterror;
+			}
+			if ((ireg->iis != ISAR_IIS_FIRM) || ireg->cmsb || len) {
+				printk(KERN_ERR"isar wrong prog response (%x,%x,%x)\n",
+					ireg->iis, ireg->cmsb, len);
+				ret = 1;goto reterror;
+			}
+		}
+		printk(KERN_DEBUG"isar firmware block %5d words loaded\n",
+			blk_head.len);
+	}
+	msg[0] = 0xff;
+	msg[1] = 0xfe;
+	ireg->bstat = 0;
+	if (!sendmsg(cs, ISAR_HIS_STDSP, 0, 2, msg)) {
+		printk(KERN_ERR"isar sendmsg start dsp failed\n");
+		ret = 1;goto reterror;
+	}
+	if (!waitrecmsg(cs, &len, tmp, 100000)) {
+		printk(KERN_ERR"isar waitrecmsg start dsp failed\n");
+		ret = 1;goto reterror;
+	}
+	if ((ireg->iis != ISAR_IIS_STDSP) || ireg->cmsb || len) {
+		printk(KERN_ERR"isar wrong start dsp response (%x,%x,%x)\n",
+			ireg->iis, ireg->cmsb, len);
+		ret = 1;goto reterror;
+	} else
+		printk(KERN_DEBUG"isar start dsp success\n");
+	/* NORMAL mode entered */
+	/* Enable IRQs of ISAR */
+	cs->BC_Write_Reg(cs, 0, ISAR_IRQBIT, ISAR_IRQSTA);
+	save_flags(flags);
+	sti();
+	cnt = 1000; /* max 1s */
+	while ((!ireg->bstat) && cnt) {
+		udelay(1000);
+		cnt--;
+	}
+	if (!cnt) {
+		printk(KERN_ERR"isar no general status event received\n");
+		ret = 1;goto reterrflg;
+	} else {
+		printk(KERN_DEBUG"isar general status event %x\n",
+			ireg->bstat);
+	}
+	ireg->iis = 0;
+	if (!sendmsg(cs, ISAR_HIS_DIAG, ISAR_CTRL_STST, 0, NULL)) {
+		printk(KERN_ERR"isar sendmsg self tst failed\n");
+		ret = 1;goto reterrflg;
+	}
+	cnt = 1000; /* max 10 ms */
+	while ((ireg->iis != ISAR_IIS_DIAG) && cnt) {
+		udelay(10);
+		cnt--;
+	}
+	if (!cnt) {
+		printk(KERN_ERR"isar no self tst response\n");
+		ret = 1;goto reterrflg;
+	} else if ((ireg->cmsb == ISAR_CTRL_STST) && (ireg->clsb == 1)
+		&& (ireg->par[0] == 0)) {
+		printk(KERN_DEBUG"isar selftest OK\n");
+	} else {
+		printk(KERN_DEBUG"isar selftest not OK %x/%x/%x\n",
+			ireg->cmsb, ireg->clsb, ireg->par[0]);
+		ret = 1;goto reterror;
+	}
+	ireg->iis = 0;
+	if (!sendmsg(cs, ISAR_HIS_DIAG, ISAR_CTRL_SWVER, 0, NULL)) {
+		printk(KERN_ERR"isar RQST SVN failed\n");
+		ret = 1;goto reterror;
+	}
+	cnt = 10000; /* max 100 ms */
+	while ((ireg->iis != ISAR_IIS_DIAG) && cnt) {
+		udelay(10);
+		cnt--;
+	}
+	if (!cnt) {
+		printk(KERN_ERR"isar no SVN response\n");
+		ret = 1;goto reterrflg;
+	} else {
+		if ((ireg->cmsb == ISAR_CTRL_SWVER) && (ireg->clsb == 1))
+			printk(KERN_DEBUG"isar software version %#x\n",
+				ireg->par[0]);
+		else {
+			printk(KERN_ERR"isar wrong swver response (%x,%x) cnt(%d)\n",
+				ireg->cmsb, ireg->clsb, cnt);
+			ret = 1;goto reterrflg;
+		}
+	}
+	cs->debug = debug;
+	isar_setup(cs);
+	ret = 0;
+reterrflg:
+	restore_flags(flags);
+reterror:
+	cs->debug = debug;
+	if (ret)
+		/* disable ISAR IRQ */
+		cs->BC_Write_Reg(cs, 0, ISAR_IRQBIT, 0);
+	kfree(msg);
+	kfree(tmpmsg);
+	return(ret);
+}
+
+void
+isar_sched_event(struct BCState *bcs, int event)
+{
+	bcs->event |= 1 << event;
+	queue_task(&bcs->tqueue, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+}
+
+static inline void
+isar_rcv_frame(struct IsdnCardState *cs, struct BCState *bcs)
+{
+	u_char *ptr;
+	struct sk_buff *skb;
+	struct isar_reg *ireg = bcs->hw.isar.reg;
+	
+	if (!ireg->clsb) {
+		debugl1(cs, "isar zero len frame");
+		cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+		return;
+	}
+	switch (bcs->mode) {
+	case L1_MODE_NULL:
+		debugl1(cs, "isar mode 0 spurious IIS_RDATA %x/%x/%x",
+			ireg->iis, ireg->cmsb, ireg->clsb);
+		printk(KERN_WARNING"isar mode 0 spurious IIS_RDATA %x/%x/%x\n",
+			ireg->iis, ireg->cmsb, ireg->clsb);
+		cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+		break;
+	case L1_MODE_TRANS:
+		if ((skb = dev_alloc_skb(ireg->clsb))) {
+			rcv_mbox(cs, ireg, (u_char *)skb_put(skb, ireg->clsb));
+			skb_queue_tail(&bcs->rqueue, skb);
+			isar_sched_event(bcs, B_RCVBUFREADY);
+		} else {
+			printk(KERN_WARNING "HiSax: skb out of memory\n");
+			cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+		}
+		break;
+	case L1_MODE_HDLC:
+		if ((bcs->hw.isar.rcvidx + ireg->clsb) > HSCX_BUFMAX) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "isar_rcv_frame: incoming packet too large");
+			cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+			bcs->hw.isar.rcvidx = 0;
+		} else if (ireg->cmsb & HDLC_ERROR) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "isar frame error %x len %d",
+					ireg->cmsb, ireg->clsb);
+			bcs->hw.isar.rcvidx = 0;
+			cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+		} else {
+			if (ireg->cmsb & HDLC_FSD)
+				bcs->hw.isar.rcvidx = 0;
+			ptr = bcs->hw.isar.rcvbuf + bcs->hw.isar.rcvidx;
+			bcs->hw.isar.rcvidx += ireg->clsb;
+			rcv_mbox(cs, ireg, ptr);
+			if (ireg->cmsb & HDLC_FED) {
+				if (bcs->hw.isar.rcvidx < 3) { /* last 2 bytes are the FCS */
+					printk(KERN_WARNING "ISAR: HDLC frame too short(%d)\n",
+						bcs->hw.isar.rcvidx);
+				} else if (!(skb = dev_alloc_skb(bcs->hw.isar.rcvidx-2)))
+					printk(KERN_WARNING "ISAR: receive out of memory\n");
+				else {
+					memcpy(skb_put(skb, bcs->hw.isar.rcvidx-2),
+						bcs->hw.isar.rcvbuf, bcs->hw.isar.rcvidx-2);
+					skb_queue_tail(&bcs->rqueue, skb);
+					isar_sched_event(bcs, B_RCVBUFREADY);
+				}
+			}
+		}
+		break;
+	default:
+		printk(KERN_ERR"isar_rcv_frame mode (%x)error\n", bcs->mode);
+		cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+		break;
+	}
+}
+
+void
+isar_fill_fifo(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int count;
+	u_char msb;
+	u_char *ptr;
+	long flags;
+
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "isar_fill_fifo");
+	if (!bcs->tx_skb)
+		return;
+	if (bcs->tx_skb->len <= 0)
+		return;
+	if (!(bcs->hw.isar.reg->bstat & 
+		(bcs->hw.isar.dpath == 1 ? BSTAT_RDM1 : BSTAT_RDM2)))
+		return;
+	if (bcs->tx_skb->len > bcs->hw.isar.mml) {
+		msb = 0;
+		count = bcs->hw.isar.mml;
+	} else {
+		count = bcs->tx_skb->len;
+		msb = HDLC_FED;
+	}
+	if (!bcs->hw.isar.txcnt)
+		msb |= HDLC_FST;
+	save_flags(flags);
+	cli();
+	ptr = bcs->tx_skb->data;
+	skb_pull(bcs->tx_skb, count);
+	bcs->tx_cnt -= count;
+	bcs->hw.isar.txcnt += count;
+	switch (bcs->mode) {
+	case L1_MODE_NULL:
+		printk(KERN_ERR"isar_fill_fifo wrong mode 0\n");
+		break;
+	case L1_MODE_TRANS:
+		if (!sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | ISAR_HIS_SDATA,
+			0, count, ptr)) {
+			if (cs->debug)
+				debugl1(cs, "isar bin data send dp%d failed",
+					bcs->hw.isar.dpath);
+		}
+		break;
+	case L1_MODE_HDLC:
+		if (!sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | ISAR_HIS_SDATA,
+			msb, count, ptr)) {
+			if (cs->debug)
+				debugl1(cs, "isar hdlc data send dp%d failed",
+					bcs->hw.isar.dpath);
+		}
+		break;
+	default:
+		printk(KERN_ERR"isar_fill_fifo mode (%x)error\n", bcs->mode);
+		break;
+	}
+	restore_flags(flags);
+}
+
+inline
+struct BCState *sel_bcs_isar(struct IsdnCardState *cs, u_char dpath)
+{
+	if ((!dpath) || (dpath == 3))
+		return(NULL);
+	if (cs->bcs[0].hw.isar.dpath == dpath)
+		return(&cs->bcs[0]);
+	if (cs->bcs[1].hw.isar.dpath == dpath)
+		return(&cs->bcs[1]);
+	return(NULL);
+}
+
+inline void
+send_frames(struct BCState *bcs)
+{
+	if (bcs->tx_skb) {
+		if (bcs->tx_skb->len) {
+			isar_fill_fifo(bcs);
+			return;
+		} else {
+			if (bcs->st->lli.l1writewakeup &&
+				(PACKET_NOACK != bcs->tx_skb->pkt_type))
+					bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.isar.txcnt);
+			dev_kfree_skb(bcs->tx_skb);
+			bcs->hw.isar.txcnt = 0; 
+			bcs->tx_skb = NULL;
+		}
+	}
+	if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+		bcs->hw.isar.txcnt = 0;
+		test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+		isar_fill_fifo(bcs);
+	} else {
+		test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		isar_sched_event(bcs, B_XMTBUFREADY);
+	}
+}
+
+inline void
+check_send(struct IsdnCardState *cs, u_char rdm)
+{
+	struct BCState *bcs;
+	
+	if (rdm & BSTAT_RDM1) {
+		if ((bcs = sel_bcs_isar(cs, 1))) {
+			if (bcs->mode) {
+				send_frames(bcs);
+			}
+		}
+	}
+	if (rdm & BSTAT_RDM2) {
+		if ((bcs = sel_bcs_isar(cs, 2))) {
+			if (bcs->mode) {
+				send_frames(bcs);
+			}
+		}
+	}
+	
+}
+
+static char debbuf[64];
+
+void
+isar_int_main(struct IsdnCardState *cs)
+{
+	long flags;
+	struct isar_reg *ireg = cs->bcs[0].hw.isar.reg;
+	struct BCState *bcs;
+
+	save_flags(flags);
+	cli();
+	get_irq_infos(cs, ireg);
+	switch (ireg->iis & ISAR_IIS_MSCMSD) {
+		case ISAR_IIS_RDATA:
+			if ((bcs = sel_bcs_isar(cs, ireg->iis >> 6))) {
+				isar_rcv_frame(cs, bcs);
+			} else {
+				debugl1(cs, "isar spurious IIS_RDATA %x/%x/%x",
+					ireg->iis, ireg->cmsb, ireg->clsb);
+				printk(KERN_WARNING"isar spurious IIS_RDATA %x/%x/%x\n",
+					ireg->iis, ireg->cmsb, ireg->clsb);
+				cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+			}
+			break;
+		case ISAR_IIS_GSTEV:
+			cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+			ireg->bstat |= ireg->cmsb;
+			check_send(cs, ireg->cmsb);
+			break;
+		case ISAR_IIS_BSTEV:
+			cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "Buffer STEV dpath%d msb(%x)",
+					ireg->iis>>6, ireg->cmsb);
+			break;
+		case ISAR_IIS_DIAG:
+		case ISAR_IIS_PSTRSP:
+		case ISAR_IIS_PSTEV:
+		case ISAR_IIS_BSTRSP:
+		case ISAR_IIS_IOM2RSP:
+			rcv_mbox(cs, ireg, (u_char *)ireg->par);
+			if ((cs->debug & (L1_DEB_HSCX | L1_DEB_HSCX_FIFO))
+				== L1_DEB_HSCX) {
+				u_char *tp=debbuf;
+
+				tp += sprintf(debbuf, "msg iis(%x) msb(%x)",
+					ireg->iis, ireg->cmsb);
+				QuickHex(tp, (u_char *)ireg->par, ireg->clsb);
+				debugl1(cs, debbuf);
+			}
+			break;
+		default:
+			rcv_mbox(cs, ireg, debbuf);
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "unhandled msg iis(%x) ctrl(%x/%x)",
+					ireg->iis, ireg->cmsb, ireg->clsb);
+			break;
+	}
+	restore_flags(flags);
+}
+
+void
+setup_pump(struct BCState *bcs) {
+	struct IsdnCardState *cs = bcs->cs;
+	u_char dps = SET_DPS(bcs->hw.isar.dpath);
+	
+	switch (bcs->mode) {
+		case L1_MODE_NULL:
+		case L1_MODE_TRANS:
+		case L1_MODE_HDLC:
+			if (!sendmsg(cs, dps | ISAR_HIS_PUMPCFG, PMOD_BYPASS, 0, NULL)) {
+				if (cs->debug)
+					debugl1(cs, "isar pump bypass cfg dp%d failed",
+						bcs->hw.isar.dpath);
+			}
+			break;
+	}
+	if (!sendmsg(cs, dps | ISAR_HIS_PSTREQ, 0, 0, NULL)) {
+		if (cs->debug)
+			debugl1(cs, "isar pump status req dp%d failed",
+				bcs->hw.isar.dpath);
+	}
+}
+
+void
+setup_sart(struct BCState *bcs) {
+	struct IsdnCardState *cs = bcs->cs;
+	u_char dps = SET_DPS(bcs->hw.isar.dpath);
+	
+	switch (bcs->mode) {
+		case L1_MODE_NULL:
+			if (!sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_DISABLE, 0, NULL)) {
+				if (cs->debug)
+					debugl1(cs, "isar sart disable dp%d failed",
+						bcs->hw.isar.dpath);
+			}
+			break;
+		case L1_MODE_TRANS:
+			if (!sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_BINARY, 2, "\0\0")) {
+				if (cs->debug)
+					debugl1(cs, "isar sart binary dp%d failed",
+						bcs->hw.isar.dpath);
+			}
+			break;
+		case L1_MODE_HDLC:
+			if (!sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_HDLC, 1, "\0")) {
+				if (cs->debug)
+					debugl1(cs, "isar sart binary dp%d failed",
+						bcs->hw.isar.dpath);
+			}
+			break;
+	}
+	if (!sendmsg(cs, dps | ISAR_HIS_BSTREQ, 0, 0, NULL)) {
+		if (cs->debug)
+			debugl1(cs, "isar buf stat req dp%d failed",
+				bcs->hw.isar.dpath);
+	}
+}
+
+void
+setup_iom2(struct BCState *bcs) {
+	struct IsdnCardState *cs = bcs->cs;
+	u_char dps = SET_DPS(bcs->hw.isar.dpath);
+	u_char cmsb = 0, msg[5] = {0x10,0,0,0,0};
+	
+	switch (bcs->mode) {
+		case L1_MODE_NULL:
+			/* dummy slot */
+			msg[1] = msg[3] = bcs->hw.isar.dpath + 2;
+			break;
+		case L1_MODE_TRANS:
+		case L1_MODE_HDLC:
+			cmsb = 0x80;
+			if (bcs->channel)
+				msg[1] = msg[3] = 1;
+			break;
+	}
+	if (!sendmsg(cs, dps | ISAR_HIS_IOM2CFG, cmsb, 5, msg)) {
+		if (cs->debug)
+			debugl1(cs, "isar iom2 dp%d failed", bcs->hw.isar.dpath);
+	}
+	if (!sendmsg(cs, dps | ISAR_HIS_IOM2REQ, 0, 0, NULL)) {
+		if (cs->debug)
+			debugl1(cs, "isar IOM2 cfg req dp%d failed",
+				bcs->hw.isar.dpath);
+	}
+}
+
+int
+modeisar(struct BCState *bcs, int mode, int bc)
+{
+	struct IsdnCardState *cs = bcs->cs;
+
+	/* Here we are selecting the best datapath for requested mode */
+	if(bcs->mode == L1_MODE_NULL) { /* New Setup */
+		bcs->channel = bc;
+		switch (mode) {
+			case L1_MODE_NULL: /* init */
+				break;
+			case L1_MODE_TRANS:
+			case L1_MODE_HDLC:
+				/* best is datapath 2 */
+				if (!test_and_set_bit(ISAR_DP2_USE, 
+					&bcs->hw.isar.reg->Flags))
+					bcs->hw.isar.dpath = 2;
+				else if (!test_and_set_bit(ISAR_DP1_USE,
+					&bcs->hw.isar.reg->Flags))
+					bcs->hw.isar.dpath = 1;
+				else {
+					printk(KERN_ERR"isar modeisar both pathes in use\n");
+					return(1);
+				}
+				break;
+		}
+	}
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "isar dp%d mode %d->%d ichan %d",
+			bcs->hw.isar.dpath, bcs->mode, mode, bc);
+	bcs->mode = mode;
+	setup_pump(bcs);
+	setup_sart(bcs);
+	setup_iom2(bcs);
+	if (bcs->mode == L1_MODE_NULL) {
+		/* Clear resources */
+		if (bcs->hw.isar.dpath == 1)
+			test_and_clear_bit(ISAR_DP1_USE, &bcs->hw.isar.reg->Flags);
+		else if (bcs->hw.isar.dpath == 2)
+			test_and_clear_bit(ISAR_DP2_USE, &bcs->hw.isar.reg->Flags);
+		bcs->hw.isar.dpath = 0;
+	}
+	return(0);
+}
+
+void
+isar_setup(struct IsdnCardState *cs)
+{
+	u_char msg;
+	int i;
+	
+	/* Dpath 1, 2 */
+	msg = 61;
+	for (i=0; i<2; i++) {
+		/* Buffer Config */
+		if (!sendmsg(cs, (i ? ISAR_HIS_DPS2 : ISAR_HIS_DPS1) |
+			ISAR_HIS_P12CFG, 4, 1, &msg)) {
+			if (cs->debug)
+				debugl1(cs, "isar P%dCFG failed", i+1);
+		}
+		cs->bcs[i].hw.isar.mml = msg;
+		cs->bcs[i].mode = 0;
+		cs->bcs[i].hw.isar.dpath = i + 1;
+		modeisar(&cs->bcs[i], 0, 0);
+	}
+}
+
+void
+isar_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	long flags;
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+			save_flags(flags);
+			cli();
+			if (st->l1.bcs->tx_skb) {
+				skb_queue_tail(&st->l1.bcs->squeue, skb);
+				restore_flags(flags);
+			} else {
+				st->l1.bcs->tx_skb = skb;
+				test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+				if (st->l1.bcs->cs->debug & L1_DEB_HSCX)
+					debugl1(st->l1.bcs->cs, "DRQ set BC_FLG_BUSY");
+				st->l1.bcs->hw.isar.txcnt = 0;
+				restore_flags(flags);
+				st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
+			}
+			break;
+		case (PH_PULL | INDICATION):
+			if (st->l1.bcs->tx_skb) {
+				printk(KERN_WARNING "isar_l2l1: this shouldn't happen\n");
+				break;
+			}
+			test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+			if (st->l1.bcs->cs->debug & L1_DEB_HSCX)
+				debugl1(st->l1.bcs->cs, "PUI set BC_FLG_BUSY");
+			st->l1.bcs->tx_skb = skb;
+			st->l1.bcs->hw.isar.txcnt = 0;
+			st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
+			break;
+		case (PH_PULL | REQUEST):
+			if (!st->l1.bcs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (PH_ACTIVATE | REQUEST):
+			test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+			modeisar(st->l1.bcs, st->l1.mode, st->l1.bc);
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | REQUEST):
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | CONFIRM):
+			test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+			test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+			if (st->l1.bcs->cs->debug & L1_DEB_HSCX)
+				debugl1(st->l1.bcs->cs, "PDAC clear BC_FLG_BUSY");
+			modeisar(st->l1.bcs, 0, st->l1.bc);
+			st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+			break;
+	}
+}
+
+void
+close_isarstate(struct BCState *bcs)
+{
+	modeisar(bcs, 0, bcs->channel);
+	if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (bcs->hw.isar.rcvbuf) {
+			kfree(bcs->hw.isar.rcvbuf);
+			bcs->hw.isar.rcvbuf = NULL;
+		}
+		discard_queue(&bcs->rqueue);
+		discard_queue(&bcs->squeue);
+		if (bcs->tx_skb) {
+			dev_kfree_skb(bcs->tx_skb);
+			bcs->tx_skb = NULL;
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			if (bcs->cs->debug & L1_DEB_HSCX)
+				debugl1(bcs->cs, "closeisar clear BC_FLG_BUSY");
+		}
+	}
+}
+
+int
+open_isarstate(struct IsdnCardState *cs, struct BCState *bcs)
+{
+	if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (!(bcs->hw.isar.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) {
+			printk(KERN_WARNING
+			       "HiSax: No memory for isar.rcvbuf\n");
+			return (1);
+		}
+		skb_queue_head_init(&bcs->rqueue);
+		skb_queue_head_init(&bcs->squeue);
+	}
+	bcs->tx_skb = NULL;
+	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "openisar clear BC_FLG_BUSY");
+	bcs->event = 0;
+	bcs->hw.isar.rcvidx = 0;
+	bcs->tx_cnt = 0;
+	return (0);
+}
+
+int
+setstack_isar(struct PStack *st, struct BCState *bcs)
+{
+	bcs->channel = st->l1.bc;
+	if (open_isarstate(st->l1.hardware, bcs))
+		return (-1);
+	st->l1.bcs = bcs;
+	st->l2.l2l1 = isar_l2l1;
+	setstack_manager(st);
+	bcs->st = st;
+	setstack_l1_B(st);
+	return (0);
+}
+
+HISAX_INITFUNC(void 
+initisar(struct IsdnCardState *cs))
+{
+	cs->bcs[0].BC_SetStack = setstack_isar;
+	cs->bcs[1].BC_SetStack = setstack_isar;
+	cs->bcs[0].BC_Close = close_isarstate;
+	cs->bcs[1].BC_Close = close_isarstate;
+}
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/isar.h linux/drivers/isdn/hisax/isar.h
--- v2.3.3/linux/drivers/isdn/hisax/isar.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/isar.h	Sun May 23 10:03:41 1999
@@ -0,0 +1,91 @@
+/* $Id: isar.h,v 1.2 1998/11/15 23:54:54 keil Exp $
+ * isar.h   ISAR (Siemens PSB 7110) specific defines
+ *
+ * Author Karsten Keil (keil@isdn4linux.de)
+ *
+ *
+ * $Log: isar.h,v $
+ * Revision 1.2  1998/11/15 23:54:54  keil
+ * changes from 2.0
+ *
+ * Revision 1.1  1998/08/13 23:33:48  keil
+ * First version, only init
+ *
+ *
+ */
+ 
+#define ISAR_IRQMSK	0x04
+#define ISAR_IRQSTA	0x04
+#define ISAR_IRQBIT	0x75
+#define ISAR_CTRL_H	0x61
+#define ISAR_CTRL_L	0x60
+#define ISAR_IIS	0x58
+#define ISAR_IIA	0x58
+#define ISAR_HIS	0x50
+#define ISAR_HIA	0x50
+#define ISAR_MBOX	0x4c
+#define ISAR_WADR	0x4a
+#define ISAR_RADR	0x48 
+
+#define ISAR_HIS_VNR	0x14
+#define ISAR_HIS_DKEY	0x02
+#define ISAR_HIS_FIRM	0x1e
+#define ISAR_HIS_STDSP  0x08
+#define ISAR_HIS_DIAG	0x05
+#define ISAR_HIS_P0CFG	0x3c
+#define ISAR_HIS_P12CFG	0x24
+#define ISAR_HIS_SARTCFG	0x25	
+#define ISAR_HIS_PUMPCFG	0x26	
+#define ISAR_HIS_IOM2CFG	0x27
+#define ISAR_HIS_IOM2REQ	0x07
+#define ISAR_HIS_BSTREQ	0x0c
+#define ISAR_HIS_PSTREQ	0x0e
+#define ISAR_HIS_SDATA	0x20
+#define ISAR_HIS_DPS1	0x40
+#define ISAR_HIS_DPS2	0x80
+#define SET_DPS(x)	((x<<6) & 0xc0)
+
+#define ISAR_IIS_MSCMSD 0x3f
+#define ISAR_IIS_VNR	0x15
+#define ISAR_IIS_DKEY	0x03
+#define ISAR_IIS_FIRM	0x1f
+#define ISAR_IIS_STDSP  0x09
+#define ISAR_IIS_DIAG	0x25
+#define ISAR_IIS_GSTEV	0x0
+#define ISAR_IIS_BSTEV	0x28
+#define ISAR_IIS_BSTRSP	0x2c
+#define ISAR_IIS_PSTRSP	0x2e
+#define ISAR_IIS_PSTEV	0x2a
+#define ISAR_IIS_IOM2RSP	0x27
+
+#define ISAR_IIS_RDATA	0x20
+#define ISAR_CTRL_SWVER	0x10
+#define ISAR_CTRL_STST	0x40
+
+#define ISAR_MSG_HWVER	{0x20, 0, 1}
+
+#define ISAR_DP1_USE	1
+#define ISAR_DP2_USE	2
+
+#define PMOD_BYPASS	7
+
+#define SMODE_DISABLE	0
+#define SMODE_HDLC	3
+#define SMODE_BINARY	4
+
+#define HDLC_FED	0x40
+#define HDLC_FSD	0x20
+#define HDLC_FST	0x20
+#define HDLC_ERROR	0x1c
+
+#define BSTAT_RDM0	0x1
+#define BSTAT_RDM1	0x2
+#define BSTAT_RDM2	0x4
+#define BSTAT_RDM3	0x8
+
+
+extern int ISARVersion(struct IsdnCardState *cs, char *s);
+extern int isar_load_firmware(struct IsdnCardState *cs, u_char *buf);
+extern void isar_int_main(struct IsdnCardState *cs);
+extern void initisar(struct IsdnCardState *cs);
+extern void isar_fill_fifo(struct BCState *bcs);
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/isdnl1.c linux/drivers/isdn/hisax/isdnl1.c
--- v2.3.3/linux/drivers/isdn/hisax/isdnl1.c	Mon Mar 15 16:11:30 1999
+++ linux/drivers/isdn/hisax/isdnl1.c	Wed May 26 16:55:40 1999
@@ -1,9 +1,13 @@
-/* $Id: isdnl1.c,v 2.18 1998/02/12 23:07:42 keil Exp $
+/* $Id: isdnl1.c,v 2.31 1998/11/15 23:54:56 keil Exp $
 
  * isdnl1.c     common low level stuff for Siemens Chipsetbased isdn cards
  *              based on the teles driver from Jan den Ouden
  *
- * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author       Karsten Keil (keil@isdn4linux.de)
+ *
+ *		This file is (c) under GNU PUBLIC LICENSE
+ *		For changes and modifications please read
+ *		../../../Documentation/isdn/HiSax.cert
  *
  * Thanks to    Jan den Ouden
  *              Fritz Elfert
@@ -11,6 +15,43 @@
  *
  *
  * $Log: isdnl1.c,v $
+ * Revision 2.31  1998/11/15 23:54:56  keil
+ * changes from 2.0
+ *
+ * Revision 2.30  1998/09/30 22:27:00  keil
+ * Add init of l1.Flags
+ *
+ * Revision 2.29  1998/09/27 23:54:43  keil
+ * cosmetics
+ *
+ * Revision 2.28  1998/09/27 12:52:23  keil
+ * Fix against segfault, if the driver cannot allocate an IRQ channel
+ *
+ * Revision 2.27  1998/08/13 23:36:39  keil
+ * HiSax 3.1 - don't work stable with current LinkLevel
+ *
+ * Revision 2.26  1998/07/15 15:01:31  calle
+ * Support for AVM passive PCMCIA cards:
+ *    A1 PCMCIA, FRITZ!Card PCMCIA and FRITZ!Card PCMCIA 2.0
+ *
+ * Revision 2.25  1998/05/25 14:10:09  keil
+ * HiSax 3.0
+ * X.75 and leased are working again.
+ *
+ * Revision 2.24  1998/05/25 12:58:04  keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
+ * Revision 2.22  1998/04/15 16:40:13  keil
+ * Add S0Box and Teles PCI support
+ * Fix cardnr overwrite bug
+ *
+ * Revision 2.21  1998/04/10 10:35:28  paul
+ * fixed (silly?) warnings from egcs on Alpha.
+ *
+ * Revision 2.20  1998/03/09 23:19:27  keil
+ * Changes for PCMCIA
+ *
  * Revision 2.18  1998/02/12 23:07:42  keil
  * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
  *
@@ -79,102 +120,20 @@
  *
  */
 
-const char *l1_revision = "$Revision: 2.18 $";
+const char *l1_revision = "$Revision: 2.31 $";
 
 #define __NO_VERSION__
-#include <linux/config.h>
 #include "hisax.h"
 #include "isdnl1.h"
-#include <linux/kernel_stat.h>
-#if (LINUX_VERSION_CODE < 0x020150) /* 2.1.80 */
-#define kstat_irqs( PAR ) kstat.interrupts( (PAR) )
-#endif
-
-
-
-#if CARD_TELES0
-extern int setup_teles0(struct IsdnCard *card);
-#endif
-
-#if CARD_TELES3
-extern int setup_teles3(struct IsdnCard *card);
-#endif
-
-#if CARD_AVM_A1
-extern int setup_avm_a1(struct IsdnCard *card);
-#endif
-
-#if CARD_ELSA
-extern int setup_elsa(struct IsdnCard *card);
-#endif
 
-#if CARD_IX1MICROR2
-extern int setup_ix1micro(struct IsdnCard *card);
-#endif
-
-#if CARD_DIEHLDIVA
-extern	int  setup_diva(struct IsdnCard *card);
-#endif
-
-#if CARD_ASUSCOM
-extern int setup_asuscom(struct IsdnCard *card);
-#endif
+#define TIMER3_VALUE 7000
 
-#if CARD_TELEINT
-extern int setup_TeleInt(struct IsdnCard *card);
-#endif
-
-#if CARD_SEDLBAUER
-extern int setup_sedlbauer(struct IsdnCard *card);
-#endif
-
-#if CARD_SPORTSTER
-extern int setup_sportster(struct IsdnCard *card);
-#endif
-
-#if CARD_MIC
-extern int setup_mic(struct IsdnCard *card);
-#endif
-
-#if CARD_NETJET
-extern int setup_netjet(struct IsdnCard *card);
-#endif
-
-#if CARD_TELES3C
-extern int setup_t163c(struct IsdnCard *card);
-#endif
-
-#if CARD_AMD7930 || CARD_DBRI
-extern int setup_foreign(struct IsdnCard *card);
-#endif
-
-#if CARD_NICCY
-extern int setup_niccy(struct IsdnCard *card);
-#endif
-
-#define HISAX_STATUS_BUFSIZE 4096
-#define ISDN_CTRL_DEBUG 1
-#define INCLUDE_INLINE_FUNCS
-#include <linux/tqueue.h>
-#include <linux/interrupt.h>
-const char *CardType[] =
-{"No Card", "Teles 16.0", "Teles 8.0", "Teles 16.3", "Creatix/Teles PnP",
- "AVM A1", "Elsa ML", "Elsa Quickstep", "Teles PCMCIA", "ITK ix1-micro Rev.2",
- "Elsa PCMCIA", "Eicon.Diehl Diva", "ISDNLink", "TeleInt", "Teles 16.3c", 
- "Sedlbauer Speed Card", "USR Sportster", "ith mic Linux", "Elsa PCI",
- "Compaq ISA", "NETjet", "Teles PCI", "Sedlbauer Speed Star (PCMCIA)",
- "AMD 7930", "NICCY", "DBRI"
-};
-
-extern struct IsdnCard cards[];
-extern int nrcards;
-extern char *HiSax_id;
-extern struct IsdnBuffers *tracebuf;
-
-#define TIMER3_VALUE 7
+static
+struct Fsm l1fsm_b =
+{NULL, 0, 0, NULL, NULL};
 
 static
-struct Fsm l1fsm =
+struct Fsm l1fsm_d =
 {NULL, 0, 0, NULL, NULL};
 
 enum {
@@ -187,9 +146,9 @@
 	ST_L1_F8,
 };
 
-#define L1_STATE_COUNT (ST_L1_F8+1)
+#define L1D_STATE_COUNT (ST_L1_F8+1)
 
-static char *strL1State[] =
+static char *strL1DState[] =
 {
 	"ST_L1_F2",
 	"ST_L1_F3",
@@ -201,7 +160,25 @@
 };
 
 enum {
+	ST_L1_NULL,
+	ST_L1_WAIT_ACT,
+	ST_L1_WAIT_DEACT,
+	ST_L1_ACTIV,
+};
+
+#define L1B_STATE_COUNT (ST_L1_ACTIV+1)
+
+static char *strL1BState[] =
+{
+	"ST_L1_NULL",
+	"ST_L1_WAIT_ACT",
+	"ST_L1_WAIT_DEACT",
+	"ST_L1_ACTIV",
+};
+
+enum {
 	EV_PH_ACTIVATE,
+	EV_PH_DEACTIVATE,
 	EV_RESET_IND,
 	EV_DEACT_CNF,
 	EV_DEACT_IND,
@@ -219,6 +196,7 @@
 static char *strL1Event[] =
 {
 	"EV_PH_ACTIVATE",
+	"EV_PH_DEACTIVATE",
 	"EV_RESET_IND",
 	"EV_DEACT_CNF",
 	"EV_DEACT_IND",
@@ -231,154 +209,30 @@
 	"EV_TIMER3",
 };
 
-/*
- * Find card with given driverId
- */
-static inline struct IsdnCardState
-*hisax_findcard(int driverid)
-{
-	int i;
-
-	for (i = 0; i < nrcards; i++)
-		if (cards[i].cs)
-			if (cards[i].cs->myid == driverid)
-				return (cards[i].cs);
-	return (NULL);
-}
-
-int
-HiSax_readstatus(u_char * buf, int len, int user, int id, int channel)
-{
-	int count;
-	u_char *p;
-	struct IsdnCardState *csta = hisax_findcard(id);
-
-	if (csta) {
-		for (p = buf, count = 0; count < len; p++, count++) {
-			if (user)
-				put_user(*csta->status_read++, p);
-			else
-				*p++ = *csta->status_read++;
-			if (csta->status_read > csta->status_end)
-				csta->status_read = csta->status_buf;
-		}
-		return count;
-	} else {
-		printk(KERN_ERR
-		 "HiSax: if_readstatus called with invalid driverId!\n");
-		return -ENODEV;
-	}
-}
-
-#if ISDN_CTRL_DEBUG
-void
-HiSax_putstatus(struct IsdnCardState *csta, char *buf)
-{
-	long flags;
-	int len, count, i;
-	u_char *p;
-	isdn_ctrl ic;
-
-	save_flags(flags);
-	cli();
-	count = 0;
-	len = strlen(buf);
-
-	if (!csta) {
-		printk(KERN_WARNING "HiSax: No CardStatus for message %s", buf);
-		restore_flags(flags);
-		return;
-	}
-	for (p = buf, i = len; i > 0; i--, p++) {
-		*csta->status_write++ = *p;
-		if (csta->status_write > csta->status_end)
-			csta->status_write = csta->status_buf;
-		count++;
-	}
-	restore_flags(flags);
-	if (count) {
-		ic.command = ISDN_STAT_STAVAIL;
-		ic.driver = csta->myid;
-		ic.arg = count;
-		csta->iif.statcallb(&ic);
-	}
-}
-#else
-#define KDEBUG_DEF
-#include "../kdebug.h"
-
-static int DbgLineNr=0,DbgSequenzNr=1;
-
 void
-HiSax_putstatus(struct IsdnCardState *csta, char *buf)
+debugl1(struct IsdnCardState *cs, char *fmt, ...)
 {
-	char tmp[512];
+	va_list args;
+	char tmp[8];
 	
-	if (DbgLineNr==23)
-		DbgLineNr=0;
-	sprintf(tmp, "%5d %s",DbgSequenzNr++,buf);
-	gput_str(tmp,0,DbgLineNr++);
-}	
-#endif
-
-int
-ll_run(struct IsdnCardState *csta)
-{
-	long flags;
-	isdn_ctrl ic;
-
-	save_flags(flags);
-	cli();
-	ic.driver = csta->myid;
-	ic.command = ISDN_STAT_RUN;
-	csta->iif.statcallb(&ic);
-	restore_flags(flags);
-	return 0;
-}
-
-void
-ll_stop(struct IsdnCardState *csta)
-{
-	isdn_ctrl ic;
-
-	ic.command = ISDN_STAT_STOP;
-	ic.driver = csta->myid;
-	csta->iif.statcallb(&ic);
-	CallcFreeChan(csta);
-}
-
-static void
-ll_unload(struct IsdnCardState *csta)
-{
-	isdn_ctrl ic;
-
-	ic.command = ISDN_STAT_UNLOAD;
-	ic.driver = csta->myid;
-	csta->iif.statcallb(&ic);
-	if (csta->status_buf)
-		kfree(csta->status_buf);
-	csta->status_read = NULL;
-	csta->status_write = NULL;
-	csta->status_end = NULL;
-	kfree(csta->dlogspace);
-}
-
-void
-debugl1(struct IsdnCardState *cs, char *msg)
-{
-	char tmp[1024], tm[32];
-
-	jiftime(tm, jiffies);
-	sprintf(tmp, "%s Card %d %s\n", tm, cs->cardnr + 1, msg);
-	HiSax_putstatus(cs, tmp);
+	va_start(args, fmt);
+	sprintf(tmp, "Card%d ", cs->cardnr + 1);
+	VHiSax_putstatus(cs, tmp, fmt, args);
+	va_end(args);
 }
 
 static void
-l1m_debug(struct FsmInst *fi, char *s)
+l1m_debug(struct FsmInst *fi, char *fmt, ...)
 {
+	va_list args;
 	struct PStack *st = fi->userdata;
+	struct IsdnCardState *cs = st->l1.hardware;
+	char tmp[8];
 	
-	debugl1(st->l1.hardware, s);
+	va_start(args, fmt);
+	sprintf(tmp, "Card%d ", cs->cardnr + 1);
+	VHiSax_putstatus(cs, tmp, fmt, args);
+	va_end(args);
 }
 
 void
@@ -389,9 +243,9 @@
 	st = cs->stlist;
 	while (st) {
 		if (test_and_clear_bit(FLG_L1_ACTIVATING, &st->l1.Flags))
-			st->l1.l1man(st, PH_ACTIVATE_CNF, NULL);
+			st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
 		else
-			st->l1.l1man(st, PH_ACTIVATE_IND, NULL);
+			st->l1.l1l2(st, PH_ACTIVATE | INDICATION, NULL);
 		st = st->next;
 	}
 }
@@ -404,8 +258,8 @@
 	st = cs->stlist;
 	while (st) {
 		if (test_bit(FLG_L1_DBUSY, &cs->HW_Flags))
-			st->l1.l1l2(st, PH_PAUSE_CNF, NULL);
-		st->l1.l1man(st, PH_DEACTIVATE_IND, NULL);
+			st->l1.l1l2(st, PH_PAUSE | CONFIRM, NULL);
+		st->l1.l1l2(st, PH_DEACTIVATE | INDICATION, NULL);
 		st = st->next;
 	}
 	test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags);
@@ -422,7 +276,7 @@
 	stptr = cs->stlist;
 	while (stptr != NULL)
 		if (test_and_clear_bit(FLG_L1_PULL_REQ, &stptr->l1.Flags)) {
-			stptr->l1.l1l2(stptr, PH_PULL_CNF, NULL);
+			stptr->l1.l1l2(stptr, PH_PULL | CONFIRM, NULL);
 			break;
 		} else
 			stptr = stptr->next;
@@ -434,7 +288,6 @@
 	struct sk_buff *skb, *nskb;
 	struct PStack *stptr = cs->stlist;
 	int found, tei, sapi;
-	char tmp[64];
 
 	if (stptr)
 		if (test_bit(FLG_L1_ACTTIMER, &stptr->l1.Flags))
@@ -448,16 +301,15 @@
 		sapi = skb->data[0] >> 2;
 		tei = skb->data[1] >> 1;
 
+		if (cs->debug & DEB_DLOG_HEX)
+			LogFrame(cs, skb->data, skb->len);
+		if (cs->debug & DEB_DLOG_VERBOSE)
+			dlogframe(cs, skb, 1);
 		if (tei == GROUP_TEI) {
-			if (sapi == CTRL_SAPI) {	/* sapi 0 */
-				if (cs->dlogflag) {
-					LogFrame(cs, skb->data, skb->len);
-					dlogframe(cs, skb->data + 3, skb->len - 3,
-						  "Q.931 frame network->user broadcast");
-				}
+			if (sapi == CTRL_SAPI) { /* sapi 0 */
 				while (stptr != NULL) {
 					if ((nskb = skb_clone(skb, GFP_ATOMIC)))
-						stptr->l1.l1l2(stptr, PH_DATA_IND, nskb);
+						stptr->l1.l1l2(stptr, PH_DATA | INDICATION, nskb);
 					else
 						printk(KERN_WARNING "HiSax: isdn broadcast buffer shortage\n");
 					stptr = stptr->next;
@@ -465,37 +317,24 @@
 			} else if (sapi == TEI_SAPI) {
 				while (stptr != NULL) {
 					if ((nskb = skb_clone(skb, GFP_ATOMIC)))
-						stptr->l1.l1tei(stptr, PH_DATA_IND, nskb);
+						stptr->l1.l1tei(stptr, PH_DATA | INDICATION, nskb);
 					else
 						printk(KERN_WARNING "HiSax: tei broadcast buffer shortage\n");
 					stptr = stptr->next;
 				}
 			}
 			dev_kfree_skb(skb);
-		} else if (sapi == CTRL_SAPI) {
+		} else if (sapi == CTRL_SAPI) { /* sapi 0 */
 			found = 0;
 			while (stptr != NULL)
 				if (tei == stptr->l2.tei) {
-					stptr->l1.l1l2(stptr, PH_DATA_IND, skb);
+					stptr->l1.l1l2(stptr, PH_DATA | INDICATION, skb);
 					found = !0;
 					break;
 				} else
 					stptr = stptr->next;
-			if (!found) {
-				/* BD 10.10.95
-				 * Print out D-Channel msg not processed
-				 * by isdn4linux
-				 */
-
-				if ((!(skb->data[0] >> 2)) && (!(skb->data[2] & 0x01))) {
-					sprintf(tmp,
-						"Q.931 frame network->user with tei %d (not for us)",
-						skb->data[1] >> 1);
-					LogFrame(cs, skb->data, skb->len);
-					dlogframe(cs, skb->data + 4, skb->len - 4, tmp);
-				}
+			if (!found)
 				dev_kfree_skb(skb);
-			}
 		}
 	}
 }
@@ -505,14 +344,18 @@
 {
 	struct PStack *st = bcs->st;
 
-	if (test_bit(BC_FLG_BUSY, &bcs->Flag))
+	if (test_bit(BC_FLG_BUSY, &bcs->Flag)) {
+		debugl1(bcs->cs, "BC_BUSY Error");
 		return;
+	}
 
 	if (test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags))
-		st->l1.l1l2(st, PH_PULL_CNF, NULL);
-	if (!test_bit(BC_FLG_ACTIV, &bcs->Flag))
-		if (!test_bit(BC_FLG_BUSY, &bcs->Flag) && (!skb_queue_len(&bcs->squeue)))
-			st->ma.manl1(st, PH_DEACTIVATE_CNF, 0);
+		st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+	if (!test_bit(BC_FLG_ACTIV, &bcs->Flag)) {
+		if (!test_bit(BC_FLG_BUSY, &bcs->Flag) && (!skb_queue_len(&bcs->squeue))) {
+			st->l2.l2l1(st, PH_DEACTIVATE | CONFIRM, NULL);
+		}
+	}
 }
 
 static void
@@ -520,8 +363,12 @@
 {
 	struct sk_buff *skb;
 
+	if (bcs->st->l1.l1m.state == ST_L1_WAIT_ACT) {
+		FsmDelTimer(&bcs->st->l1.timer, 4);
+		FsmEvent(&bcs->st->l1.l1m, EV_TIMER_ACT, NULL);
+	}
 	while ((skb = skb_dequeue(&bcs->rqueue))) {
-		bcs->st->l1.l1l2(bcs->st, PH_DATA_IND, skb);
+		bcs->st->l1.l1l2(bcs->st, PH_DATA | INDICATION, skb);
 	}
 }
 
@@ -581,443 +428,6 @@
 	bcs->Flag = 0;
 }
 
-static void
-closecard(int cardnr)
-{
-	struct IsdnCardState *csta = cards[cardnr].cs;
-	struct sk_buff *skb;
-	
-	if (csta->bcs->BC_Close != NULL) { 
-		csta->bcs->BC_Close(csta->bcs + 1);
-		csta->bcs->BC_Close(csta->bcs);
-	}
-
-	if (csta->rcvbuf) {
-		kfree(csta->rcvbuf);
-		csta->rcvbuf = NULL;
-	}
-	while ((skb = skb_dequeue(&csta->rq))) {
-		dev_kfree_skb(skb);
-	}
-	while ((skb = skb_dequeue(&csta->sq))) {
-		dev_kfree_skb(skb);
-	}
-	if (csta->tx_skb) {
-		dev_kfree_skb(csta->tx_skb);
-		csta->tx_skb = NULL;
-	}
-	if (csta->mon_rx) {
-		kfree(csta->mon_rx);
-		csta->mon_rx = NULL;
-	}
-	if (csta->mon_tx) {
-		kfree(csta->mon_tx);
-		csta->mon_tx = NULL;
-	}
-	csta->cardmsg(csta, CARD_RELEASE, NULL);
-	del_timer(&csta->dbusytimer);
-	ll_unload(csta);
-}
-
-HISAX_INITFUNC(static int init_card(struct IsdnCardState *cs))
-{
-	int irq_cnt, cnt = 3;
-	long flags;
-
-	save_flags(flags);
-	cli();
-	irq_cnt = kstat_irqs(cs->irq);
-	printk(KERN_INFO "%s: IRQ %d count %d\n", CardType[cs->typ], cs->irq,
-		irq_cnt);
-	if (cs->cardmsg(cs, CARD_SETIRQ, NULL)) {
-		printk(KERN_WARNING "HiSax: couldn't get interrupt %d\n",
-			cs->irq);
-		return(1);
-	}
-	while (cnt) {
-		cs->cardmsg(cs, CARD_INIT, NULL);
-		sti();
-		current->state = TASK_INTERRUPTIBLE;
-		/* Timeout 10ms */
-		schedule_timeout((10 * HZ) / 1000);
-		restore_flags(flags);
-		printk(KERN_INFO "%s: IRQ %d count %d\n", CardType[cs->typ],
-			cs->irq, kstat_irqs(cs->irq));
-		if (kstat_irqs(cs->irq) == irq_cnt) {
-			printk(KERN_WARNING
-			       "%s: IRQ(%d) getting no interrupts during init %d\n",
-			       CardType[cs->typ], cs->irq, 4 - cnt);
-			if (cnt == 1) {
-				free_irq(cs->irq, cs);
-				return (2);
-			} else {
-				cs->cardmsg(cs, CARD_RESET, NULL);
-				cnt--;
-			}
-		} else {
-			cs->cardmsg(cs, CARD_TEST, NULL);
-			return(0);
-		}
-	}
-	restore_flags(flags);
-	return(3);
-}
-
-HISAX_INITFUNC(static int
-checkcard(int cardnr, char *id, int *busy_flag))
-{
-	long flags;
-	int ret = 0;
-	struct IsdnCard *card = cards + cardnr;
-	struct IsdnCardState *cs;
-
-	save_flags(flags);
-	cli();
-	if (!(cs = (struct IsdnCardState *)
-	      kmalloc(sizeof(struct IsdnCardState), GFP_ATOMIC))) {
-		printk(KERN_WARNING
-		       "HiSax: No memory for IsdnCardState(card %d)\n",
-		       cardnr + 1);
-		restore_flags(flags);
-		return (0);
-	}
-	card->cs = cs;
-	cs->cardnr = cardnr;
-	cs->debug = L1_DEB_WARN;
-	cs->HW_Flags = 0;
-	cs->busy_flag = busy_flag;
-#if TEI_PER_CARD
-#else
-	test_and_set_bit(FLG_TWO_DCHAN, &cs->HW_Flags);
-#endif
-	cs->protocol = card->protocol;
-
-	if ((card->typ > 0) && (card->typ < 31)) {
-		if (!((1 << card->typ) & SUPORTED_CARDS)) {
-			printk(KERN_WARNING
-			     "HiSax: Support for %s Card not selected\n",
-			       CardType[card->typ]);
-			restore_flags(flags);
-			return (0);
-		}
-	} else {
-		printk(KERN_WARNING
-		       "HiSax: Card Type %d out of range\n",
-		       card->typ);
-		restore_flags(flags);
-		return (0);
-	}
-	if (!(cs->dlogspace = kmalloc(4096, GFP_ATOMIC))) {
-		printk(KERN_WARNING
-		       "HiSax: No memory for dlogspace(card %d)\n",
-		       cardnr + 1);
-		restore_flags(flags);
-		return (0);
-	}
-	if (!(cs->status_buf = kmalloc(HISAX_STATUS_BUFSIZE, GFP_ATOMIC))) {
-		printk(KERN_WARNING
-		       "HiSax: No memory for status_buf(card %d)\n",
-		       cardnr + 1);
-		kfree(cs->dlogspace);
-		restore_flags(flags);
-		return (0);
-	}
-	cs->stlist = NULL;
-	cs->dlogflag = 0;
-	cs->mon_tx = NULL;
-	cs->mon_rx = NULL;
-	cs->status_read = cs->status_buf;
-	cs->status_write = cs->status_buf;
-	cs->status_end = cs->status_buf + HISAX_STATUS_BUFSIZE - 1;
-	cs->typ = card->typ;
-	strcpy(cs->iif.id, id);
-	cs->iif.channels = 2;
-	cs->iif.maxbufsize = MAX_DATA_SIZE;
-	cs->iif.hl_hdrlen = MAX_HEADER_LEN;
-	cs->iif.features =
-	    ISDN_FEATURE_L2_X75I |
-	    ISDN_FEATURE_L2_HDLC |
-	    ISDN_FEATURE_L2_TRANS |
-	    ISDN_FEATURE_L3_TRANS |
-#ifdef	CONFIG_HISAX_1TR6
-	    ISDN_FEATURE_P_1TR6 |
-#endif
-#ifdef	CONFIG_HISAX_EURO
-	    ISDN_FEATURE_P_EURO |
-#endif
-#ifdef        CONFIG_HISAX_NI1
-	    ISDN_FEATURE_P_NI1 |
-#endif
-	    0;
-
-	cs->iif.command = HiSax_command;
-	cs->iif.writecmd = NULL;
-	cs->iif.writebuf_skb = HiSax_writebuf_skb;
-	cs->iif.readstat = HiSax_readstatus;
-	register_isdn(&cs->iif);
-	cs->myid = cs->iif.channels;
-	printk(KERN_INFO
-	       "HiSax: Card %d Protocol %s Id=%s (%d)\n", cardnr + 1,
-	       (card->protocol == ISDN_PTYPE_1TR6) ? "1TR6" :
-	       (card->protocol == ISDN_PTYPE_EURO) ? "EDSS1" :
-	       (card->protocol == ISDN_PTYPE_LEASED) ? "LEASED" :
-	       (card->protocol == ISDN_PTYPE_NI1) ? "NI1" :
-	       "NONE", cs->iif.id, cs->myid);
-	switch (card->typ) {
-#if CARD_TELES0
-		case ISDN_CTYPE_16_0:
-		case ISDN_CTYPE_8_0:
-			ret = setup_teles0(card);
-			break;
-#endif
-#if CARD_TELES3
-		case ISDN_CTYPE_16_3:
-		case ISDN_CTYPE_PNP:
-		case ISDN_CTYPE_TELESPCMCIA:
-		case ISDN_CTYPE_COMPAQ_ISA:
-			ret = setup_teles3(card);
-			break;
-#endif
-#if CARD_AVM_A1
-		case ISDN_CTYPE_A1:
-			ret = setup_avm_a1(card);
-			break;
-#endif
-#if CARD_ELSA
-		case ISDN_CTYPE_ELSA:
-		case ISDN_CTYPE_ELSA_PNP:
-		case ISDN_CTYPE_ELSA_PCMCIA:
-		case ISDN_CTYPE_ELSA_PCI:
-			ret = setup_elsa(card);
-			break;
-#endif
-#if CARD_IX1MICROR2
-		case ISDN_CTYPE_IX1MICROR2:
-			ret = setup_ix1micro(card);
-			break;
-#endif
-#if CARD_DIEHLDIVA
-		case ISDN_CTYPE_DIEHLDIVA:
-			ret = setup_diva(card);
-			break;
-#endif
-#if CARD_ASUSCOM
-		case ISDN_CTYPE_ASUSCOM:
-			ret = setup_asuscom(card);
-			break;
-#endif
-#if CARD_TELEINT
-		case ISDN_CTYPE_TELEINT:
-			ret = setup_TeleInt(card);
-			break;
-#endif
-#if CARD_SEDLBAUER
-		case ISDN_CTYPE_SEDLBAUER:
-		case ISDN_CTYPE_SEDLBAUER_PCMCIA:
-			ret = setup_sedlbauer(card);
-			break;
-#endif
-#if CARD_SPORTSTER
-		case ISDN_CTYPE_SPORTSTER:
-			ret = setup_sportster(card);
-			break;
-#endif
-#if CARD_MIC
-		case ISDN_CTYPE_MIC:
-			ret = setup_mic(card);
-			break;
-#endif
-#if CARD_NETJET
-		case ISDN_CTYPE_NETJET:
-			ret = setup_netjet(card);
-			break;
-#endif
-#if CARD_TELES3C
-		case ISDN_CTYPE_TELES3C:
-			ret = setup_t163c(card);
-			break;
-#endif
-#if CARD_NICCY
-		case ISDN_CTYPE_NICCY:
-			ret = setup_niccy(card);
-			break;
-#endif
-#if CARD_AMD7930 || CARD_DBRI
-		case ISDN_CTYPE_AMD7930:
-		case ISDN_CTYPE_DBRI:
-			ret = setup_foreign(card);
-			break;
-#endif
-		default:
-			printk(KERN_WARNING "HiSax: Unknown Card Typ %d\n",
-			       card->typ);
-			ll_unload(cs);
-			restore_flags(flags);
-			return (0);
-	}
-	if (!ret) {
-		ll_unload(cs);
-		restore_flags(flags);
-		return (0);
-	}
-	if (!(cs->rcvbuf = kmalloc(MAX_DFRAME_LEN, GFP_ATOMIC))) {
-		printk(KERN_WARNING
-		       "HiSax: No memory for isac rcvbuf\n");
-		return (1);
-	}
-	cs->rcvidx = 0;
-	cs->tx_skb = NULL;
-	cs->tx_cnt = 0;
-	cs->event = 0;
-	cs->tqueue.next = 0;
-	cs->tqueue.sync = 0;
-	cs->tqueue.data = cs;
-
-	skb_queue_head_init(&cs->rq);
-	skb_queue_head_init(&cs->sq);
-
-	init_bcstate(cs, 0);
-	init_bcstate(cs, 1);
-	ret = init_card(cs);
-	if (ret) {
-		closecard(cardnr);
-		restore_flags(flags);
-		return (0);
-	}
-	init_tei(cs, cs->protocol);
-	CallcNewChan(cs);
-	ll_run(cs);
-	cs->l1cmd(cs, PH_RESET_REQ, NULL);
-	restore_flags(flags);
-	return (1);
-}
-
-HISAX_INITFUNC(void
-HiSax_shiftcards(int idx))
-{
-	int i;
-
-	for (i = idx; i < 15; i++)
-		memcpy(&cards[i], &cards[i + 1], sizeof(cards[i]));
-}
-
-HISAX_INITFUNC(int
-HiSax_inithardware(int *busy_flag))
-{
-	int foundcards = 0;
-	int i = 0;
-	int t = ',';
-	int flg = 0;
-	char *id;
-	char *next_id = HiSax_id;
-	char ids[20];
-
-	if (strchr(HiSax_id, ','))
-		t = ',';
-	else if (strchr(HiSax_id, '%'))
-		t = '%';
-
-	while (i < nrcards) {
-		if (cards[i].typ < 1)
-			break;
-		id = next_id;
-		if ((next_id = strchr(id, t))) {
-			*next_id++ = 0;
-			strcpy(ids, id);
-			flg = i + 1;
-		} else {
-			next_id = id;
-			if (flg >= i)
-				strcpy(ids, id);
-			else
-				sprintf(ids, "%s%d", id, i);
-		}
-		if (checkcard(i, ids, busy_flag)) {
-			foundcards++;
-			i++;
-		} else {
-			printk(KERN_WARNING "HiSax: Card %s not installed !\n",
-			       CardType[cards[i].typ]);
-			if (cards[i].cs)
-				kfree((void *) cards[i].cs);
-			cards[i].cs = NULL;
-			HiSax_shiftcards(i);
-		}
-	}
-	return foundcards;
-}
-
-void
-HiSax_closehardware(void)
-{
-	int i;
-	long flags;
-
-	save_flags(flags);
-	cli();
-	for (i = 0; i < nrcards; i++)
-		if (cards[i].cs) {
-			ll_stop(cards[i].cs);
-			release_tei(cards[i].cs);
-			closecard(i);
-			free_irq(cards[i].cs->irq, cards[i].cs);
-			kfree((void *) cards[i].cs);
-			cards[i].cs = NULL;
-		}
-	Isdnl1Free();
-	TeiFree();
-	Isdnl2Free();
-	CallcFree();
-	restore_flags(flags);
-}
-
-void
-HiSax_reportcard(int cardnr)
-{
-	struct IsdnCardState *cs = cards[cardnr].cs;
-	struct PStack *stptr;
-	struct l3_process *pc;
-	int j, i = 1;
-
-	printk(KERN_DEBUG "HiSax: reportcard No %d\n", cardnr + 1);
-	printk(KERN_DEBUG "HiSax: Type %s\n", CardType[cs->typ]);
-	printk(KERN_DEBUG "HiSax: debuglevel %x\n", cs->debug);
-	printk(KERN_DEBUG "HiSax: HiSax_reportcard address 0x%lX\n",
-	       (ulong) & HiSax_reportcard);
-	printk(KERN_DEBUG "HiSax: cs 0x%lX\n", (ulong) cs);
-	printk(KERN_DEBUG "HiSax: cs stl 0x%lX\n", (ulong) & (cs->stlist));
-	stptr = cs->stlist;
-	while (stptr != NULL) {
-		printk(KERN_DEBUG "HiSax: dst%d 0x%lX\n", i, (ulong) stptr);
-		printk(KERN_DEBUG "HiSax: dst%d stp 0x%lX\n", i, (ulong) stptr->l1.stlistp);
-		printk(KERN_DEBUG "HiSax:   tei %d sapi %d\n",
-		       stptr->l2.tei, stptr->l2.sap);
-		printk(KERN_DEBUG "HiSax:      man 0x%lX\n", (ulong) stptr->ma.layer);
-		pc = stptr->l3.proc;
-		while (pc) {
-			printk(KERN_DEBUG "HiSax: l3proc %x 0x%lX\n", pc->callref,
-			       (ulong) pc);
-			printk(KERN_DEBUG "HiSax:    state %d  st 0x%lX chan 0x%lX\n",
-			    pc->state, (ulong) pc->st, (ulong) pc->chan);
-			pc = pc->next;
-		}
-		stptr = stptr->next;
-		i++;
-	}
-	for (j = 0; j < 2; j++) {
-		printk(KERN_DEBUG "HiSax: ch%d 0x%lX\n", j,
-		       (ulong) & cs->channel[j]);
-		stptr = cs->channel[j].b_st;
-		i = 1;
-		while (stptr != NULL) {
-			printk(KERN_DEBUG "HiSax:  b_st%d 0x%lX\n", i, (ulong) stptr);
-			printk(KERN_DEBUG "HiSax:    man 0x%lX\n", (ulong) stptr->ma.layer);
-			stptr = stptr->next;
-			i++;
-		}
-	}
-}
-
 #ifdef L2FRAME_DEBUG		/* psa */
 
 char *
@@ -1052,7 +462,7 @@
 	}
 }
 
-static char tmp[20];
+static char tmpdeb[32];
 
 char *
 l2frames(u_char * ptr)
@@ -1061,7 +471,7 @@
 		case 1:
 		case 5:
 		case 9:
-			sprintf(tmp, "%s[%d](nr %d)", l2cmd(ptr[2]), ptr[3] & 1, ptr[3] >> 1);
+			sprintf(tmpdeb, "%s[%d](nr %d)", l2cmd(ptr[2]), ptr[3] & 1, ptr[3] >> 1);
 			break;
 		case 0x6f:
 		case 0x0f:
@@ -1070,36 +480,33 @@
 		case 0x63:
 		case 0x87:
 		case 0xaf:
-			sprintf(tmp, "%s[%d]", l2cmd(ptr[2]), (ptr[2] & 0x10) >> 4);
+			sprintf(tmpdeb, "%s[%d]", l2cmd(ptr[2]), (ptr[2] & 0x10) >> 4);
 			break;
 		default:
 			if (!(ptr[2] & 1)) {
-				sprintf(tmp, "I[%d](ns %d, nr %d)", ptr[3] & 1, ptr[2] >> 1, ptr[3] >> 1);
+				sprintf(tmpdeb, "I[%d](ns %d, nr %d)", ptr[3] & 1, ptr[2] >> 1, ptr[3] >> 1);
 				break;
 			} else
 				return "invalid command";
 	}
 
 
-	return tmp;
+	return tmpdeb;
 }
 
 void
 Logl2Frame(struct IsdnCardState *cs, struct sk_buff *skb, char *buf, int dir)
 {
-	char tmp[132];
 	u_char *ptr;
 
 	ptr = skb->data;
 
 	if (ptr[0] & 1 || !(ptr[1] & 1))
-		debugl1(cs, "Addres not LAPD");
-	else {
-		sprintf(tmp, "%s %s: %s%c (sapi %d, tei %d)",
+		debugl1(cs, "Address not LAPD");
+	else
+		debugl1(cs, "%s %s: %s%c (sapi %d, tei %d)",
 			(dir ? "<-" : "->"), buf, l2frames(ptr),
 			((ptr[0] & 2) >> 1) == dir ? 'C' : 'R', ptr[0] >> 2, ptr[1] >> 1);
-		debugl1(cs, tmp);
-	}
 }
 #endif
 
@@ -1113,11 +520,10 @@
 l1_deact_cnf(struct FsmInst *fi, int event, void *arg)
 {
 	struct PStack *st = fi->userdata;
-	struct IsdnCardState *cs = st->l1.hardware;
 
 	FsmChangeState(fi, ST_L1_F3);
 	if (test_bit(FLG_L1_ACTIVATING, &st->l1.Flags))
-		cs->l1cmd(cs, PH_ENABLE_REQ, NULL);
+		st->l1.l1hw(st, HW_ENABLE | REQUEST, NULL);
 }
 
 static void
@@ -1126,24 +532,23 @@
 	struct PStack *st = fi->userdata;
 
 	FsmChangeState(fi, ST_L1_F3);
-	if (!test_bit(FLG_L1_T3RUN, &st->l1.Flags)) {
+//	if (!test_bit(FLG_L1_T3RUN, &st->l1.Flags)) {
 		FsmDelTimer(&st->l1.timer, 1);
 		FsmAddTimer(&st->l1.timer, 550, EV_TIMER_DEACT, NULL, 2);
 		test_and_set_bit(FLG_L1_DEACTTIMER, &st->l1.Flags);
-	}
+//	}
 }
 
 static void
 l1_power_up(struct FsmInst *fi, int event, void *arg)
 {
 	struct PStack *st = fi->userdata;
-	struct IsdnCardState *cs = st->l1.hardware;
 
 	if (test_bit(FLG_L1_ACTIVATING, &st->l1.Flags)) {
 		FsmChangeState(fi, ST_L1_F4);
-		cs->l1cmd(cs, PH_INFO3_REQ, NULL);
+		st->l1.l1hw(st, HW_INFO3 | REQUEST, NULL);
 		FsmDelTimer(&st->l1.timer, 1);
-		FsmAddTimer(&st->l1.timer, TIMER3_VALUE * HZ, EV_TIMER3, NULL, 2);
+		FsmAddTimer(&st->l1.timer, TIMER3_VALUE, EV_TIMER3, NULL, 2);
 		test_and_set_bit(FLG_L1_T3RUN, &st->l1.Flags);
 	} else
 		FsmChangeState(fi, ST_L1_F3);
@@ -1165,20 +570,18 @@
 l1_info2_ind(struct FsmInst *fi, int event, void *arg)
 {
 	struct PStack *st = fi->userdata;
-	struct IsdnCardState *cs = st->l1.hardware;
 
 	FsmChangeState(fi, ST_L1_F6);
-	cs->l1cmd(cs, PH_INFO3_REQ, NULL);
+	st->l1.l1hw(st, HW_INFO3 | REQUEST, NULL);
 }
 
 static void
 l1_info4_ind(struct FsmInst *fi, int event, void *arg)
 {
 	struct PStack *st = fi->userdata;
-	struct IsdnCardState *cs = st->l1.hardware;
 
 	FsmChangeState(fi, ST_L1_F7);
-	cs->l1cmd(cs, PH_INFO3_REQ, NULL);
+	st->l1.l1hw(st, HW_INFO3 | REQUEST, NULL);
 	if (test_and_clear_bit(FLG_L1_DEACTTIMER, &st->l1.Flags))
 		FsmDelTimer(&st->l1.timer, 4);
 	if (!test_bit(FLG_L1_ACTIVATED, &st->l1.Flags)) {
@@ -1193,50 +596,61 @@
 l1_timer3(struct FsmInst *fi, int event, void *arg)
 {
 	struct PStack *st = fi->userdata;
-	struct IsdnCardState *cs = st->l1.hardware;
-	
+
 	test_and_clear_bit(FLG_L1_T3RUN, &st->l1.Flags);	
-        if (test_and_clear_bit(FLG_L1_ACTIVATING, &st->l1.Flags))
-        	L1deactivated(cs);
-        if (st->l1.l1m.state != ST_L1_F6)
-        	FsmChangeState(fi, ST_L1_F3);
+	if (test_and_clear_bit(FLG_L1_ACTIVATING, &st->l1.Flags))
+		L1deactivated(st->l1.hardware);
+	if (st->l1.l1m.state != ST_L1_F6) {
+		FsmChangeState(fi, ST_L1_F3);
+		st->l1.l1hw(st, HW_ENABLE | REQUEST, NULL);
+	}
 }
 
 static void
 l1_timer_act(struct FsmInst *fi, int event, void *arg)
 {
 	struct PStack *st = fi->userdata;
-	struct IsdnCardState *cs = st->l1.hardware;
 	
 	test_and_clear_bit(FLG_L1_ACTTIMER, &st->l1.Flags);
 	test_and_set_bit(FLG_L1_ACTIVATED, &st->l1.Flags);
-	L1activated(cs);
+	L1activated(st->l1.hardware);
 }
 
 static void
 l1_timer_deact(struct FsmInst *fi, int event, void *arg)
 {
 	struct PStack *st = fi->userdata;
-	struct IsdnCardState *cs = st->l1.hardware;
 	
 	test_and_clear_bit(FLG_L1_DEACTTIMER, &st->l1.Flags);
 	test_and_clear_bit(FLG_L1_ACTIVATED, &st->l1.Flags);
-	L1deactivated(cs);
-	cs->l1cmd(cs, PH_DEACT_ACK, NULL);
+	L1deactivated(st->l1.hardware);
+	st->l1.l1hw(st, HW_DEACTIVATE | RESPONSE, NULL);
 }
 
 static void
 l1_activate(struct FsmInst *fi, int event, void *arg)
 {
 	struct PStack *st = fi->userdata;
-	struct IsdnCardState *cs = st->l1.hardware;
                 
-	cs->l1cmd(cs, PH_RESET_REQ, NULL);
+	st->l1.l1hw(st, HW_RESET | REQUEST, NULL);
+}
+
+static void
+l1_activate_no(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	if ((!test_bit(FLG_L1_DEACTTIMER, &st->l1.Flags)) && (!test_bit(FLG_L1_T3RUN, &st->l1.Flags))) {
+		test_and_clear_bit(FLG_L1_ACTIVATING, &st->l1.Flags);
+		L1deactivated(st->l1.hardware);
+	}
 }
 
-static struct FsmNode L1FnList[] HISAX_INITDATA =
+static struct FsmNode L1DFnList[] HISAX_INITDATA =
 {
 	{ST_L1_F3, EV_PH_ACTIVATE, l1_activate},
+	{ST_L1_F6, EV_PH_ACTIVATE, l1_activate_no},
+	{ST_L1_F8, EV_PH_ACTIVATE, l1_activate_no},
 	{ST_L1_F3, EV_RESET_IND, l1_reset},
 	{ST_L1_F4, EV_RESET_IND, l1_reset},
 	{ST_L1_F5, EV_RESET_IND, l1_reset},
@@ -1280,86 +694,159 @@
 	{ST_L1_F8, EV_TIMER_DEACT, l1_timer_deact},
 };
 
-#define L1_FN_COUNT (sizeof(L1FnList)/sizeof(struct FsmNode))
+#define L1D_FN_COUNT (sizeof(L1DFnList)/sizeof(struct FsmNode))
+
+static void
+l1b_activate(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	FsmChangeState(fi, ST_L1_WAIT_ACT);
+	FsmAddTimer(&st->l1.timer, st->l1.delay, EV_TIMER_ACT, NULL, 2);
+}
+
+static void
+l1b_deactivate(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	FsmChangeState(fi, ST_L1_WAIT_DEACT);
+	FsmAddTimer(&st->l1.timer, 10, EV_TIMER_DEACT, NULL, 2);
+}
+
+static void
+l1b_timer_act(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	FsmChangeState(fi, ST_L1_ACTIV);
+	st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
+}
+
+static void
+l1b_timer_deact(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	FsmChangeState(fi, ST_L1_NULL);
+	st->l2.l2l1(st, PH_DEACTIVATE | CONFIRM, NULL);
+}
+
+static struct FsmNode L1BFnList[] HISAX_INITDATA =
+{
+	{ST_L1_NULL, EV_PH_ACTIVATE, l1b_activate},
+	{ST_L1_WAIT_ACT, EV_TIMER_ACT, l1b_timer_act},
+	{ST_L1_ACTIV, EV_PH_DEACTIVATE, l1b_deactivate},
+	{ST_L1_WAIT_DEACT, EV_TIMER_DEACT, l1b_timer_deact},
+};
+
+#define L1B_FN_COUNT (sizeof(L1BFnList)/sizeof(struct FsmNode))
 
 HISAX_INITFUNC(void Isdnl1New(void))
 {
-	l1fsm.state_count = L1_STATE_COUNT;
-	l1fsm.event_count = L1_EVENT_COUNT;
-	l1fsm.strEvent = strL1Event;
-	l1fsm.strState = strL1State;
-	FsmNew(&l1fsm, L1FnList, L1_FN_COUNT);
+	l1fsm_d.state_count = L1D_STATE_COUNT;
+	l1fsm_d.event_count = L1_EVENT_COUNT;
+	l1fsm_d.strEvent = strL1Event;
+	l1fsm_d.strState = strL1DState;
+	FsmNew(&l1fsm_d, L1DFnList, L1D_FN_COUNT);
+	l1fsm_b.state_count = L1B_STATE_COUNT;
+	l1fsm_b.event_count = L1_EVENT_COUNT;
+	l1fsm_b.strEvent = strL1Event;
+	l1fsm_b.strState = strL1BState;
+	FsmNew(&l1fsm_b, L1BFnList, L1B_FN_COUNT);
 }
 
 void Isdnl1Free(void)
 {
-	FsmFree(&l1fsm);
+	FsmFree(&l1fsm_d);
+	FsmFree(&l1fsm_b);
 }
 
 static void
-dch_manl1(struct PStack *st, int pr,
-	   void *arg)
+dch_l2l1(struct PStack *st, int pr, void *arg)
 {
 	struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
-	char tmp[32];
 
 	switch (pr) {
-		case PH_ACTIVATE_REQ:
-			if (cs->debug) {
-				sprintf(tmp, "PH_ACTIVATE_REQ %s",
-					strL1State[st->l1.l1m.state]);
-				debugl1(cs, tmp);
-			}
+		case (PH_DATA | REQUEST):
+		case (PH_PULL | REQUEST):
+		case (PH_PULL |INDICATION):
+			st->l1.l1hw(st, pr, arg);
+			break;
+		case (PH_ACTIVATE | REQUEST):
+			if (cs->debug)
+				debugl1(cs, "PH_ACTIVATE_REQ %s",
+					strL1DState[st->l1.l1m.state]);
 			if (test_bit(FLG_L1_ACTIVATED, &st->l1.Flags))
-				st->l1.l1man(st, PH_ACTIVATE_CNF, NULL);
+				st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
 			else {
 				test_and_set_bit(FLG_L1_ACTIVATING, &st->l1.Flags);
 				FsmEvent(&st->l1.l1m, EV_PH_ACTIVATE, arg);
 			}
 			break;
-		case PH_DEACTIVATE_REQ:
-			if (cs->debug) {
-				sprintf(tmp, "PH_DEACTIVATE_REQ %s",
-					strL1State[st->l1.l1m.state]);
-				debugl1(cs, tmp);
-			}
-			break;
-		case PH_TESTLOOP_REQ:
-			if (1 & (int) arg)
+		case (PH_TESTLOOP | REQUEST):
+			if (1 & (long) arg)
 				debugl1(cs, "PH_TEST_LOOP B1");
-			if (2 & (int) arg)
+			if (2 & (long) arg)
 				debugl1(cs, "PH_TEST_LOOP B2");
-			if (!(3 & (int) arg))
+			if (!(3 & (long) arg))
 				debugl1(cs, "PH_TEST_LOOP DISABLED");
-			cs->l1cmd(cs, PH_TESTLOOP_REQ, arg);
-			break;
-		case PH_RESET_IND:
-			FsmEvent(&st->l1.l1m, EV_RESET_IND, arg);
-			break;
-		case PH_DEACT_CNF:
-			FsmEvent(&st->l1.l1m, EV_DEACT_CNF, arg);
+			st->l1.l1hw(st, HW_TESTLOOP | REQUEST, arg);
 			break;
-		case PH_DEACT_IND:
-			FsmEvent(&st->l1.l1m, EV_DEACT_IND, arg);
-			break;
-		case PH_POWERUP_CNF:
-			FsmEvent(&st->l1.l1m, EV_POWER_UP, arg);
-			break;
-		case PH_RSYNC_IND:
-			FsmEvent(&st->l1.l1m, EV_RSYNC_IND, arg);
-			break;
-		case PH_INFO2_IND:
-			FsmEvent(&st->l1.l1m, EV_INFO2_IND, arg);
+		default:
+			if (cs->debug)
+				debugl1(cs, "dch_l2l1 msg %04X unhandled", pr);
 			break;
-		case PH_I4_P8_IND:
-		case PH_I4_P10_IND:
-			FsmEvent(&st->l1.l1m, EV_INFO4_IND, arg);
+	}
+}
+
+void
+l1_msg(struct IsdnCardState *cs, int pr, void *arg) {
+	struct PStack *st;
+
+	st = cs->stlist;
+	
+	while (st) {
+		switch(pr) {
+			case (HW_RESET | INDICATION):
+				FsmEvent(&st->l1.l1m, EV_RESET_IND, arg);
+				break;
+			case (HW_DEACTIVATE | CONFIRM):
+				FsmEvent(&st->l1.l1m, EV_DEACT_CNF, arg);
+				break;
+			case (HW_DEACTIVATE | INDICATION):
+				FsmEvent(&st->l1.l1m, EV_DEACT_IND, arg);
+				break;
+			case (HW_POWERUP | CONFIRM):
+				FsmEvent(&st->l1.l1m, EV_POWER_UP, arg);
+				break;
+			case (HW_RSYNC | INDICATION):
+				FsmEvent(&st->l1.l1m, EV_RSYNC_IND, arg);
+				break;
+			case (HW_INFO2 | INDICATION):
+				FsmEvent(&st->l1.l1m, EV_INFO2_IND, arg);
+				break;
+			case (HW_INFO4_P8 | INDICATION):
+			case (HW_INFO4_P10 | INDICATION):
+				FsmEvent(&st->l1.l1m, EV_INFO4_IND, arg);
+				break;
+			default:
+				if (cs->debug)
+					debugl1(cs, "l1msg %04X unhandled", pr);
+				break;
+		}
+		st = st->next;
+	}
+}
+
+void
+l1_msg_b(struct PStack *st, int pr, void *arg) {
+	switch(pr) {
+		case (PH_ACTIVATE | REQUEST):
+			FsmEvent(&st->l1.l1m, EV_PH_ACTIVATE, NULL);
 			break;
-		default:
-			if (cs->debug) {
-				sprintf(tmp, "dch_manl1 msg %04X unhandled", pr);
-				debugl1(cs, tmp);
-			}
+		case (PH_DEACTIVATE | REQUEST):
+			FsmEvent(&st->l1.l1m, EV_PH_DEACTIVATE, NULL);
 			break;
 	}
 }
@@ -1369,7 +856,7 @@
 {
 	st->l1.hardware = cs;
 	st->protocol = cs->protocol;
-	st->l1.l1m.fsm = &l1fsm;
+	st->l1.l1m.fsm = &l1fsm_d;
 	st->l1.l1m.state = ST_L1_F3;
 	st->l1.l1m.debug = cs->debug;
 	st->l1.l1m.userdata = st;
@@ -1379,7 +866,22 @@
 	setstack_tei(st);
 	setstack_manager(st);
 	st->l1.stlistp = &(cs->stlist);
-	st->ma.manl1 = dch_manl1;
+	st->l2.l2l1  = dch_l2l1;
 	st->l1.Flags = 0;
 	cs->setstack_d(st, cs);
+}
+
+void
+setstack_l1_B(struct PStack *st)
+{
+	struct IsdnCardState *cs = st->l1.hardware;
+
+	st->l1.l1m.fsm = &l1fsm_b;
+	st->l1.l1m.state = ST_L1_NULL;
+	st->l1.l1m.debug = cs->debug;
+	st->l1.l1m.userdata = st;
+	st->l1.l1m.userint = 0;
+	st->l1.l1m.printdebug = l1m_debug;
+	st->l1.Flags = 0;
+	FsmInitTimer(&st->l1.l1m, &st->l1.timer);
 }
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/isdnl1.h linux/drivers/isdn/hisax/isdnl1.h
--- v2.3.3/linux/drivers/isdn/hisax/isdnl1.h	Wed Apr  1 16:20:58 1998
+++ linux/drivers/isdn/hisax/isdnl1.h	Sun May 23 10:03:41 1999
@@ -1,6 +1,16 @@
-/* $Id: isdnl1.h,v 2.5 1998/02/02 13:36:58 keil Exp $
+/* $Id: isdnl1.h,v 2.8 1998/11/15 23:54:59 keil Exp $
 
  * $Log: isdnl1.h,v $
+ * Revision 2.8  1998/11/15 23:54:59  keil
+ * changes from 2.0
+ *
+ * Revision 2.7  1998/09/30 22:21:55  keil
+ * cosmetics
+ *
+ * Revision 2.6  1998/05/25 12:58:06  keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
  * Revision 2.5  1998/02/02 13:36:58  keil
  * more debug
  *
@@ -22,21 +32,6 @@
  *
  */
 
-
-#define L2FRAME_DEBUG
-
-/* DEBUG Level */
-
-#define	L1_DEB_WARN		0x01
-#define	L1_DEB_INTSTAT		0x02
-#define	L1_DEB_ISAC		0x04
-#define	L1_DEB_ISAC_FIFO	0x08
-#define	L1_DEB_HSCX		0x10
-#define	L1_DEB_HSCX_FIFO	0x20
-#define	L1_DEB_LAPD	        0x40
-#define	L1_DEB_IPAC	        0x80
-#define	L1_DEB_RECEIVE_FRAME    0x100
-
 #define D_RCVBUFREADY	0
 #define D_XMTBUFREADY	1
 #define D_L1STATECHANGE	2
@@ -49,11 +44,12 @@
 #define B_RCVBUFREADY 0
 #define B_XMTBUFREADY 1
 
-extern void debugl1(struct IsdnCardState *sp, char *msg);
+extern void debugl1(struct IsdnCardState *cs, char *fmt, ...);
 extern void DChannel_proc_xmt(struct IsdnCardState *cs);
 extern void DChannel_proc_rcv(struct IsdnCardState *cs);
-
+extern void l1_msg(struct IsdnCardState *cs, int pr, void *arg);
+extern void l1_msg_b(struct PStack *st, int pr, void *arg);
 
 #ifdef L2FRAME_DEBUG
-extern void Logl2Frame(struct IsdnCardState *sp, struct sk_buff *skb, char *buf, int dir);
+extern void Logl2Frame(struct IsdnCardState *cs, struct sk_buff *skb, char *buf, int dir);
 #endif
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/isdnl2.c linux/drivers/isdn/hisax/isdnl2.c
--- v2.3.3/linux/drivers/isdn/hisax/isdnl2.c	Thu May 14 18:43:55 1998
+++ linux/drivers/isdn/hisax/isdnl2.c	Sun May 23 10:03:41 1999
@@ -1,12 +1,42 @@
-/* $Id: isdnl2.c,v 2.7 1998/02/12 23:07:47 keil Exp $
+/* $Id: isdnl2.c,v 2.16 1998/11/15 23:55:01 keil Exp $
 
- * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author       Karsten Keil (keil@isdn4linux.de)
  *              based on the teles driver from Jan den Ouden
  *
+ *		This file is (c) under GNU PUBLIC LICENSE
+ *		For changes and modifications please read
+ *		../../../Documentation/isdn/HiSax.cert
+ *
  * Thanks to    Jan den Ouden
  *              Fritz Elfert
  *
  * $Log: isdnl2.c,v $
+ * Revision 2.16  1998/11/15 23:55:01  keil
+ * changes from 2.0
+ *
+ * Revision 2.15  1998/08/13 23:36:42  keil
+ * HiSax 3.1 - don't work stable with current LinkLevel
+ *
+ * Revision 2.14  1998/06/19 15:19:18  keil
+ * fix LAPB tx_cnt for none I-frames
+ *
+ * Revision 2.13  1998/06/18 23:17:20  keil
+ * LAPB bugfix
+ *
+ * Revision 2.12  1998/05/25 14:10:12  keil
+ * HiSax 3.0
+ * X.75 and leased are working again.
+ *
+ * Revision 2.11  1998/05/25 12:58:08  keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
+ * Revision 2.9  1998/04/10 10:35:30  paul
+ * fixed (silly?) warnings from egcs on Alpha.
+ *
+ * Revision 2.8  1998/03/07 22:57:04  tsbogend
+ * made HiSax working on Linux/Alpha
+ *
  * Revision 2.7  1998/02/12 23:07:47  keil
  * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
  *
@@ -25,7 +55,7 @@
  * Old stuff is still in the separate branch.
  *
  * Revision 2.2  1997/07/31 11:49:05  keil
- * Eroor handling for no TEI assign
+ * Error handling for no TEI assign
  *
  * Revision 2.1  1997/07/27 21:34:38  keil
  * cosmetics
@@ -41,9 +71,9 @@
 #include "hisax.h"
 #include "isdnl2.h"
 
-const char *l2_revision = "$Revision: 2.7 $";
+const char *l2_revision = "$Revision: 2.16 $";
 
-static void l2m_debug(struct FsmInst *fi, char *s);
+static void l2m_debug(struct FsmInst *fi, char *fmt, ...);
 
 static
 struct Fsm l2fsm =
@@ -91,7 +121,6 @@
 	EV_L2_MDL_ASSIGN,
 	EV_L2_MDL_REMOVE,
 	EV_L2_MDL_ERROR,
-	EV_L2_MDL_NOTEIPROC,
 	EV_L1_DEACTIVATE,
 	EV_L2_T200,
 	EV_L2_T203,
@@ -117,7 +146,6 @@
 	"EV_L2_MDL_ASSIGN",
 	"EV_L2_MDL_REMOVE",
 	"EV_L2_MDL_ERROR",
-	"EV_L2_MDL_NOTEIPROC",
 	"EV_L1_DEACTIVATE",
 	"EV_L2_T200",
 	"EV_L2_T203",
@@ -161,26 +189,6 @@
 	return ((p1 < st->l2.window) && !test_bit(FLG_PEER_BUSY, &st->l2.flag));
 }
 
-static void
-discard_i_queue(struct PStack *st)
-{
-	struct sk_buff *skb;
-
-	while ((skb = skb_dequeue(&st->l2.i_queue))) {
-		dev_kfree_skb(skb);
-	}
-}
-
-static void
-discard_ui_queue(struct PStack *st)
-{
-	struct sk_buff *skb;
-
-	while ((skb = skb_dequeue(&st->l2.ui_queue))) {
-		dev_kfree_skb(skb);
-	}
-}
-
 inline void
 clear_exception(struct Layer2 *l2)
 {
@@ -224,20 +232,17 @@
 	}
 }
 
-static void
-enqueue_ui(struct PStack *st,
-	   struct sk_buff *skb)
-{
-	st->l2.l2l1(st, PH_DATA_REQ, skb);
-}
-
-static void
+inline static void
 enqueue_super(struct PStack *st,
 	      struct sk_buff *skb)
 {
-	st->l2.l2l1(st, PH_DATA_REQ, skb);
+	if (test_bit(FLG_LAPB, &st->l2.flag))
+		st->l1.bcs->tx_cnt += skb->len;
+	st->l2.l2l1(st, PH_DATA | REQUEST, skb);
 }
 
+#define enqueue_ui(a, b) enqueue_super(a, b)
+
 inline int
 IsUI(u_char * data, int ext)
 {
@@ -272,6 +277,16 @@
 }
 
 inline int
+IsSFrame(u_char * data, int ext)
+{
+	register u_char d = *data;
+	
+	if (!ext)
+		d &= 0xf;
+	return(((d & 0xf3) == 1) && ((d & 0x0c) != 0x0c));
+}
+
+inline int
 IsSABMX(u_char * data, int ext)
 {
 	u_char d = data[0] & ~0x10;
@@ -393,15 +408,15 @@
 	switch (event) {
 		case EV_L2_UA:
 			if (get_PollFlagFree(st, skb))
-				st->ma.layer(st, MDL_ERROR_IND, (void *) 'C');
+				st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'C');
 			else
-				st->ma.layer(st, MDL_ERROR_IND, (void *) 'D');
+				st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'D');
 			break;
 		case EV_L2_DM:
 			if (get_PollFlagFree(st, skb))
-				st->ma.layer(st, MDL_ERROR_IND, (void *) 'B');
+				st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'B');
 			else {
-				st->ma.layer(st, MDL_ERROR_IND, (void *) 'E');
+				st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'E');
 				establishlink(fi);
 				test_and_clear_bit(FLG_L3_INIT, &st->l2.flag);
 			}
@@ -415,9 +430,16 @@
 	struct PStack *st = fi->userdata;
 	int state = fi->state;
 
-	FsmChangeState(fi, ST_L2_3); 
-	if (state == ST_L2_1)
-		st->l2.l2tei(st, MDL_ASSIGN_IND, NULL);
+	
+	if (test_bit(FLG_FIXED_TEI, &st->l2.flag)) {
+		FsmChangeState(fi, ST_L2_4);
+		establishlink(fi);
+		test_and_set_bit(FLG_L3_INIT, &st->l2.flag);
+	} else {
+		FsmChangeState(fi, ST_L2_3);
+		if (state == ST_L2_1)
+			st->l2.l2tei(st, MDL_ASSIGN | INDICATION, NULL);
+	}
 }
 
 static void
@@ -444,7 +466,7 @@
 	skb_queue_tail(&st->l2.ui_queue, skb);
 	if (fi->state == ST_L2_1) {
 		FsmChangeState(fi, ST_L2_2);
-		st->l2.l2tei(st, MDL_ASSIGN_IND, NULL);
+		st->l2.l2tei(st, MDL_ASSIGN | INDICATION, NULL);
 	}
 	if (fi->state > ST_L2_3)
 		l2_send_ui(st);
@@ -458,10 +480,10 @@
 
 	skb_pull(skb, l2headersize(&st->l2, 1));
 	if (skb->len > st->l2.maxlen) { 
-		st->ma.layer(st, MDL_ERROR_IND, (void *) 'O');
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'O');
 		FreeSkb(skb);
 	} else
-		st->l2.l2l3(st, DL_UNIT_DATA, skb);
+		st->l2.l2l3(st, DL_UNIT_DATA | INDICATION, skb);
 }
 
 static void
@@ -470,7 +492,7 @@
 	struct PStack *st = fi->userdata;
 
 	if (fi->state != ST_L2_4)
-		discard_i_queue(st);
+		discard_queue(&st->l2.i_queue);
 	if (fi->state != ST_L2_5)
 		establishlink(fi);
 	test_and_set_bit(FLG_L3_INIT, &st->l2.flag);
@@ -482,13 +504,13 @@
 	struct PStack *st = fi->userdata;
 
 	if (fi->state == ST_L2_4) {
-		st->l2.l2man(st, DL_RELEASE, NULL);
+		st->l2.l2l3(st, DL_RELEASE  | CONFIRM, NULL);
 		return;
 	} else if (fi->state == ST_L2_5) {
 		test_and_set_bit(FLG_PEND_REL, &st->l2.flag);
 		return;
 	}
-	discard_i_queue(st);
+	discard_queue(&st->l2.i_queue);
 	FsmChangeState(fi, ST_L2_6);
 	st->l2.rc = 0;
 	send_uframe(st, DISC | 0x10, CMD);
@@ -510,14 +532,14 @@
 	if (test_bit(FLG_ORIG, &st->l2.flag))
 		rsp = !rsp;
 	if (rsp) {
-		st->ma.layer(st, MDL_ERROR_IND, (void *) 'L');
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'L');
 		FreeSkb(skb);
 		if ((state == ST_L2_7) || (state == ST_L2_8))
 			establishlink(fi);
 		return;
 	}	
 	if (skb->len != (l2addrsize(&st->l2) + 1)) {
-		st->ma.layer(st, MDL_ERROR_IND, (void *) 'N');
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N');
 		FreeSkb(skb);
 		if ((state == ST_L2_7) || (state == ST_L2_8))
 			establishlink(fi);
@@ -532,9 +554,9 @@
 	if (ST_L2_5 == state)
 		return;
 	if (ST_L2_4 != state) {
-		st->ma.layer(st, MDL_ERROR_IND, (void *) 'F');
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'F');
 		if (st->l2.vs != st->l2.va) {
-			discard_i_queue(st);
+			discard_queue(&st->l2.i_queue);
 			est = 1;
 		} else
 			est = 0;
@@ -547,14 +569,14 @@
 	FsmChangeState(fi, ST_L2_7);
 	if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag))
 		FsmDelTimer(&st->l2.t200, 2);
-	FsmAddTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 3);
+	FsmRestartTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 3);
 
 	if (est)
-		st->l2.l2man(st, DL_ESTABLISH, NULL);
+		st->l2.l2l3(st, DL_ESTABLISH | INDICATION, NULL);
 
 	if (ST_L2_8 == state)
 		if (skb_queue_len(&st->l2.i_queue) && cansend(st))
-			st->l2.l2l1(st, PH_PULL_REQ, NULL);
+			st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
 }
 
 static void
@@ -571,14 +593,14 @@
 		rsp = !rsp;
 
 	if (rsp) {
-		st->ma.layer(st, MDL_ERROR_IND, (void *) 'L');
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'L');
 		FreeSkb(skb);
 		if ((state == ST_L2_7) || (state == ST_L2_8))
 			establishlink(fi);
 		return;
 	}	
 	if (skb->len != (l2addrsize(&st->l2) + 1)) {
-		st->ma.layer(st, MDL_ERROR_IND, (void *) 'N');
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N');
 		FreeSkb(skb);
 		if ((state == ST_L2_7) || (state == ST_L2_8))
 			establishlink(fi);
@@ -600,8 +622,11 @@
 			FsmDelTimer(&st->l2.t200, 2);
 	}
 	send_uframe(st, cmd | PollFlag, RSP);
-	if (rel)
-		st->l2.l2man(st, DL_RELEASE, NULL);
+	if (rel) {
+		if (test_bit(FLG_LAPB, &st->l2.flag))
+			st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL);
+		st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL);
+	}
 }
 
 
@@ -610,7 +635,8 @@
 {
 	struct PStack *st = fi->userdata;
 	struct sk_buff *skb = arg;
-	u_char PollFlag, est = 1;
+	int pr=-1;
+	u_char PollFlag;
 	int state,rsp;
 
 	state = fi->state;
@@ -619,14 +645,14 @@
 		rsp = !rsp;
 
 	if (!rsp) {
-		st->ma.layer(st, MDL_ERROR_IND, (void *) 'L');
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'L');
 		FreeSkb(skb);
 		if ((state == ST_L2_7) || (state == ST_L2_8))
 			establishlink(fi);
 		return;
 	}	
 	if (skb->len != (l2addrsize(&st->l2) + 1)) {
-		st->ma.layer(st, MDL_ERROR_IND, (void *) 'N');
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N');
 		FreeSkb(skb);
 		if ((fi->state == ST_L2_7) || (fi->state == ST_L2_8))
 			establishlink(fi);
@@ -643,30 +669,31 @@
 		FsmDelTimer(&st->l2.t200, 2);
 	if (fi->state == ST_L2_5) {
 		if (test_and_clear_bit(FLG_PEND_REL, &st->l2.flag)) {
-			discard_i_queue(st);
+			discard_queue(&st->l2.i_queue);
 			st->l2.rc = 0;
 			send_uframe(st, DISC | 0x10, CMD);
 			FsmChangeState(fi, ST_L2_6);
 			FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 4);
 			test_and_set_bit(FLG_T200_RUN, &st->l2.flag);
 		} else {
-			if (!test_and_clear_bit(FLG_L3_INIT, &st->l2.flag)) {
-				if (st->l2.vs != st->l2.va)
-					discard_i_queue(st);
-				else
-					est = 0;
+			if (test_and_clear_bit(FLG_L3_INIT, &st->l2.flag)) {
+				pr = DL_ESTABLISH | CONFIRM;
+			} else if (st->l2.vs != st->l2.va) {
+				discard_queue(&st->l2.i_queue);
+				pr = DL_ESTABLISH | INDICATION;
 			}
 			st->l2.vs = 0;
 			st->l2.va = 0;
 			st->l2.vr = 0;
 			st->l2.sow = 0;
 			FsmChangeState(fi, ST_L2_7);
-			FsmAddTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 4);
-			if (est)
-				st->l2.l2man(st, DL_ESTABLISH, NULL);
+			if (pr > -1)
+				st->l2.l2l3(st, pr, NULL);
 		}
 	} else {		/* ST_L2_6 */
-		st->l2.l2man(st, DL_RELEASE, NULL);
+		if (test_bit(FLG_LAPB, &st->l2.flag))
+			st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL);
+		st->l2.l2l3(st, DL_RELEASE | CONFIRM, NULL);
 		FsmChangeState(fi, ST_L2_4);
 	}
 }
@@ -685,14 +712,14 @@
 		rsp = !rsp;
 
 	if (!rsp) {
-		st->ma.layer(st, MDL_ERROR_IND, (void *) 'L');
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'L');
 		FreeSkb(skb);
 		if ((state == ST_L2_7) || (state == ST_L2_8))
 			establishlink(fi);
 		return;
 	}	
 	if (skb->len != (l2addrsize(&st->l2) + 1)) {
-		st->ma.layer(st, MDL_ERROR_IND, (void *) 'N');
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N');
 		FreeSkb(skb);
 		if ((fi->state == ST_L2_7) || (fi->state == ST_L2_8))
 			establishlink(fi);
@@ -700,15 +727,41 @@
 	}
 	PollFlag = get_PollFlagFree(st, skb);
 	if (!PollFlag) {
-		establishlink(fi);
-		test_and_clear_bit(FLG_L3_INIT, &st->l2.flag);
+		if (fi->state == ST_L2_4) {
+			establishlink(fi);
+			test_and_clear_bit(FLG_L3_INIT, &st->l2.flag);
+			FsmChangeState(fi, ST_L2_5);
+		} else if ((fi->state == ST_L2_7) || (fi->state == ST_L2_8)) {
+			st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'E');
+			establishlink(fi);
+		}
 	} else {
-		if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag))
-			FsmDelTimer(&st->l2.t200, 2);
-	 	if (fi->state == ST_L2_5 && !test_bit(FLG_L3_INIT, &st->l2.flag))
-			discard_i_queue(st);
-		st->l2.l2man(st, DL_RELEASE, NULL);
-		FsmChangeState(fi, ST_L2_4);
+		switch (fi->state) {
+			case ST_L2_8:
+				 establishlink(fi);
+			case ST_L2_7:
+				st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'B');
+				break;
+			case ST_L2_4:
+				break;
+			case ST_L2_5:
+				if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag))
+					FsmDelTimer(&st->l2.t200, 2);
+				discard_queue(&st->l2.i_queue);
+				if (test_bit(FLG_LAPB, &st->l2.flag))
+					st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL);
+				st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL);
+				FsmChangeState(fi, ST_L2_4);
+				break;
+			case ST_L2_6:
+				if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag))
+					FsmDelTimer(&st->l2.t200, 2);
+				if (test_bit(FLG_LAPB, &st->l2.flag))
+					st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL);
+				st->l2.l2l3(st, DL_RELEASE | CONFIRM, NULL);
+				FsmChangeState(fi, ST_L2_4);
+				break;
+		}
 	}
 }
 
@@ -763,7 +816,7 @@
 {
 	struct PStack *st = fi->userdata;
 
-	st->ma.layer(st, MDL_ERROR_IND, (void *) 'J');
+	st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'J');
 	establishlink(fi);
 }
 
@@ -785,11 +838,13 @@
 			if (p1 < 0)
 				p1 += (test_bit(FLG_MOD128, &l2->flag) ? 128 : 8);
 			p1 = (p1 + l2->sow) % l2->window;
+			if (test_bit(FLG_LAPB, &l2->flag))
+				st->l1.bcs->tx_cnt += l2->windowar[p1]->len + l2headersize(l2, 0);
 			skb_queue_head(&l2->i_queue, l2->windowar[p1]);
 			l2->windowar[p1] = NULL;
 		}
 		restore_flags(flags);
-		st->l2.l2l1(st, PH_PULL_REQ, NULL);
+		st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
 	}
 }
 
@@ -818,9 +873,11 @@
 			PollFlag = (skb->data[1] & 0x1) == 0x1;
 			nr = skb->data[1] >> 1;
 		} else {
-			st->ma.layer(st, MDL_ERROR_IND, (void *) 'N');
+			if (skb->len >2) {
+				st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N');
+				establishlink(fi);
+			}
 			FreeSkb(skb);
-			establishlink(fi);
 			return;
 		}
 	} else {
@@ -828,7 +885,7 @@
 			PollFlag = (skb->data[0] & 0x10);
 			nr = (skb->data[0] >> 5) & 0x7;
 		} else {
-			st->ma.layer(st, MDL_ERROR_IND, (void *) 'N');
+			st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N');
 			FreeSkb(skb);
 			establishlink(fi);
 			return;
@@ -839,7 +896,7 @@
 	if ((!rsp) && PollFlag)
 		enquiry_response(st);
 	if (rsp && PollFlag)
-		st->ma.layer(st, MDL_ERROR_IND, (void *) 'A');
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'A');
 	if (legalnr(st, nr)) {
 		if (typ == REJ) {
 			setva(st, nr);
@@ -862,13 +919,13 @@
 			test_and_set_bit(FLG_T200_RUN, &st->l2.flag);
 		}
 		if (skb_queue_len(&st->l2.i_queue) && (typ == RR))
-			st->l2.l2l1(st, PH_PULL_REQ, NULL);
+			st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
 	} else
 		nrerrorrecovery(fi);
 
 	if ((fi->userint & LC_FLUSH_WAIT) && rsp && !(skb_queue_len(&st->l2.i_queue))) {
 		fi->userint &= ~LC_FLUSH_WAIT;
-		st->l2.l2man(st, DL_FLUSH, NULL);
+		st->l2.l2l3(st, DL_FLUSH | INDICATION, NULL);
 	}
 }
 
@@ -883,7 +940,7 @@
 	if (!((fi->state == ST_L2_5) && test_bit(FLG_L3_INIT, &st->l2.flag)))
 		skb_queue_tail(&st->l2.i_queue, skb);
 	if (fi->state == ST_L2_7)
-		st->l2.l2l1(st, PH_PULL_REQ, NULL);
+		st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
 }
 
 static void
@@ -891,17 +948,15 @@
 {
 	struct PStack *st = fi->userdata;
 	struct sk_buff *skb = arg;
-	struct IsdnCardState *sp = st->l1.hardware;
 	struct Layer2 *l2 = &(st->l2);
-	int PollFlag, ns, nr, i, hs, rsp;
-	char str[64];
+	int PollFlag, ns, nr, i, rsp;
 
 	rsp = *skb->data & 0x2;
 	if (test_bit(FLG_ORIG, &l2->flag))
 		rsp = !rsp;
 
 	if (rsp) {
-		st->ma.layer(st, MDL_ERROR_IND, (void *) 'L');
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'L');
 		FreeSkb(skb);
 		establishlink(fi);
 		return;
@@ -909,12 +964,10 @@
 	i = l2addrsize(l2);
 	if (test_bit(FLG_MOD128, &l2->flag)) {
 		if (skb->len <= (i + 1)) {
-			st->ma.layer(st, MDL_ERROR_IND, (void *) 'N');
 			FreeSkb(skb);
-			establishlink(fi);
 			return;
 		} else if ((skb->len - i - 1) > l2->maxlen) { 
-			st->ma.layer(st, MDL_ERROR_IND, (void *) 'O');
+			st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'O');
 			FreeSkb(skb);
 			establishlink(fi);
 			return;
@@ -924,12 +977,12 @@
 		nr = (skb->data[i + 1] >> 1) & 0x7f;
 	} else {
 		if (skb->len <= i) {
-			st->ma.layer(st, MDL_ERROR_IND, (void *) 'N');
+			st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N');
 			FreeSkb(skb);
 			establishlink(fi);
 			return;
 		} else if ((skb->len - i) > l2->maxlen) { 
-			st->ma.layer(st, MDL_ERROR_IND, (void *) 'O');
+			st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'O');
 			FreeSkb(skb);
 			establishlink(fi);
 			return;
@@ -944,20 +997,12 @@
 	} else if (l2->vr == ns) {
 		l2->vr = (l2->vr + 1) % (test_bit(FLG_MOD128, &l2->flag) ? 128 : 8);
 		test_and_clear_bit(FLG_REJEXC, &l2->flag);
-		if (test_bit(FLG_LAPD, &l2->flag))
-			if (sp->dlogflag) {
-				hs = l2headersize(l2, 0);
-				LogFrame(st->l1.hardware, skb->data, skb->len);
-				sprintf(str, "Q.931 frame network->user tei %d", st->l2.tei);
-				dlogframe(st->l1.hardware, skb->data + hs,
-					  skb->len - hs, str);
-			}
 		if (PollFlag)
 			enquiry_response(st);
 		else
 			test_and_set_bit(FLG_ACK_PEND, &l2->flag);
 		skb_pull(skb, l2headersize(l2, 0));
-		st->l2.l2l3(st, DL_DATA, skb);
+		st->l2.l2l3(st, DL_DATA | INDICATION, skb);
 	} else {
 		/* n(s)!=v(r) */
 		FreeSkb(skb);
@@ -990,7 +1035,7 @@
 	}
 
 	if (skb_queue_len(&st->l2.i_queue) && (fi->state == ST_L2_7))
-		st->l2.l2l1(st, PH_PULL_REQ, NULL);
+		st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
 	if (test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag))
 		enquiry_cr(st, RR, RSP, 0);
 }
@@ -1000,7 +1045,7 @@
 {
 	struct PStack *st = fi->userdata;
 
-	st->l2.tei = (int) arg;
+	st->l2.tei = (long) arg;
 
 	if (fi->state == ST_L2_3) {
 		establishlink(fi);
@@ -1012,12 +1057,6 @@
 }
 
 static void
-l2_no_tei(struct FsmInst *fi, int event, void *arg)
-{
-	FsmChangeState(fi, ST_L2_4);
-}
-
-static void
 l2_st5_tout_200(struct FsmInst *fi, int event, void *arg)
 {
 	struct PStack *st = fi->userdata;
@@ -1028,9 +1067,11 @@
 	} else if (st->l2.rc == st->l2.N200) {
 		FsmChangeState(fi, ST_L2_4);
 		test_and_clear_bit(FLG_T200_RUN, &st->l2.flag);
-		discard_i_queue(st);
-		st->ma.layer(st, MDL_ERROR_IND, (void *) 'G');
-		st->l2.l2man(st, DL_RELEASE, NULL);
+		discard_queue(&st->l2.i_queue);
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'G');
+		if (test_bit(FLG_LAPB, &st->l2.flag))
+			st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL);
+		st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL);
 	} else {
 		st->l2.rc++;
 		FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9);
@@ -1049,8 +1090,10 @@
 		FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9);
 	} else if (st->l2.rc == st->l2.N200) {
 		FsmChangeState(fi, ST_L2_4);
-		st->ma.layer(st, MDL_ERROR_IND, (void *) 'H');
-		st->l2.l2man(st, DL_RELEASE, NULL);
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'H');
+		if (test_bit(FLG_LAPB, &st->l2.flag))
+			st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL);
+		st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL);
 	} else {
 		st->l2.rc++;
 		FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200,
@@ -1146,14 +1189,14 @@
 		memcpy(skb_put(skb, oskb->len), oskb->data, oskb->len);
 		FreeSkb(oskb);
 	}
-	st->l2.l2l1(st, PH_PULL_IND, skb);
+	st->l2.l2l1(st, PH_PULL | INDICATION, skb);
 	test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag);
 	if (!test_and_set_bit(FLG_T200_RUN, &st->l2.flag)) {
 		FsmDelTimer(&st->l2.t203, 13);
 		FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 11);
 	}
 	if (skb_queue_len(&l2->i_queue) && cansend(st))
-		st->l2.l2l1(st, PH_PULL_REQ, NULL);
+		st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
 }
 
 static void
@@ -1179,7 +1222,7 @@
 			PollFlag = (skb->data[1] & 0x1) == 0x1;
 			nr = skb->data[1] >> 1;
 		} else {
-			st->ma.layer(st, MDL_ERROR_IND, (void *) 'N');
+			st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N');
 			FreeSkb(skb);
 			establishlink(fi);
 			return;
@@ -1189,7 +1232,7 @@
 			PollFlag = (skb->data[0] & 0x10);
 			nr = (skb->data[0] >> 5) & 0x7;
 		} else {
-			st->ma.layer(st, MDL_ERROR_IND, (void *) 'N');
+			st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N');
 			FreeSkb(skb);
 			establishlink(fi);
 			return;
@@ -1213,10 +1256,10 @@
 			invoke_retransmission(st, nr);
 			FsmChangeState(fi, ST_L2_7);
 			if (skb_queue_len(&l2->i_queue) && cansend(st))
-				st->l2.l2l1(st, PH_PULL_REQ, NULL);
+				st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
 			else if (fi->userint & LC_FLUSH_WAIT) {
 				fi->userint &= ~LC_FLUSH_WAIT;
-				st->l2.l2man(st, DL_FLUSH, NULL);
+				st->l2.l2l3(st, DL_FLUSH | INDICATION, NULL);
 			}
 		}
 	} else {
@@ -1233,30 +1276,25 @@
 {
 	struct PStack *st = fi->userdata;
 	struct sk_buff *skb = arg;
-	char tmp[64];
 
 	skb_pull(skb, l2addrsize(&st->l2) + 1);
 	if (test_bit(FLG_MOD128, &st->l2.flag)) {
 		if (skb->len < 5)
-			st->ma.layer(st, MDL_ERROR_IND, (void *) 'N');
-		else {
-			sprintf(tmp, "FRMR information %2x %2x %2x %2x %2x",
+			st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N');
+		else
+			l2m_debug(&st->l2.l2m, "FRMR information %2x %2x %2x %2x %2x",
 				skb->data[0], skb->data[1], skb->data[2],
 				skb->data[3], skb->data[4]);
-			l2m_debug(&st->l2.l2m, tmp);
-		}
 	} else {
 		if (skb->len < 3)
-			st->ma.layer(st, MDL_ERROR_IND, (void *) 'N');
-		else {
-			sprintf(tmp, "FRMR information %2x %2x %2x",
+			st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N');
+		else
+			l2m_debug(&st->l2.l2m, "FRMR information %2x %2x %2x",
 				skb->data[0], skb->data[1], skb->data[2]);
-			l2m_debug(&st->l2.l2m, tmp);
-		}
 	}
 	if (!(skb->data[0] & 1) || ((skb->data[0] & 3) == 1) ||		/* I or S */
 	    (IsUA(skb->data, 0) && (fi->state == ST_L2_7))) {
-		st->ma.layer(st, MDL_ERROR_IND, (void *) 'K');
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'K');
 		establishlink(fi);
 		test_and_clear_bit(FLG_L3_INIT, &st->l2.flag);
 	}
@@ -1268,14 +1306,14 @@
 {
 	struct PStack *st = fi->userdata;
 
-	discard_i_queue(st);
-	discard_ui_queue(st);
+	discard_queue(&st->l2.i_queue);
+	discard_queue(&st->l2.ui_queue);
 	st->l2.tei = -1;
 	if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag))
 		FsmDelTimer(&st->l2.t200, 18);
 	FsmDelTimer(&st->l2.t203, 19);
 	if (fi->state != ST_L2_4)
-		st->l2.l2man(st, DL_RELEASE, NULL);
+		st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL);
 	FsmChangeState(fi, ST_L2_1);
 }
 
@@ -1283,33 +1321,45 @@
 l2_persistant_da(struct FsmInst *fi, int event, void *arg)
 {
 	struct PStack *st = fi->userdata;
+	int rel = DL_RELEASE | INDICATION;
+	
 	
-	discard_i_queue(st);
-	discard_ui_queue(st);
+	discard_queue(&st->l2.i_queue);
+	discard_queue(&st->l2.ui_queue);
 	if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag))
 		FsmDelTimer(&st->l2.t200, 18);
 	FsmDelTimer(&st->l2.t203, 19);
-	test_and_clear_bit(FLG_PEND_REL, &st->l2.flag);
 	clear_exception(&st->l2);
 	switch (fi->state) {
+		case ST_L2_1:
+			if (!test_and_clear_bit(FLG_ESTAB_PEND, &st->l2.flag))
+				break;
 		case ST_L2_3:
-			st->l2.l2man(st, DL_RELEASE, NULL);
+			st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL);
 		case ST_L2_2:
 			FsmChangeState(fi, ST_L2_1);
 			break;
-		case ST_L2_5:
 		case ST_L2_6:
+			rel = DL_RELEASE | CONFIRM;
+		case ST_L2_5:
+			if (test_and_clear_bit(FLG_PEND_REL, &st->l2.flag))
+				rel = DL_RELEASE | CONFIRM;
 		case ST_L2_7:
 		case ST_L2_8:
-			st->l2.l2man(st, DL_RELEASE, NULL);
+			st->l2.l2l3(st, rel, NULL);
 			FsmChangeState(fi, ST_L2_4);
 			break;
+		case ST_L2_4:
+			if (test_and_clear_bit(FLG_ESTAB_PEND, &st->l2.flag))
+				st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL);
+			break;
 	}
+	test_and_clear_bit(FLG_PEND_REL, &st->l2.flag);
+	test_and_clear_bit(FLG_L1_ACTIV, &st->l2.flag);
 }
 
 static struct FsmNode L2FnList[] HISAX_INITDATA =
 {
-	{ST_L2_1, EV_L2_MDL_NOTEIPROC, l2_no_tei},
 	{ST_L2_1, EV_L2_DL_ESTABLISH, l2_dl_establish},
 	{ST_L2_2, EV_L2_DL_ESTABLISH, l2_dl_establish},
 	{ST_L2_4, EV_L2_DL_ESTABLISH, l2_establish},
@@ -1359,8 +1409,8 @@
 	{ST_L2_4, EV_L2_DM, l2_got_dm},
 	{ST_L2_5, EV_L2_DM, l2_got_dm},
 	{ST_L2_6, EV_L2_DM, l2_got_dm},
-	{ST_L2_7, EV_L2_DM, l2_mdl_error},
-	{ST_L2_8, EV_L2_DM, l2_mdl_error},
+	{ST_L2_7, EV_L2_DM, l2_got_dm},
+	{ST_L2_8, EV_L2_DM, l2_got_dm},
 	{ST_L2_1, EV_L2_UI, l2_got_ui},
 	{ST_L2_2, EV_L2_UI, l2_got_ui},
 	{ST_L2_3, EV_L2_UI, l2_got_ui},
@@ -1381,6 +1431,7 @@
 	{ST_L2_8, EV_L2_T200, l2_st78_tout_200},
 	{ST_L2_7, EV_L2_T203, l2_st7_tout_203},
 	{ST_L2_7, EV_L2_ACK_PULL, l2_pull_iqueue},
+	{ST_L2_1, EV_L1_DEACTIVATE, l2_persistant_da},
 	{ST_L2_2, EV_L1_DEACTIVATE, l2_persistant_da},
 	{ST_L2_3, EV_L1_DEACTIVATE, l2_persistant_da},
 	{ST_L2_4, EV_L1_DEACTIVATE, l2_persistant_da},
@@ -1400,19 +1451,19 @@
 	int ret = 1, len;
 
 	switch (pr) {
-		case (PH_DATA_IND):
+		case (PH_DATA | INDICATION):
 			datap = skb->data;
 			len = l2addrsize(&st->l2);
 			if (skb->len > len)
 				datap += len;
 			else {
-				st->ma.layer(st, MDL_ERROR_IND, (void *) 'N');
+				st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N');
 				FreeSkb(skb);
 				return;
 			}
 			if (!(*datap & 1))	/* I-Frame */
 				ret = FsmEvent(&st->l2.l2m, EV_L2_I, skb);
-			else if ((*datap & 3) == 1)	/* S-Frame */
+			else if (IsSFrame(datap, test_bit(FLG_MOD128, &st->l2.flag)))
 				ret = FsmEvent(&st->l2.l2m, EV_L2_SUPER, skb);
 			else if (IsUI(datap, test_bit(FLG_MOD128, &st->l2.flag)))
 				ret = FsmEvent(&st->l2.l2m, EV_L2_UI, skb);
@@ -1427,23 +1478,39 @@
 			else if (IsFRMR(datap, test_bit(FLG_MOD128, &st->l2.flag)))
 				ret = FsmEvent(&st->l2.l2m, EV_L2_FRMR, skb);
 			else {
-				ret = 0;
-				st->ma.layer(st, MDL_ERROR_IND, (void *) 'L');
-				FreeSkb(skb);
+				ret = 1;
+				if ((st->l2.l2m.state == ST_L2_7) ||
+					(st->l2.l2m.state == ST_L2_8))
+					establishlink(&st->l2.l2m);
+				st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'L');
 			}
 			if (ret) {
 				FreeSkb(skb);
 			}
 			break;
-		case (PH_PULL_CNF):
+		case (PH_PULL | CONFIRM):
 			FsmEvent(&st->l2.l2m, EV_L2_ACK_PULL, arg);
 			break;
-		case (PH_PAUSE_IND):
+		case (PH_PAUSE | INDICATION):
 			test_and_set_bit(FLG_DCHAN_BUSY, &st->l2.flag);
 			break;
-		case (PH_PAUSE_CNF):
+		case (PH_PAUSE | CONFIRM):
 			test_and_clear_bit(FLG_DCHAN_BUSY, &st->l2.flag);
 			break;
+		case (PH_ACTIVATE | CONFIRM):
+		case (PH_ACTIVATE | INDICATION):
+			test_and_set_bit(FLG_L1_ACTIV, &st->l2.flag);
+			if (test_and_clear_bit(FLG_ESTAB_PEND, &st->l2.flag))
+				FsmEvent(&st->l2.l2m, EV_L2_DL_ESTABLISH, arg);
+			break;
+		case (PH_DEACTIVATE | INDICATION):
+		case (PH_DEACTIVATE | CONFIRM):
+			test_and_clear_bit(FLG_L1_ACTIV, &st->l2.flag);
+			FsmEvent(&st->l2.l2m, EV_L1_DEACTIVATE, arg);
+			break;
+		default:
+			l2m_debug(&st->l2.l2m, "l2 unknown pr %04x", pr);
+			break;
 	}
 }
 
@@ -1451,45 +1518,46 @@
 isdnl2_l3l2(struct PStack *st, int pr, void *arg)
 {
 	switch (pr) {
-		case (DL_DATA):
+		case (DL_DATA | REQUEST):
 			if (FsmEvent(&st->l2.l2m, EV_L2_DL_DATA, arg)) {
 				dev_kfree_skb((struct sk_buff *) arg);
 			}
 			break;
-		case (DL_UNIT_DATA):
+		case (DL_UNIT_DATA | REQUEST):
 			if (FsmEvent(&st->l2.l2m, EV_L2_DL_UNIT_DATA, arg)) {
 				dev_kfree_skb((struct sk_buff *) arg);
 			}
 			break;
-	}
-}
-
-static void
-isdnl2_manl2(struct PStack *st, int pr, void *arg)
-{
-	switch (pr) {
-		case (DL_ESTABLISH):
-			FsmEvent(&st->l2.l2m, EV_L2_DL_ESTABLISH, arg);
+		case (DL_ESTABLISH | REQUEST):
+			if (test_bit(FLG_L1_ACTIV, &st->l2.flag)) {
+				if (test_bit(FLG_LAPD, &st->l2.flag) ||
+					test_bit(FLG_ORIG, &st->l2.flag)) {
+					FsmEvent(&st->l2.l2m, EV_L2_DL_ESTABLISH, arg);
+				}
+			} else {
+				if (test_bit(FLG_LAPD, &st->l2.flag) ||
+					test_bit(FLG_ORIG, &st->l2.flag)) {
+					test_and_set_bit(FLG_ESTAB_PEND, &st->l2.flag);
+				}
+				st->l2.l2l1(st, PH_ACTIVATE, NULL);
+			}
 			break;
-		case (DL_RELEASE):
+		case (DL_RELEASE | REQUEST):
+			if (test_bit(FLG_LAPB, &st->l2.flag)) {
+				st->l2.l2l1(st, PH_DEACTIVATE, NULL);
+			}
 			FsmEvent(&st->l2.l2m, EV_L2_DL_RELEASE, arg);
 			break;
-		case (MDL_NOTEIPROC):
-			FsmEvent(&st->l2.l2m, EV_L2_MDL_NOTEIPROC, NULL);
-			break;
-		case (DL_FLUSH):
+		case (DL_FLUSH | REQUEST):
 			(&st->l2.l2m)->userint |= LC_FLUSH_WAIT;
 			break;
-		case (PH_DEACTIVATE_IND):
-			FsmEvent(&st->l2.l2m, EV_L1_DEACTIVATE, arg);
-			break;
-		case (MDL_ASSIGN_REQ):
+		case (MDL_ASSIGN | REQUEST):
 			FsmEvent(&st->l2.l2m, EV_L2_MDL_ASSIGN, arg);
 			break;
-		case (MDL_REMOVE_REQ):
+		case (MDL_REMOVE | REQUEST):
 			FsmEvent(&st->l2.l2m, EV_L2_MDL_REMOVE, arg);
 			break;
-		case (MDL_ERROR_REQ):
+		case (MDL_ERROR | RESPONSE):
 			FsmEvent(&st->l2.l2m, EV_L2_MDL_ERROR, arg);
 			break;
 	}
@@ -1500,20 +1568,20 @@
 {
 	FsmDelTimer(&st->l2.t200, 15);
 	FsmDelTimer(&st->l2.t203, 16);
-	discard_i_queue(st);
-	discard_ui_queue(st);
+	discard_queue(&st->l2.i_queue);
+	discard_queue(&st->l2.ui_queue);
 	ReleaseWin(&st->l2);
 }
 
 static void
-l2m_debug(struct FsmInst *fi, char *s)
+l2m_debug(struct FsmInst *fi, char *fmt, ...)
 {
+	va_list args;
 	struct PStack *st = fi->userdata;
-	char tm[32], str[256];
 
-	jiftime(tm, jiffies);
-	sprintf(str, "%s %s %s\n", tm, st->l2.debug_id, s);
-	HiSax_putstatus(st->l1.hardware, str);
+	va_start(args, fmt);
+	VHiSax_putstatus(st->l1.hardware, st->l2.debug_id, fmt, args);
+	va_end(args);
 }
 
 void
@@ -1521,7 +1589,6 @@
 {
 	st->l1.l1l2 = isdnl2_l1l2;
 	st->l3.l3l2 = isdnl2_l3l2;
-	st->ma.manl2 = isdnl2_manl2;
 
 	skb_queue_head_init(&st->l2.i_queue);
 	skb_queue_head_init(&st->l2.ui_queue);
@@ -1529,6 +1596,9 @@
 	st->l2.debug = 0;
 
 	st->l2.l2m.fsm = &l2fsm;
+	if (test_bit(FLG_LAPB, &st->l2.flag))
+		st->l2.l2m.state = ST_L2_4;
+	else
 	st->l2.l2m.state = ST_L2_1;
 	st->l2.l2m.debug = 0;
 	st->l2.l2m.userdata = st;
@@ -1540,9 +1610,27 @@
 	FsmInitTimer(&st->l2.l2m, &st->l2.t203);
 }
 
+static void
+transl2_l3l2(struct PStack *st, int pr, void *arg)
+{
+	switch (pr) {
+		case (DL_DATA | REQUEST):
+		case (DL_UNIT_DATA | REQUEST):
+			st->l2.l2l1(st, PH_DATA | REQUEST, arg);
+			break;
+		case (DL_ESTABLISH | REQUEST):
+			st->l2.l2l1(st, PH_ACTIVATE | REQUEST, NULL);
+			break;
+		case (DL_RELEASE | REQUEST):
+			st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL);
+			break;
+	}
+}
+
 void
 setstack_transl2(struct PStack *st)
 {
+	st->l3.l3l2 = transl2_l3l2;
 }
 
 void
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/isdnl3.c linux/drivers/isdn/hisax/isdnl3.c
--- v2.3.3/linux/drivers/isdn/hisax/isdnl3.c	Wed Apr  1 16:20:59 1998
+++ linux/drivers/isdn/hisax/isdnl3.c	Sun May 23 10:03:41 1999
@@ -1,12 +1,27 @@
-/* $Id: isdnl3.c,v 2.5 1998/02/12 23:07:52 keil Exp $
+/* $Id: isdnl3.c,v 2.8 1998/11/15 23:55:04 keil Exp $
 
- * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author       Karsten Keil (keil@isdn4linux.de)
  *              based on the teles driver from Jan den Ouden
  *
+ *		This file is (c) under GNU PUBLIC LICENSE
+ *		For changes and modifications please read
+ *		../../../Documentation/isdn/HiSax.cert
+ *
  * Thanks to    Jan den Ouden
  *              Fritz Elfert
  *
  * $Log: isdnl3.c,v $
+ * Revision 2.8  1998/11/15 23:55:04  keil
+ * changes from 2.0
+ *
+ * Revision 2.7  1998/05/25 14:10:15  keil
+ * HiSax 3.0
+ * X.75 and leased are working again.
+ *
+ * Revision 2.6  1998/05/25 12:58:11  keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
  * Revision 2.5  1998/02/12 23:07:52  keil
  * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
  *
@@ -47,7 +62,60 @@
 #include "isdnl3.h"
 #include <linux/config.h>
 
-const char *l3_revision = "$Revision: 2.5 $";
+const char *l3_revision = "$Revision: 2.8 $";
+
+static
+struct Fsm l3fsm =
+{NULL, 0, 0, NULL, NULL};
+
+enum {
+	ST_L3_LC_REL,
+	ST_L3_LC_ESTAB_WAIT,
+	ST_L3_LC_REL_WAIT,
+	ST_L3_LC_ESTAB,
+};
+
+#define L3_STATE_COUNT (ST_L3_LC_ESTAB+1)
+
+static char *strL3State[] =
+{
+	"ST_L3_LC_REL",
+	"ST_L3_LC_ESTAB_WAIT",
+	"ST_L3_LC_REL_WAIT",
+	"ST_L3_LC_ESTAB",
+};
+
+enum {
+	EV_ESTABLISH_REQ,
+	EV_ESTABLISH_IND,
+	EV_ESTABLISH_CNF,
+	EV_RELEASE_REQ,
+	EV_RELEASE_CNF,
+	EV_RELEASE_IND,
+};
+
+#define L3_EVENT_COUNT (EV_RELEASE_IND+1)
+
+static char *strL3Event[] =
+{
+	"EV_ESTABLISH_REQ",
+	"EV_ESTABLISH_IND",
+	"EV_ESTABLISH_CNF",
+	"EV_RELEASE_REQ",
+	"EV_RELEASE_CNF",
+	"EV_RELEASE_IND",
+};
+
+static void
+l3m_debug(struct FsmInst *fi, char *fmt, ...)
+{
+	va_list args;
+	struct PStack *st = fi->userdata;
+
+	va_start(args, fmt);
+	VHiSax_putstatus(st->l1.hardware, st->l3.debug_id, fmt, args);
+	va_end(args);
+}
 
 u_char *
 findie(u_char * p, int size, u_char ie, int wanted_set)
@@ -115,25 +183,11 @@
 }
 
 void
-l3_debug(struct PStack *st, char *s)
-{
-	char str[256], tm[32];
-
-	jiftime(tm, jiffies);
-	sprintf(str, "%s l3 %s\n", tm, s);
-	HiSax_putstatus(st->l1.hardware, str);
-}
-
-void
 newl3state(struct l3_process *pc, int state)
 {
-	char tmp[80];
-
-	if (pc->debug & L3_DEB_STATE) {
-		sprintf(tmp, "newstate cr %d %d --> %d", pc->callref,
+	if (pc->debug & L3_DEB_STATE)
+		l3_debug(pc->st, "newstate cr %d %d --> %d", pc->callref,
 			pc->state, state);
-		l3_debug(pc->st, tmp);
-	}
 	pc->state = state;
 }
 
@@ -197,7 +251,7 @@
 {
 	struct sk_buff *skb = arg;
 
-	HiSax_putstatus(st->l1.hardware, "L3 no D protocol\n");
+	HiSax_putstatus(st->l1.hardware, "L3", "no D protocol");
 	if (skb) {
 		dev_kfree_skb(skb);
 	}
@@ -277,16 +331,25 @@
 		pp = np;
 		np = np->next;
 	}
-	printk(KERN_ERR "HiSax internal L3 error CR not in list\n");
+	printk(KERN_ERR "HiSax internal L3 error CR(%d) not in list\n", p->callref);
+	l3_debug(p->st, "HiSax internal L3 error CR(%d) not in list", p->callref);
 };
 
 void
-setstack_isdnl3(struct PStack *st, struct Channel *chanp)
+setstack_l3dc(struct PStack *st, struct Channel *chanp)
 {
 	char tmp[64];
 
 	st->l3.proc   = NULL;
 	st->l3.global = NULL;
+	skb_queue_head_init(&st->l3.squeue);
+	st->l3.l3m.fsm = &l3fsm;
+	st->l3.l3m.state = ST_L3_LC_REL;
+	st->l3.l3m.debug = 1;
+	st->l3.l3m.userdata = st;
+	st->l3.l3m.userint = 0;
+	st->l3.l3m.printdebug = l3m_debug;
+	strcpy(st->l3.debug_id, "L3DC ");
 
 #ifdef	CONFIG_HISAX_EURO
 	if (st->protocol == ISDN_PTYPE_EURO) {
@@ -321,6 +384,11 @@
 }
 
 void
+isdnl3_trans(struct PStack *st, int pr, void *arg) {
+	st->l3.l3l2(st, pr, arg);
+}
+
+void
 releasestack_isdnl3(struct PStack *st)
 {
 	while (st->l3.proc)
@@ -330,4 +398,136 @@
 		kfree(st->l3.global);
 		st->l3.global = NULL;
 	}
+	discard_queue(&st->l3.squeue);
+}
+
+void
+setstack_l3bc(struct PStack *st, struct Channel *chanp)
+{
+
+	st->l3.proc   = NULL;
+	st->l3.global = NULL;
+	skb_queue_head_init(&st->l3.squeue);
+	st->l3.l3m.fsm = &l3fsm;
+	st->l3.l3m.state = ST_L3_LC_REL;
+	st->l3.l3m.debug = 1;
+	st->l3.l3m.userdata = st;
+	st->l3.l3m.userint = 0;
+	st->l3.l3m.printdebug = l3m_debug;
+	strcpy(st->l3.debug_id, "L3BC ");
+	st->lli.l4l3 = isdnl3_trans;
+}
+
+static void
+lc_activate(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	FsmChangeState(fi, ST_L3_LC_ESTAB_WAIT);
+	st->l3.l3l2(st, DL_ESTABLISH | REQUEST, NULL);
+}
+
+static void
+lc_connect(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	FsmChangeState(fi, ST_L3_LC_ESTAB);
+	while ((skb = skb_dequeue(&st->l3.squeue))) {
+		st->l3.l3l2(st, DL_DATA | REQUEST, skb);
+	}
+	st->l3.l3l4(st, DL_ESTABLISH | INDICATION, NULL);
+}
+
+static void
+lc_release_req(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	if (fi->state == ST_L3_LC_ESTAB_WAIT)
+		FsmChangeState(fi, ST_L3_LC_REL);
+	else
+		FsmChangeState(fi, ST_L3_LC_REL_WAIT);
+	st->l3.l3l2(st, DL_RELEASE | REQUEST, NULL);
+}
+
+static void
+lc_release_ind(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	FsmChangeState(fi, ST_L3_LC_REL);
+	discard_queue(&st->l3.squeue);
+	st->l3.l3l4(st, DL_RELEASE | INDICATION, NULL);
+}
+
+/* *INDENT-OFF* */
+static struct FsmNode L3FnList[] HISAX_INITDATA =
+{
+	{ST_L3_LC_REL,		EV_ESTABLISH_REQ,	lc_activate},
+	{ST_L3_LC_REL,		EV_ESTABLISH_IND,	lc_connect},
+	{ST_L3_LC_REL,		EV_ESTABLISH_CNF,	lc_connect},
+	{ST_L3_LC_ESTAB_WAIT,	EV_ESTABLISH_CNF,	lc_connect},
+	{ST_L3_LC_ESTAB_WAIT,	EV_RELEASE_REQ,		lc_release_req},
+	{ST_L3_LC_ESTAB_WAIT,	EV_RELEASE_IND,		lc_release_ind},
+	{ST_L3_LC_ESTAB,	EV_RELEASE_IND,		lc_release_ind},
+	{ST_L3_LC_ESTAB,	EV_RELEASE_REQ,		lc_release_req},
+	{ST_L3_LC_REL_WAIT,	EV_RELEASE_CNF,		lc_release_ind},
+	{ST_L3_LC_REL_WAIT,	EV_ESTABLISH_REQ,	lc_activate},
+};
+/* *INDENT-ON* */
+
+#define L3_FN_COUNT (sizeof(L3FnList)/sizeof(struct FsmNode))
+
+void
+l3_msg(struct PStack *st, int pr, void *arg)
+{
+	
+	switch (pr) {
+		case (DL_DATA | REQUEST):
+			if (st->l3.l3m.state == ST_L3_LC_ESTAB) {
+				st->l3.l3l2(st, pr, arg);
+			} else {
+				struct sk_buff *skb = arg;
+
+				skb_queue_head(&st->l3.squeue, skb);
+				FsmEvent(&st->l3.l3m, EV_ESTABLISH_REQ, NULL); 
+			}
+			break;
+		case (DL_ESTABLISH | REQUEST):
+			FsmEvent(&st->l3.l3m, EV_ESTABLISH_REQ, NULL);
+			break;
+		case (DL_ESTABLISH | CONFIRM):
+			FsmEvent(&st->l3.l3m, EV_ESTABLISH_CNF, NULL);
+			break;
+		case (DL_ESTABLISH | INDICATION):
+			FsmEvent(&st->l3.l3m, EV_ESTABLISH_IND, NULL);
+			break;
+		case (DL_RELEASE | INDICATION):
+			FsmEvent(&st->l3.l3m, EV_RELEASE_IND, NULL);
+			break;
+		case (DL_RELEASE | CONFIRM):
+			FsmEvent(&st->l3.l3m, EV_RELEASE_CNF, NULL);
+			break;
+		case (DL_RELEASE | REQUEST):
+			FsmEvent(&st->l3.l3m, EV_RELEASE_REQ, NULL);
+			break;
+	}
+}
+
+HISAX_INITFUNC(void
+Isdnl3New(void))
+{
+	l3fsm.state_count = L3_STATE_COUNT;
+	l3fsm.event_count = L3_EVENT_COUNT;
+	l3fsm.strEvent = strL3Event;
+	l3fsm.strState = strL3State;
+	FsmNew(&l3fsm, L3FnList, L3_FN_COUNT);
+}
+
+void
+Isdnl3Free(void)
+{
+	FsmFree(&l3fsm);
 }
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/isdnl3.h linux/drivers/isdn/hisax/isdnl3.h
--- v2.3.3/linux/drivers/isdn/hisax/isdnl3.h	Wed Apr  1 16:20:59 1998
+++ linux/drivers/isdn/hisax/isdnl3.h	Sun May 23 10:03:41 1999
@@ -1,6 +1,17 @@
-/* $Id: isdnl3.h,v 2.0 1997/07/27 21:15:42 keil Exp $
+/* $Id: isdnl3.h,v 2.3 1998/11/15 23:55:06 keil Exp $
 
  * $Log: isdnl3.h,v $
+ * Revision 2.3  1998/11/15 23:55:06  keil
+ * changes from 2.0
+ *
+ * Revision 2.2  1998/05/25 14:10:17  keil
+ * HiSax 3.0
+ * X.75 and leased are working again.
+ *
+ * Revision 2.1  1998/05/25 12:58:13  keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
  * Revision 2.0  1997/07/27 21:15:42  keil
  * New Callref based layer3
  *
@@ -31,11 +42,12 @@
 
 struct stateentry {
 	int state;
-	u_char primitive;
+	int primitive;
 	void (*rout) (struct l3_process *, u_char, void *);
 };
 
-extern void l3_debug(struct PStack *st, char *s);
+#define l3_debug(st, fmt, args...) HiSax_putstatus(st->l1.hardware, "l3 ", fmt, ## args)
+
 extern void newl3state(struct l3_process *pc, int state);
 extern void L3InitTimer(struct l3_process *pc, struct L3Timer *t);
 extern void L3DelTimer(struct L3Timer *t);
@@ -45,3 +57,4 @@
 extern struct l3_process *new_l3_process(struct PStack *st, int cr);
 extern void release_l3_process(struct l3_process *p);
 extern struct l3_process *getl3proc(struct PStack *st, int cr);
+extern void l3_msg(struct PStack *st, int pr, void *arg);
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/ix1_micro.c linux/drivers/isdn/hisax/ix1_micro.c
--- v2.3.3/linux/drivers/isdn/hisax/ix1_micro.c	Wed Apr  1 16:21:01 1998
+++ linux/drivers/isdn/hisax/ix1_micro.c	Sun May 23 10:03:41 1999
@@ -1,4 +1,4 @@
-/* $Id: ix1_micro.c,v 2.6 1998/02/11 17:28:09 keil Exp $
+/* $Id: ix1_micro.c,v 2.7 1998/04/15 16:44:31 keil Exp $
 
  * ix1_micro.c  low level stuff for ITK ix1-micro Rev.2 isdn cards
  *              derived from the original file teles3.c from Karsten Keil
@@ -11,6 +11,9 @@
  *              Beat Doebeli
  *
  * $Log: ix1_micro.c,v $
+ * Revision 2.7  1998/04/15 16:44:31  keil
+ * new init code
+ *
  * Revision 2.6  1998/02/11 17:28:09  keil
  * Niccy PnP/PCI support
  *
@@ -81,7 +84,7 @@
 #include "isdnl1.h"
 
 extern const char *CardType[];
-const char *ix1_revision = "$Revision: 2.6 $";
+const char *ix1_revision = "$Revision: 2.7 $";
 
 #define byteout(addr,val) outb(val,addr)
 #define bytein(addr) inb(addr)
@@ -277,10 +280,7 @@
 			return(request_irq(cs->irq, &ix1micro_interrupt,
 					I4L_IRQ_FLAG, "HiSax", cs));
 		case CARD_INIT:
-			clear_pending_isac_ints(cs);
-			clear_pending_hscx_ints(cs);
-			initisac(cs);
-			inithscx(cs);
+			inithscxisac(cs, 3);
 			return(0);
 		case CARD_TEST:
 			return(0);
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/l3_1tr6.c linux/drivers/isdn/hisax/l3_1tr6.c
--- v2.3.3/linux/drivers/isdn/hisax/l3_1tr6.c	Wed Apr  1 16:21:02 1998
+++ linux/drivers/isdn/hisax/l3_1tr6.c	Sun May 23 10:03:41 1999
@@ -1,11 +1,29 @@
-/* $Id: l3_1tr6.c,v 2.4 1998/02/12 23:07:57 keil Exp $
+/* $Id: l3_1tr6.c,v 2.8 1998/11/15 23:55:08 keil Exp $
 
  *  German 1TR6 D-channel protocol
  *
- * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author       Karsten Keil (keil@isdn4linux.de)
+ *
+ *		This file is (c) under GNU PUBLIC LICENSE
+ *		For changes and modifications please read
+ *		../../../Documentation/isdn/HiSax.cert
  *
  *
  * $Log: l3_1tr6.c,v $
+ * Revision 2.8  1998/11/15 23:55:08  keil
+ * changes from 2.0
+ *
+ * Revision 2.7  1998/08/13 23:36:45  keil
+ * HiSax 3.1 - don't work stable with current LinkLevel
+ *
+ * Revision 2.6  1998/05/25 14:10:18  keil
+ * HiSax 3.0
+ * X.75 and leased are working again.
+ *
+ * Revision 2.5  1998/05/25 12:58:14  keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
  * Revision 2.4  1998/02/12 23:07:57  keil
  * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
  *
@@ -38,7 +56,7 @@
 #include <linux/ctype.h>
 
 extern char *HiSax_getrev(const char *revision);
-const char *l3_1tr6_revision = "$Revision: 2.4 $";
+const char *l3_1tr6_revision = "$Revision: 2.8 $";
 
 #define MsgHead(ptr, cref, mty, dis) \
 	*ptr++ = dis; \
@@ -56,66 +74,34 @@
 		return;
 	p = skb_put(skb, 4);
 	MsgHead(p, pc->callref, mt, pd);
-	pc->st->l3.l3l2(pc->st, DL_DATA, skb);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
 }
 
-static int
-l31tr6_check_messagetype_validity(int mt, int pd) {
-/* verify if a message type exists */
-
-	if (pd == PROTO_DIS_N0)
-		switch(mt) {
-		   case MT_N0_REG_IND:
-		   case MT_N0_CANC_IND:
-		   case MT_N0_FAC_STA:
-		   case MT_N0_STA_ACK:
-		   case MT_N0_STA_REJ:
-		   case MT_N0_FAC_INF:
-		   case MT_N0_INF_ACK:
-		   case MT_N0_INF_REJ:
-		   case MT_N0_CLOSE:
-		   case MT_N0_CLO_ACK:
-			return(1);
-		   default:
-			return(0);
-		}
-	else if (pd == PROTO_DIS_N1)
-		switch(mt) {
-		   case MT_N1_ESC:
-		   case MT_N1_ALERT:
-		   case MT_N1_CALL_SENT:
-		   case MT_N1_CONN:
-		   case MT_N1_CONN_ACK:
-		   case MT_N1_SETUP:
-		   case MT_N1_SETUP_ACK:
-		   case MT_N1_RES:
-		   case MT_N1_RES_ACK:
-		   case MT_N1_RES_REJ:
-		   case MT_N1_SUSP:
-		   case MT_N1_SUSP_ACK:
-		   case MT_N1_SUSP_REJ:
-		   case MT_N1_USER_INFO:
-		   case MT_N1_DET:
-		   case MT_N1_DISC:
-		   case MT_N1_REL:
-		   case MT_N1_REL_ACK:
-		   case MT_N1_CANC_ACK:
-		   case MT_N1_CANC_REJ:
-		   case MT_N1_CON_CON:
-		   case MT_N1_FAC:
-		   case MT_N1_FAC_ACK:
-		   case MT_N1_FAC_CAN:
-		   case MT_N1_FAC_REG:
-		   case MT_N1_FAC_REJ:
-		   case MT_N1_INFO:
-		   case MT_N1_REG_ACK:
-		   case MT_N1_REG_REJ:
-		   case MT_N1_STAT:
-		   	return (1);
-		   default:
-		   	return(0);
-		}
-	return(0);
+static void
+l3_1tr6_release_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	StopAllL3Timer(pc);
+	newl3state(pc, 19);
+	l3_1TR6_message(pc, MT_N1_REL, PROTO_DIS_N1);
+	L3AddTimer(&pc->timer, T308, CC_T308_1);
+}
+
+static void
+l3_1tr6_invalid(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+
+	dev_kfree_skb(skb);
+	l3_1tr6_release_req(pc, 0, NULL);
+}
+
+static void
+l3_1tr6_error(struct l3_process *pc, u_char *msg, struct sk_buff *skb)
+{
+	dev_kfree_skb(skb);
+	if (pc->st->l3.debug & L3_DEB_WARN)
+		l3_debug(pc->st, msg);
+	l3_1tr6_release_req(pc, 0, NULL);
 }
 
 static void
@@ -204,7 +190,7 @@
 	L3DelTimer(&pc->timer);
 	L3AddTimer(&pc->timer, T303, CC_T303);
 	newl3state(pc, 1);
-	pc->st->l3.l3l2(pc->st, DL_DATA, skb);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
 }
 
 static void
@@ -220,17 +206,29 @@
 	/* Channel Identification */
 	p = skb->data;
 	if ((p = findie(p, skb->len, WE0_chanID, 0))) {
-		pc->para.bchannel = p[2] & 0x3;
-		bcfound++;
-	} else if (pc->st->l3.debug & L3_DEB_WARN)
-		l3_debug(pc->st, "setup without bchannel");
+		if (p[1] != 1) {
+			l3_1tr6_error(pc, "setup wrong chanID len", skb);
+			return;
+		}
+		if ((p[2] & 0xf4) != 0x80) {
+			l3_1tr6_error(pc, "setup wrong WE0_chanID", skb);
+			return;
+		}
+		if ((pc->para.bchannel = p[2] & 0x3))
+				bcfound++;
+	} else {
+		l3_1tr6_error(pc, "missing setup chanID", skb);
+		return;
+	}
 
 	p = skb->data;
 	if ((p = findie(p, skb->len, WE6_serviceInd, 6))) {
 		pc->para.setup.si1 = p[2];
 		pc->para.setup.si2 = p[3];
-	} else if (pc->st->l3.debug & L3_DEB_WARN)
-		l3_debug(pc->st, "setup without service indicator");
+	} else {
+		l3_1tr6_error(pc, "missing setup SI", skb);
+		return;
+	}
 
 	p = skb->data;
 	if ((p = findie(p, skb->len, WE0_destAddr, 0)))
@@ -261,7 +259,7 @@
 			l3_debug(pc->st, tmp);
 		}
 		newl3state(pc, 6);
-		pc->st->l3.l3l4(pc, CC_SETUP_IND, NULL);
+		pc->st->l3.l3l4(pc->st, CC_SETUP | INDICATION, pc);
 	} else
 		release_l3_process(pc);
 }
@@ -276,12 +274,22 @@
 	p = skb->data;
 	newl3state(pc, 2);
 	if ((p = findie(p, skb->len, WE0_chanID, 0))) {
+		if (p[1] != 1) {
+			l3_1tr6_error(pc, "setup_ack wrong chanID len", skb);
+			return;
+		}
+		if ((p[2] & 0xf4) != 0x80) {
+			l3_1tr6_error(pc, "setup_ack wrong WE0_chanID", skb);
+			return;
+		}
 		pc->para.bchannel = p[2] & 0x3;
-	} else if (pc->st->l3.debug & L3_DEB_WARN)
-		l3_debug(pc->st, "setup answer without bchannel");
+	} else {
+		l3_1tr6_error(pc, "missing setup_ack WE0_chanID", skb);
+		return;
+	}
 	dev_kfree_skb(skb);
 	L3AddTimer(&pc->timer, T304, CC_T304);
-	pc->st->l3.l3l4(pc, CC_MORE_INFO, NULL);
+	pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc);
 }
 
 static void
@@ -293,13 +301,27 @@
 	L3DelTimer(&pc->timer);
 	p = skb->data;
 	if ((p = findie(p, skb->len, WE0_chanID, 0))) {
+		if (p[1] != 1) {
+			l3_1tr6_error(pc, "call sent wrong chanID len", skb);
+			return;
+		}
+		if ((p[2] & 0xf4) != 0x80) {
+			l3_1tr6_error(pc, "call sent wrong WE0_chanID", skb);
+			return;
+		}
+		if ((pc->state == 2) && (pc->para.bchannel != (p[2] & 0x3))) {
+			l3_1tr6_error(pc, "call sent wrong chanID value", skb);
+			return;
+		}
 		pc->para.bchannel = p[2] & 0x3;
-	} else if (pc->st->l3.debug & L3_DEB_WARN)
-		l3_debug(pc->st, "setup answer without bchannel");
+	} else {
+		l3_1tr6_error(pc, "missing call sent WE0_chanID", skb);
+		return;
+	}
 	dev_kfree_skb(skb);
 	L3AddTimer(&pc->timer, T310, CC_T310);
 	newl3state(pc, 3);
-	pc->st->l3.l3l4(pc, CC_PROCEEDING_IND, NULL);
+	pc->st->l3.l3l4(pc->st, CC_PROCEEDING | INDICATION, pc);
 }
 
 static void
@@ -310,7 +332,7 @@
 	dev_kfree_skb(skb);
 	L3DelTimer(&pc->timer);	/* T304 */
 	newl3state(pc, 4);
-	pc->st->l3.l3l4(pc, CC_ALERTING_IND, NULL);
+	pc->st->l3.l3l4(pc->st, CC_ALERTING | INDICATION, pc);
 }
 
 static void
@@ -330,7 +352,7 @@
 		}
 		if (tmpcharge > pc->para.chargeinfo) {
 			pc->para.chargeinfo = tmpcharge;
-			pc->st->l3.l3l4(pc, CC_INFO_CHARGE, NULL);
+			pc->st->l3.l3l4(pc->st, CC_CHARGE | INDICATION, pc);
 		}
 		if (pc->st->l3.debug & L3_DEB_CHARGE) {
 			sprintf(tmp, "charging info %d", pc->para.chargeinfo);
@@ -356,10 +378,14 @@
 	struct sk_buff *skb = arg;
 
 	L3DelTimer(&pc->timer);	/* T310 */
+	if (!findie(skb->data, skb->len, WE6_date, 6)) {
+		l3_1tr6_error(pc, "missing connect date", skb);
+		return;
+	}
 	newl3state(pc, 10);
 	dev_kfree_skb(skb);
 	pc->para.chargeinfo = 0;
-	pc->st->l3.l3l4(pc, CC_SETUP_CNF, NULL);
+	pc->st->l3.l3l4(pc->st, CC_SETUP | CONFIRM, pc);
 }
 
 static void
@@ -380,13 +406,16 @@
 			pc->para.cause = 0;
 			pc->para.loc = 0;
 		}
-	} else
+	} else {
 		pc->para.cause = -1;
+		l3_1tr6_error(pc, "missing REL cause", skb);
+		return;
+	}
 	dev_kfree_skb(skb);
 	StopAllL3Timer(pc);
 	newl3state(pc, 0);
 	l3_1TR6_message(pc, MT_N1_REL_ACK, PROTO_DIS_N1);
-	pc->st->l3.l3l4(pc, CC_RELEASE_IND, NULL);
+	pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
 	release_l3_process(pc);
 }
 
@@ -399,7 +428,7 @@
 	StopAllL3Timer(pc);
 	newl3state(pc, 0);
 	pc->para.cause = -1;
-	pc->st->l3.l3l4(pc, CC_RELEASE_CNF, NULL);
+	pc->st->l3.l3l4(pc->st, CC_RELEASE | CONFIRM, pc);
 	release_l3_process(pc);
 }
 
@@ -421,7 +450,7 @@
 		}
 		if (tmpcharge > pc->para.chargeinfo) {
 			pc->para.chargeinfo = tmpcharge;
-			pc->st->l3.l3l4(pc, CC_INFO_CHARGE, NULL);
+			pc->st->l3.l3l4(pc->st, CC_CHARGE | INDICATION, pc);
 		}
 		if (pc->st->l3.debug & L3_DEB_CHARGE) {
 			sprintf(tmp, "charging info %d", pc->para.chargeinfo);
@@ -448,9 +477,13 @@
 			l3_debug(pc->st, "cause not found");
 		pc->para.cause = -1;
 	}
+	if (!findie(skb->data, skb->len, WE6_date, 6)) {
+		l3_1tr6_error(pc, "missing connack date", skb);
+		return;
+	}
 	dev_kfree_skb(skb);
 	newl3state(pc, 12);
-	pc->st->l3.l3l4(pc, CC_DISCONNECT_IND, NULL);
+	pc->st->l3.l3l4(pc->st, CC_DISCONNECT | INDICATION, pc);
 }
 
 
@@ -459,11 +492,15 @@
 {
 	struct sk_buff *skb = arg;
 
+	if (!findie(skb->data, skb->len, WE6_date, 6)) {
+		l3_1tr6_error(pc, "missing connack date", skb);
+		return;
+	}
 	dev_kfree_skb(skb);
 	newl3state(pc, 10);
 	pc->para.chargeinfo = 0;
 	L3DelTimer(&pc->timer);
-	pc->st->l3.l3l4(pc, CC_SETUP_COMPLETE_IND, NULL);
+	pc->st->l3.l3l4(pc->st, CC_SETUP_COMPL | INDICATION, pc);
 }
 
 static void
@@ -502,7 +539,7 @@
 	if (!(skb = l3_alloc_skb(l)))
 		return;
 	memcpy(skb_put(skb, l), tmp, l);
-	pc->st->l3.l3l2(pc->st, DL_DATA, skb);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
 	L3DelTimer(&pc->timer);
 	L3AddTimer(&pc->timer, T313, CC_T313);
 }
@@ -545,20 +582,11 @@
 	if (!(skb = l3_alloc_skb(l)))
 		return;
 	memcpy(skb_put(skb, l), tmp, l);
-	pc->st->l3.l3l2(pc->st, DL_DATA, skb);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
 	L3AddTimer(&pc->timer, T305, CC_T305);
 }
 
 static void
-l3_1tr6_release_req(struct l3_process *pc, u_char pr, void *arg)
-{
-	StopAllL3Timer(pc);
-	newl3state(pc, 19);
-	l3_1TR6_message(pc, MT_N1_REL, PROTO_DIS_N1);
-	L3AddTimer(&pc->timer, T308, CC_T308_1);
-}
-
-static void
 l3_1tr6_t303(struct l3_process *pc, u_char pr, void *arg)
 {
 	if (pc->N303 > 0) {
@@ -567,8 +595,8 @@
 		l3_1tr6_setup_req(pc, pr, arg);
 	} else {
 		L3DelTimer(&pc->timer);
-		pc->st->l3.l3l4(pc, CC_NOSETUP_RSP_ERR, NULL);
-		release_l3_process(pc);
+		pc->para.cause = 0;
+		l3_1tr6_disconnect_req(pc, 0, NULL);
 	}
 }
 
@@ -578,7 +606,7 @@
 	L3DelTimer(&pc->timer);
 	pc->para.cause = 0xE6;
 	l3_1tr6_disconnect_req(pc, pr, NULL);
-	pc->st->l3.l3l4(pc, CC_SETUP_ERR, NULL);
+	pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
 }
 
 static void
@@ -613,7 +641,7 @@
 	if (!(skb = l3_alloc_skb(l)))
 		return;
 	memcpy(skb_put(skb, l), tmp, l);
-	pc->st->l3.l3l2(pc->st, DL_DATA, skb);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
 	L3AddTimer(&pc->timer, T308, CC_T308_1);
 }
 
@@ -623,7 +651,7 @@
 	L3DelTimer(&pc->timer);
 	pc->para.cause = 0xE6;
 	l3_1tr6_disconnect_req(pc, pr, NULL);
-	pc->st->l3.l3l4(pc, CC_SETUP_ERR, NULL);
+	pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
 }
 
 static void
@@ -632,7 +660,7 @@
 	L3DelTimer(&pc->timer);
 	pc->para.cause = 0xE6;
 	l3_1tr6_disconnect_req(pc, pr, NULL);
-	pc->st->l3.l3l4(pc, CC_CONNECT_ERR, NULL);
+	pc->st->l3.l3l4(pc->st, CC_CONNECT_ERR, pc);
 }
 
 static void
@@ -648,29 +676,29 @@
 l3_1tr6_t308_2(struct l3_process *pc, u_char pr, void *arg)
 {
 	L3DelTimer(&pc->timer);
-	pc->st->l3.l3l4(pc, CC_RELEASE_ERR, NULL);
+	pc->st->l3.l3l4(pc->st, CC_RELEASE_ERR, pc);
 	release_l3_process(pc);
 }
 /* *INDENT-OFF* */
 static struct stateentry downstl[] =
 {
 	{SBIT(0),
-	 CC_SETUP_REQ, l3_1tr6_setup_req},
+	 CC_SETUP | REQUEST, l3_1tr6_setup_req},
    	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(6) | SBIT(7) | SBIT(8) |
     	 SBIT(10),
-    	 CC_DISCONNECT_REQ, l3_1tr6_disconnect_req},
+    	 CC_DISCONNECT | REQUEST, l3_1tr6_disconnect_req},
 	{SBIT(12),
-	 CC_RELEASE_REQ, l3_1tr6_release_req},
+	 CC_RELEASE | REQUEST, l3_1tr6_release_req},
 	{ALL_STATES,
-	 CC_DLRL, l3_1tr6_reset},
+	 CC_DLRL | REQUEST, l3_1tr6_reset},
 	{SBIT(6),
-	 CC_IGNORE, l3_1tr6_reset},
+	 CC_IGNORE | REQUEST, l3_1tr6_reset},
 	{SBIT(6),
-	 CC_REJECT_REQ, l3_1tr6_disconnect_req},
+	 CC_REJECT | REQUEST, l3_1tr6_disconnect_req},
 	{SBIT(6),
-	 CC_ALERTING_REQ, l3_1tr6_alert_req},
+	 CC_ALERTING | REQUEST, l3_1tr6_alert_req},
 	{SBIT(6) | SBIT(7),
-	 CC_SETUP_RSP, l3_1tr6_setup_rsp},
+	 CC_SETUP | RESPONSE, l3_1tr6_setup_rsp},
 	{SBIT(1),
 	 CC_T303, l3_1tr6_t303},
 	{SBIT(2),
@@ -687,12 +715,14 @@
 	 CC_T308_2, l3_1tr6_t308_2},
 };
 
-static int downstl_len = sizeof(downstl) /
-sizeof(struct stateentry);
+#define DOWNSTL_LEN \
+	(sizeof(downstl) / sizeof(struct stateentry))
 
 static struct stateentry datastln1[] =
 {
 	{SBIT(0),
+	 MT_N1_INVALID, l3_1tr6_invalid},
+	{SBIT(0),
 	 MT_N1_SETUP, l3_1tr6_setup},
 	{SBIT(1),
 	 MT_N1_SETUP_ACK, l3_1tr6_setup_ack},
@@ -711,18 +741,20 @@
 	{SBIT(10),
 	 MT_N1_INFO, l3_1tr6_info},
 	{SBIT(0) | SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) |
-	 SBIT(10) | SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19),
+	 SBIT(10) | SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17),
 	 MT_N1_REL, l3_1tr6_rel},
 	{SBIT(19),
+	 MT_N1_REL, l3_1tr6_rel_ack},
+	{SBIT(0) | SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) |
+	 SBIT(10) | SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17),
+	 MT_N1_REL_ACK, l3_1tr6_invalid},
+	{SBIT(19),
 	 MT_N1_REL_ACK, l3_1tr6_rel_ack}
 };
 /* *INDENT-ON* */
 
-
-
-
-static int datastln1_len = sizeof(datastln1) /
-sizeof(struct stateentry);
+#define DATASTLN1_LEN \
+	(sizeof(datastln1) / sizeof(struct stateentry))
 
 static void
 up1tr6(struct PStack *st, int pr, void *arg)
@@ -732,9 +764,21 @@
 	struct sk_buff *skb = arg;
 	char tmp[80];
 
+	switch (pr) {
+		case (DL_DATA | INDICATION):
+		case (DL_UNIT_DATA | INDICATION):
+			break;
+		case (DL_ESTABLISH | CONFIRM):
+		case (DL_ESTABLISH | INDICATION):
+		case (DL_RELEASE | INDICATION):
+		case (DL_RELEASE | CONFIRM):
+			l3_msg(st, pr, arg);
+			return;
+			break;
+	}
 	if (skb->len < 4) {
 		if (st->l3.debug & L3_DEB_PROTERR) {
-			sprintf(tmp, "up1tr6 len only %d", skb->len);
+			sprintf(tmp, "up1tr6 len only %ld", skb->len);
 			l3_debug(st, tmp);
 		}
 		dev_kfree_skb(skb);
@@ -742,8 +786,8 @@
 	}
 	if ((skb->data[0] & 0xfe) != PROTO_DIS_N0) {
 		if (st->l3.debug & L3_DEB_PROTERR) {
-			sprintf(tmp, "up1tr6%sunexpected discriminator %x message len %d",
-				(pr == DL_DATA) ? " " : "(broadcast) ",
+			sprintf(tmp, "up1tr6%sunexpected discriminator %x message len %ld",
+				(pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
 				skb->data[0], skb->len);
 			l3_debug(st, tmp);
 		}
@@ -764,12 +808,33 @@
 		dev_kfree_skb(skb);
 		if (st->l3.debug & L3_DEB_STATE) {
 			sprintf(tmp, "up1tr6%s N0 mt %x unhandled",
-			     (pr == DL_DATA) ? " " : "(broadcast) ", mt);
+			     (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ", mt);
 			l3_debug(st, tmp);
 		}
 	} else if (skb->data[0] == PROTO_DIS_N1) {
 		if (!(proc = getl3proc(st, cr))) {
-			if ((mt == MT_N1_SETUP) && (cr < 128)) {
+			if (mt == MT_N1_SETUP) { 
+				if (cr < 128) {
+					if (!(proc = new_l3_process(st, cr))) {
+						if (st->l3.debug & L3_DEB_PROTERR) {
+							sprintf(tmp, "up1tr6 no roc mem");
+							l3_debug(st, tmp);
+						}
+						dev_kfree_skb(skb);
+						return;
+					}
+				} else {
+					dev_kfree_skb(skb);
+					return;
+				}
+			} else if ((mt == MT_N1_REL) || (mt == MT_N1_REL_ACK) ||
+				(mt == MT_N1_CANC_ACK) || (mt == MT_N1_CANC_REJ) ||
+				(mt == MT_N1_REG_ACK) || (mt == MT_N1_REG_REJ) ||
+				(mt == MT_N1_SUSP_ACK) || (mt == MT_N1_RES_REJ) ||
+				(mt == MT_N1_INFO)) {
+				dev_kfree_skb(skb);
+				return;
+			} else {
 				if (!(proc = new_l3_process(st, cr))) {
 					if (st->l3.debug & L3_DEB_PROTERR) {
 						sprintf(tmp, "up1tr6 no roc mem");
@@ -778,20 +843,18 @@
 					dev_kfree_skb(skb);
 					return;
 				}
-			} else {
-				dev_kfree_skb(skb);
-				return;
+				mt = MT_N1_INVALID;
 			}
 		}
-		for (i = 0; i < datastln1_len; i++)
+		for (i = 0; i < DATASTLN1_LEN; i++)
 			if ((mt == datastln1[i].primitive) &&
 			    ((1 << proc->state) & datastln1[i].state))
 				break;
-		if (i == datastln1_len) {
+		if (i == DATASTLN1_LEN) {
 			dev_kfree_skb(skb);
 			if (st->l3.debug & L3_DEB_STATE) {
 				sprintf(tmp, "up1tr6%sstate %d mt %x unhandled",
-				  (pr == DL_DATA) ? " " : "(broadcast) ",
+				  (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
 					proc->state, mt);
 				l3_debug(st, tmp);
 			}
@@ -799,7 +862,7 @@
 		} else {
 			if (st->l3.debug & L3_DEB_STATE) {
 				sprintf(tmp, "up1tr6%sstate %d mt %x",
-				  (pr == DL_DATA) ? " " : "(broadcast) ",
+				  (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
 					proc->state, mt);
 				l3_debug(st, tmp);
 			}
@@ -816,7 +879,10 @@
 	struct Channel *chan;
 	char tmp[80];
 
-	if (CC_SETUP_REQ == pr) {
+	if (((DL_ESTABLISH | REQUEST)== pr) || ((DL_RELEASE | REQUEST)== pr)) {
+		l3_msg(st, pr, NULL);
+		return;
+	} else if ((CC_SETUP | REQUEST) == pr) {
 		chan = arg;
 		cr = newcallref();
 		cr |= 0x80;
@@ -832,11 +898,11 @@
 		proc = arg;
 	}
 
-	for (i = 0; i < downstl_len; i++)
+	for (i = 0; i < DOWNSTL_LEN; i++)
 		if ((pr == downstl[i].primitive) &&
 		    ((1 << proc->state) & downstl[i].state))
 			break;
-	if (i == downstl_len) {
+	if (i == DOWNSTL_LEN) {
 		if (st->l3.debug & L3_DEB_STATE) {
 			sprintf(tmp, "down1tr6 state %d prim %d unhandled",
 				proc->state, pr);
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/l3_1tr6.h linux/drivers/isdn/hisax/l3_1tr6.h
--- v2.3.3/linux/drivers/isdn/hisax/l3_1tr6.h	Wed Apr  1 16:21:02 1998
+++ linux/drivers/isdn/hisax/l3_1tr6.h	Sun May 23 10:03:41 1999
@@ -1,8 +1,11 @@
-/* $Id: l3_1tr6.h,v 2.0 1997/07/27 21:15:47 keil Exp $
+/* $Id: l3_1tr6.h,v 2.1 1998/08/13 23:36:48 keil Exp $
  *
  *  German 1TR6 D-channel protocol defines
  *
  * $Log: l3_1tr6.h,v $
+ * Revision 2.1  1998/08/13 23:36:48  keil
+ * HiSax 3.1 - don't work stable with current LinkLevel
+ *
  * Revision 2.0  1997/07/27 21:15:47  keil
  * New Callref based layer3
  *
@@ -64,6 +67,7 @@
 #define MT_N1_REG_ACK 0x6C
 #define MT_N1_REG_REJ 0x6F
 #define MT_N1_STAT 0x63
+#define MT_N1_INVALID 0
 
 /*
  * W Elemente
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/l3dss1.c linux/drivers/isdn/hisax/l3dss1.c
--- v2.3.3/linux/drivers/isdn/hisax/l3dss1.c	Wed Apr  1 16:21:02 1998
+++ linux/drivers/isdn/hisax/l3dss1.c	Sun May 23 10:03:41 1999
@@ -1,14 +1,36 @@
-/* $Id: l3dss1.c,v 2.7 1998/02/12 23:08:01 keil Exp $
+/* $Id: l3dss1.c,v 2.12 1998/11/15 23:55:10 keil Exp $
 
  * EURO/DSS1 D-channel protocol
  *
- * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author       Karsten Keil (keil@isdn4linux.de)
  *              based on the teles driver from Jan den Ouden
  *
+ *		This file is (c) under GNU PUBLIC LICENSE
+ *		For changes and modifications please read
+ *		../../../Documentation/isdn/HiSax.cert
+ *
  * Thanks to    Jan den Ouden
  *              Fritz Elfert
  *
  * $Log: l3dss1.c,v $
+ * Revision 2.12  1998/11/15 23:55:10  keil
+ * changes from 2.0
+ *
+ * Revision 2.11  1998/08/13 23:36:51  keil
+ * HiSax 3.1 - don't work stable with current LinkLevel
+ *
+ * Revision 2.10  1998/05/25 14:10:20  keil
+ * HiSax 3.0
+ * X.75 and leased are working again.
+ *
+ * Revision 2.9  1998/05/25 12:58:17  keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
+ * Revision 2.8  1998/03/19 13:18:47  keil
+ * Start of a CAPI like interface for supplementary Service
+ * first service: SUSPEND
+ *
  * Revision 2.7  1998/02/12 23:08:01  keil
  * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
  *
@@ -50,9 +72,7 @@
 #include <linux/ctype.h>
 
 extern char *HiSax_getrev(const char *revision);
-const char *dss1_revision = "$Revision: 2.7 $";
-
-#define EXT_BEARER_CAPS 1
+const char *dss1_revision = "$Revision: 2.12 $";
 
 #define	MsgHead(ptr, cref, mty) \
 	*ptr++ = 0x8; \
@@ -61,12 +81,11 @@
 	*ptr++ = mty
 
 
-#ifdef HISAX_DE_AOC
+#if HISAX_DE_AOC
 static void
-l3dss1_parse_facility(struct l3_process *pc, u_char *p)
+l3dss1_parse_facility(struct l3_process *pc, u_char * p)
 {
 	int qd_len = 0;
-	char tmp[32];
 
 	p++;
 	qd_len = *p++;
@@ -74,91 +93,99 @@
 		l3_debug(pc->st, "qd_len == 0");
 		return;
 	}
-	if((*p & 0x1F) != 0x11) {	/* Service discriminator, supplementary service */
+	if ((*p & 0x1F) != 0x11) {	/* Service discriminator, supplementary service */
 		l3_debug(pc->st, "supplementary service != 0x11");
 		return;
 	}
-	while(qd_len > 0 && !(*p & 0x80)) {	/* extension ? */
-		p++; qd_len--;
-	} 
-	if(qd_len < 2) {
+	while (qd_len > 0 && !(*p & 0x80)) {	/* extension ? */
+		p++;
+		qd_len--;
+	}
+	if (qd_len < 2) {
 		l3_debug(pc->st, "qd_len < 2");
 		return;
 	}
-	p++; qd_len--;
-	if((*p & 0xE0) != 0xA0) {	/* class and form */
+	p++;
+	qd_len--;
+	if ((*p & 0xE0) != 0xA0) {	/* class and form */
 		l3_debug(pc->st, "class and form != 0xA0");
 		return;
 	}
-	switch(*p & 0x1F) {		/* component tag */
-	    case 1: /* invoke */
-		{
-		    unsigned char nlen, ilen;
-		    int ident;
-    
-		    p++; qd_len--;
-		    if(qd_len < 1) {
-			    l3_debug(pc->st, "qd_len < 1");
-			    break;
-		    }
-		    if(*p & 0x80) { /* length format */
-			    l3_debug(pc->st, "*p & 0x80 length format");
-			    break;
-		    }
-		    nlen = *p++; qd_len--;
-		    if(qd_len < nlen) {
-			    l3_debug(pc->st, "qd_len < nlen");
-			    return;
-		    }
-		    qd_len -= nlen;
-    
-		    if(nlen < 2) {
-			    l3_debug(pc->st, "nlen < 2");
-			    return;
-		    }
-		    if(*p != 0x02) {	/* invoke identifier tag */
-			    l3_debug(pc->st, "invoke identifier tag !=0x02");
-			    return;
-		    }
-		    p++; nlen--;
-		    if(*p & 0x80) { /* length format */
-			    l3_debug(pc->st, "*p & 0x80 length format 2");
-			    break;
-		    }
-		    ilen = *p++; nlen--;
-		    if(ilen > nlen || ilen == 0) {
-			    l3_debug(pc->st, "ilen > nlen || ilen == 0");
-			    return;
-		    }
-		    nlen -= ilen;
-		    ident = 0;
-		    while(ilen > 0) {
-			    ident = (ident << 8) | (*p++ & 0xFF);	/* invoke identifier */
-			    ilen--;
-		    }
-    
-		    if(nlen < 2) {
-			    l3_debug(pc->st, "nlen < 2 22");
-			    return;
-		    }
-		    if(*p != 0x02)	{	/* operation value */ 
-			    l3_debug(pc->st, "operation value !=0x02");
-			    return;
-		    }
-		    p++; nlen--;
-		    ilen = *p++; nlen--;
-		    if(ilen > nlen || ilen == 0) {
-			    l3_debug(pc->st, "ilen > nlen || ilen == 0 22");
-			    return;
-		    }
-		    nlen -= ilen;
-		    ident = 0;
-		    while(ilen > 0) {
-			    ident = (ident << 8) | (*p++ & 0xFF);
-			    ilen--;
-		    }
-    
-    #define FOO1(s,a,b) \
+	switch (*p & 0x1F) {	/* component tag */
+		case 1:	/* invoke */
+			{
+				unsigned char nlen = 0, ilen;
+				int ident;
+
+				p++;
+				qd_len--;
+				if (qd_len < 1) {
+					l3_debug(pc->st, "qd_len < 1");
+					break;
+				}
+				if (*p & 0x80) {	/* length format */
+					l3_debug(pc->st, "*p & 0x80 length format");
+					break;
+				}
+				nlen = *p++;
+				qd_len--;
+				if (qd_len < nlen) {
+					l3_debug(pc->st, "qd_len < nlen");
+					return;
+				}
+				qd_len -= nlen;
+
+				if (nlen < 2) {
+					l3_debug(pc->st, "nlen < 2");
+					return;
+				}
+				if (*p != 0x02) {	/* invoke identifier tag */
+					l3_debug(pc->st, "invoke identifier tag !=0x02");
+					return;
+				}
+				p++;
+				nlen--;
+				if (*p & 0x80) {	/* length format */
+					l3_debug(pc->st, "*p & 0x80 length format 2");
+					break;
+				}
+				ilen = *p++;
+				nlen--;
+				if (ilen > nlen || ilen == 0) {
+					l3_debug(pc->st, "ilen > nlen || ilen == 0");
+					return;
+				}
+				nlen -= ilen;
+				ident = 0;
+				while (ilen > 0) {
+					ident = (ident << 8) | (*p++ & 0xFF);	/* invoke identifier */
+					ilen--;
+				}
+
+				if (nlen < 2) {
+					l3_debug(pc->st, "nlen < 2 22");
+					return;
+				}
+				if (*p != 0x02) {	/* operation value */
+					l3_debug(pc->st, "operation value !=0x02");
+					return;
+				}
+				p++;
+				nlen--;
+				ilen = *p++;
+				nlen--;
+				if (ilen > nlen || ilen == 0) {
+					l3_debug(pc->st, "ilen > nlen || ilen == 0 22");
+					return;
+				}
+				nlen -= ilen;
+				ident = 0;
+				while (ilen > 0) {
+					ident = (ident << 8) | (*p++ & 0xFF);
+					ilen--;
+				}
+
+#define FOO1(s,a,b) \
 	    while(nlen > 1) {		\
 		    int ilen = p[1];	\
 		    if(nlen < ilen+2) {	\
@@ -174,72 +201,74 @@
 			    p += ilen+2;	\
 		    }			\
 	    }
-			    
-		    switch(ident) {
-		    default:
-			    break;
-		    case 0x22: /* during */
-			    FOO1("1A",0x30,FOO1("1C",0xA1,FOO1("1D",0x30,FOO1("1E",0x02,({
-				    ident = 0;
-				    while(ilen > 0) {
-					    ident = (ident<<8) | *p++;
-					    ilen--;
-				    }
-				    if (ident > pc->para.chargeinfo) {
-					    pc->para.chargeinfo = ident;
-					    pc->st->l3.l3l4(pc, CC_INFO_CHARGE, NULL);
-				    }
-				    if (pc->st->l3.debug & L3_DEB_CHARGE) {
-					    if (*(p+2) == 0) {
-						    sprintf(tmp, "charging info during %d", pc->para.chargeinfo);
-						    l3_debug(pc->st, tmp);
-					    }
-					    else {
-					    sprintf(tmp, "charging info final %d", pc->para.chargeinfo);
-					    l3_debug(pc->st, tmp);
-					    }
-				    }
-			    })))))
-			    break;
-		    case 0x24: /* final */
-			    FOO1("2A",0x30,FOO1("2B",0x30,FOO1("2C",0xA1,FOO1("2D",0x30,FOO1("2E",0x02,({
-				    ident = 0;
-				    while(ilen > 0) {
-					    ident = (ident<<8) | *p++;
-					    ilen--;
-				    }
-				    if (ident > pc->para.chargeinfo) {
-					    pc->para.chargeinfo = ident;
-					    pc->st->l3.l3l4(pc, CC_INFO_CHARGE, NULL);
-				    }
-				    if (pc->st->l3.debug & L3_DEB_CHARGE) {
-					    sprintf(tmp, "charging info final %d", pc->para.chargeinfo);
-					    l3_debug(pc->st, tmp);
-				    }
-			    }))))))
-		    break;
-		    }
-    #undef FOO1
-    
-		}
-	    break;
-	    case 2: /* return result */
-		    l3_debug(pc->st, "return result break");
-		    break;
-	    case 3: /* return error */
-		    l3_debug(pc->st, "return error break");
-		    break;
-	    default:
-		    l3_debug(pc->st, "default break");
-		    break;
+
+				switch (ident) {
+					default:
+						break;
+					case 0x22:	/* during */
+						FOO1("1A", 0x30, FOO1("1C", 0xA1, FOO1("1D", 0x30, FOO1("1E", 0x02, ( {
+							       ident = 0;
+							nlen = (nlen)?nlen:0; /* Make gcc happy */
+							while (ilen > 0) {
+														     ident = (ident << 8) | *p++;
+								  ilen--;
+									}
+														     if (ident > pc->para.chargeinfo) {
+														     pc->para.chargeinfo = ident;
+														     pc->st->l3.l3l4(pc->st, CC_CHARGE | INDICATION, pc);
+									}
+														     if (pc->st->l3.debug & L3_DEB_CHARGE) {
+														     if (*(p + 2) == 0) {
+														     l3_debug(pc->st, "charging info during %d", pc->para.chargeinfo);
+									}
+								   else {
+														     l3_debug(pc->st, "charging info final %d", pc->para.chargeinfo);
+									}
+									}
+									}
+								    )))))
+							break;
+					case 0x24:	/* final */
+						FOO1("2A", 0x30, FOO1("2B", 0x30, FOO1("2C", 0xA1, FOO1("2D", 0x30, FOO1("2E", 0x02, ( {
+							       ident = 0;
+							nlen = (nlen)?nlen:0; /* Make gcc happy */
+							while (ilen > 0) {
+																      ident = (ident << 8) | *p++;
+								  ilen--;
+									}
+																      if (ident > pc->para.chargeinfo) {
+																      pc->para.chargeinfo = ident;
+																      pc->st->l3.l3l4(pc->st, CC_CHARGE | INDICATION, pc);
+									}
+																      if (pc->st->l3.debug & L3_DEB_CHARGE) {
+																      l3_debug(pc->st, "charging info final %d", pc->para.chargeinfo);
+									}
+									}
+								   ))))))
+							break;
+				}
+#undef FOO1
+
+			}
+			break;
+		case 2:	/* return result */
+			l3_debug(pc->st, "return result break");
+			break;
+		case 3:	/* return error */
+			l3_debug(pc->st, "return error break");
+			break;
+		default:
+			l3_debug(pc->st, "default break");
+			break;
 	}
 }
-#endif	
+#endif
 
-static int 
-l3dss1_check_messagetype_validity(int mt) {
+static int
+l3dss1_check_messagetype_validity(int mt)
+{
 /* verify if a message type exists */
-	switch(mt) {
+	switch (mt) {
 		case MT_ALERTING:
 		case MT_CALL_PROCEEDING:
 		case MT_CONNECT:
@@ -283,7 +312,7 @@
 		return;
 	p = skb_put(skb, 4);
 	MsgHead(p, pc->callref, mt);
-	pc->st->l3.l3l2(pc->st, DL_DATA, skb);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
 }
 
 static void
@@ -314,165 +343,244 @@
 	StopAllL3Timer(pc);
 	pc->para.cause = cause;
 	newl3state(pc, 0);
-	pc->st->l3.l3l4(pc, CC_RELEASE_CNF, NULL);
+	pc->st->l3.l3l4(pc->st, CC_RELEASE | CONFIRM, pc);
 	release_l3_process(pc);
 }
 
-#ifdef EXT_BEARER_CAPS
+#if EXT_BEARER_CAPS
+
+u_char *
+EncodeASyncParams(u_char * p, u_char si2)
+{				// 7c 06 88  90 21 42 00 bb
+
+	p[0] = p[1] = 0;
+	p[2] = 0x80;
+	if (si2 & 32)		// 7 data bits
+
+		p[2] += 16;
+	else			// 8 data bits
+
+		p[2] += 24;
+
+	if (si2 & 16)		// 2 stop bits
+
+		p[2] += 96;
+	else			// 1 stop bit
+
+		p[2] = 32;
+
+	if (si2 & 8)		// even parity
+
+		p[2] += 2;
+	else			// no parity
 
-u_char *EncodeASyncParams(u_char *p, u_char si2)
-{ // 7c 06 88  90 21 42 00 bb
+		p[2] += 3;
 
-  p[0] = p[1] = 0; p[2] = 0x80;
-  if (si2 & 32) // 7 data bits
-    p[2] += 16;
-  else          // 8 data bits
-    p[2] +=24;
-
-  if (si2 & 16) // 2 stop bits
-    p[2] += 96;
-  else          // 1 stop bit
-    p[2] = 32;
-
-  if (si2 & 8)  // even parity
-    p[2] += 2;
-  else          // no parity
-    p[2] += 3;
-
-  switch (si2 & 0x07)
-  {
-    case 0:     p[0] = 66;      // 1200 bit/s
-                break;
-    case 1:     p[0] = 88;      // 1200/75 bit/s
-                break;
-    case 2:     p[0] = 87;      // 75/1200 bit/s
-                break;
-    case 3:     p[0] = 67;      // 2400 bit/s
-                break;
-    case 4:     p[0] = 69;      // 4800 bit/s
-                break;
-    case 5:     p[0] = 72;      // 9600 bit/s
-                break;
-    case 6:     p[0] = 73;      // 14400 bit/s
-                break;
-    case 7:     p[0] = 75;      // 19200 bit/s
-                break;
-  }
-  return p+3;
-}
-
-u_char EncodeSyncParams(u_char si2, u_char ai)
-{
-
-  switch (si2)
-  {
-    case 0:     return ai + 2;  // 1200 bit/s
-    case 1:     return ai + 24; // 1200/75 bit/s
-    case 2:     return ai + 23; // 75/1200 bit/s
-    case 3:     return ai + 3;  // 2400 bit/s
-    case 4:     return ai + 5;  // 4800 bit/s
-    case 5:     return ai + 8;  // 9600 bit/s
-    case 6:     return ai + 9;  // 14400 bit/s
-    case 7:     return ai + 11; // 19200 bit/s
-    case 8:     return ai + 14; // 48000 bit/s
-    case 9:     return ai + 15; // 56000 bit/s
-    case 15:    return ai + 40; // negotiate bit/s
-    default:    break;
-  }
-  return ai;
-}
-
-
-static u_char DecodeASyncParams(u_char si2, u_char *p)
-{ u_char info;
-
-  switch (p[5])
-  {
-    case 66: // 1200 bit/s
-             break; // si2 bleibt gleich
-    case 88: // 1200/75 bit/s
-             si2 += 1;
-             break;
-    case 87: // 75/1200 bit/s
-             si2 += 2;
-             break;
-    case 67: // 2400 bit/s
-             si2 += 3;
-             break;
-    case 69: // 4800 bit/s
-             si2 += 4;
-             break;
-    case 72: // 9600 bit/s
-             si2 += 5;
-             break;
-    case 73: // 14400 bit/s
-             si2 += 6;
-             break;
-    case 75: // 19200 bit/s
-             si2 += 7;
-             break;
-  }
-
-  info = p[7] & 0x7f;
-  if ((info & 16) && (!(info & 8)))   // 7 data bits
-    si2 += 32;                        // else 8 data bits
-  if ((info & 96) == 96)              // 2 stop bits
-    si2 += 16;                        // else 1 stop bit
-  if ((info & 2) && (!(info & 1)))    // even parity
-    si2 += 8;                         // else no parity
-
-  return si2;
-}
-
-
-static u_char DecodeSyncParams(u_char si2, u_char info)
-{
-  info &= 0x7f;
-  switch (info)
-  {
-    case 40: // bit/s aushandeln  --- hat nicht geklappt, ai wird 165 statt 175!
-      return si2 + 15;
-    case 15: // 56000 bit/s --- hat nicht geklappt, ai wird 0 statt 169 !
-      return si2 + 9;
-    case 14: // 48000 bit/s
-      return si2 + 8;
-    case 11: // 19200 bit/s
-      return si2 + 7;
-    case 9:  // 14400 bit/s
-      return si2 + 6;
-    case 8:  // 9600  bit/s
-      return si2 + 5;
-    case 5:  // 4800  bit/s
-      return si2 + 4;
-    case 3:  // 2400  bit/s
-      return si2 + 3;
-    case 23: // 75/1200 bit/s
-      return si2 + 2;
-    case 24: // 1200/75 bit/s
-      return si2 + 1;
-    default: // 1200 bit/s
-      return si2;
-  }
-}
-
-static u_char DecodeSI2(struct sk_buff *skb)
-{ u_char *p; //, *pend=skb->data + skb->len;
-
-        if ((p = findie(skb->data, skb->len, 0x7c, 0)))
-        {
-          switch (p[4] & 0x0f)
-          {
-            case 0x01:  if (p[1] == 0x04) // sync. Bitratenadaption
-                          return DecodeSyncParams(160, p[5]); // V.110/X.30
-                        else if (p[1] == 0x06) // async. Bitratenadaption
-                          return DecodeASyncParams(192, p);   // V.110/X.30
-                        break;
-            case 0x08:  // if (p[5] == 0x02) // sync. Bitratenadaption
-                          return DecodeSyncParams(176, p[5]); // V.120
-                        break;
-          }
-        }
-        return 0;
+	switch (si2 & 0x07) {
+		case 0:
+			p[0] = 66;	// 1200 bit/s
+
+			break;
+		case 1:
+			p[0] = 88;	// 1200/75 bit/s
+
+			break;
+		case 2:
+			p[0] = 87;	// 75/1200 bit/s
+
+			break;
+		case 3:
+			p[0] = 67;	// 2400 bit/s
+
+			break;
+		case 4:
+			p[0] = 69;	// 4800 bit/s
+
+			break;
+		case 5:
+			p[0] = 72;	// 9600 bit/s
+
+			break;
+		case 6:
+			p[0] = 73;	// 14400 bit/s
+
+			break;
+		case 7:
+			p[0] = 75;	// 19200 bit/s
+
+			break;
+	}
+	return p + 3;
+}
+
+u_char
+EncodeSyncParams(u_char si2, u_char ai)
+{
+
+	switch (si2) {
+		case 0:
+			return ai + 2;	// 1200 bit/s
+
+		case 1:
+			return ai + 24;		// 1200/75 bit/s
+
+		case 2:
+			return ai + 23;		// 75/1200 bit/s
+
+		case 3:
+			return ai + 3;	// 2400 bit/s
+
+		case 4:
+			return ai + 5;	// 4800 bit/s
+
+		case 5:
+			return ai + 8;	// 9600 bit/s
+
+		case 6:
+			return ai + 9;	// 14400 bit/s
+
+		case 7:
+			return ai + 11;		// 19200 bit/s
+
+		case 8:
+			return ai + 14;		// 48000 bit/s
+
+		case 9:
+			return ai + 15;		// 56000 bit/s
+
+		case 15:
+			return ai + 40;		// negotiate bit/s
+
+		default:
+			break;
+	}
+	return ai;
+}
+
+
+static u_char
+DecodeASyncParams(u_char si2, u_char * p)
+{
+	u_char info;
+
+	switch (p[5]) {
+		case 66:	// 1200 bit/s
+
+			break;	// si2 don't change
+
+		case 88:	// 1200/75 bit/s
+
+			si2 += 1;
+			break;
+		case 87:	// 75/1200 bit/s
+
+			si2 += 2;
+			break;
+		case 67:	// 2400 bit/s
+
+			si2 += 3;
+			break;
+		case 69:	// 4800 bit/s
+
+			si2 += 4;
+			break;
+		case 72:	// 9600 bit/s
+
+			si2 += 5;
+			break;
+		case 73:	// 14400 bit/s
+
+			si2 += 6;
+			break;
+		case 75:	// 19200 bit/s
+
+			si2 += 7;
+			break;
+	}
+
+	info = p[7] & 0x7f;
+	if ((info & 16) && (!(info & 8)))	// 7 data bits
+
+		si2 += 32;	// else 8 data bits
+
+	if ((info & 96) == 96)	// 2 stop bits
+
+		si2 += 16;	// else 1 stop bit
+
+	if ((info & 2) && (!(info & 1)))	// even parity
+
+		si2 += 8;	// else no parity
+
+	return si2;
+}
+
+
+static u_char
+DecodeSyncParams(u_char si2, u_char info)
+{
+	info &= 0x7f;
+	switch (info) {
+		case 40:	// bit/s negotiation failed  ai := 165 not 175!
+
+			return si2 + 15;
+		case 15:	// 56000 bit/s failed, ai := 0 not 169 !
+
+			return si2 + 9;
+		case 14:	// 48000 bit/s
+
+			return si2 + 8;
+		case 11:	// 19200 bit/s
+
+			return si2 + 7;
+		case 9:	// 14400 bit/s
+
+			return si2 + 6;
+		case 8:	// 9600  bit/s
+
+			return si2 + 5;
+		case 5:	// 4800  bit/s
+
+			return si2 + 4;
+		case 3:	// 2400  bit/s
+
+			return si2 + 3;
+		case 23:	// 75/1200 bit/s
+
+			return si2 + 2;
+		case 24:	// 1200/75 bit/s
+
+			return si2 + 1;
+		default:	// 1200 bit/s
+
+			return si2;
+	}
+}
+
+static u_char
+DecodeSI2(struct sk_buff *skb)
+{
+	u_char *p;		//, *pend=skb->data + skb->len;
+
+	if ((p = findie(skb->data, skb->len, 0x7c, 0))) {
+		switch (p[4] & 0x0f) {
+			case 0x01:
+				if (p[1] == 0x04)	// sync. Bitratenadaption
+
+					return DecodeSyncParams(160, p[5]);	// V.110/X.30
+
+				else if (p[1] == 0x06)	// async. Bitratenadaption
+
+					return DecodeASyncParams(192, p);	// V.110/X.30
+
+				break;
+			case 0x08:	// if (p[5] == 0x02) // sync. Bitratenadaption
+
+				return DecodeSyncParams(176, p[5]);	// V.120
+
+				break;
+		}
+	}
+	return 0;
 }
 
 #endif
@@ -498,7 +606,7 @@
 	/*
 	 * Set Bearer Capability, Map info from 1TR6-convention to EDSS1
 	 */
-#ifdef HISAX_EURO_SENDCOMPLETE
+#if HISAX_EURO_SENDCOMPLETE
 	*p++ = 0xa1;		/* complete indicator */
 #endif
 	switch (pc->para.setup.si1) {
@@ -558,11 +666,11 @@
 	msn = pc->para.setup.eazmsn;
 	sub = NULL;
 	sp = msn;
-	while (*sp) { 
+	while (*sp) {
 		if ('.' == *sp) {
 			sub = sp;
 			*sp = 0;
-		} else 
+		} else
 			sp++;
 	}
 	if (*msn) {
@@ -579,20 +687,20 @@
 	}
 	if (sub) {
 		*sub++ = '.';
-		*p++ = 0x6d; /* Calling party subaddress */
- 		*p++ = strlen(sub) + 2;
+		*p++ = 0x6d;	/* Calling party subaddress */
+		*p++ = strlen(sub) + 2;
 		*p++ = 0x80;	/* NSAP coded */
 		*p++ = 0x50;	/* local IDI format */
- 		while (*sub)
+		while (*sub)
 			*p++ = *sub++ & 0x7f;
 	}
 	sub = NULL;
 	sp = teln;
-	while (*sp) { 
+	while (*sp) {
 		if ('.' == *sp) {
 			sub = sp;
 			*sp = 0;
-		} else 
+		} else
 			sp++;
 	}
 	*p++ = 0x70;
@@ -604,31 +712,47 @@
 
 	if (sub) {
 		*sub++ = '.';
-		*p++ = 0x71; /* Called party subaddress */
- 		*p++ = strlen(sub) + 2;
+		*p++ = 0x71;	/* Called party subaddress */
+		*p++ = strlen(sub) + 2;
 		*p++ = 0x80;	/* NSAP coded */
 		*p++ = 0x50;	/* local IDI format */
- 		while (*sub)
+		while (*sub)
 			*p++ = *sub++ & 0x7f;
 	}
+#if EXT_BEARER_CAPS
+	if ((pc->para.setup.si2 >= 160) && (pc->para.setup.si2 <= 175)) {	// sync. Bitratenadaption, V.110/X.30
 
-#ifdef EXT_BEARER_CAPS
-        if ((pc->para.setup.si2 >= 160) && (pc->para.setup.si2 <= 175))
-        { // sync. Bitratenadaption, V.110/X.30
-          *p++ = 0x7c; *p++ = 0x04; *p++ = 0x88; *p++ = 0x90; *p++ = 0x21;
-          *p++ = EncodeSyncParams(pc->para.setup.si2 - 160, 0x80);
-        }
-        else if ((pc->para.setup.si2 >= 176) && (pc->para.setup.si2 <= 191))
-        { // sync. Bitratenadaption, V.120
-          *p++ = 0x7c; *p++ = 0x05; *p++ = 0x88; *p++ = 0x90; *p++ = 0x28;
-          *p++ = EncodeSyncParams(pc->para.setup.si2 - 176, 0);
-          *p++ = 0x82;
-        }
-        else if (pc->para.setup.si2 >= 192)
-        { // async. Bitratenadaption, V.110/X.30
-          *p++ = 0x7c; *p++ = 0x06; *p++ = 0x88; *p++ = 0x90; *p++ = 0x21;
-          p = EncodeASyncParams(p, pc->para.setup.si2 - 192);
-        }
+		*p++ = 0x7c;
+		*p++ = 0x04;
+		*p++ = 0x88;
+		*p++ = 0x90;
+		*p++ = 0x21;
+		*p++ = EncodeSyncParams(pc->para.setup.si2 - 160, 0x80);
+	} else if ((pc->para.setup.si2 >= 176) && (pc->para.setup.si2 <= 191)) {	// sync. Bitratenadaption, V.120
+
+		*p++ = 0x7c;
+		*p++ = 0x05;
+		*p++ = 0x88;
+		*p++ = 0x90;
+		*p++ = 0x28;
+		*p++ = EncodeSyncParams(pc->para.setup.si2 - 176, 0);
+		*p++ = 0x82;
+	} else if (pc->para.setup.si2 >= 192) {		// async. Bitratenadaption, V.110/X.30
+
+		*p++ = 0x7c;
+		*p++ = 0x06;
+		*p++ = 0x88;
+		*p++ = 0x90;
+		*p++ = 0x21;
+		p = EncodeASyncParams(p, pc->para.setup.si2 - 192);
+#if HISAX_SEND_STD_LLC_IE
+	} else {
+		*p++ = 0x7c;
+		*p++ = 0x02;
+		*p++ = 0x88;
+		*p++ = 0x90;
+#endif
+	}
 #endif
 	l = p - tmp;
 	if (!(skb = l3_alloc_skb(l)))
@@ -637,7 +761,7 @@
 	L3DelTimer(&pc->timer);
 	L3AddTimer(&pc->timer, T303, CC_T303);
 	newl3state(pc, 1);
-	pc->st->l3.l3l2(pc->st, DL_DATA, skb);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
 }
 
 static void
@@ -657,7 +781,7 @@
 	dev_kfree_skb(skb);
 	newl3state(pc, 3);
 	L3AddTimer(&pc->timer, T310, CC_T310);
-	pc->st->l3.l3l4(pc, CC_PROCEEDING_IND, NULL);
+	pc->st->l3.l3l4(pc->st, CC_PROCEEDING | INDICATION, pc);
 }
 
 static void
@@ -677,7 +801,7 @@
 	dev_kfree_skb(skb);
 	newl3state(pc, 2);
 	L3AddTimer(&pc->timer, T304, CC_T304);
-	pc->st->l3.l3l4(pc, CC_MORE_INFO, NULL);
+	pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc);
 }
 
 static void
@@ -699,7 +823,7 @@
 	dev_kfree_skb(skb);
 	newl3state(pc, 12);
 	pc->para.cause = cause;
-	pc->st->l3.l3l4(pc, CC_DISCONNECT_IND, NULL);
+	pc->st->l3.l3l4(pc->st, CC_DISCONNECT | INDICATION, pc);
 }
 
 static void
@@ -711,7 +835,7 @@
 	L3DelTimer(&pc->timer);	/* T310 */
 	newl3state(pc, 10);
 	pc->para.chargeinfo = 0;
-	pc->st->l3.l3l4(pc, CC_SETUP_CNF, NULL);
+	pc->st->l3.l3l4(pc->st, CC_SETUP | CONFIRM, pc);
 }
 
 static void
@@ -722,49 +846,48 @@
 	dev_kfree_skb(skb);
 	L3DelTimer(&pc->timer);	/* T304 */
 	newl3state(pc, 4);
-	pc->st->l3.l3l4(pc, CC_ALERTING_IND, NULL);
+	pc->st->l3.l3l4(pc->st, CC_ALERTING | INDICATION, pc);
 }
 
 static void
 l3dss1_msg_without_setup(struct l3_process *pc, u_char pr, void *arg)
 {
-  /* This routine is called if here was no SETUP made (checks in dss1up and in
-   * l3dss1_setup) and a RELEASE_COMPLETE have to be sent with an error code
-   * It is called after it is veryfied that Layer2 is up.
-   * The cause value is allready in pc->para.cause
-   * MT_STATUS_ENQUIRE in the NULL state is handled too
-   */
+	/* This routine is called if here was no SETUP made (checks in dss1up and in
+	 * l3dss1_setup) and a RELEASE_COMPLETE have to be sent with an error code
+	 * MT_STATUS_ENQUIRE in the NULL state is handled too
+	 */
 	u_char tmp[16];
-	u_char *p=tmp;
+	u_char *p = tmp;
 	int l;
 	struct sk_buff *skb;
 
 	switch (pc->para.cause) {
-	  case  81: /* 0x51 invalid callreference */
-	  case  96: /* 0x60 mandory IE missing */
-	  case 101: /* 0x65 incompatible Callstate */
-	  	MsgHead(p, pc->callref, MT_RELEASE_COMPLETE);
-		*p++ = IE_CAUSE;
-		*p++ = 0x2;
-		*p++ = 0x80;
-		*p++ = pc->para.cause | 0x80;
-		break;
-	  default:
-	  	printk(KERN_ERR "HiSax internal error l3dss1_msg_without_setup\n");
-	  	return;
-	}	
+		case 81:	/* 0x51 invalid callreference */
+		case 88:	/* 0x58 incomp destination */
+		case 96:	/* 0x60 mandory IE missing */
+		case 101:	/* 0x65 incompatible Callstate */
+			MsgHead(p, pc->callref, MT_RELEASE_COMPLETE);
+			*p++ = IE_CAUSE;
+			*p++ = 0x2;
+			*p++ = 0x80;
+			*p++ = pc->para.cause | 0x80;
+			break;
+		default:
+			printk(KERN_ERR "HiSax internal error l3dss1_msg_without_setup\n");
+			return;
+	}
 	l = p - tmp;
 	if (!(skb = l3_alloc_skb(l)))
 		return;
 	memcpy(skb_put(skb, l), tmp, l);
-	pc->st->l3.l3l2(pc->st, DL_DATA, skb);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
 	release_l3_process(pc);
 }
 
 static void
 l3dss1_setup(struct l3_process *pc, u_char pr, void *arg)
 {
-        u_char *p, *ptmp[8];
+	u_char *p, *ptmp[8];
 	int i;
 	int bcfound = 0;
 	char tmp[80];
@@ -772,9 +895,9 @@
 
 	/* ETS 300-104 1.3.4 and 1.3.5
 	 * we need to detect unknown inform. element from 0 to 7
-	 */	
+	 */
 	p = skb->data;
-	for(i = 0; i < 8; i++)
+	for (i = 0; i < 8; i++)
 		ptmp[i] = skb->data;
 	if (findie(ptmp[1], skb->len, 0x01, 0)
 	    || findie(ptmp[2], skb->len, 0x02, 0)
@@ -782,18 +905,14 @@
 	    || findie(ptmp[5], skb->len, 0x05, 0)
 	    || findie(ptmp[6], skb->len, 0x06, 0)
 	    || findie(ptmp[7], skb->len, 0x07, 0)) {
-	  	/* if ie is < 8 and not 0 nor 4, send RELEASE_COMPLETE 
-	  	 * cause 0x60
-	  	 */
-	  	pc->para.cause = 0x60;
+		/* if ie is < 8 and not 0 nor 4, send RELEASE_COMPLETE
+		 * cause 0x60
+		 */
+		pc->para.cause = 0x60;
 		dev_kfree_skb(skb);
-		if (pc->state == 0)
-			pc->st->l3.l3l4(pc, CC_ESTABLISH, NULL);
-		else
-			l3dss1_msg_without_setup(pc, pr, NULL);
+		l3dss1_msg_without_setup(pc, pr, NULL);
 		return;
 	}
-
 	/*
 	 * Channel Identification
 	 */
@@ -804,9 +923,14 @@
 			bcfound++;
 		else if (pc->debug & L3_DEB_WARN)
 			l3_debug(pc->st, "setup without bchannel");
-	} else if (pc->debug & L3_DEB_WARN)
-		l3_debug(pc->st, "setup without bchannel");
-
+	} else {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "setup without bchannel");
+		pc->para.cause = 0x60;
+		dev_kfree_skb(skb);
+		l3dss1_msg_without_setup(pc, pr, NULL);
+		return;
+	}
 	/*
 	   * Bearer Capabilities
 	 */
@@ -824,10 +948,10 @@
 				/* Unrestricted digital information */
 				pc->para.setup.si1 = 7;
 /* JIM, 05.11.97 I wanna set service indicator 2 */
-#ifdef EXT_BEARER_CAPS
-                                pc->para.setup.si2 = DecodeSI2(skb);
-                                printk(KERN_DEBUG "HiSax: SI=%d, AI=%d\n",
-                                       pc->para.setup.si1, pc->para.setup.si2);
+#if EXT_BEARER_CAPS
+				pc->para.setup.si2 = DecodeSI2(skb);
+				printk(KERN_DEBUG "HiSax: SI=%d, AI=%d\n",
+				 pc->para.setup.si1, pc->para.setup.si2);
 #endif
 				break;
 			case 0x09:
@@ -849,12 +973,9 @@
 		if (pc->debug & L3_DEB_WARN)
 			l3_debug(pc->st, "setup without bearer capabilities");
 		/* ETS 300-104 1.3.3 */
-	  	pc->para.cause = 0x60;
+		pc->para.cause = 0x60;
 		dev_kfree_skb(skb);
-		if (pc->state == 0)
-			pc->st->l3.l3l4(pc, CC_ESTABLISH, NULL);
-		else
-			l3dss1_msg_without_setup(pc, pr, NULL);
+		l3dss1_msg_without_setup(pc, pr, NULL);
 		return;
 	}
 
@@ -867,8 +988,8 @@
 	p = skb->data;
 	if ((p = findie(p, skb->len, 0x71, 0))) {
 		/* Called party subaddress */
-		if ((p[1]>=2) && (p[2]==0x80) && (p[3]==0x50)) {
-			tmp[0]='.';
+		if ((p[1] >= 2) && (p[2] == 0x80) && (p[3] == 0x50)) {
+			tmp[0] = '.';
 			iecpy(&tmp[1], p, 2);
 			strcat(pc->para.setup.eazmsn, tmp);
 		} else if (pc->debug & L3_DEB_WARN)
@@ -892,24 +1013,28 @@
 	p = skb->data;
 	if ((p = findie(p, skb->len, 0x6d, 0))) {
 		/* Calling party subaddress */
-		if ((p[1]>=2) && (p[2]==0x80) && (p[3]==0x50)) {
-			tmp[0]='.';
+		if ((p[1] >= 2) && (p[2] == 0x80) && (p[3] == 0x50)) {
+			tmp[0] = '.';
 			iecpy(&tmp[1], p, 2);
 			strcat(pc->para.setup.phone, tmp);
 		} else if (pc->debug & L3_DEB_WARN)
 			l3_debug(pc->st, "wrong calling subaddress");
 	}
-
 	dev_kfree_skb(skb);
 
 	if (bcfound) {
 		if ((pc->para.setup.si1 != 7) && (pc->debug & L3_DEB_WARN)) {
-			sprintf(tmp, "non-digital call: %s -> %s",
-				pc->para.setup.phone, pc->para.setup.eazmsn);
-			l3_debug(pc->st, tmp);
+			l3_debug(pc->st, "non-digital call: %s -> %s",
+			    pc->para.setup.phone, pc->para.setup.eazmsn);
+		}
+		if ((pc->para.setup.si1 != 7) &&
+		    test_bit(FLG_PTP, &pc->st->l2.flag)) {
+			pc->para.cause = 0x58;
+			l3dss1_msg_without_setup(pc, pr, NULL);
+			return;
 		}
 		newl3state(pc, 6);
-		pc->st->l3.l3l4(pc, CC_SETUP_IND, NULL);
+		pc->st->l3.l3l4(pc->st, CC_SETUP | INDICATION, pc);
 	} else
 		release_l3_process(pc);
 }
@@ -938,7 +1063,7 @@
 	dev_kfree_skb(skb);
 	newl3state(pc, 10);
 	L3DelTimer(&pc->timer);
-	pc->st->l3.l3l4(pc, CC_SETUP_COMPLETE_IND, NULL);
+	pc->st->l3.l3l4(pc->st, CC_SETUP_COMPL | INDICATION, pc);
 }
 
 static void
@@ -967,7 +1092,7 @@
 		return;
 	memcpy(skb_put(skb, l), tmp, l);
 	newl3state(pc, 11);
-	pc->st->l3.l3l2(pc->st, DL_DATA, skb);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
 	L3AddTimer(&pc->timer, T305, CC_T305);
 }
 
@@ -994,8 +1119,8 @@
 	if (!(skb = l3_alloc_skb(l)))
 		return;
 	memcpy(skb_put(skb, l), tmp, l);
-	pc->st->l3.l3l2(pc->st, DL_DATA, skb);
-	pc->st->l3.l3l4(pc, CC_RELEASE_IND, NULL);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
 	newl3state(pc, 0);
 	release_l3_process(pc);
 }
@@ -1016,8 +1141,8 @@
 	}
 	p = skb->data;
 	if ((p = findie(p, skb->len, IE_FACILITY, 0))) {
-#ifdef HISAX_DE_AOC
-	    l3dss1_parse_facility(pc,p);
+#if HISAX_DE_AOC
+		l3dss1_parse_facility(pc, p);
 #else
 		p = NULL;
 #endif
@@ -1026,7 +1151,7 @@
 	StopAllL3Timer(pc);
 	pc->para.cause = cause;
 	l3dss1_message(pc, MT_RELEASE_COMPLETE);
-	pc->st->l3.l3l4(pc, CC_RELEASE_IND, NULL);
+	pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
 	newl3state(pc, 0);
 	release_l3_process(pc);
 }
@@ -1064,15 +1189,15 @@
 	if (!(skb = l3_alloc_skb(l)))
 		return;
 	memcpy(skb_put(skb, l), tmp, l);
-	pc->st->l3.l3l2(pc->st, DL_DATA, skb);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
 }
 
 static void
 l3dss1_status_req(struct l3_process *pc, u_char pr, void *arg)
 {
-  /* ETS 300-104 7.4.1, 8.4.1, 10.3.1, 11.4.1, 12.4.1, 13.4.1, 14.4.1...
-     if setup has been made and a non expected message type is received, we must send MT_STATUS cause 0x62  */
-        u_char tmp[16];
+	/* ETS 300-104 7.4.1, 8.4.1, 10.3.1, 11.4.1, 12.4.1, 13.4.1, 14.4.1...
+	   if setup has been made and a non expected message type is received, we must send MT_STATUS cause 0x62  */
+	u_char tmp[16];
 	u_char *p = tmp;
 	int l;
 	struct sk_buff *skb = arg;
@@ -1084,7 +1209,7 @@
 	*p++ = IE_CAUSE;
 	*p++ = 0x2;
 	*p++ = 0x80;
-	*p++ = 0x62 | 0x80;		/* status sending */
+	*p++ = 0x62 | 0x80;	/* status sending */
 
 	*p++ = 0x14;		/* CallState */
 	*p++ = 0x1;
@@ -1094,7 +1219,7 @@
 	if (!(skb = l3_alloc_skb(l)))
 		return;
 	memcpy(skb_put(skb, l), tmp, l);
-	pc->st->l3.l3l2(pc->st, DL_DATA, skb);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
 }
 
 static void
@@ -1107,18 +1232,18 @@
 
 	if ((p = findie(p, skb->len, IE_CALL_STATE, 0))) {
 		p++;
-		if (1== *p++)
+		if (1 == *p++)
 			callState = *p;
 	}
-	if(callState == 0) {
+	if (callState == 0) {
 		/* ETS 300-104 7.6.1, 8.6.1, 10.6.1... and 16.1
 		 * set down layer 3 without sending any message
 		 */
-		pc->st->l3.l3l4(pc, CC_RELEASE_IND, NULL);
+		pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
 		newl3state(pc, 0);
 		release_l3_process(pc);
 	} else {
-		pc->st->l3.l3l4(pc, CC_IGNORE, NULL);
+		pc->st->l3.l3l4(pc->st, CC_IGNORE | INDICATION, pc);
 	}
 }
 
@@ -1131,7 +1256,7 @@
 		l3dss1_setup_req(pc, pr, arg);
 	} else {
 		L3DelTimer(&pc->timer);
-		pc->st->l3.l3l4(pc, CC_NOSETUP_RSP_ERR, NULL);
+		pc->st->l3.l3l4(pc->st, CC_NOSETUP_RSP, pc);
 		release_l3_process(pc);
 	}
 }
@@ -1142,7 +1267,7 @@
 	L3DelTimer(&pc->timer);
 	pc->para.cause = 0xE6;
 	l3dss1_disconnect_req(pc, pr, NULL);
-	pc->st->l3.l3l4(pc, CC_SETUP_ERR, NULL);
+	pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
 
 }
 
@@ -1171,7 +1296,7 @@
 		return;
 	memcpy(skb_put(skb, l), tmp, l);
 	newl3state(pc, 19);
-	pc->st->l3.l3l2(pc->st, DL_DATA, skb);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
 	L3AddTimer(&pc->timer, T308, CC_T308_1);
 }
 
@@ -1181,7 +1306,7 @@
 	L3DelTimer(&pc->timer);
 	pc->para.cause = 0xE6;
 	l3dss1_disconnect_req(pc, pr, NULL);
-	pc->st->l3.l3l4(pc, CC_SETUP_ERR, NULL);
+	pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
 }
 
 static void
@@ -1190,7 +1315,7 @@
 	L3DelTimer(&pc->timer);
 	pc->para.cause = 0xE6;
 	l3dss1_disconnect_req(pc, pr, NULL);
-	pc->st->l3.l3l4(pc, CC_CONNECT_ERR, NULL);
+	pc->st->l3.l3l4(pc->st, CC_CONNECT_ERR, pc);
 }
 
 static void
@@ -1206,15 +1331,37 @@
 l3dss1_t308_2(struct l3_process *pc, u_char pr, void *arg)
 {
 	L3DelTimer(&pc->timer);
-	pc->st->l3.l3l4(pc, CC_RELEASE_ERR, NULL);
+	pc->st->l3.l3l4(pc->st, CC_RELEASE_ERR, pc);
 	release_l3_process(pc);
 }
 
 static void
+l3dss1_t318(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.cause = 0x66;	/* Timer expiry */
+	pc->para.loc = 0;	/* local */
+	pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc);
+	newl3state(pc, 19);
+	l3dss1_message(pc, MT_RELEASE);
+	L3AddTimer(&pc->timer, T308, CC_T308_1);
+}
+
+static void
+l3dss1_t319(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.cause = 0x66;	/* Timer expiry */
+	pc->para.loc = 0;	/* local */
+	pc->st->l3.l3l4(pc->st, CC_SUSPEND_ERR, pc);
+	newl3state(pc, 10);
+}
+
+static void
 l3dss1_restart(struct l3_process *pc, u_char pr, void *arg)
 {
 	L3DelTimer(&pc->timer);
-	pc->st->l3.l3l4(pc, CC_DLRL, NULL);
+	pc->st->l3.l3l4(pc->st, CC_DLRL | INDICATION, pc);
 	release_l3_process(pc);
 }
 
@@ -1233,31 +1380,30 @@
 	if ((p = findie(p, skb->len, IE_CAUSE, 0))) {
 		p++;
 		l = *p++;
-		t += sprintf(t,"Status CR %x Cause:", pc->callref);
+		t += sprintf(t, "Status CR %x Cause:", pc->callref);
 		while (l--) {
-		        cause = *p;
-			t += sprintf(t," %2x",*p++);
+			cause = *p;
+			t += sprintf(t, " %2x", *p++);
 		}
 	} else
-		sprintf(t,"Status CR %x no Cause", pc->callref);
+		sprintf(t, "Status CR %x no Cause", pc->callref);
 	l3_debug(pc->st, tmp);
 	p = skb->data;
 	t = tmp;
-	t += sprintf(t,"Status state %x ", pc->state);
+	t += sprintf(t, "Status state %x ", pc->state);
 	if ((p = findie(p, skb->len, IE_CALL_STATE, 0))) {
 		p++;
-		if (1== *p++) {
-		        callState = *p;
-			t += sprintf(t,"peer state %x" , *p);
-		}
-		else
-			t += sprintf(t,"peer state len error");
+		if (1 == *p++) {
+			callState = *p;
+			t += sprintf(t, "peer state %x", *p);
+		} else
+			t += sprintf(t, "peer state len error");
 	} else
-		sprintf(t,"no peer state");
+		sprintf(t, "no peer state");
 	l3_debug(pc->st, tmp);
-	if(((cause & 0x7f) == 0x6f) && (callState == 0)) {
-		/* ETS 300-104 7.6.1, 8.6.1, 10.6.1... 
-		 * if received MT_STATUS with cause == 0x6f and call 
+	if (((cause & 0x7f) == 0x6f) && (callState == 0)) {
+		/* ETS 300-104 7.6.1, 8.6.1, 10.6.1...
+		 * if received MT_STATUS with cause == 0x6f and call
 		 * state == 0, then we must set down layer 3
 		 */
 		l3dss1_release_ind(pc, pr, arg);
@@ -1268,56 +1414,189 @@
 static void
 l3dss1_facility(struct l3_process *pc, u_char pr, void *arg)
 {
-        u_char *p;
+	u_char *p;
 	struct sk_buff *skb = arg;
 
 	p = skb->data;
 	if ((p = findie(p, skb->len, IE_FACILITY, 0))) {
-#ifdef HISAX_DE_AOC
-	    l3dss1_parse_facility(pc,p);
+#if HISAX_DE_AOC
+		l3dss1_parse_facility(pc, p);
 #else
 		p = NULL;
 #endif
 	}
 }
 
+static void
+l3dss1_suspend_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[32];
+	u_char *p = tmp;
+	u_char i, l;
+	u_char *msg = pc->chan->setup.phone;
+
+	MsgHead(p, pc->callref, MT_SUSPEND);
+
+	*p++ = IE_CALLID;
+	l = *msg++;
+	if (l && (l <= 10)) {	/* Max length 10 octets */
+		*p++ = l;
+		for (i = 0; i < l; i++)
+			*p++ = *msg++;
+	} else {
+		l3_debug(pc->st, "SUS wrong CALLID len %d", l);
+		return;
+	}
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	newl3state(pc, 15);
+	L3AddTimer(&pc->timer, T319, CC_T319);
+}
+
+static void
+l3dss1_suspend_ack(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+
+	L3DelTimer(&pc->timer);
+	newl3state(pc, 0);
+	dev_kfree_skb(skb);
+	pc->para.cause = -1;
+	pc->st->l3.l3l4(pc->st, CC_SUSPEND | CONFIRM, pc);
+	release_l3_process(pc);
+}
+
+static void
+l3dss1_suspend_rej(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char *p;
+	struct sk_buff *skb = arg;
+	int cause = -1;
+
+	L3DelTimer(&pc->timer);
+	p = skb->data;
+	if ((p = findie(p, skb->len, IE_CAUSE, 0))) {
+		p++;
+		if (*p++ == 2)
+			pc->para.loc = *p++;
+		cause = *p & 0x7f;
+	}
+	dev_kfree_skb(skb);
+	pc->para.cause = cause;
+	pc->st->l3.l3l4(pc->st, CC_SUSPEND_ERR, pc);
+	newl3state(pc, 10);
+}
+
+static void
+l3dss1_resume_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[32];
+	u_char *p = tmp;
+	u_char i, l;
+	u_char *msg = pc->para.setup.phone;
+
+	MsgHead(p, pc->callref, MT_RESUME);
+
+	*p++ = IE_CALLID;
+	l = *msg++;
+	if (l && (l <= 10)) {	/* Max length 10 octets */
+		*p++ = l;
+		for (i = 0; i < l; i++)
+			*p++ = *msg++;
+	} else {
+		l3_debug(pc->st, "RES wrong CALLID len %d", l);
+		return;
+	}
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	newl3state(pc, 17);
+	L3AddTimer(&pc->timer, T319, CC_T319);
+}
+
+static void
+l3dss1_resume_ack(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char *p;
+	struct sk_buff *skb = arg;
+
+	L3DelTimer(&pc->timer);
+	p = skb->data;
+	if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) {
+		pc->para.bchannel = p[2] & 0x3;
+		if ((!pc->para.bchannel) && (pc->debug & L3_DEB_WARN))
+			l3_debug(pc->st, "resume ack without bchannel");
+	} else if (pc->debug & L3_DEB_WARN)
+		l3_debug(pc->st, "resume ack without bchannel");
+	dev_kfree_skb(skb);
+	pc->st->l3.l3l4(pc->st, CC_RESUME | CONFIRM, pc);
+	newl3state(pc, 10);
+}
+
+static void
+l3dss1_resume_rej(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char *p;
+	struct sk_buff *skb = arg;
+	int cause = -1;
+
+	L3DelTimer(&pc->timer);
+	p = skb->data;
+	if ((p = findie(p, skb->len, IE_CAUSE, 0))) {
+		p++;
+		if (*p++ == 2)
+			pc->para.loc = *p++;
+		cause = *p & 0x7f;
+	}
+	dev_kfree_skb(skb);
+	pc->para.cause = cause;
+	newl3state(pc, 0);
+	pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc);
+	release_l3_process(pc);
+}
 
-	
 static void
 l3dss1_global_restart(struct l3_process *pc, u_char pr, void *arg)
 {
 	u_char tmp[32];
 	u_char *p;
-	u_char ri, chan=0;
+	u_char ri, ch = 0, chan = 0;
 	int l;
 	struct sk_buff *skb = arg;
 	struct l3_process *up;
-	
+
 	newl3state(pc, 2);
 	L3DelTimer(&pc->timer);
 	p = skb->data;
 	if ((p = findie(p, skb->len, IE_RESTART_IND, 0))) {
-	        ri = p[2];
-	        sprintf(tmp, "Restart %x", ri);
+		ri = p[2];
+		l3_debug(pc->st, "Restart %x", ri);
 	} else {
-		sprintf(tmp, "Restart without restart IE");
+		l3_debug(pc->st, "Restart without restart IE");
 		ri = 0x86;
 	}
-	l3_debug(pc->st, tmp);
 	p = skb->data;
 	if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) {
 		chan = p[2] & 3;
-		sprintf(tmp, "Restart for channel %d", chan);
-		l3_debug(pc->st, tmp);
+		ch = p[2];
+		if (pc->st->l3.debug)
+			l3_debug(pc->st, "Restart for channel %d", chan);
 	}
 	dev_kfree_skb(skb);
 	newl3state(pc, 2);
 	up = pc->st->l3.proc;
 	while (up) {
-		if ((ri & 7)==7)
-			up->st->lli.l4l3(up->st, CC_RESTART, up);
+		if ((ri & 7) == 7)
+			up->st->lli.l4l3(up->st, CC_RESTART | REQUEST, up);
 		else if (up->para.bchannel == chan)
-				up->st->lli.l4l3(up->st, CC_RESTART, up);
+			up->st->lli.l4l3(up->st, CC_RESTART | REQUEST, up);
 		up = up->next;
 	}
 	p = tmp;
@@ -1325,9 +1604,9 @@
 	if (chan) {
 		*p++ = IE_CHANNEL_ID;
 		*p++ = 1;
-		*p++ = chan | 0x80;
+		*p++ = ch | 0x80;
 	}
-	*p++ = 0x79; /* RESTART Ind */
+	*p++ = 0x79;		/* RESTART Ind */
 	*p++ = 1;
 	*p++ = ri;
 	l = p - tmp;
@@ -1335,32 +1614,33 @@
 		return;
 	memcpy(skb_put(skb, l), tmp, l);
 	newl3state(pc, 0);
-	pc->st->l3.l3l2(pc->st, DL_DATA, skb);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
 }
-
 /* *INDENT-OFF* */
 static struct stateentry downstatelist[] =
 {
 	{SBIT(0),
-	 CC_ESTABLISH, l3dss1_msg_without_setup},
+	 CC_SETUP | REQUEST, l3dss1_setup_req},
 	{SBIT(0),
-	 CC_SETUP_REQ, l3dss1_setup_req},
+	 CC_RESUME | REQUEST, l3dss1_resume_req},
 	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(6) | SBIT(7) | SBIT(8) | SBIT(10),
-	 CC_DISCONNECT_REQ, l3dss1_disconnect_req},
+	 CC_DISCONNECT | REQUEST, l3dss1_disconnect_req},
 	{SBIT(12),
-	 CC_RELEASE_REQ, l3dss1_release_req},
+	 CC_RELEASE | REQUEST, l3dss1_release_req},
 	{ALL_STATES,
-	 CC_DLRL, l3dss1_reset},
+	 CC_DLRL | REQUEST, l3dss1_reset},
 	{ALL_STATES,
-	 CC_RESTART, l3dss1_restart},
+	 CC_RESTART | REQUEST, l3dss1_restart},
 	{SBIT(6),
-	 CC_IGNORE, l3dss1_reset},
+	 CC_IGNORE | REQUEST, l3dss1_reset},
 	{SBIT(6),
-	 CC_REJECT_REQ, l3dss1_reject_req},
+	 CC_REJECT | REQUEST, l3dss1_reject_req},
 	{SBIT(6),
-	 CC_ALERTING_REQ, l3dss1_alert_req},
+	 CC_ALERTING | REQUEST, l3dss1_alert_req},
 	{SBIT(6) | SBIT(7),
-	 CC_SETUP_RSP, l3dss1_setup_rsp},
+	 CC_SETUP | RESPONSE, l3dss1_setup_rsp},
+	{SBIT(10),
+	 CC_SUSPEND | REQUEST, l3dss1_suspend_req},
 	{SBIT(1),
 	 CC_T303, l3dss1_t303},
 	{SBIT(2),
@@ -1371,14 +1651,18 @@
 	 CC_T313, l3dss1_t313},
 	{SBIT(11),
 	 CC_T305, l3dss1_t305},
+	{SBIT(15),
+	 CC_T319, l3dss1_t319},
+	{SBIT(17),
+	 CC_T318, l3dss1_t318},
 	{SBIT(19),
 	 CC_T308_1, l3dss1_t308_1},
 	{SBIT(19),
 	 CC_T308_2, l3dss1_t308_2},
 };
 
-static int downsllen = sizeof(downstatelist) /
-sizeof(struct stateentry);
+#define DOWNSLLEN \
+	(sizeof(downstatelist) / sizeof(struct stateentry))
 
 static struct stateentry datastatelist[] =
 {
@@ -1408,10 +1692,10 @@
 	 SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19),
 	 MT_RELEASE_COMPLETE, l3dss1_release_cmpl},
 	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10) |
-	 SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) /*| SBIT(19)*/,
+	 SBIT(11) | SBIT(12) | SBIT(15) /* | SBIT(17) | SBIT(19)*/,
 	 MT_RELEASE, l3dss1_release},
 	{SBIT(19),  MT_RELEASE, l3dss1_release_ind},
-	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10),
+	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10) | SBIT(15),
 	 MT_DISCONNECT, l3dss1_disconnect},
 	{SBIT(11),
 	 MT_DISCONNECT, l3dss1_release_req},
@@ -1423,61 +1707,57 @@
 	 MT_CONNECT_ACKNOWLEDGE, l3dss1_status_req},
 	{SBIT(8),
 	 MT_CONNECT_ACKNOWLEDGE, l3dss1_connect_ack},
-	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(8) | SBIT(10) | SBIT(11) | SBIT(19),
+	{SBIT(15),
+	 MT_SUSPEND_ACKNOWLEDGE, l3dss1_suspend_ack},
+	{SBIT(15),
+	 MT_SUSPEND_REJECT, l3dss1_suspend_rej},
+	{SBIT(17),
+	 MT_RESUME_ACKNOWLEDGE, l3dss1_resume_ack},
+	{SBIT(17),
+	 MT_RESUME_REJECT, l3dss1_resume_rej},
+	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(8) | SBIT(10) | SBIT(11) | SBIT(15) | SBIT(17) | SBIT(19),
 	 MT_INVALID, l3dss1_status_req},
 };
 
-static int datasllen = sizeof(datastatelist) / sizeof(struct stateentry);
+#define DATASLLEN \
+	(sizeof(datastatelist) / sizeof(struct stateentry))
 
 static struct stateentry globalmes_list[] =
 {
 	{ALL_STATES,
-         MT_STATUS, l3dss1_status},
+	 MT_STATUS, l3dss1_status},
 	{SBIT(0),
 	 MT_RESTART, l3dss1_global_restart},
 /*	{SBIT(1),
-	 MT_RESTART_ACKNOWLEDGE, l3dss1_restart_ack},                                  
+	 MT_RESTART_ACKNOWLEDGE, l3dss1_restart_ack},
 */
 };
-static int globalm_len = sizeof(globalmes_list) / sizeof(struct stateentry);
-
-#if 0
-static struct stateentry globalcmd_list[] =
-{
-	{ALL_STATES,
-         CC_STATUS, l3dss1_status_req},
-	{SBIT(0),
-	 CC_RESTART, l3dss1_restart_req},
-};
-
-static int globalc_len = sizeof(globalcmd_list) / sizeof(struct stateentry);
-#endif
+#define GLOBALM_LEN \
+	(sizeof(globalmes_list) / sizeof(struct stateentry))
 /* *INDENT-ON* */
 
+
 static void
 global_handler(struct PStack *st, int mt, struct sk_buff *skb)
 {
 	int i;
-	char tmp[64];
 	struct l3_process *proc = st->l3.global;
-	
-	for (i = 0; i < globalm_len; i++)
+
+	for (i = 0; i < GLOBALM_LEN; i++)
 		if ((mt == globalmes_list[i].primitive) &&
 		    ((1 << proc->state) & globalmes_list[i].state))
 			break;
-	if (i == globalm_len) {
+	if (i == GLOBALM_LEN) {
 		dev_kfree_skb(skb);
 		if (st->l3.debug & L3_DEB_STATE) {
-			sprintf(tmp, "dss1 global state %d mt %x unhandled",
+			l3_debug(st, "dss1 global state %d mt %x unhandled",
 				proc->state, mt);
-			l3_debug(st, tmp);
 		}
 		return;
 	} else {
 		if (st->l3.debug & L3_DEB_STATE) {
-			sprintf(tmp, "dss1 global %d mt %x",
+			l3_debug(st, "dss1 global %d mt %x",
 				proc->state, mt);
-			l3_debug(st, tmp);
 		}
 		globalmes_list[i].rout(proc, mt, skb);
 	}
@@ -1490,24 +1770,34 @@
 	char *ptr;
 	struct sk_buff *skb = arg;
 	struct l3_process *proc;
-	char tmp[80];
 
+	switch (pr) {
+		case (DL_DATA | INDICATION):
+		case (DL_UNIT_DATA | INDICATION):
+			break;
+		case (DL_ESTABLISH | CONFIRM):
+		case (DL_ESTABLISH | INDICATION):
+		case (DL_RELEASE | INDICATION):
+		case (DL_RELEASE | CONFIRM):
+			l3_msg(st, pr, arg);
+			return;
+			break;
+	}
 	if (skb->data[0] != PROTO_DIS_EURO) {
 		if (st->l3.debug & L3_DEB_PROTERR) {
-			sprintf(tmp, "dss1up%sunexpected discriminator %x message len %d",
-				(pr == DL_DATA) ? " " : "(broadcast) ",
-				skb->data[0], skb->len);
-			l3_debug(st, tmp);
+			l3_debug(st, "dss1up%sunexpected discriminator %x message len %d",
+				 (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
+				 skb->data[0], skb->len);
 		}
 		dev_kfree_skb(skb);
 		return;
 	}
 	cr = getcallref(skb->data);
 	mt = skb->data[skb->data[1] + 2];
-	if (!cr) {				/* Global CallRef */
+	if (!cr) {		/* Global CallRef */
 		global_handler(st, mt, skb);
 		return;
-	} else if (cr == -1) {			/* Dummy Callref */
+	} else if (cr == -1) {	/* Dummy Callref */
 		dev_kfree_skb(skb);
 		return;
 	} else if (!(proc = getl3proc(st, cr))) {
@@ -1515,7 +1805,7 @@
 		 * this callreference is active
 		 */
 		if (mt == MT_SETUP) {
-		/* Setup creates a new transaction process */
+			/* Setup creates a new transaction process */
 			if (!(proc = new_l3_process(st, cr))) {
 				/* May be to answer with RELEASE_COMPLETE and
 				 * CAUSE 0x2f "Resource unavailable", but this
@@ -1526,14 +1816,14 @@
 			}
 		} else if (mt == MT_STATUS) {
 			cause = 0;
-			if((ptr = findie(skb->data, skb->len, IE_CAUSE, 0)) != NULL) {
-				  ptr++;
-				  if (*ptr++ == 2)
-				  	ptr++;
-				  cause = *ptr & 0x7f;
+			if ((ptr = findie(skb->data, skb->len, IE_CAUSE, 0)) != NULL) {
+				ptr++;
+				if (*ptr++ == 2)
+					ptr++;
+				cause = *ptr & 0x7f;
 			}
 			callState = 0;
-			if((ptr = findie(skb->data, skb->len, IE_CALL_STATE, 0)) != NULL) {
+			if ((ptr = findie(skb->data, skb->len, IE_CALL_STATE, 0)) != NULL) {
 				ptr++;
 				if (*ptr++ == 2)
 					ptr++;
@@ -1549,29 +1839,29 @@
 				return;
 			} else {
 				/* ETS 300-104 part 2.4.2
-				 * if setup has not been made and a message type 
+				 * if setup has not been made and a message type
 				 * MT_STATUS is received with call state != 0,
 				 * we must send MT_RELEASE_COMPLETE cause 101
 				 */
 				dev_kfree_skb(skb);
 				if ((proc = new_l3_process(st, cr))) {
-					proc->para.cause = 0x65; /* 101 */
-					proc->st->l3.l3l4(proc, CC_ESTABLISH, NULL);
+					proc->para.cause = 0x65;	/* 101 */
+					l3dss1_msg_without_setup(proc, 0, NULL);
 				}
 				return;
 			}
-		} else if (mt == MT_RELEASE_COMPLETE){
+		} else if (mt == MT_RELEASE_COMPLETE) {
 			dev_kfree_skb(skb);
 			return;
 		} else {
 			/* ETS 300-104 part 2
-			 * if setup has not been made and a message type 
+			 * if setup has not been made and a message type
 			 * (except MT_SETUP and RELEASE_COMPLETE) is received,
 			 * we must send MT_RELEASE_COMPLETE cause 81 */
 			dev_kfree_skb(skb);
 			if ((proc = new_l3_process(st, cr))) {
-				proc->para.cause = 0x51; /* 81 */
-				proc->st->l3.l3l4(proc, CC_ESTABLISH, NULL);
+				proc->para.cause = 0x51;	/* 81 */
+				l3dss1_msg_without_setup(proc, 0, NULL);
 			}
 			return;
 		}
@@ -1581,28 +1871,25 @@
 		 * if setup has been made and invalid message type is received,
 		 * we must send MT_STATUS cause 0x62
 		 */
-		mt = MT_INVALID;  /* sorry, not clean, but do the right thing ;-) */
+		mt = MT_INVALID;	/* sorry, not clean, but do the right thing ;-) */
 	}
-
-	for (i = 0; i < datasllen; i++)
+	for (i = 0; i < DATASLLEN; i++)
 		if ((mt == datastatelist[i].primitive) &&
 		    ((1 << proc->state) & datastatelist[i].state))
 			break;
-	if (i == datasllen) {
+	if (i == DATASLLEN) {
 		dev_kfree_skb(skb);
 		if (st->l3.debug & L3_DEB_STATE) {
-			sprintf(tmp, "dss1up%sstate %d mt %x unhandled",
-				(pr == DL_DATA) ? " " : "(broadcast) ",
+			l3_debug(st, "dss1up%sstate %d mt %x unhandled",
+				(pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
 				proc->state, mt);
-			l3_debug(st, tmp);
 		}
 		return;
 	} else {
 		if (st->l3.debug & L3_DEB_STATE) {
-			sprintf(tmp, "dss1up%sstate %d mt %x",
-				(pr == DL_DATA) ? " " : "(broadcast) ",
+			l3_debug(st, "dss1up%sstate %d mt %x",
+				(pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
 				proc->state, mt);
-			l3_debug(st, tmp);
 		}
 		datastatelist[i].rout(proc, pr, skb);
 	}
@@ -1614,9 +1901,11 @@
 	int i, cr;
 	struct l3_process *proc;
 	struct Channel *chan;
-	char tmp[80];
 
-	if (CC_SETUP_REQ == pr) {
+	if (((DL_ESTABLISH | REQUEST) == pr) || ((DL_RELEASE | REQUEST) == pr)) {
+		l3_msg(st, pr, NULL);
+		return;
+	} else if (((CC_SETUP | REQUEST) == pr) || ((CC_RESUME | REQUEST) == pr)) {
 		chan = arg;
 		cr = newcallref();
 		cr |= 0x80;
@@ -1630,24 +1919,22 @@
 		proc = arg;
 	}
 	if (!proc) {
-		printk(KERN_ERR "HiSax internal error dss1down without proc\n");
+		printk(KERN_ERR "HiSax dss1down without proc pr=%04x\n", pr);
 		return;
 	}
-	for (i = 0; i < downsllen; i++)
+	for (i = 0; i < DOWNSLLEN; i++)
 		if ((pr == downstatelist[i].primitive) &&
 		    ((1 << proc->state) & downstatelist[i].state))
 			break;
-	if (i == downsllen) {
+	if (i == DOWNSLLEN) {
 		if (st->l3.debug & L3_DEB_STATE) {
-			sprintf(tmp, "dss1down state %d prim %d unhandled",
+			l3_debug(st, "dss1down state %d prim %d unhandled",
 				proc->state, pr);
-			l3_debug(st, tmp);
 		}
 	} else {
 		if (st->l3.debug & L3_DEB_STATE) {
-			sprintf(tmp, "dss1down state %d prim %d",
+			l3_debug(st, "dss1down state %d prim %d",
 				proc->state, pr);
-			l3_debug(st, tmp);
 		}
 		downstatelist[i].rout(proc, pr, arg);
 	}
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/l3dss1.h linux/drivers/isdn/hisax/l3dss1.h
--- v2.3.3/linux/drivers/isdn/hisax/l3dss1.h	Wed Apr  1 16:21:02 1998
+++ linux/drivers/isdn/hisax/l3dss1.h	Sun May 23 10:03:41 1999
@@ -1,8 +1,12 @@
-/* $Id: l3dss1.h,v 1.5 1998/02/02 13:34:30 keil Exp $
+/* $Id: l3dss1.h,v 1.6 1998/03/19 13:18:50 keil Exp $
  *
  *  DSS1 (Euro) D-channel protocol defines
  *
  * $Log: l3dss1.h,v $
+ * Revision 1.6  1998/03/19 13:18:50  keil
+ * Start of a CAPI like interface for supplementary Service
+ * first service: SUSPEND
+ *
  * Revision 1.5  1998/02/02 13:34:30  keil
  * Support australian Microlink net and german AOCD
  *
@@ -63,8 +67,9 @@
 
 #define MT_INVALID             0xff
 
-#define IE_CAUSE               0x08
 #define IE_BEARER              0x04
+#define IE_CAUSE               0x08
+#define IE_CALLID              0x10
 #define IE_FACILITY            0x1c
 #define IE_CALL_STATE          0x14
 #define IE_CHANNEL_ID          0x18
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/lmgr.c linux/drivers/isdn/hisax/lmgr.c
--- v2.3.3/linux/drivers/isdn/hisax/lmgr.c	Wed Apr  1 16:21:02 1998
+++ linux/drivers/isdn/hisax/lmgr.c	Sun May 23 10:03:41 1999
@@ -1,11 +1,21 @@
-/* $Id: lmgr.c,v 1.2 1997/10/29 19:09:34 keil Exp $
+/* $Id: lmgr.c,v 1.5 1998/11/15 23:55:12 keil Exp $
 
- * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author       Karsten Keil (keil@isdn4linux.de)
  *
  *
  *  Layermanagement module
  *
  * $Log: lmgr.c,v $
+ * Revision 1.5  1998/11/15 23:55:12  keil
+ * changes from 2.0
+ *
+ * Revision 1.4  1998/05/25 12:58:19  keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
+ * Revision 1.3  1998/03/07 22:57:06  tsbogend
+ * made HiSax working on Linux/Alpha
+ *
  * Revision 1.2  1997/10/29 19:09:34  keil
  * new L1
  *
@@ -26,7 +36,7 @@
 		case 'D':
 		case 'G':
 		case 'H':
-			st->l2.l2tei(st, MDL_ERROR_REQ, NULL);
+			st->l2.l2tei(st, MDL_ERROR | REQUEST, NULL);
 			break;
 	}
 }
@@ -34,17 +44,15 @@
 static void
 hisax_manager(struct PStack *st, int pr, void *arg)
 {
-	char tm[32], str[256];
-	int Code;
+	long Code;
 
 	switch (pr) {
-		case MDL_ERROR_IND:
-			Code = (int) arg;
-			jiftime(tm, jiffies);
-			sprintf(str, "%s manager: MDL_ERROR %c %s\n", tm,
-				Code, test_bit(FLG_LAPD, &st->l2.flag) ?
+		case (MDL_ERROR | INDICATION):
+			Code = (long) arg;
+			HiSax_putstatus(st->l1.hardware, "manager: MDL_ERROR",
+				"%c %s\n", (char)Code, 
+				test_bit(FLG_LAPD, &st->l2.flag) ?
 				"D-channel" : "B-channel");
-			HiSax_putstatus(st->l1.hardware, str);
 			if (test_bit(FLG_LAPD, &st->l2.flag))
 				error_handling_dchan(st, Code);
 			break;
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/md5sums.asc linux/drivers/isdn/hisax/md5sums.asc
--- v2.3.3/linux/drivers/isdn/hisax/md5sums.asc	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/md5sums.asc	Sun May 23 10:03:41 1999
@@ -0,0 +1,29 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+
+# This are valid md5sums for certificated HiSax driver.
+# The certification is valid only if the md5sums of all files match.
+# The certification is valid only for ELSA QuickStep cards in the moment.
+# Read ../../../Documentation/isdn/HiSax.cert for more informations.
+# 
+a273c532aec063574273ee519975cd9a  isac.c
+27c5c5bfa2ceabf02e2e6d686b03abde  isdnl1.c
+8c89ac659d3188ab997fb575da22b566  isdnl2.c
+d0fa912aa284b8fd19fed86b65999f6f  isdnl3.c
+1bce120740b615006286ad9b2d7fcdcb  tei.c
+8845f88dd17917d9b58badeff1605057  callc.c
+f3ec2a634f06074d16167aaba02b6dc1  cert.c
+71840ec8189f42b0db86fb38e5e5984c  l3dss1.c
+1882de8bea921b9ccd98fbe77267aa04  l3_1tr6.c
+3bd7af3a11693d028300278744d0da09  elsa.c
+# end of md5sums
+
+-----BEGIN PGP SIGNATURE-----
+Version: 2.6.3i
+Charset: noconv
+
+iQCVAwUBNrl5JDpxHvX/mS9tAQHm8wP+Nk64UJ2abdDG/igXZSrwcYhX/Kp7cxt9
+ccYp+aaur+pALA0lxwY3xcLt9u36fCYuTLHAVmQoiC9Vbemj37yzM2rUpz9nkw/7
+D6gLqZs2jxVpAwVVJgp0JwDONKXaRX6Lt2EPD9PTW6vxRWEu0HqGhM5hrtd/o4rV
+mC1W7Wj13XM=
+=LdhT
+-----END PGP SIGNATURE-----
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/mic.c linux/drivers/isdn/hisax/mic.c
--- v2.3.3/linux/drivers/isdn/hisax/mic.c	Wed Apr  1 16:21:02 1998
+++ linux/drivers/isdn/hisax/mic.c	Sun May 23 10:03:41 1999
@@ -1,4 +1,4 @@
-/* $Id: mic.c,v 1.6 1998/02/17 15:39:57 keil Exp $
+/* $Id: mic.c,v 1.7 1998/04/15 16:44:32 keil Exp $
 
  * mic.c  low level stuff for mic cards
  *
@@ -8,6 +8,9 @@
  *
  *
  * $Log: mic.c,v $
+ * Revision 1.7  1998/04/15 16:44:32  keil
+ * new init code
+ *
  * Revision 1.6  1998/02/17 15:39:57  keil
  * fix reset problem
  *
@@ -37,7 +40,7 @@
 
 extern const char *CardType[];
 
-const char *mic_revision = "$Revision: 1.6 $";
+const char *mic_revision = "$Revision: 1.7 $";
 
 #define byteout(addr,val) outb(val,addr)
 #define bytein(addr) inb(addr)
@@ -220,10 +223,7 @@
 					I4L_IRQ_FLAG, "HiSax", cs));
 		case CARD_INIT:
 			inithscx(cs); /* /RTSA := ISAC RST */
-			clear_pending_isac_ints(cs);
-			clear_pending_hscx_ints(cs);
-			initisac(cs);
-			inithscx(cs);
+			inithscxisac(cs, 3);
 			return(0);
 		case CARD_TEST:
 			return(0);
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/netjet.c linux/drivers/isdn/hisax/netjet.c
--- v2.3.3/linux/drivers/isdn/hisax/netjet.c	Thu Nov  5 09:58:44 1998
+++ linux/drivers/isdn/hisax/netjet.c	Sun May 23 10:03:41 1999
@@ -1,13 +1,30 @@
-/* $Id: netjet.c,v 1.3 1998/02/12 23:08:05 keil Exp $
+/* $Id: netjet.c,v 1.8 1998/11/15 23:55:14 keil Exp $
 
  * netjet.c     low level stuff for Traverse Technologie NETJet ISDN cards
  *
- * Author     Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author     Karsten Keil (keil@isdn4linux.de)
  *
  * Thanks to Traverse Technologie Australia for documents and informations
  *
  *
  * $Log: netjet.c,v $
+ * Revision 1.8  1998/11/15 23:55:14  keil
+ * changes from 2.0
+ *
+ * Revision 1.7  1998/09/30 22:24:48  keil
+ * Fix missing line in setstack*
+ *
+ * Revision 1.6  1998/08/13 23:36:54  keil
+ * HiSax 3.1 - don't work stable with current LinkLevel
+ *
+ * Revision 1.5  1998/05/25 12:58:21  keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
+ * Revision 1.4  1998/04/15 16:42:35  keil
+ * new init code
+ * new PCI init (2.1.94)
+ *
  * Revision 1.3  1998/02/12 23:08:05  keil
  * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
  *
@@ -25,15 +42,12 @@
 #include "hscx.h"
 #include "isdnl1.h"
 #include <linux/pci.h>
-#include <linux/bios32.h>
 #include <linux/interrupt.h>
-#define fcstab  ppp_crc16_table
 #include <linux/ppp_defs.h>
-extern __u16 ppp_crc16_table[256]; /* from ppp code */
 
 extern const char *CardType[];
 
-const char *NETjet_revision = "$Revision: 1.3 $";
+const char *NETjet_revision = "$Revision: 1.8 $";
 
 #define byteout(addr,val) outb(val,addr)
 #define bytein(addr) inb(addr)
@@ -62,6 +76,12 @@
 
 #define NETJET_ISAC_OFF	0xc0
 #define NETJET_ISACIRQ	0x10
+#define NETJET_IRQM0_READ	0x0c
+#define NETJET_IRQM0_READ_1	0x04
+#define NETJET_IRQM0_READ_2	0x08
+#define NETJET_IRQM0_WRITE	0x03
+#define NETJET_IRQM0_WRITE_1	0x01
+#define NETJET_IRQM0_WRITE_2	0x02
 
 #define NETJET_DMA_SIZE 512
 
@@ -115,6 +135,42 @@
 	insb(cs->hw.njet.isac, data, size);
 }
 
+__u16 fcstab[256] =
+{
+	0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+	0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+	0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+	0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+	0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+	0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+	0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+	0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+	0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+	0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+	0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+	0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+	0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+	0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+	0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+	0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+	0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+	0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+	0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+	0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+	0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+	0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+	0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+	0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+	0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+	0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+	0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+	0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+	0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+	0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+	0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+	0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
+
 static void 
 WriteISACfifo(struct IsdnCardState *cs, u_char *data, int size)
 {
@@ -146,25 +202,20 @@
 mode_tiger(struct BCState *bcs, int mode, int bc)
 {
 	struct IsdnCardState *cs = bcs->cs;
-	char tmp[64];
 
-	if (cs->debug & L1_DEB_HSCX) {
-		sprintf(tmp, "Tiger mode %d bchan %d/%d",
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "Tiger mode %d bchan %d/%d",
 			mode, bc, bcs->channel);
-		debugl1(cs, tmp);
-	}
 	bcs->mode = mode;
 	bcs->channel = bc;
 	switch (mode) {
 		case (L1_MODE_NULL):
 			fill_mem(bcs, bcs->hw.tiger.send,
 				NETJET_DMA_SIZE, bc, 0xff);
-			if (cs->debug & L1_DEB_HSCX) {
-				sprintf(tmp, "Tiger stat rec %d/%d send %d",
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "Tiger stat rec %d/%d send %d",
 					bcs->hw.tiger.r_tot, bcs->hw.tiger.r_err,
 					bcs->hw.tiger.s_tot); 
-				debugl1(cs, tmp);
-			}
 			if ((cs->bcs[0].mode == L1_MODE_NULL) &&
 				(cs->bcs[1].mode == L1_MODE_NULL)) {
 				cs->hw.njet.dmactrl = 0;
@@ -197,16 +248,14 @@
 			test_and_set_bit(BC_FLG_EMPTY, &bcs->Flag);
 			break;
 	}
-	if (cs->debug & L1_DEB_HSCX) {
-		sprintf(tmp, "tiger: set %x %x %x  %x/%x  pulse=%d",
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "tiger: set %x %x %x  %x/%x  pulse=%d",
 			bytein(cs->hw.njet.base + NETJET_DMACTRL),
 			bytein(cs->hw.njet.base + NETJET_IRQMASK0),
 			bytein(cs->hw.njet.base + NETJET_IRQSTAT0),
 			inl(cs->hw.njet.base + NETJET_DMA_READ_ADR),
 			inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR),
 			bytein(cs->hw.njet.base + NETJET_PULSE_CNT));
-		debugl1(cs, tmp);
-	}
 }
 
 static u_char dummyrr(struct IsdnCardState *cs, int chan, u_char off)
@@ -266,7 +315,7 @@
 			val >>= 1;\
 		}
 
-static void make_raw_data(struct BCState *bcs) {
+static int make_raw_data(struct BCState *bcs) {
 	register u_int i,s_cnt=0;
 	register u_char j;
 	register u_char val;
@@ -274,13 +323,15 @@
 	register u_char s_val = 0;
 	register u_char bitcnt = 0;
 	u_int fcs;
-	char tmp[64];
-	
 	
+	if (!bcs->tx_skb) {
+		debugl1(bcs->cs, "tiger make_raw: NULL skb");
+		return(1);
+	}
 	bcs->hw.tiger.sendbuf[s_cnt++] = HDLC_FLAG_VALUE;
 	fcs = PPP_INITFCS;
-	for (i=0; i<bcs->hw.tiger.tx_skb->len; i++) {
-		val = bcs->hw.tiger.tx_skb->data[i];
+	for (i=0; i<bcs->tx_skb->len; i++) {
+		val = bcs->tx_skb->data[i];
 		fcs = PPP_FCS (fcs, val);
 		MAKE_RAW_BYTE;
 	}
@@ -303,11 +354,9 @@
 		}
 		val >>= 1;
 	}
-	if (bcs->cs->debug & L1_DEB_HSCX) {
-		sprintf(tmp,"tiger make_raw: in %d out %d.%d",
-			bcs->hw.tiger.tx_skb->len, s_cnt, bitcnt);
-		debugl1(bcs->cs,tmp);
-	}
+	if (bcs->cs->debug & L1_DEB_HSCX)
+		debugl1(bcs->cs,"tiger make_raw: in %ld out %d.%d",
+			bcs->tx_skb->len, s_cnt, bitcnt);
 	if (bitcnt) {
 		while (8>bitcnt++) {
 			s_val >>= 1;
@@ -316,8 +365,9 @@
 		bcs->hw.tiger.sendbuf[s_cnt++] = s_val;
 	}
 	bcs->hw.tiger.sendcnt = s_cnt;
-	bcs->tx_cnt -= bcs->hw.tiger.tx_skb->len;
+	bcs->tx_cnt -= bcs->tx_skb->len;
 	bcs->hw.tiger.sp = bcs->hw.tiger.sendbuf;
+	return(0);
 }
 
 static void got_frame(struct BCState *bcs, int count) {
@@ -349,7 +399,6 @@
 	register u_char r_val = bcs->hw.tiger.r_val;
 	register u_int bitcnt = bcs->hw.tiger.r_bitcnt;
 	u_int *p = buf;
-	char tmp[64];
         
 	for (i=0;i<cnt;i++) {
 		val = bcs->channel ? ((*p>>8) & 0xff) : (*p & 0xff);
@@ -370,11 +419,9 @@
 				} else {
 					r_one=0;
 					state= HDLC_FLAG_SEARCH;
-					if (bcs->cs->debug & L1_DEB_HSCX) {
-						sprintf(tmp,"tiger read_raw: zBit(%d,%d,%d) %x",
+					if (bcs->cs->debug & L1_DEB_HSCX)
+						debugl1(bcs->cs,"tiger read_raw: zBit(%d,%d,%d) %x",
 							bcs->hw.tiger.r_tot,i,j,val);
-						debugl1(bcs->cs,tmp);
-					}
 				}
 			} else if (state == HDLC_FLAG_SEARCH) { 
 				if (val & 1) {
@@ -387,11 +434,9 @@
 						bitcnt=0;
 						r_val=0;
 						state=HDLC_FLAG_FOUND;
-						if (bcs->cs->debug & L1_DEB_HSCX) {
-							sprintf(tmp,"tiger read_raw: flag(%d,%d,%d) %x",
+						if (bcs->cs->debug & L1_DEB_HSCX)
+							debugl1(bcs->cs,"tiger read_raw: flag(%d,%d,%d) %x",
 								bcs->hw.tiger.r_tot,i,j,val);
-							debugl1(bcs->cs,tmp);
-						}
 					}
 					r_one=0;
 				}
@@ -425,12 +470,10 @@
 					bcs->hw.tiger.r_fcs = PPP_INITFCS;
 					bcs->hw.tiger.rcvbuf[0] = r_val;
 					bcs->hw.tiger.r_fcs = PPP_FCS (bcs->hw.tiger.r_fcs, r_val);
-					if (bcs->cs->debug & L1_DEB_HSCX) {
-						sprintf(tmp,"tiger read_raw: byte1(%d,%d,%d) rval %x val %x i %x",
+					if (bcs->cs->debug & L1_DEB_HSCX)
+						debugl1(bcs->cs,"tiger read_raw: byte1(%d,%d,%d) rval %x val %x i %x",
 							bcs->hw.tiger.r_tot,i,j,r_val,val,
 							bcs->cs->hw.njet.irqstat0);
-						debugl1(bcs->cs,tmp);
-					}
 				}
 			} else if (state ==  HDLC_FRAME_FOUND) {
 				if (val & 1) {
@@ -453,11 +496,9 @@
 							state=HDLC_FLAG_SEARCH;
 							bcs->hw.tiger.r_err++;
 						} else {
-							if (bcs->cs->debug & L1_DEB_HSCX) {
-								sprintf(tmp,"tiger frame end(%d,%d): fcs(%x) i %x",
+							if (bcs->cs->debug & L1_DEB_HSCX)
+								debugl1(bcs->cs,"tiger frame end(%d,%d): fcs(%x) i %x",
 									i,j,bcs->hw.tiger.r_fcs, bcs->cs->hw.njet.irqstat0);
-								debugl1(bcs->cs, tmp);
-							}
 							if (bcs->hw.tiger.r_fcs == PPP_GOODFCS) {
 								got_frame(bcs, (bitcnt>>3)-3);
 							} else
@@ -509,7 +550,15 @@
 	u_int *p;
 	int cnt = NETJET_DMA_SIZE/2;
 	
-	if (cs->hw.njet.irqstat0 & 4)
+	if ((cs->hw.njet.irqstat0 & cs->hw.njet.last_is0) & NETJET_IRQM0_READ) {
+		debugl1(cs,"tiger warn read double dma %x/%x",
+			cs->hw.njet.irqstat0, cs->hw.njet.last_is0);
+		return;
+	} else {
+		cs->hw.njet.last_is0 &= ~NETJET_IRQM0_READ;
+		cs->hw.njet.last_is0 |= (cs->hw.njet.irqstat0 & NETJET_IRQM0_READ);
+	}	
+	if (cs->hw.njet.irqstat0 & NETJET_IRQM0_READ_1)
 		p = cs->bcs[0].hw.tiger.rec + NETJET_DMA_SIZE - 1;
 	else
 		p = cs->bcs[0].hw.tiger.rec + cnt - 1;
@@ -517,32 +566,28 @@
 		read_raw(cs->bcs, p, cnt);
 	if (cs->bcs[1].mode == L1_MODE_HDLC)
 		read_raw(cs->bcs + 1, p, cnt);
-	cs->hw.njet.irqstat0 &= 0xf3;
+	cs->hw.njet.irqstat0 &= ~NETJET_IRQM0_READ;
 }
 
 static void write_raw(struct BCState *bcs, u_int *buf, int cnt);
 
 static void fill_dma(struct BCState *bcs)
 {
-	char tmp[64];
 	register u_int *p, *sp;
 	register int cnt;
 
-	if (!bcs->hw.tiger.tx_skb)
+	if (!bcs->tx_skb)
 		return;
-	if (bcs->cs->debug & L1_DEB_HSCX) {
-		sprintf(tmp,"tiger fill_dma1: c%d %4x", bcs->channel,
+	if (bcs->cs->debug & L1_DEB_HSCX)
+		debugl1(bcs->cs,"tiger fill_dma1: c%d %4x", bcs->channel,
 			bcs->Flag);
-			debugl1(bcs->cs,tmp);
-	}
 	if (test_and_set_bit(BC_FLG_BUSY, &bcs->Flag))
 		return;
-	make_raw_data(bcs);
-	if (bcs->cs->debug & L1_DEB_HSCX) {
-		sprintf(tmp,"tiger fill_dma2: c%d %4x", bcs->channel,
+	if (make_raw_data(bcs))
+		return;		
+	if (bcs->cs->debug & L1_DEB_HSCX)
+		debugl1(bcs->cs,"tiger fill_dma2: c%d %4x", bcs->channel,
 			bcs->Flag);
-			debugl1(bcs->cs,tmp);
-	}
 	if (test_and_clear_bit(BC_FLG_NOFRAME, &bcs->Flag)) {
 		write_raw(bcs, bcs->hw.tiger.sendp, bcs->hw.tiger.free);
 	} else if (test_and_clear_bit(BC_FLG_HALF, &bcs->Flag)) {
@@ -582,17 +627,14 @@
 		}
 		write_raw(bcs, p, cnt);
 	}
-	if (bcs->cs->debug & L1_DEB_HSCX) {
-		sprintf(tmp,"tiger fill_dma3: c%d %4x", bcs->channel,
+	if (bcs->cs->debug & L1_DEB_HSCX)
+		debugl1(bcs->cs,"tiger fill_dma3: c%d %4x", bcs->channel,
 			bcs->Flag);
-		debugl1(bcs->cs,tmp);
-	}
 }
 
 static void write_raw(struct BCState *bcs, u_int *buf, int cnt) {
 	u_int mask, val, *p=buf;
 	u_int i, s_cnt;
-	char tmp[64];
         
         if (cnt <= 0)
         	return;
@@ -617,26 +659,23 @@
 				p = bcs->hw.tiger.send;
 		}
 		bcs->hw.tiger.s_tot += s_cnt;
-		if (bcs->cs->debug & L1_DEB_HSCX) {
-			sprintf(tmp,"tiger write_raw: c%d %x-%x %d/%d %d %x", bcs->channel,
-			(u_int)buf, (u_int)p, s_cnt, cnt, bcs->hw.tiger.sendcnt,
-			bcs->cs->hw.njet.irqstat0);
-			debugl1(bcs->cs,tmp);
-		}
+		if (bcs->cs->debug & L1_DEB_HSCX)
+			debugl1(bcs->cs,"tiger write_raw: c%d %x-%x %d/%d %d %x", bcs->channel,
+				(u_int)buf, (u_int)p, s_cnt, cnt,
+				bcs->hw.tiger.sendcnt, bcs->cs->hw.njet.irqstat0);
 		if (bcs->cs->debug & L1_DEB_HSCX_FIFO)
 			printframe(bcs->cs, bcs->hw.tiger.sp, s_cnt, "snd");
 		bcs->hw.tiger.sp += s_cnt;
 		bcs->hw.tiger.sendp = p;
 		if (!bcs->hw.tiger.sendcnt) {
-			if (!bcs->hw.tiger.tx_skb) {
-				sprintf(tmp,"tiger write_raw: NULL skb s_cnt %d", s_cnt);
-				debugl1(bcs->cs, tmp);
+			if (!bcs->tx_skb) {
+				debugl1(bcs->cs,"tiger write_raw: NULL skb s_cnt %d", s_cnt);
 			} else {
 				if (bcs->st->lli.l1writewakeup &&
-					(PACKET_NOACK != bcs->hw.tiger.tx_skb->pkt_type))
-					bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.tiger.tx_skb->len);
-				dev_kfree_skb(bcs->hw.tiger.tx_skb);
-				bcs->hw.tiger.tx_skb = NULL;
+					(PACKET_NOACK != bcs->tx_skb->pkt_type))
+					bcs->st->lli.l1writewakeup(bcs->st, bcs->tx_skb->len);
+				dev_kfree_skb(bcs->tx_skb);
+				bcs->tx_skb = NULL;
 			}
 			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
 			bcs->hw.tiger.free = cnt - s_cnt;
@@ -646,7 +685,7 @@
 				test_and_clear_bit(BC_FLG_HALF, &bcs->Flag);
 				test_and_set_bit(BC_FLG_NOFRAME, &bcs->Flag);
 			}
-			if ((bcs->hw.tiger.tx_skb = skb_dequeue(&bcs->squeue))) {
+			if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
 				fill_dma(bcs);
 			} else {
 				mask ^= 0xffffffff;
@@ -656,11 +695,9 @@
 						if (p>bcs->hw.tiger.s_end)
 							p = bcs->hw.tiger.send;
 					}
-					if (bcs->cs->debug & L1_DEB_HSCX) {
-						sprintf(tmp, "tiger write_raw: fill rest %d",
+					if (bcs->cs->debug & L1_DEB_HSCX)
+						debugl1(bcs->cs, "tiger write_raw: fill rest %d",
 							cnt - s_cnt);
-						debugl1(bcs->cs,tmp);
-					}
 				}
 				bcs->event |= 1 << B_XMTBUFREADY;
 				queue_task(&bcs->tqueue, &tq_immediate);
@@ -671,24 +708,28 @@
 		test_and_set_bit(BC_FLG_HALF, &bcs->Flag);
 		fill_mem(bcs, buf, cnt, bcs->channel, 0xff);
 		bcs->hw.tiger.free += cnt;
-		if (bcs->cs->debug & L1_DEB_HSCX) {
-			sprintf(tmp,"tiger write_raw: fill half");
-			debugl1(bcs->cs,tmp);
-		}
+		if (bcs->cs->debug & L1_DEB_HSCX)
+			debugl1(bcs->cs,"tiger write_raw: fill half");
 	} else if (test_and_clear_bit(BC_FLG_HALF, &bcs->Flag)) {
 		test_and_set_bit(BC_FLG_EMPTY, &bcs->Flag);
 		fill_mem(bcs, buf, cnt, bcs->channel, 0xff);
-		if (bcs->cs->debug & L1_DEB_HSCX) {
-			sprintf(tmp,"tiger write_raw: fill full");
-			debugl1(bcs->cs,tmp);
-		}
+		if (bcs->cs->debug & L1_DEB_HSCX)
+			debugl1(bcs->cs,"tiger write_raw: fill full");
 	}
 }
 
 static void write_tiger(struct IsdnCardState *cs) {
 	u_int *p, cnt = NETJET_DMA_SIZE/2;
 	
-	if (cs->hw.njet.irqstat0  & 1)
+	if ((cs->hw.njet.irqstat0 & cs->hw.njet.last_is0) & NETJET_IRQM0_WRITE) {
+		debugl1(cs,"tiger warn write double dma %x/%x",
+			cs->hw.njet.irqstat0, cs->hw.njet.last_is0);
+		return;
+	} else {
+		cs->hw.njet.last_is0 &= ~NETJET_IRQM0_WRITE;
+		cs->hw.njet.last_is0 |= (cs->hw.njet.irqstat0 & NETJET_IRQM0_WRITE);
+	}	
+	if (cs->hw.njet.irqstat0  & NETJET_IRQM0_WRITE_1)
 		p = cs->bcs[0].hw.tiger.send + NETJET_DMA_SIZE - 1;
 	else
 		p = cs->bcs[0].hw.tiger.send + cnt - 1;
@@ -696,7 +737,7 @@
 		write_raw(cs->bcs, p, cnt);
 	if (cs->bcs[1].mode == L1_MODE_HDLC)
 		write_raw(cs->bcs + 1, p, cnt);
-	cs->hw.njet.irqstat0 &= 0xfc;
+	cs->hw.njet.irqstat0 &= ~NETJET_IRQM0_WRITE;
 }
 
 static void
@@ -706,45 +747,58 @@
 	long flags;
 
 	switch (pr) {
-		case (PH_DATA_REQ):
+		case (PH_DATA | REQUEST):
 			save_flags(flags);
 			cli();
-			if (st->l1.bcs->hw.tiger.tx_skb) {
+			if (st->l1.bcs->tx_skb) {
 				skb_queue_tail(&st->l1.bcs->squeue, skb);
 				restore_flags(flags);
 			} else {
-				st->l1.bcs->hw.tiger.tx_skb = skb;
+				st->l1.bcs->tx_skb = skb;
 				st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
 				restore_flags(flags);
 			}
 			break;
-		case (PH_PULL_IND):
-			if (st->l1.bcs->hw.tiger.tx_skb) {
+		case (PH_PULL | INDICATION):
+			if (st->l1.bcs->tx_skb) {
 				printk(KERN_WARNING "tiger_l2l1: this shouldn't happen\n");
 				break;
 			}
 			save_flags(flags);
 			cli();
-			st->l1.bcs->hw.tiger.tx_skb = skb;
+			st->l1.bcs->tx_skb = skb;
 			st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
 			restore_flags(flags);
 			break;
-		case (PH_PULL_REQ):
-			if (!st->l1.bcs->hw.tiger.tx_skb) {
+		case (PH_PULL | REQUEST):
+			if (!st->l1.bcs->tx_skb) {
 				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
-				st->l1.l1l2(st, PH_PULL_CNF, NULL);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
 			} else
 				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
 			break;
+		case (PH_ACTIVATE | REQUEST):
+			test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+			mode_tiger(st->l1.bcs, st->l1.mode, st->l1.bc);
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | REQUEST):
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | CONFIRM):
+			test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+			test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+			mode_tiger(st->l1.bcs, 0, st->l1.bc);
+			st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+			break;
 	}
 }
 
+
 void
 close_tigerstate(struct BCState *bcs)
 {
-	struct sk_buff *skb;
-
-	mode_tiger(bcs, 0, 0);
+	mode_tiger(bcs, 0, bcs->channel);
 	if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
 		if (bcs->hw.tiger.rcvbuf) {
 			kfree(bcs->hw.tiger.rcvbuf);
@@ -754,32 +808,26 @@
 			kfree(bcs->hw.tiger.sendbuf);
 			bcs->hw.tiger.sendbuf = NULL;
 		}
-		while ((skb = skb_dequeue(&bcs->rqueue))) {
-			dev_kfree_skb(skb);
-		}
-		while ((skb = skb_dequeue(&bcs->squeue))) {
-			dev_kfree_skb(skb);
-		}
-		if (bcs->hw.tiger.tx_skb) {
-			dev_kfree_skb(bcs->hw.tiger.tx_skb);
-			bcs->hw.tiger.tx_skb = NULL;
+		discard_queue(&bcs->rqueue);
+		discard_queue(&bcs->squeue);
+		if (bcs->tx_skb) {
+			dev_kfree_skb(bcs->tx_skb);
+			bcs->tx_skb = NULL;
 			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
 		}
 	}
 }
 
 static int
-open_tigerstate(struct IsdnCardState *cs, int bc)
+open_tigerstate(struct IsdnCardState *cs, struct BCState *bcs)
 {
-	struct BCState *bcs = cs->bcs + bc;
-
 	if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
-		if (!(bcs->hw.tiger.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_KERNEL))) {
+		if (!(bcs->hw.tiger.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) {
 			printk(KERN_WARNING
 			       "HiSax: No memory for tiger.rcvbuf\n");
 			return (1);
 		}
-		if (!(bcs->hw.tiger.sendbuf = kmalloc(RAW_BUFMAX, GFP_KERNEL))) {
+		if (!(bcs->hw.tiger.sendbuf = kmalloc(RAW_BUFMAX, GFP_ATOMIC))) {
 			printk(KERN_WARNING
 			       "HiSax: No memory for tiger.sendbuf\n");
 			return (1);
@@ -787,7 +835,7 @@
 		skb_queue_head_init(&bcs->rqueue);
 		skb_queue_head_init(&bcs->squeue);
 	}
-	bcs->hw.tiger.tx_skb = NULL;
+	bcs->tx_skb = NULL;
 	bcs->hw.tiger.sendcnt = 0;
 	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
 	bcs->event = 0;
@@ -795,34 +843,17 @@
 	return (0);
 }
 
-static void
-tiger_manl1(struct PStack *st, int pr,
-	  void *arg)
-{
-	switch (pr) {
-		case (PH_ACTIVATE_REQ):
-			test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
-			mode_tiger(st->l1.bcs, st->l1.mode, st->l1.bc);
-			st->l1.l1man(st, PH_ACTIVATE_CNF, NULL);
-			break;
-		case (PH_DEACTIVATE_REQ):
-			if (!test_bit(BC_FLG_BUSY, &st->l1.bcs->Flag))
-				mode_tiger(st->l1.bcs, 0, 0);
-			test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
-			break;
-	}
-}
-
 int
 setstack_tiger(struct PStack *st, struct BCState *bcs)
 {
-	if (open_tigerstate(st->l1.hardware, bcs->channel))
+	bcs->channel = st->l1.bc;
+	if (open_tigerstate(st->l1.hardware, bcs))
 		return (-1);
 	st->l1.bcs = bcs;
 	st->l2.l2l1 = tiger_l2l1;
-	st->ma.manl1 = tiger_manl1;
 	setstack_manager(st);
 	bcs->st = st;
+	setstack_l1_B(st);
 	return (0);
 }
 
@@ -830,8 +861,6 @@
 __initfunc(void
 inittiger(struct IsdnCardState *cs))
 {
-	char tmp[128];
-
 	if (!(cs->bcs[0].hw.tiger.send = kmalloc(NETJET_DMA_SIZE * sizeof(unsigned int),
 		GFP_KERNEL | GFP_DMA))) {
 		printk(KERN_WARNING
@@ -845,9 +874,8 @@
 	cs->bcs[1].hw.tiger.s_end = cs->bcs[0].hw.tiger.s_end;
 	
 	memset(cs->bcs[0].hw.tiger.send, 0xff, NETJET_DMA_SIZE * sizeof(unsigned int));
-	sprintf(tmp, "tiger: send buf %x - %x", (u_int)cs->bcs[0].hw.tiger.send,
+	debugl1(cs, "tiger: send buf %x - %x", (u_int)cs->bcs[0].hw.tiger.send,
 		(u_int)(cs->bcs[0].hw.tiger.send + NETJET_DMA_SIZE - 1));
-	debugl1(cs, tmp);
 	outl(virt_to_bus(cs->bcs[0].hw.tiger.send),
 		cs->hw.njet.base + NETJET_DMA_READ_START);
 	outl(virt_to_bus(cs->bcs[0].hw.tiger.s_irq),
@@ -860,9 +888,8 @@
 		       "HiSax: No memory for tiger.rec\n");
 		return;
 	}
-	sprintf(tmp, "tiger: rec buf %x - %x", (u_int)cs->bcs[0].hw.tiger.rec,
+	debugl1(cs, "tiger: rec buf %x - %x", (u_int)cs->bcs[0].hw.tiger.rec,
 		(u_int)(cs->bcs[0].hw.tiger.rec + NETJET_DMA_SIZE - 1));
-	debugl1(cs, tmp);
 	cs->bcs[1].hw.tiger.rec = cs->bcs[0].hw.tiger.rec;
 	memset(cs->bcs[0].hw.tiger.rec, 0xff, NETJET_DMA_SIZE * sizeof(unsigned int));
 	outl(virt_to_bus(cs->bcs[0].hw.tiger.rec),
@@ -871,11 +898,10 @@
 		cs->hw.njet.base + NETJET_DMA_WRITE_IRQ);
 	outl(virt_to_bus(cs->bcs[0].hw.tiger.rec + NETJET_DMA_SIZE - 1),
 		cs->hw.njet.base + NETJET_DMA_WRITE_END);
-	sprintf(tmp, "tiger: dmacfg  %x/%x  pulse=%d",
+	debugl1(cs, "tiger: dmacfg  %x/%x  pulse=%d",
 		inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR),
 		inl(cs->hw.njet.base + NETJET_DMA_READ_ADR),
 		bytein(cs->hw.njet.base + NETJET_PULSE_CNT));
-	debugl1(cs, tmp);
 	cs->hw.njet.last_is0 = 0;
 	cs->bcs[0].BC_SetStack = setstack_tiger;
 	cs->bcs[1].BC_SetStack = setstack_tiger;
@@ -906,8 +932,8 @@
 netjet_interrupt(int intno, void *dev_id, struct pt_regs *regs)
 {
 	struct IsdnCardState *cs = dev_id;
-	u_char val, sval, stat = 1;
-	char tmp[128];
+	u_char val, sval;
+	long flags;
 
 	if (!cs) {
 		printk(KERN_WARNING "NETjet: Spurious interrupt!\n");
@@ -916,49 +942,49 @@
 	if (!((sval = bytein(cs->hw.njet.base + NETJET_IRQSTAT1)) &
 		NETJET_ISACIRQ)) {
 		val = ReadISAC(cs, ISAC_ISTA);
-		if (cs->debug & L1_DEB_ISAC) {
-			sprintf(tmp, "tiger: i1 %x %x", sval, val);
-			debugl1(cs, tmp);
-		}
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "tiger: i1 %x %x", sval, val);
 		if (val) {
 			isac_interrupt(cs, val);
-			stat |= 2;
+			WriteISAC(cs, ISAC_MASK, 0xFF);
+			WriteISAC(cs, ISAC_MASK, 0x0);
 		}
 	}
-	if ((cs->hw.njet.irqstat0 = bytein(cs->hw.njet.base + NETJET_IRQSTAT0))) {
-/*		sprintf(tmp, "tiger: ist0 %x  %x %x  %x/%x  pulse=%d",
+	save_flags(flags);
+	cli();
+	if ((sval = bytein(cs->hw.njet.base + NETJET_IRQSTAT0))) {
+		if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+			restore_flags(flags);
+			return;
+		}
+		cs->hw.njet.irqstat0 = sval;
+		restore_flags(flags);
+/*		debugl1(cs, "tiger: ist0 %x  %x %x  %x/%x  pulse=%d",
 			sval, 
 			bytein(cs->hw.njet.base + NETJET_DMACTRL),
 			bytein(cs->hw.njet.base + NETJET_IRQMASK0),
 			inl(cs->hw.njet.base + NETJET_DMA_READ_ADR),
 			inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR),
 			bytein(cs->hw.njet.base + NETJET_PULSE_CNT));
-		debugl1(cs, tmp);
 */
-		if (cs->hw.njet.last_is0 & cs->hw.njet.irqstat0 & 0xf) {
-			sprintf(tmp, "tiger: ist0 %x->%x irq lost",
-				cs->hw.njet.last_is0, cs->hw.njet.irqstat0);
-			debugl1(cs, tmp);
-		}
-		cs->hw.njet.last_is0 = cs->hw.njet.irqstat0;
 /*		cs->hw.njet.irqmask0 = ((0x0f & cs->hw.njet.irqstat0) ^ 0x0f) | 0x30;
 */		byteout(cs->hw.njet.base + NETJET_IRQSTAT0, cs->hw.njet.irqstat0);
 /*		byteout(cs->hw.njet.base + NETJET_IRQMASK0, cs->hw.njet.irqmask0);
-*/		if (cs->hw.njet.irqstat0 & 0x0c)
+*/		if (cs->hw.njet.irqstat0 & NETJET_IRQM0_READ)
 			read_tiger(cs);
-		if (cs->hw.njet.irqstat0 & 0x03)
+		if (cs->hw.njet.irqstat0 & NETJET_IRQM0_WRITE)
 			write_tiger(cs);
-	}
+		test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+	} else
+		restore_flags(flags);
+
 /*	if (!testcnt--) {
 		cs->hw.njet.dmactrl = 0;
 		byteout(cs->hw.njet.base + NETJET_DMACTRL,
 			cs->hw.njet.dmactrl);
 		byteout(cs->hw.njet.base + NETJET_IRQMASK0, 0);
 	}
-*/	if (stat & 2) {
-		WriteISAC(cs, ISAC_MASK, 0xFF);
-		WriteISAC(cs, ISAC_MASK, 0x0);
-	}
+*/
 }
 
 static void
@@ -1006,11 +1032,13 @@
 			return(0);
 		case CARD_SETIRQ:
 			return(request_irq(cs->irq, &netjet_interrupt,
-					I4L_IRQ_FLAG, "HiSax", cs));
+					I4L_IRQ_FLAG | SA_SHIRQ, "HiSax", cs));
 		case CARD_INIT:
 			inittiger(cs);
 			clear_pending_isac_ints(cs);
 			initisac(cs);
+			/* Reenable all IRQ */
+			cs->writeisac(cs, ISAC_MASK, 0);
 			return(0);
 		case CARD_TEST:
 			return(0);
@@ -1020,7 +1048,7 @@
 
 
 
-static 	int pci_index __initdata = 0;
+static 	struct pci_dev *dev_netjet __initdata = NULL;
 
 __initfunc(int
 setup_netjet(struct IsdnCard *card))
@@ -1028,52 +1056,37 @@
 	int bytecnt;
 	struct IsdnCardState *cs = card->cs;
 	char tmp[64];
-#if CONFIG_PCI
-	u_char pci_bus, pci_device_fn, pci_irq;
-	u_int pci_ioaddr, found;
-#endif
 
 	strcpy(tmp, NETjet_revision);
 	printk(KERN_INFO "HiSax: Traverse Tech. NETjet driver Rev. %s\n", HiSax_getrev(tmp));
 	if (cs->typ != ISDN_CTYPE_NETJET)
 		return(0);
+	test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
 #if CONFIG_PCI
-	found = 0;
-	for (; pci_index < 0xff; pci_index++) {
-		if (pcibios_find_device(PCI_VENDOR_TRAVERSE_TECH,
-			PCI_NETJET_ID, pci_index, &pci_bus, &pci_device_fn)
-			== PCIBIOS_SUCCESSFUL)
-			found = 1;
-		else
-			break;
-		/* get IRQ */
-		pcibios_read_config_byte(pci_bus, pci_device_fn,
-			PCI_INTERRUPT_LINE, &pci_irq);
-
-		/* get IO address */
-		pcibios_read_config_dword(pci_bus, pci_device_fn,
-			PCI_BASE_ADDRESS_0, &pci_ioaddr);
-		if (found)
-			break;
-	}
-	if (!found) {
-		printk(KERN_WARNING "NETjet: No PCI card found\n");
+	if (!pci_present()) {
+		printk(KERN_ERR "Netjet: no PCI bus present\n");
 		return(0);
 	}
-	if (!pci_irq) {
+	if ((dev_netjet = pci_find_device(PCI_VENDOR_TRAVERSE_TECH,
+		PCI_NETJET_ID,  dev_netjet))) {
+		cs->irq = dev_netjet->irq;
+		if (!cs->irq) {
 		printk(KERN_WARNING "NETjet: No IRQ for PCI card found\n");
 		return(0);
 	}
-	if (!pci_ioaddr) {
+		cs->hw.njet.base = dev_netjet->base_address[0] &
+			PCI_BASE_ADDRESS_IO_MASK; 
+		if (!cs->hw.njet.base) {
 		printk(KERN_WARNING "NETjet: No IO-Adr for PCI card found\n");
 		return(0);
 	}
-	pci_ioaddr &= ~3; /* remove io/mem flag */
-	cs->hw.njet.base = pci_ioaddr; 
-	cs->hw.njet.auxa = pci_ioaddr + NETJET_AUXDATA;
-	cs->hw.njet.isac = pci_ioaddr | NETJET_ISAC_OFF;
-	cs->irq = pci_irq;
+		cs->hw.njet.auxa = cs->hw.njet.base + NETJET_AUXDATA;
+		cs->hw.njet.isac = cs->hw.njet.base | NETJET_ISAC_OFF;
 	bytecnt = 256;
+	} else {
+		printk(KERN_WARNING "NETjet: No PCI card found\n");
+		return(0);
+	}
 #else
 	printk(KERN_WARNING "NETjet: NO_PCI_BIOS\n");
 	printk(KERN_WARNING "NETjet: unable to config NETJET PCI\n");
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/niccy.c linux/drivers/isdn/hisax/niccy.c
--- v2.3.3/linux/drivers/isdn/hisax/niccy.c	Tue Apr  7 07:52:04 1998
+++ linux/drivers/isdn/hisax/niccy.c	Sun May 23 10:03:41 1999
@@ -1,4 +1,4 @@
-/* $Id: niccy.c,v 1.2 1998/02/11 17:31:04 keil Exp $
+/* $Id: niccy.c,v 1.4 1998/04/16 19:16:48 keil Exp $
 
  * niccy.c  low level stuff for Dr. Neuhaus NICCY PnP and NICCY PCI and
  *          compatible (SAGEM cybermodem)
@@ -8,24 +8,28 @@
  * Thanks to Dr. Neuhaus and SAGEM for informations
  *
  * $Log: niccy.c,v $
- * Revision 1.2  1998/02/11 17:31:04  keil
- * new file
+ * Revision 1.4  1998/04/16 19:16:48  keil
+ * need config.h
  *
+ * Revision 1.3  1998/04/15 16:42:59  keil
+ * new init code
  *
+ * Revision 1.2  1998/02/11 17:31:04  keil
+ * new file
  *
  */
 
-#include <linux/config.h>
+
 #define __NO_VERSION__
+#include <linux/config.h>
 #include "hisax.h"
 #include "isac.h"
 #include "hscx.h"
 #include "isdnl1.h"
 #include <linux/pci.h>
-#include <linux/bios32.h>
 
 extern const char *CardType[];
-const char *niccy_revision = "$Revision: 1.2 $";
+const char *niccy_revision = "$Revision: 1.4 $";
 
 #define byteout(addr,val) outb(val,addr)
 #define bytein(addr) inb(addr)
@@ -44,6 +48,10 @@
 /* PCI stuff */
 #define PCI_VENDOR_DR_NEUHAUS	0x1267
 #define PCI_NICCY_ID		0x1016
+#define PCI_IRQ_CTRL_REG	0x38
+#define PCI_IRQ_ENABLE		0x1f00
+#define PCI_IRQ_DISABLE		0xff0000
+#define PCI_IRQ_ASSERT		0x800000
 
 static inline u_char
 readreg(unsigned int ale, unsigned int adr, u_char off)
@@ -152,6 +160,13 @@
 		printk(KERN_WARNING "Niccy: Spurious interrupt!\n");
 		return;
 	}
+	if (cs->subtyp == NICCY_PCI) {
+		int ival;
+		ival = inl(cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
+		if (!(ival & PCI_IRQ_ASSERT)) /* IRQ not for us (shared) */
+			return;
+		outl(ival, cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
+	}
 	val = readreg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_ISTA + 0x40);
       Start_HSCX:
 	if (val) {
@@ -191,9 +206,15 @@
 void
 release_io_niccy(struct IsdnCardState *cs)
 {
-	if (cs->subtyp == NICCY_PCI)
+	if (cs->subtyp == NICCY_PCI) {
+		int val;
+		
+		val = inl(cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
+		val &= PCI_IRQ_DISABLE;
+		outl(val, cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
+		release_region(cs->hw.niccy.cfg_reg, 0x80);
 		release_region(cs->hw.niccy.isac, 4);
-	else {
+	} else {
 		release_region(cs->hw.niccy.isac, 2);
 		release_region(cs->hw.niccy.isac_ale, 2);
 	}
@@ -202,12 +223,20 @@
 static void
 niccy_reset(struct IsdnCardState *cs)
 {
-	// No reset procedure known
+	int val, nval;
+	
+	val = inl(cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
+	nval = val | PCI_IRQ_ENABLE;
+	outl(nval, cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
+
+	inithscxisac(cs, 3);
 }
 
 static int
 niccy_card_msg(struct IsdnCardState *cs, int mt, void *arg)
 {
+	int imode;
+	
 	switch (mt) {
 		case CARD_RESET:
 			niccy_reset(cs);
@@ -216,13 +245,15 @@
 			release_io_niccy(cs);
 			return(0);
 		case CARD_SETIRQ:
+			if (cs->subtyp == NICCY_PCI)
+				imode = I4L_IRQ_FLAG | SA_SHIRQ;
+			else
+				imode = I4L_IRQ_FLAG;
 			return(request_irq(cs->irq, &niccy_interrupt,
-					I4L_IRQ_FLAG, "HiSax", cs));
+				imode, "HiSax", cs));
+			break;
 		case CARD_INIT:
-			clear_pending_isac_ints(cs);
-			clear_pending_hscx_ints(cs);
-			initisac(cs);
-			inithscx(cs);
+			niccy_reset(cs);
 			return(0);
 		case CARD_TEST:
 			return(0);
@@ -230,7 +261,7 @@
 	return(0);
 }
 
-static 	int pci_index __initdata = 0;
+static 	struct pci_dev *niccy_dev __initdata = NULL;
 
 __initfunc(int
 setup_niccy(struct IsdnCard *card))
@@ -272,49 +303,41 @@
 			request_region(cs->hw.niccy.isac_ale, 2, "niccy addr");
 	} else {
 #if CONFIG_PCI
-		u_char pci_bus, pci_device_fn, pci_irq;
 		u_int pci_ioaddr;
 
+		if (!pci_present()) {
+			printk(KERN_ERR "Niccy: no PCI bus present\n");
+			return(0);
+		}
+
 		cs->subtyp = 0;
-		for (; pci_index < 0xff; pci_index++) {
-			if (pcibios_find_device(PCI_VENDOR_DR_NEUHAUS,
-			   PCI_NICCY_ID, pci_index, &pci_bus, &pci_device_fn)
-			   == PCIBIOS_SUCCESSFUL)
-				cs->subtyp = NICCY_PCI;
-			else
-				break;
+		if ((niccy_dev = pci_find_device(PCI_VENDOR_DR_NEUHAUS,
+			   PCI_NICCY_ID, niccy_dev))) {
 			/* get IRQ */
-			pcibios_read_config_byte(pci_bus, pci_device_fn,
-				PCI_INTERRUPT_LINE, &pci_irq);
-
-			/* get IO address */
-			/* if it won't work try the other PCI addresses
-			 * PCI_BASE_ADDRESS_0 ... PCI_BASE_ADDRESS_5
-			 */
-			pcibios_read_config_dword(pci_bus, pci_device_fn,
-				PCI_BASE_ADDRESS_2, &pci_ioaddr);
-			if (cs->subtyp)
-				break;
-		}
-		if (!cs->subtyp) {
-			printk(KERN_WARNING "Niccy: No PCI card found\n");
+			if (!niccy_dev->irq) {
+				printk(KERN_WARNING "Niccy: No IRQ for PCI card found\n");
 			return(0);
 		}
-		if (!pci_irq) {
-			printk(KERN_WARNING "Niccy: No IRQ for PCI card found\n");
+			cs->irq = niccy_dev->irq;
+			if (!niccy_dev->base_address[0]) {
+				printk(KERN_WARNING "Niccy: No IO-Adr for PCI cfg found\n");
 			return(0);
 		}
-
-		if (!pci_ioaddr) {
+			cs->hw.niccy.cfg_reg = niccy_dev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK;
+			if (!niccy_dev->base_address[1]) {
 			printk(KERN_WARNING "Niccy: No IO-Adr for PCI card found\n");
 			return(0);
 		}
-		pci_ioaddr &= ~3; /* remove io/mem flag */
+			pci_ioaddr = niccy_dev->base_address[1] & PCI_BASE_ADDRESS_IO_MASK;
 		cs->hw.niccy.isac = pci_ioaddr + ISAC_PCI_DATA;
 		cs->hw.niccy.isac_ale = pci_ioaddr + ISAC_PCI_ADDR;
 		cs->hw.niccy.hscx = pci_ioaddr + HSCX_PCI_DATA;
 		cs->hw.niccy.hscx_ale = pci_ioaddr + HSCX_PCI_ADDR;
-		cs->irq = pci_irq;
+			cs->subtyp = NICCY_PCI;
+		} else {
+			printk(KERN_WARNING "Niccy: No PCI card found\n");
+			return(0);
+		}
 		if (check_region((cs->hw.niccy.isac), 4)) {
 			printk(KERN_WARNING
 				"HiSax: %s data port %x-%x already in use\n",
@@ -324,6 +347,17 @@
 			return (0);
 		} else
 			request_region(cs->hw.niccy.isac, 4, "niccy");
+		if (check_region(cs->hw.niccy.cfg_reg, 0x80)) {
+			printk(KERN_WARNING
+			       "HiSax: %s pci port %x-%x already in use\n",
+				CardType[card->typ],
+				cs->hw.niccy.cfg_reg,
+				cs->hw.niccy.cfg_reg + 0x80);
+			release_region(cs->hw.niccy.isac, 4);
+			return (0);
+		} else {
+			request_region(cs->hw.niccy.cfg_reg, 0x80, "niccy pci");
+		}
 #else
 		printk(KERN_WARNING "Niccy: io0 0 and NO_PCI_BIOS\n");
 		printk(KERN_WARNING "Niccy: unable to config NICCY PCI\n");
@@ -334,7 +368,6 @@
 		"HiSax: %s %s config irq:%d data:0x%X ale:0x%X\n",
 		CardType[cs->typ], (cs->subtyp==1) ? "PnP":"PCI",
 		cs->irq, cs->hw.niccy.isac, cs->hw.niccy.isac_ale);
-	niccy_reset(cs);
 	cs->readisac = &ReadISAC;
 	cs->writeisac = &WriteISAC;
 	cs->readisacfifo = &ReadISACfifo;
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/q931.c linux/drivers/isdn/hisax/q931.c
--- v2.3.3/linux/drivers/isdn/hisax/q931.c	Wed Apr  1 16:21:02 1998
+++ linux/drivers/isdn/hisax/q931.c	Sun May 23 10:03:41 1999
@@ -1,4 +1,4 @@
-/* $Id: q931.c,v 1.6 1997/07/27 21:09:44 keil Exp $
+/* $Id: q931.c,v 1.7 1998/11/15 23:55:17 keil Exp $
 
  * q931.c       code to decode ITU Q.931 call control messages
  *
@@ -14,6 +14,9 @@
  *
  *
  * $Log: q931.c,v $
+ * Revision 1.7  1998/11/15 23:55:17  keil
+ * changes from 2.0
+ *
  * Revision 1.6  1997/07/27 21:09:44  keil
  * move functions to isdnl3.c
  *
@@ -159,7 +162,7 @@
 	{MT_N0_CLO_ACK, "CLOse ACKnowledge"}
 };
 
-int mt_n0_len = (sizeof(mt_n0) / sizeof(struct MessageType));
+#define MT_N0_LEN (sizeof(mt_n0) / sizeof(struct MessageType))
 
 static
 struct MessageType mt_n1[] =
@@ -196,7 +199,7 @@
 	{MT_N1_STAT, "STATus"}
 };
 
-int mt_n1_len = (sizeof(mt_n1) / sizeof(struct MessageType));
+#define MT_N1_LEN (sizeof(mt_n1) / sizeof(struct MessageType))
 
 static struct MessageType fac_1tr6[] =
 {
@@ -220,9 +223,7 @@
 	{FAC_Rueckwechsel, "Rueckwechsel"},
 	{FAC_Umleitung, "Umleitung"}
 };
-int fac_1tr6_len = (sizeof(fac_1tr6) / sizeof(struct MessageType));
-
-
+#define FAC_1TR6_LEN (sizeof(fac_1tr6) / sizeof(struct MessageType))
 
 static int
 prbits(char *dest, u_char b, int start, int len)
@@ -925,7 +926,7 @@
 	{WE0_userInfo, "User Info", general}
 };
 
-static int we_0_len = (sizeof(we_0) / sizeof(struct InformationElement));
+#define WE_0_LEN (sizeof(we_0) / sizeof(struct InformationElement))
 
 static struct InformationElement we_6[] =
 {
@@ -937,7 +938,7 @@
 	{WE6_statusCalled, "Status Called", general},
 	{WE6_addTransAttr, "Additional Transmission Attributes", general}
 };
-static int we_6_len = (sizeof(we_6) / sizeof(struct InformationElement));
+#define WE_6_LEN (sizeof(we_6) / sizeof(struct InformationElement))
 
 int
 QuickHex(char *txt, u_char * p, int cnt)
@@ -964,39 +965,92 @@
 }
 
 void
-LogFrame(struct IsdnCardState *sp, u_char * buf, int size)
+LogFrame(struct IsdnCardState *cs, u_char * buf, int size)
 {
 	char *dp;
 
 	if (size < 1)
 		return;
-	dp = sp->dlogspace;
-	if (size < 4096 / 3 - 10) {
-		dp += sprintf(dp, "HEX:");
+	dp = cs->dlog;
+	if (size < MAX_DLOG_SPACE / 3 - 10) {
+		*dp++ = 'H';
+		*dp++ = 'E';
+		*dp++ = 'X';
+		*dp++ = ':';
 		dp += QuickHex(dp, buf, size);
 		dp--;
 		*dp++ = '\n';
 		*dp = 0;
+		HiSax_putstatus(cs, NULL, cs->dlog);
 	} else
-		sprintf(dp, "LogFrame: warning Frame too big (%d)\n",
-			size);
-	HiSax_putstatus(sp, sp->dlogspace);
+		HiSax_putstatus(cs, "LogFrame: ", "warning Frame too big (%d)", size);
 }
 
 void
-dlogframe(struct IsdnCardState *sp, u_char * buf, int size, char *comment)
+dlogframe(struct IsdnCardState *cs, struct sk_buff *skb, int dir)
 {
-	u_char *bend = buf + size;
+	u_char *bend, *buf;
 	char *dp;
 	unsigned char pd, cr_l, cr, mt;
-	int i, cs = 0, cs_old = 0, cs_fest = 0;
+	unsigned char sapi, tei, ftyp;
+	int i, cset = 0, cs_old = 0, cs_fest = 0;
+	int size, finish = 0;
 
-	if (size < 1)
+	if (skb->len < 3)
 		return;
 	/* display header */
-	dp = sp->dlogspace;
-	dp += sprintf(dp, "%s\n", comment);
-
+	dp = cs->dlog;
+	dp += jiftime(dp, jiffies);
+	*dp++ = ' ';
+	sapi = skb->data[0] >> 2;
+	tei  = skb->data[1] >> 1;
+	ftyp = skb->data[2];
+	buf = skb->data;
+	dp += sprintf(dp, "frame %s ", dir ? "network->user" : "user->network");
+	size = skb->len;
+	
+	if (tei == GROUP_TEI) {
+		if (sapi == CTRL_SAPI) { /* sapi 0 */
+			if (ftyp == 3) {
+				dp += sprintf(dp, "broadcast\n");
+				buf += 3;
+				size -= 3;
+			} else {
+				dp += sprintf(dp, "no UI broadcast\n");
+				finish = 1;
+			}
+		} else if (sapi == TEI_SAPI) {
+			dp += sprintf(dp, "tei managment\n");
+			finish = 1;
+		} else {
+			dp += sprintf(dp, "unknown sapi %d broadcast\n", sapi);
+			finish = 1;
+		}
+	} else {
+		if (sapi == CTRL_SAPI) {
+			if (!(ftyp & 1)) { /* IFrame */
+				dp += sprintf(dp, "with tei %d\n", tei);
+				buf += 4;
+				size -= 4;
+			} else {
+				dp += sprintf(dp, "SFrame with tei %d\n", tei);
+				finish = 1;
+			}
+		} else {
+			dp += sprintf(dp, "unknown sapi %d tei %d\n", sapi, tei);
+			finish = 1;
+		}
+	}
+	bend = skb->data + skb->len;
+	if (buf >= bend) {
+		dp += sprintf(dp, "frame too short\n");
+		finish = 1;
+	}
+	if (finish) {
+		*dp = 0;
+		HiSax_putstatus(cs, NULL, cs->dlog);
+		return;
+	}
 	if ((0xfe & buf[0]) == PROTO_DIS_N0) {	/* 1TR6 */
 		/* locate message type */
 		pd = *buf++;
@@ -1007,11 +1061,11 @@
 			cr = 0;
 		mt = *buf++;
 		if (pd == PROTO_DIS_N0) {	/* N0 */
-			for (i = 0; i < mt_n0_len; i++)
+			for (i = 0; i < MT_N0_LEN; i++)
 				if (mt_n0[i].nr == mt)
 					break;
 			/* display message type if it exists */
-			if (i == mt_n0_len)
+			if (i == MT_N0_LEN)
 				dp += sprintf(dp, "callref %d %s size %d unknown message type N0 %x!\n",
 					      cr & 0x7f, (cr & 0x80) ? "called" : "caller",
 					      size, mt);
@@ -1020,11 +1074,11 @@
 					      cr & 0x7f, (cr & 0x80) ? "called" : "caller",
 					      size, mt_n0[i].descr);
 		} else {	/* N1 */
-			for (i = 0; i < mt_n1_len; i++)
+			for (i = 0; i < MT_N1_LEN; i++)
 				if (mt_n1[i].nr == mt)
 					break;
 			/* display message type if it exists */
-			if (i == mt_n1_len)
+			if (i == MT_N1_LEN)
 				dp += sprintf(dp, "callref %d %s size %d unknown message type N1 %x!\n",
 					      cr & 0x7f, (cr & 0x80) ? "called" : "caller",
 					      size, mt);
@@ -1041,8 +1095,8 @@
 				switch ((*buf >> 4) & 7) {
 					case 1:
 						dp += sprintf(dp, "  Shift %x\n", *buf & 0xf);
-						cs_old = cs;
-						cs = *buf & 7;
+						cs_old = cset;
+						cset = *buf & 7;
 						cs_fest = *buf & 8;
 						break;
 					case 3:
@@ -1066,33 +1120,33 @@
 				continue;
 			}
 			/* No, locate it in the table */
-			if (cs == 0) {
-				for (i = 0; i < we_0_len; i++)
+			if (cset == 0) {
+				for (i = 0; i < WE_0_LEN; i++)
 					if (*buf == we_0[i].nr)
 						break;
 
 				/* When found, give appropriate msg */
-				if (i != we_0_len) {
+				if (i != WE_0_LEN) {
 					dp += sprintf(dp, "  %s\n", we_0[i].descr);
 					dp += we_0[i].f(dp, buf);
 				} else
-					dp += sprintf(dp, "  Codeset %d attribute %x attribute size %d\n", cs, *buf, buf[1]);
-			} else if (cs == 6) {
-				for (i = 0; i < we_6_len; i++)
+					dp += sprintf(dp, "  Codeset %d attribute %x attribute size %d\n", cset, *buf, buf[1]);
+			} else if (cset == 6) {
+				for (i = 0; i < WE_6_LEN; i++)
 					if (*buf == we_6[i].nr)
 						break;
 
 				/* When found, give appropriate msg */
-				if (i != we_6_len) {
+				if (i != WE_6_LEN) {
 					dp += sprintf(dp, "  %s\n", we_6[i].descr);
 					dp += we_6[i].f(dp, buf);
 				} else
-					dp += sprintf(dp, "  Codeset %d attribute %x attribute size %d\n", cs, *buf, buf[1]);
+					dp += sprintf(dp, "  Codeset %d attribute %x attribute size %d\n", cset, *buf, buf[1]);
 			} else
-				dp += sprintf(dp, "  Unknown Codeset %d attribute %x attribute size %d\n", cs, *buf, buf[1]);
+				dp += sprintf(dp, "  Unknown Codeset %d attribute %x attribute size %d\n", cset, *buf, buf[1]);
 			/* Skip to next element */
 			if (cs_fest == 8) {
-				cs = cs_old;
+				cset = cs_old;
 				cs_old = 0;
 				cs_fest = 0;
 			}
@@ -1170,6 +1224,6 @@
 	} else {
 		dp += sprintf(dp, "Unknown protocol %x!", buf[0]);
 	}
-	dp += sprintf(dp, "\n");
-	HiSax_putstatus(sp, sp->dlogspace);
+	*dp = 0;
+	HiSax_putstatus(cs, NULL, cs->dlog);
 }
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/rawhdlc.c linux/drivers/isdn/hisax/rawhdlc.c
--- v2.3.3/linux/drivers/isdn/hisax/rawhdlc.c	Wed May 20 18:54:37 1998
+++ linux/drivers/isdn/hisax/rawhdlc.c	Sun May 23 10:03:41 1999
@@ -1,4 +1,4 @@
-/* $Id: rawhdlc.c,v 1.2 1998/02/09 10:53:51 keil Exp $
+/* $Id: rawhdlc.c,v 1.3 1998/06/17 19:51:21 he Exp $
 
  * rawhdlc.c     support routines for cards that don't support HDLC
  *
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/s0box.c linux/drivers/isdn/hisax/s0box.c
--- v2.3.3/linux/drivers/isdn/hisax/s0box.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/s0box.c	Sun May 23 10:03:41 1999
@@ -0,0 +1,277 @@
+/* $Id: s0box.c,v 2.1 1998/04/15 16:38:24 keil Exp $
+
+ * s0box.c      low level stuff for Creatix S0BOX
+ *
+ * Author       S0BOX specific stuff: Enrik Berkhan (enrik@starfleet.inka.de)
+ *
+ *
+ */
+#define __NO_VERSION__
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+
+extern const char *CardType[];
+const char *s0box_revision = "$Revision: 2.1 $";
+
+static inline void
+writereg(unsigned int padr, signed int addr, u_char off, u_char val) {
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	outb_p(0x1c,padr+2);
+	outb_p(0x14,padr+2);
+	outb_p((addr+off)&0x7f,padr);
+	outb_p(0x16,padr+2);
+	outb_p(val,padr);
+	outb_p(0x17,padr+2);
+	outb_p(0x14,padr+2);
+	outb_p(0x1c,padr+2);
+	restore_flags(flags);
+}
+
+static u_char nibtab[] = { 1, 9, 5, 0xd, 3, 0xb, 7, 0xf,
+			 0, 0, 0, 0, 0, 0, 0, 0,
+			 0, 8, 4, 0xc, 2, 0xa, 6, 0xe } ;
+
+static inline u_char
+readreg(unsigned int padr, signed int addr, u_char off) {
+	register u_char n1, n2;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	outb_p(0x1c,padr+2);
+	outb_p(0x14,padr+2);
+	outb_p((addr+off)|0x80,padr);
+	outb_p(0x16,padr+2);
+	outb_p(0x17,padr+2);
+	n1 = (inb_p(padr+1) >> 3) & 0x17;
+	outb_p(0x16,padr+2);
+	n2 = (inb_p(padr+1) >> 3) & 0x17;
+	outb_p(0x14,padr+2);
+	outb_p(0x1c,padr+2);
+	restore_flags(flags);
+	return nibtab[n1] | (nibtab[n2] << 4);
+}
+
+static inline void
+read_fifo(unsigned int padr, signed int adr, u_char * data, int size)
+{
+	int i;
+	register u_char n1, n2;
+	
+	outb_p(0x1c, padr+2);
+	outb_p(0x14, padr+2);
+	outb_p(adr|0x80, padr);
+	outb_p(0x16, padr+2);
+	for (i=0; i<size; i++) {
+		outb_p(0x17, padr+2);
+		n1 = (inb_p(padr+1) >> 3) & 0x17;
+		outb_p(0x16,padr+2);
+		n2 = (inb_p(padr+1) >> 3) & 0x17;
+		*(data++)=nibtab[n1] | (nibtab[n2] << 4);
+	}
+	outb_p(0x14,padr+2);
+	outb_p(0x1c,padr+2);
+	return;
+}
+
+static inline void
+write_fifo(unsigned int padr, signed int adr, u_char * data, int size)
+{
+	int i;
+	outb_p(0x1c, padr+2);
+	outb_p(0x14, padr+2);
+	outb_p(adr&0x7f, padr);
+	for (i=0; i<size; i++) {
+		outb_p(0x16, padr+2);
+		outb_p(*(data++), padr);
+		outb_p(0x17, padr+2);
+	}
+	outb_p(0x14,padr+2);
+	outb_p(0x1c,padr+2);
+	return;
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	read_fifo(cs->hw.teles3.cfg_reg, cs->hw.teles3.isacfifo, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	write_fifo(cs->hw.teles3.cfg_reg, cs->hw.teles3.isacfifo, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return (readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[hscx], offset));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[hscx], offset, value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[nr], reg)
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[nr], reg, data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscxfifo[nr], ptr, cnt)
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscxfifo[nr], ptr, cnt)
+
+#include "hscx_irq.c"
+
+static void
+s0box_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+#define MAXCOUNT 20
+	struct IsdnCardState *cs = dev_id;
+	u_char val, stat = 0;
+	int count = 0;
+
+	if (!cs) {
+		printk(KERN_WARNING "Teles: Spurious interrupt!\n");
+		return;
+	}
+	val = readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[1], HSCX_ISTA);
+      Start_HSCX:
+	if (val) {
+		hscx_int_main(cs, val);
+		stat |= 1;
+	}
+	val = readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, ISAC_ISTA);
+      Start_ISAC:
+	if (val) {
+		isac_interrupt(cs, val);
+		stat |= 2;
+	}
+	count++;
+	val = readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[1], HSCX_ISTA);
+	if (val && count < MAXCOUNT) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX IntStat after IntRoutine");
+		goto Start_HSCX;
+	}
+	val = readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, ISAC_ISTA);
+	if (val && count < MAXCOUNT) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	if (count >= MAXCOUNT)
+		printk(KERN_WARNING "S0Box: more than %d loops in s0box_interrupt\n", count);
+	if (stat & 1) {
+		writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[0], HSCX_MASK, 0xFF);
+		writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[1], HSCX_MASK, 0xFF);
+		writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[0], HSCX_MASK, 0x0);
+		writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[1], HSCX_MASK, 0x0);
+	}
+	if (stat & 2) {
+		writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, ISAC_MASK, 0xFF);
+		writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, ISAC_MASK, 0x0);
+	}
+}
+
+void
+release_io_s0box(struct IsdnCardState *cs)
+{
+	release_region(cs->hw.teles3.cfg_reg, 8);
+}
+
+static int
+S0Box_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	switch (mt) {
+		case CARD_RESET:
+			break;
+		case CARD_RELEASE:
+			release_io_s0box(cs);
+			break;
+		case CARD_SETIRQ:
+			return(request_irq(cs->irq, &s0box_interrupt,
+					I4L_IRQ_FLAG, "HiSax", cs));
+		case CARD_INIT:
+			inithscxisac(cs, 3);
+			break;
+		case CARD_TEST:
+			break;
+	}
+	return(0);
+}
+
+__initfunc(int
+setup_s0box(struct IsdnCard *card))
+{
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, s0box_revision);
+	printk(KERN_INFO "HiSax: S0Box IO driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_S0BOX)
+		return (0);
+
+	cs->hw.teles3.cfg_reg = card->para[1];
+	cs->hw.teles3.hscx[0] = -0x20;
+	cs->hw.teles3.hscx[1] = 0x0;
+	cs->hw.teles3.isac = 0x20;
+	cs->hw.teles3.isacfifo = cs->hw.teles3.isac + 0x3e;
+	cs->hw.teles3.hscxfifo[0] = cs->hw.teles3.hscx[0] + 0x3e;
+	cs->hw.teles3.hscxfifo[1] = cs->hw.teles3.hscx[1] + 0x3e;
+	cs->irq = card->para[0];
+	if (check_region(cs->hw.teles3.cfg_reg,8)) {
+		printk(KERN_WARNING
+		       "HiSax: %s ports %x-%x already in use\n",
+		       CardType[cs->typ],
+                       cs->hw.teles3.cfg_reg,
+                       cs->hw.teles3.cfg_reg + 7);
+		return 0;
+	} else
+		request_region(cs->hw.teles3.cfg_reg, 8, "S0Box parallel I/O");
+	printk(KERN_INFO
+	       "HiSax: %s config irq:%d isac:0x%x  cfg:0x%x\n",
+	       CardType[cs->typ], cs->irq,
+	       cs->hw.teles3.isac, cs->hw.teles3.cfg_reg);
+	printk(KERN_INFO
+	       "HiSax: hscx A:0x%x  hscx B:0x%x\n",
+	       cs->hw.teles3.hscx[0], cs->hw.teles3.hscx[1]);
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &S0Box_card_msg;
+	ISACVersion(cs, "S0Box:");
+	if (HscxVersion(cs, "S0Box:")) {
+		printk(KERN_WARNING
+		       "S0Box: wrong HSCX versions check IO address\n");
+		release_io_s0box(cs);
+		return (0);
+	}
+	return (1);
+}
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/sedlbauer.c linux/drivers/isdn/hisax/sedlbauer.c
--- v2.3.3/linux/drivers/isdn/hisax/sedlbauer.c	Thu Nov  5 09:58:44 1998
+++ linux/drivers/isdn/hisax/sedlbauer.c	Sun May 23 10:03:41 1999
@@ -1,11 +1,14 @@
-/* $Id: sedlbauer.c,v 1.6 1998/02/09 18:46:06 keil Exp $
+/* $Id: sedlbauer.c,v 1.9 1998/11/15 23:55:20 keil Exp $
 
  * sedlbauer.c  low level stuff for Sedlbauer cards
- *              includes Support for the Sedlbauer Speed Star 
- *              derived from the original file dynalink.c from Karsten Keil
+ *              includes support for the Sedlbauer speed star (speed star II),
+ *              support for the Sedlbauer speed fax+,
+ *              support for the Sedlbauer ISDN-Controller PC/104 and
+ *              support for the Sedlbauer speed pci
+ *              derived from the original file asuscom.c from Karsten Keil
  *
  * Copyright (C) 1997,1998 Marcus Niemann (for the modifications to
- *                                         the original file dynalink.c)
+ *                                         the original file asuscom.c)
  *
  * Author     Marcus Niemann (niemann@www-bib.fh-bielefeld.de)
  *
@@ -14,6 +17,15 @@
  *            Edgar Toernig
  *
  * $Log: sedlbauer.c,v $
+ * Revision 1.9  1998/11/15 23:55:20  keil
+ * changes from 2.0
+ *
+ * Revision 1.8  1998/08/13 23:34:51  keil
+ * starting speedfax+ (ISAR) support
+ *
+ * Revision 1.7  1998/04/15 16:44:33  keil
+ * new init code
+ *
  * Revision 1.6  1998/02/09 18:46:06  keil
  * Support for Sedlbauer PCMCIA (Marcus Niemann)
  *
@@ -35,36 +47,91 @@
  *
  */
 
+/* Supported cards:
+ * Card:	Chip:		Configuration:	Comment:
+ * ---------------------------------------------------------------------
+ * Speed Card	ISAC_HSCX	DIP-SWITCH
+ * Speed Win	ISAC_HSCX	ISAPNP
+ * Speed Fax+	ISAC_ISAR	ISAPNP		#HDLC works#
+ * Speed Star	ISAC_HSCX	CARDMGR
+ * Speed Win2	IPAC		ISAPNP
+ * ISDN PC/104	IPAC		DIP-SWITCH
+ * Speed Star2	IPAC		CARDMGR
+ * Speed PCI	IPAC		PNP		
+ *
+ * Important:
+ * For the sedlbauer speed fax+ to work properly you have to download 
+ * the firmware onto the card.
+ * For example: hisaxctrl <DriverID> 9 ISAR.BIN
+*/
+
+#define SEDLBAUER_PCI 1
+
 #define __NO_VERSION__
+#include <linux/config.h>
 #include "hisax.h"
 #include "isac.h"
+#include "ipac.h"
 #include "hscx.h"
+#include "isar.h"
 #include "isdnl1.h"
+#include <linux/pci.h>
 
 extern const char *CardType[];
 
-const char *Sedlbauer_revision = "$Revision: 1.6 $";
+const char *Sedlbauer_revision = "$Revision: 1.9 $";
 
 const char *Sedlbauer_Types[] =
-{"None", "Speed Card", "Speed Win", "Speed Star"};
+	{"None", "speed card/win", "speed star", "speed fax+", 
+	"speed win II / ISDN PC/104", "speed star II", "speed pci"};
+
+#ifdef SEDLBAUER_PCI
+#define PCI_VENDOR_SEDLBAUER	0xe159
+#define PCI_SPEEDPCI_ID	0x02
+#endif
  
-#define SEDL_SPEED_CARD 1
-#define SEDL_SPEED_WIN  2
-#define SEDL_SPEED_STAR 3
+#define SEDL_SPEED_CARD_WIN	1
+#define SEDL_SPEED_STAR 	2
+#define SEDL_SPEED_FAX		3
+#define SEDL_SPEED_WIN2_PC104 	4
+#define SEDL_SPEED_STAR2 	5
+#define SEDL_SPEED_PCI   	6
+
+#define SEDL_CHIP_TEST		0
+#define SEDL_CHIP_ISAC_HSCX	1
+#define SEDL_CHIP_ISAC_ISAR	2
+#define SEDL_CHIP_IPAC		3
+
+#define SEDL_BUS_ISA		1
+#define SEDL_BUS_PCI		2
+#define	SEDL_BUS_PCMCIA		3
 
 #define byteout(addr,val) outb(val,addr)
 #define bytein(addr) inb(addr)
 
-#define SEDL_RESET_ON	0
-#define SEDL_RESET_OFF	1
-#define SEDL_ISAC	2
-#define SEDL_HSCX	3
-#define SEDL_ADR	4
-
-#define SEDL_PCMCIA_RESET	0
-#define SEDL_PCMCIA_ISAC	1
-#define SEDL_PCMCIA_HSCX	2
-#define SEDL_PCMCIA_ADR		4
+#define SEDL_HSCX_ISA_RESET_ON	0
+#define SEDL_HSCX_ISA_RESET_OFF	1
+#define SEDL_HSCX_ISA_ISAC	2
+#define SEDL_HSCX_ISA_HSCX	3
+#define SEDL_HSCX_ISA_ADR	4
+
+#define SEDL_HSCX_PCMCIA_RESET	0
+#define SEDL_HSCX_PCMCIA_ISAC	1
+#define SEDL_HSCX_PCMCIA_HSCX	2
+#define SEDL_HSCX_PCMCIA_ADR	4
+
+#define SEDL_ISAR_ISA_ISAC		4
+#define SEDL_ISAR_ISA_ISAR		6
+#define SEDL_ISAR_ISA_ADR		8
+#define SEDL_ISAR_ISA_ISAR_RESET_ON	10
+#define SEDL_ISAR_ISA_ISAR_RESET_OFF	12
+
+#define SEDL_IPAC_ANY_ADR 	0
+#define SEDL_IPAC_ANY_IPAC	2
+
+#define SEDL_IPAC_PCI_BASE	0
+#define SEDL_IPAC_PCI_ADR	0xc0
+#define SEDL_IPAC_PCI_IPAC	0xc8
 
 #define SEDL_RESET      0x3	/* same as DOS driver */
 
@@ -139,6 +206,29 @@
 }
 
 static u_char
+ReadISAC_IPAC(struct IsdnCardState *cs, u_char offset)
+{
+        return (readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, offset|0x80));}
+
+static void
+WriteISAC_IPAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+        writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, offset|0x80, value);
+}
+
+static void
+ReadISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+        readfifo(cs->hw.sedl.adr, cs->hw.sedl.isac, 0x80, data, size);
+}
+
+static void
+WriteISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+        writefifo(cs->hw.sedl.adr, cs->hw.sedl.isac, 0x80, data, size);
+}
+
+static u_char
 ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
 {
 	return (readreg(cs->hw.sedl.adr,
@@ -152,6 +242,34 @@
 		 cs->hw.sedl.hscx, offset + (hscx ? 0x40 : 0), value);
 }
 
+/* ISAR access routines
+ * mode = 0 access with IRQ on
+ * mode = 1 access with IRQ off
+ * mode = 2 access with IRQ off and using last offset
+ */
+  
+static u_char
+ReadISAR(struct IsdnCardState *cs, int mode, u_char offset)
+{	
+	if (mode == 0)
+		return (readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, offset));
+	else if (mode == 1)
+		byteout(cs->hw.sedl.adr, offset);
+	return(bytein(cs->hw.sedl.hscx));
+}
+
+static void
+WriteISAR(struct IsdnCardState *cs, int mode, u_char offset, u_char value)
+{
+	if (mode == 0)
+		writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, offset, value);
+	else {
+		if (mode == 1)
+			byteout(cs->hw.sedl.adr, offset);
+		byteout(cs->hw.sedl.hscx, value);
+	}
+}
+
 /*
  * fast interrupt HSCX stuff goes here
  */
@@ -180,7 +298,7 @@
 		return;
 	}
 
-        if ((cs->typ == ISDN_CTYPE_SEDLBAUER_PCMCIA) && (*cs->busy_flag == 1)) {
+	if ((cs->hw.sedl.bus == SEDL_BUS_PCMCIA) && (*cs->busy_flag == 1)) {
           /* The card tends to generate interrupts while being removed
              causing us to just crash the kernel. bad. */
           printk(KERN_WARNING "Sedlbauer: card not available!\n");
@@ -223,11 +341,101 @@
 	}
 }
 
+static void
+sedlbauer_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char ista, val, icnt = 20;
+
+	if (!cs) {
+		printk(KERN_WARNING "Sedlbauer: Spurious interrupt!\n");
+		return;
+	}
+	ista = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_ISTA);
+Start_IPAC:
+	if (cs->debug & L1_DEB_IPAC)
+		debugl1(cs, "IPAC ISTA %02X", ista);
+	if (ista & 0x0f) {
+		val = readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_ISTA + 0x40);
+		if (ista & 0x01)
+			val |= 0x01;
+		if (ista & 0x04)
+			val |= 0x02;
+		if (ista & 0x08)
+			val |= 0x04;
+		if (val)
+			hscx_int_main(cs, val);
+	}
+	if (ista & 0x20) {
+		val = 0xfe & readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_ISTA | 0x80);
+		if (val) {
+			isac_interrupt(cs, val);
+		}
+	}
+	if (ista & 0x10) {
+		val = 0x01;
+		isac_interrupt(cs, val);
+	}
+	ista  = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_ISTA);
+	if ((ista & 0x3f) && icnt) {
+		icnt--;
+		goto Start_IPAC;
+	}
+	if (!icnt)
+		printk(KERN_WARNING "Sedlbauer IRQ LOOP\n");
+	writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_MASK, 0xFF);
+	writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_MASK, 0xC0);
+}
+
+static void
+sedlbauer_interrupt_isar(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val;
+	int cnt = 20;
+
+	if (!cs) {
+		printk(KERN_WARNING "Sedlbauer: Spurious interrupt!\n");
+		return;
+	}
+
+	val = readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, ISAR_IRQBIT);
+      Start_ISAR:
+	if (val & ISAR_IRQSTA)
+		isar_int_main(cs);
+	val = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_ISTA);
+      Start_ISAC:
+	if (val)
+		isac_interrupt(cs, val);
+	val = readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, ISAR_IRQBIT);
+	if ((val & ISAR_IRQSTA) && --cnt) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "ISAR IntStat after IntRoutine");
+		goto Start_ISAR;
+	}
+	val = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_ISTA);
+	if (val && --cnt) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	if (!cnt)
+		printk(KERN_WARNING "Sedlbauer IRQ LOOP\n");
+
+	writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, ISAR_IRQBIT, 0);
+	writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_MASK, 0xFF);
+	writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_MASK, 0x0);
+	writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, ISAR_IRQBIT, ISAR_IRQMSK);
+}
+
 void
 release_io_sedlbauer(struct IsdnCardState *cs)
 {
-	int bytecnt = 8;
+	int bytecnt = (cs->subtyp == SEDL_SPEED_FAX) ? 16 : 8;
 
+	if (cs->hw.sedl.bus == SEDL_BUS_PCI) {
+		bytecnt = 256;
+	}
 	if (cs->hw.sedl.cfg_reg)
 		release_region(cs->hw.sedl.cfg_reg, bytecnt);
 }
@@ -237,16 +445,36 @@
 {
 	long flags;
 
-	if (cs->typ != ISDN_CTYPE_SEDLBAUER_PCMCIA) {
-		byteout(cs->hw.sedl.reset_on, SEDL_RESET);	/* Reset On */
-		save_flags(flags);
-		sti();
-		current->state = TASK_INTERRUPTIBLE;
-		schedule_timeout(1);
-		byteout(cs->hw.sedl.reset_off, 0);	/* Reset Off */
-		current->state = TASK_INTERRUPTIBLE;
-		schedule_timeout(1);
-		restore_flags(flags);
+	printk(KERN_INFO "Sedlbauer: resetting card\n");
+
+	if (!((cs->hw.sedl.bus == SEDL_BUS_PCMCIA) &&
+	   (cs->hw.sedl.chip == SEDL_CHIP_ISAC_HSCX))) {
+		if (cs->hw.sedl.chip == SEDL_CHIP_IPAC) {
+			writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_POTA2, 0x20);
+			save_flags(flags);
+			sti();
+			current->state = TASK_INTERRUPTIBLE;
+			schedule_timeout(1);
+			writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_POTA2, 0x0);
+			current->state = TASK_INTERRUPTIBLE;
+			schedule_timeout(1);
+			writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_CONF, 0x0);
+			writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_ACFG, 0xff);
+			writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_AOE, 0x0);
+			writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_MASK, 0xc0);
+			writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_PCFG, 0x12);
+			restore_flags(flags);
+		} else {		
+			byteout(cs->hw.sedl.reset_on, SEDL_RESET);	/* Reset On */
+			save_flags(flags);
+			sti();
+			current->state = TASK_INTERRUPTIBLE;
+			schedule_timeout(1);
+			byteout(cs->hw.sedl.reset_off, 0);	/* Reset Off */
+			current->state = TASK_INTERRUPTIBLE;
+			schedule_timeout(1);
+			restore_flags(flags);
+		}
 	}
 }
 
@@ -261,94 +489,256 @@
 			release_io_sedlbauer(cs);
 			return(0);
 		case CARD_SETIRQ:
-			return(request_irq(cs->irq, &sedlbauer_interrupt,
+			if (cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) {
+				return(request_irq(cs->irq, &sedlbauer_interrupt_isar,
 					I4L_IRQ_FLAG, "HiSax", cs));
+			} else if (cs->hw.sedl.chip == SEDL_CHIP_IPAC) {
+				return(request_irq(cs->irq, &sedlbauer_interrupt_ipac,
+					I4L_IRQ_FLAG, "HiSax", cs));
+			} else {
+				return(request_irq(cs->irq, &sedlbauer_interrupt,
+					I4L_IRQ_FLAG, "HiSax", cs));
+			}
 		case CARD_INIT:
-			clear_pending_isac_ints(cs);
-			clear_pending_hscx_ints(cs);
-			initisac(cs);
-			inithscx(cs);
+			if (cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) {
+				clear_pending_isac_ints(cs);
+				writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx,
+					ISAR_IRQBIT, 0);
+				initisac(cs);
+				initisar(cs);
+				/* Reenable all IRQ */
+				cs->writeisac(cs, ISAC_MASK, 0);
+				/* RESET Receiver and Transmitter */
+				cs->writeisac(cs, ISAC_CMDR, 0x41);
+			} else {
+				inithscxisac(cs, 3);
+			}
 			return(0);
 		case CARD_TEST:
 			return(0);
+		case CARD_LOAD_FIRM:
+			if (cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) {
+				if (isar_load_firmware(cs, arg))
+					return(1);
+				else 
+					ll_run(cs);
+			}
+			return(0);
 	}
 	return(0);
 }
 
+
+#ifdef SEDLBAUER_PCI
+static  int pci_index __initdata = 0;
+#endif
+
 __initfunc(int
 setup_sedlbauer(struct IsdnCard *card))
 {
-	int bytecnt;
+	int bytecnt, ver, val;
 	struct IsdnCardState *cs = card->cs;
 	char tmp[64];
 
 	strcpy(tmp, Sedlbauer_revision);
 	printk(KERN_INFO "HiSax: Sedlbauer driver Rev. %s\n", HiSax_getrev(tmp));
+	
  	if (cs->typ == ISDN_CTYPE_SEDLBAUER) {
- 		cs->subtyp = SEDL_SPEED_CARD;
+ 		cs->subtyp = SEDL_SPEED_CARD_WIN;
+		cs->hw.sedl.bus = SEDL_BUS_ISA;
+		cs->hw.sedl.chip = SEDL_CHIP_TEST;
  	} else if (cs->typ == ISDN_CTYPE_SEDLBAUER_PCMCIA) {	
  		cs->subtyp = SEDL_SPEED_STAR;
+		cs->hw.sedl.bus = SEDL_BUS_PCMCIA;
+		cs->hw.sedl.chip = SEDL_CHIP_TEST;
+ 	} else if (cs->typ == ISDN_CTYPE_SEDLBAUER_FAX) {	
+ 		cs->subtyp = SEDL_SPEED_FAX;
+		cs->hw.sedl.bus = SEDL_BUS_ISA;
+		cs->hw.sedl.chip = SEDL_CHIP_ISAC_ISAR;
  	} else
 		return (0);
 
 	bytecnt = 8;
-	cs->hw.sedl.cfg_reg = card->para[1];
-	cs->irq = card->para[0];
-	if (cs->subtyp == SEDL_SPEED_STAR) {
-		cs->hw.sedl.adr = cs->hw.sedl.cfg_reg + SEDL_PCMCIA_ADR;
-		cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_PCMCIA_ISAC;
-		cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + SEDL_PCMCIA_HSCX;
-		cs->hw.sedl.reset_on = cs->hw.sedl.cfg_reg + SEDL_PCMCIA_RESET;
-		cs->hw.sedl.reset_off = cs->hw.sedl.cfg_reg + SEDL_PCMCIA_RESET;
+	if (card->para[1]) {
+		cs->hw.sedl.cfg_reg = card->para[1];
+		cs->irq = card->para[0];
+		if (cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) {
+			bytecnt = 16;
+		}
 	} else {
-		cs->hw.sedl.adr = cs->hw.sedl.cfg_reg + SEDL_ADR;
-		cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_ISAC;
-		cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + SEDL_HSCX;
-		cs->hw.sedl.reset_on = cs->hw.sedl.cfg_reg + SEDL_RESET_ON;
-		cs->hw.sedl.reset_off = cs->hw.sedl.cfg_reg + SEDL_RESET_OFF;
-	}
-        
-	/* In case of the sedlbauer pcmcia card, this region is in use,
+/* Probe for Sedlbauer speed pci */
+#if SEDLBAUER_PCI
+#if CONFIG_PCI
+		for (; pci_index < 255; pci_index++) {
+			unsigned char pci_bus, pci_device_fn;
+			unsigned int ioaddr;
+			unsigned char irq;
+
+			if (pcibios_find_device (PCI_VENDOR_SEDLBAUER,
+						PCI_SPEEDPCI_ID, pci_index,
+						&pci_bus, &pci_device_fn) != 0) {
+				continue;
+			}
+			pcibios_read_config_byte(pci_bus, pci_device_fn,
+					PCI_INTERRUPT_LINE, &irq);
+			pcibios_read_config_dword(pci_bus, pci_device_fn,
+					PCI_BASE_ADDRESS_0, &ioaddr);
+			cs->irq = irq;
+			cs->hw.sedl.cfg_reg = ioaddr & PCI_BASE_ADDRESS_IO_MASK; 
+			if (!cs->hw.sedl.cfg_reg) {
+				printk(KERN_WARNING "Sedlbauer: No IO-Adr for PCI card found\n");
+				return(0);
+			}
+			cs->hw.sedl.bus = SEDL_BUS_PCI;
+			cs->hw.sedl.chip = SEDL_CHIP_IPAC;
+			cs->subtyp = SEDL_SPEED_PCI;
+			bytecnt = 256;
+			byteout(cs->hw.sedl.cfg_reg, 0xff);
+			byteout(cs->hw.sedl.cfg_reg, 0x00);
+			byteout(cs->hw.sedl.cfg_reg+ 2, 0xdd);
+			byteout(cs->hw.sedl.cfg_reg+ 5, 0x02);
+			break;
+		}	
+		if (pci_index == 255) {
+			printk(KERN_WARNING "Sedlbauer: No PCI card found\n");
+			return(0);
+		}
+		pci_index++;
+#else
+		printk(KERN_WARNING "Sedlbauer: NO_PCI_BIOS\n");
+		return (0);
+#endif /* CONFIG_PCI */
+#endif /* SEDLBAUER_PCI */
+	}	
+	
+       	/* In case of the sedlbauer pcmcia card, this region is in use,
            reserved for us by the card manager. So we do not check it
            here, it would fail. */
-	if (cs->typ != ISDN_CTYPE_SEDLBAUER_PCMCIA &&
-	   check_region((cs->hw.sedl.cfg_reg), bytecnt)) {
+	if (cs->hw.sedl.bus != SEDL_BUS_PCMCIA &&
+		check_region((cs->hw.sedl.cfg_reg), bytecnt)) {
 		printk(KERN_WARNING
-		       "HiSax: %s config port %x-%x already in use\n",
-		       CardType[card->typ],
-		       cs->hw.sedl.cfg_reg,
-		       cs->hw.sedl.cfg_reg + bytecnt);
-		return (0);
+			"HiSax: %s config port %x-%x already in use\n",
+			CardType[card->typ],
+			cs->hw.sedl.cfg_reg,
+			cs->hw.sedl.cfg_reg + bytecnt);
+			return (0);
 	} else {
 		request_region(cs->hw.sedl.cfg_reg, bytecnt, "sedlbauer isdn");
 	}
 
 	printk(KERN_INFO
-	       "Sedlbauer: defined at 0x%x IRQ %d\n",
+	       "Sedlbauer: defined at 0x%x-0x%x IRQ %d\n",
 	       cs->hw.sedl.cfg_reg,
+	       cs->hw.sedl.cfg_reg + bytecnt,
 	       cs->irq);
-	printk(KERN_WARNING
-		       "Sedlbauer %s uses ports 0x%x-0x%x\n",
-		       Sedlbauer_Types[cs->subtyp],
-		       cs->hw.sedl.cfg_reg,
-		       cs->hw.sedl.cfg_reg + bytecnt);
 
-	printk(KERN_INFO "Sedlbauer: resetting card\n");
-	reset_sedlbauer(cs);
-	cs->readisac = &ReadISAC;
-	cs->writeisac = &WriteISAC;
-	cs->readisacfifo = &ReadISACfifo;
-	cs->writeisacfifo = &WriteISACfifo;
 	cs->BC_Read_Reg = &ReadHSCX;
 	cs->BC_Write_Reg = &WriteHSCX;
 	cs->BC_Send_Data = &hscx_fill_fifo;
 	cs->cardmsg = &Sedl_card_msg;
-	ISACVersion(cs, "Sedlbauer:");
-	if (HscxVersion(cs, "Sedlbauer:")) {
-		printk(KERN_WARNING
-		    "Sedlbauer: wrong HSCX versions check IO address\n");
-		release_io_sedlbauer(cs);
-		return (0);
+
+/*
+ * testing ISA and PCMCIA Cards for IPAC, default is ISAC 
+ * do not test for PCI card, because ports are different
+ * and PCI card uses only IPAC (for the moment)
+ */	
+	if (cs->hw.sedl.bus != SEDL_BUS_PCI) {
+		val = readreg(cs->hw.sedl.cfg_reg + SEDL_IPAC_ANY_ADR,
+        	        cs->hw.sedl.cfg_reg + SEDL_IPAC_ANY_IPAC, IPAC_ID);
+	        if (val == 1) {
+		/* IPAC */
+                	cs->subtyp = SEDL_SPEED_WIN2_PC104;
+			if (cs->hw.sedl.bus == SEDL_BUS_PCMCIA) {
+				cs->subtyp = SEDL_SPEED_STAR2;
+			}
+			cs->hw.sedl.chip = SEDL_CHIP_IPAC;
+		} else {
+		/* ISAC_HSCX oder ISAC_ISAR */
+			if (cs->hw.sedl.chip == SEDL_CHIP_TEST) {
+				cs->hw.sedl.chip = SEDL_CHIP_ISAC_HSCX;
+			}
+		}
+	}
+
+/*
+ * hw.sedl.chip is now properly set
+ */
+	printk(KERN_INFO "Sedlbauer: %s detected\n",
+		Sedlbauer_Types[cs->subtyp]);
+
+
+	if (cs->hw.sedl.chip == SEDL_CHIP_IPAC) {
+	/* IPAC */
+		if (cs->hw.sedl.bus == SEDL_BUS_PCI) {
+	                cs->hw.sedl.adr  = cs->hw.sedl.cfg_reg + SEDL_IPAC_PCI_ADR;
+        	        cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_IPAC_PCI_IPAC;
+                	cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + SEDL_IPAC_PCI_IPAC;
+		} else {
+	                cs->hw.sedl.adr  = cs->hw.sedl.cfg_reg + SEDL_IPAC_ANY_ADR;
+        	        cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_IPAC_ANY_IPAC;
+                	cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + SEDL_IPAC_ANY_IPAC;
+		}
+                test_and_set_bit(HW_IPAC, &cs->HW_Flags);
+                cs->readisac = &ReadISAC_IPAC;
+                cs->writeisac = &WriteISAC_IPAC;
+                cs->readisacfifo = &ReadISACfifo_IPAC;
+                cs->writeisacfifo = &WriteISACfifo_IPAC;
+
+		val = readreg(cs->hw.sedl.adr,cs->hw.sedl.isac, IPAC_ID);
+                printk(KERN_INFO "Sedlbauer: IPAC version %x\n", val);
+		reset_sedlbauer(cs);
+	} else {
+	/* ISAC_HSCX oder ISAC_ISAR */
+		cs->readisac = &ReadISAC;
+		cs->writeisac = &WriteISAC;
+		cs->readisacfifo = &ReadISACfifo;
+		cs->writeisacfifo = &WriteISACfifo;
+		if (cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) {
+			cs->hw.sedl.adr = cs->hw.sedl.cfg_reg + SEDL_ISAR_ISA_ADR;
+			cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_ISAR_ISA_ISAC;
+			cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + SEDL_ISAR_ISA_ISAR;
+			cs->hw.sedl.reset_on = cs->hw.sedl.cfg_reg + SEDL_ISAR_ISA_ISAR_RESET_ON;
+			cs->hw.sedl.reset_off = cs->hw.sedl.cfg_reg + SEDL_ISAR_ISA_ISAR_RESET_OFF;
+			cs->bcs[0].hw.isar.reg = &cs->hw.sedl.isar;
+			cs->bcs[1].hw.isar.reg = &cs->hw.sedl.isar;
+			test_and_set_bit(HW_ISAR, &cs->HW_Flags);
+	
+			ISACVersion(cs, "Sedlbauer:");
+		
+			cs->BC_Read_Reg = &ReadISAR;
+			cs->BC_Write_Reg = &WriteISAR;
+			cs->BC_Send_Data = &isar_fill_fifo;
+			ver = ISARVersion(cs, "Sedlbauer:");
+			if (ver < 0) {
+				printk(KERN_WARNING
+					"Sedlbauer: wrong ISAR version (ret = %d)\n", ver);
+				release_io_sedlbauer(cs);
+				return (0);
+			}
+		} else {
+			if (cs->hw.sedl.bus == SEDL_BUS_PCMCIA) {
+				cs->hw.sedl.adr = cs->hw.sedl.cfg_reg + SEDL_HSCX_PCMCIA_ADR;
+				cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_HSCX_PCMCIA_ISAC;
+				cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + SEDL_HSCX_PCMCIA_HSCX;
+				cs->hw.sedl.reset_on = cs->hw.sedl.cfg_reg + SEDL_HSCX_PCMCIA_RESET;
+				cs->hw.sedl.reset_off = cs->hw.sedl.cfg_reg + SEDL_HSCX_PCMCIA_RESET;
+			} else {
+				cs->hw.sedl.adr = cs->hw.sedl.cfg_reg + SEDL_HSCX_ISA_ADR;
+				cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_HSCX_ISA_ISAC;
+				cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + SEDL_HSCX_ISA_HSCX;
+				cs->hw.sedl.reset_on = cs->hw.sedl.cfg_reg + SEDL_HSCX_ISA_RESET_ON;
+				cs->hw.sedl.reset_off = cs->hw.sedl.cfg_reg + SEDL_HSCX_ISA_RESET_OFF;
+			}
+			ISACVersion(cs, "Sedlbauer:");
+		
+			if (HscxVersion(cs, "Sedlbauer:")) {
+				printk(KERN_WARNING
+					"Sedlbauer: wrong HSCX versions check IO address\n");
+				release_io_sedlbauer(cs);
+				return (0);
+			}
+			reset_sedlbauer(cs);
+		}
 	}
 	return (1);
 }
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/sportster.c linux/drivers/isdn/hisax/sportster.c
--- v2.3.3/linux/drivers/isdn/hisax/sportster.c	Thu Nov  5 09:58:44 1998
+++ linux/drivers/isdn/hisax/sportster.c	Sun May 23 10:03:41 1999
@@ -1,4 +1,4 @@
-/* $Id: sportster.c,v 1.5 1998/02/02 13:29:46 keil Exp $
+/* $Id: sportster.c,v 1.7 1998/11/15 23:55:22 keil Exp $
 
  * sportster.c     low level stuff for USR Sportster internal TA
  *
@@ -7,6 +7,12 @@
  * Thanks to Christian "naddy" Weisgerber (3Com, US Robotics) for documentation
  *
  * $Log: sportster.c,v $
+ * Revision 1.7  1998/11/15 23:55:22  keil
+ * changes from 2.0
+ *
+ * Revision 1.6  1998/04/15 16:44:35  keil
+ * new init code
+ *
  * Revision 1.5  1998/02/02 13:29:46  keil
  * fast io
  *
@@ -30,7 +36,7 @@
 #include "isdnl1.h"
 
 extern const char *CardType[];
-const char *sportster_revision = "$Revision: 1.5 $";
+const char *sportster_revision = "$Revision: 1.7 $";
 
 #define byteout(addr,val) outb(val,addr)
 #define bytein(addr) inb(addr)
@@ -187,12 +193,10 @@
 			return(request_irq(cs->irq, &sportster_interrupt,
 					I4L_IRQ_FLAG, "HiSax", cs));
 		case CARD_INIT:
-			clear_pending_isac_ints(cs);
-			clear_pending_hscx_ints(cs);
-			initisac(cs);
-			inithscx(cs);
+			inithscxisac(cs, 1);
 			cs->hw.spt.res_irq |= SPORTSTER_INTE; /* IRQ On */
 			byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, cs->hw.spt.res_irq);
+			inithscxisac(cs, 2);
 			return(0);
 		case CARD_TEST:
 			return(0);
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/tei.c linux/drivers/isdn/hisax/tei.c
--- v2.3.3/linux/drivers/isdn/hisax/tei.c	Wed Apr  1 16:21:02 1998
+++ linux/drivers/isdn/hisax/tei.c	Sun May 23 10:03:41 1999
@@ -1,12 +1,34 @@
-/* $Id: tei.c,v 2.7 1998/02/12 23:08:11 keil Exp $
+/* $Id: tei.c,v 2.11 1998/11/15 23:55:24 keil Exp $
 
- * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author       Karsten Keil (keil@isdn4linux.de)
  *              based on the teles driver from Jan den Ouden
  *
+ *		This file is (c) under GNU PUBLIC LICENSE
+ *		For changes and modifications please read
+ *		../../../Documentation/isdn/HiSax.cert
+ *
  * Thanks to    Jan den Ouden
  *              Fritz Elfert
  *
  * $Log: tei.c,v $
+ * Revision 2.11  1998/11/15 23:55:24  keil
+ * changes from 2.0
+ *
+ * Revision 2.10  1998/05/25 14:08:10  keil
+ * HiSax 3.0
+ * fixed X.75 and leased line to work again
+ * Point2Point and fixed TEI are runtime options now:
+ *    hisaxctrl <id> 7 1  set PTP
+ *    hisaxctrl <id> 8 <TEIVALUE *2 >
+ *    set fixed TEI to TEIVALUE (0-63)
+ *
+ * Revision 2.9  1998/05/25 12:58:23  keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
+ * Revision 2.8  1998/03/07 22:57:07  tsbogend
+ * made HiSax working on Linux/Alpha
+ *
  * Revision 2.7  1998/02/12 23:08:11  keil
  * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
  *
@@ -50,7 +72,7 @@
 #include "isdnl2.h"
 #include <linux/random.h>
 
-const char *tei_revision = "$Revision: 2.7 $";
+const char *tei_revision = "$Revision: 2.11 $";
 
 #define ID_REQUEST	1
 #define ID_ASSIGNED	2
@@ -151,26 +173,24 @@
 	bp[2] = ri & 0xff;
 	bp[3] = m_id;
 	bp[4] = (tei << 1) | 1;
-	st->l2.l2l1(st, PH_DATA_REQ, skb);
+	st->l2.l2l1(st, PH_DATA | REQUEST, skb);
 }
 
 static void
 tei_id_request(struct FsmInst *fi, int event, void *arg)
 {
 	struct PStack *st = fi->userdata;
-	char tmp[64];
 
 	if (st->l2.tei != -1) {
-		sprintf(tmp, "assign request for allready asigned tei %d",
+		st->ma.tei_m.printdebug(&st->ma.tei_m,
+			"assign request for allready asigned tei %d",
 			st->l2.tei);
-		st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
 		return;
 	}
 	st->ma.ri = random_ri();
-	if (st->ma.debug) {
-		sprintf(tmp, "assign request ri %d", st->ma.ri);
-		st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
-	}
+	if (st->ma.debug)
+		st->ma.tei_m.printdebug(&st->ma.tei_m,
+			"assign request ri %d", st->ma.ri);
 	put_tei_msg(st, ID_REQUEST, st->ma.ri, 127);
 	FsmChangeState(&st->ma.tei_m, ST_TEI_IDREQ);
 	FsmAddTimer(&st->ma.t202, st->ma.T202, EV_T202, NULL, 1);
@@ -184,26 +204,24 @@
 	struct sk_buff *skb = arg;
 	struct IsdnCardState *cs;
 	int ri, tei;
-	char tmp[64];
 
 	ri = ((unsigned int) skb->data[1] << 8) + skb->data[2];
 	tei = skb->data[4] >> 1;
-	if (st->ma.debug) {
-		sprintf(tmp, "identity assign ri %d tei %d", ri, tei);
-		st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
-	}
+	if (st->ma.debug)
+		st->ma.tei_m.printdebug(&st->ma.tei_m,
+			"identity assign ri %d tei %d", ri, tei);
 	if ((ost = findtei(st, tei))) {		/* same tei is in use */
 		if (ri != ost->ma.ri) {
-			sprintf(tmp, "possible duplicate assignment tei %d", tei);
-			st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
-			ost->l2.l2tei(ost, MDL_ERROR_REQ, NULL);
+			st->ma.tei_m.printdebug(&st->ma.tei_m,
+				"possible duplicate assignment tei %d", tei);
+			ost->l2.l2tei(ost, MDL_ERROR | RESPONSE, NULL);
 		}
 	} else if (ri == st->ma.ri) {
 		FsmDelTimer(&st->ma.t202, 1);
 		FsmChangeState(&st->ma.tei_m, ST_TEI_NOP);
-		st->ma.manl2(st, MDL_ASSIGN_REQ, (void *) (int) tei);
+		st->l3.l3l2(st, MDL_ASSIGN | REQUEST, (void *) (long) tei);
 		cs = (struct IsdnCardState *) st->l1.hardware;
-		cs->cardmsg(cs, MDL_ASSIGN_REQ, NULL);
+		cs->cardmsg(cs, MDL_ASSIGN | REQUEST, NULL);
 	}
 }
 
@@ -213,14 +231,12 @@
 	struct PStack *st = fi->userdata;
 	struct sk_buff *skb = arg;
 	int ri, tei;
-	char tmp[64];
 
 	ri = ((unsigned int) skb->data[1] << 8) + skb->data[2];
 	tei = skb->data[4] >> 1;
-	if (st->ma.debug) {
-		sprintf(tmp, "identity denied ri %d tei %d", ri, tei);
-		st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
-	}
+	if (st->ma.debug)
+		st->ma.tei_m.printdebug(&st->ma.tei_m,
+			"identity denied ri %d tei %d", ri, tei);
 }
 
 static void
@@ -229,13 +245,11 @@
 	struct PStack *st = fi->userdata;
 	struct sk_buff *skb = arg;
 	int tei;
-	char tmp[64];
 
 	tei = skb->data[4] >> 1;
-	if (st->ma.debug) {
-		sprintf(tmp, "identity check req tei %d", tei);
-		st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
-	}
+	if (st->ma.debug)
+		st->ma.tei_m.printdebug(&st->ma.tei_m,
+			"identity check req tei %d", tei);
 	if ((st->l2.tei != -1) && ((tei == GROUP_TEI) || (tei == st->l2.tei))) {
 		FsmDelTimer(&st->ma.t202, 4);
 		FsmChangeState(&st->ma.tei_m, ST_TEI_NOP);
@@ -250,19 +264,17 @@
 	struct sk_buff *skb = arg;
 	struct IsdnCardState *cs;
 	int tei;
-	char tmp[64];
 
 	tei = skb->data[4] >> 1;
-	if (st->ma.debug) {
-		sprintf(tmp, "identity remove tei %d", tei);
-		st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
-	}
+	if (st->ma.debug)
+		st->ma.tei_m.printdebug(&st->ma.tei_m,
+			"identity remove tei %d", tei);
 	if ((st->l2.tei != -1) && ((tei == GROUP_TEI) || (tei == st->l2.tei))) {
 		FsmDelTimer(&st->ma.t202, 5);
 		FsmChangeState(&st->ma.tei_m, ST_TEI_NOP);
-		st->ma.manl2(st, MDL_REMOVE_REQ, 0);
+		st->l3.l3l2(st, MDL_REMOVE | REQUEST, 0);
 		cs = (struct IsdnCardState *) st->l1.hardware;
-		cs->cardmsg(cs, MDL_REMOVE_REQ, NULL);
+		cs->cardmsg(cs, MDL_REMOVE | REQUEST, NULL);
 	}
 }
 
@@ -270,12 +282,10 @@
 tei_id_verify(struct FsmInst *fi, int event, void *arg)
 {
 	struct PStack *st = fi->userdata;
-	char tmp[64];
 
-	if (st->ma.debug) {
-		sprintf(tmp, "id verify request for tei %d", st->l2.tei);
-		st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
-	}
+	if (st->ma.debug)
+		st->ma.tei_m.printdebug(&st->ma.tei_m,
+			"id verify request for tei %d", st->l2.tei);
 	put_tei_msg(st, ID_VERIFY, 0, st->l2.tei);
 	FsmChangeState(&st->ma.tei_m, ST_TEI_IDVERIFY);
 	FsmAddTimer(&st->ma.t202, st->ma.T202, EV_T202, NULL, 2);
@@ -286,24 +296,21 @@
 tei_id_req_tout(struct FsmInst *fi, int event, void *arg)
 {
 	struct PStack *st = fi->userdata;
-	char tmp[64];
 	struct IsdnCardState *cs;
 
 	if (--st->ma.N202) {
 		st->ma.ri = random_ri();
-		if (st->ma.debug) {
-			sprintf(tmp, "assign req(%d) ri %d",
-				4 - st->ma.N202, st->ma.ri);
-			st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
-		}
+		if (st->ma.debug)
+			st->ma.tei_m.printdebug(&st->ma.tei_m,
+				"assign req(%d) ri %d", 4 - st->ma.N202,
+				st->ma.ri);
 		put_tei_msg(st, ID_REQUEST, st->ma.ri, 127);
 		FsmAddTimer(&st->ma.t202, st->ma.T202, EV_T202, NULL, 3);
 	} else {
-		sprintf(tmp, "assign req failed");
-		st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
-		st->ma.manl2(st, MDL_ERROR_IND, 0);
+		st->ma.tei_m.printdebug(&st->ma.tei_m, "assign req failed");
+		st->l3.l3l2(st, MDL_ERROR | RESPONSE, 0);
 		cs = (struct IsdnCardState *) st->l1.hardware;
-		cs->cardmsg(cs, MDL_REMOVE_REQ, NULL);
+		cs->cardmsg(cs, MDL_REMOVE | REQUEST, NULL);
 		FsmChangeState(fi, ST_TEI_NOP);
 	}
 }
@@ -312,23 +319,21 @@
 tei_id_ver_tout(struct FsmInst *fi, int event, void *arg)
 {
 	struct PStack *st = fi->userdata;
-	char tmp[64];
 	struct IsdnCardState *cs;
 
 	if (--st->ma.N202) {
-		if (st->ma.debug) {
-			sprintf(tmp, "id verify req(%d) for tei %d",
+		if (st->ma.debug)
+			st->ma.tei_m.printdebug(&st->ma.tei_m,
+				"id verify req(%d) for tei %d",
 				3 - st->ma.N202, st->l2.tei);
-			st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
-		}
 		put_tei_msg(st, ID_VERIFY, 0, st->l2.tei);
 		FsmAddTimer(&st->ma.t202, st->ma.T202, EV_T202, NULL, 4);
 	} else {
-		sprintf(tmp, "verify req for tei %d failed", st->l2.tei);
-		st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
-		st->ma.manl2(st, MDL_REMOVE_REQ, 0);
+		st->ma.tei_m.printdebug(&st->ma.tei_m,
+			"verify req for tei %d failed", st->l2.tei);
+		st->l3.l3l2(st, MDL_REMOVE | REQUEST, 0);
 		cs = (struct IsdnCardState *) st->l1.hardware;
-		cs->cardmsg(cs, MDL_REMOVE_REQ, NULL);
+		cs->cardmsg(cs, MDL_REMOVE | REQUEST, NULL);
 		FsmChangeState(fi, ST_TEI_NOP);
 	}
 }
@@ -338,31 +343,34 @@
 {
 	struct sk_buff *skb = arg;
 	int mt;
-	char tmp[64];
 
-	if (pr == PH_DATA_IND) {
+	if (test_bit(FLG_FIXED_TEI, &st->l2.flag)) {
+		dev_kfree_skb(skb);
+		return;
+	}
+
+	if (pr == (PH_DATA | INDICATION)) {
 		if (skb->len < 3) {
-			sprintf(tmp, "short mgr frame %d/3", skb->len);
-			st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
+			st->ma.tei_m.printdebug(&st->ma.tei_m,
+				"short mgr frame %ld/3", skb->len);
 		} else if (((skb->data[0] >> 2) != TEI_SAPI) ||
 			   ((skb->data[1] >> 1) != GROUP_TEI)) {
-			sprintf(tmp, "wrong mgr sapi/tei %x/%x",
+			st->ma.tei_m.printdebug(&st->ma.tei_m,
+				"wrong mgr sapi/tei %x/%x",
 				skb->data[0], skb->data[1]);
-			st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
 		} else if ((skb->data[2] & 0xef) != UI) {
-			sprintf(tmp, "mgr frame is not ui %x",
-				skb->data[2]);
-			st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
+			st->ma.tei_m.printdebug(&st->ma.tei_m,
+				"mgr frame is not ui %x", skb->data[2]);
 		} else {
 			skb_pull(skb, 3);
 			if (skb->len < 5) {
-				sprintf(tmp, "short mgr frame %d/5", skb->len);
-				st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
+				st->ma.tei_m.printdebug(&st->ma.tei_m,
+					"short mgr frame %ld/5", skb->len);
 			} else if (skb->data[0] != TEI_ENTITY_ID) {
 				/* wrong management entity identifier, ignore */
-				sprintf(tmp, "tei handler wrong entity id %x\n",
+				st->ma.tei_m.printdebug(&st->ma.tei_m,
+					"tei handler wrong entity id %x",
 					skb->data[0]);
-				st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
 			} else {
 				mt = skb->data[3];
 				if (mt == ID_ASSIGNED)
@@ -374,15 +382,14 @@
 				else if (mt == ID_REMOVE)
 					FsmEvent(&st->ma.tei_m, EV_REMOVE, skb);
 				else {
-					sprintf(tmp, "tei handler wrong mt %x\n",
-						mt);
-					st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
+					st->ma.tei_m.printdebug(&st->ma.tei_m,
+						"tei handler wrong mt %x\n", mt);
 				}
 			}
 		}
 	} else {
-		sprintf(tmp, "tei handler wrong pr %x\n", pr);
-		st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
+		st->ma.tei_m.printdebug(&st->ma.tei_m,
+			"tei handler wrong pr %x\n", pr);
 	}
 	dev_kfree_skb(skb);
 }
@@ -390,20 +397,24 @@
 static void
 tei_l2tei(struct PStack *st, int pr, void *arg)
 {
+	struct IsdnCardState *cs;
+
+	if (test_bit(FLG_FIXED_TEI, &st->l2.flag)) {
+		if (pr == (MDL_ASSIGN | INDICATION)) {
+			if (st->ma.debug)
+				st->ma.tei_m.printdebug(&st->ma.tei_m,
+					"fixed assign tei %d", st->l2.tei);
+			st->l3.l3l2(st, MDL_ASSIGN | REQUEST, (void *) (long) st->l2.tei);
+			cs = (struct IsdnCardState *) st->l1.hardware;
+			cs->cardmsg(cs, MDL_ASSIGN | REQUEST, NULL);
+		}
+		return;
+	}
 	switch (pr) {
-		case (MDL_ASSIGN_IND):
-#ifdef TEI_FIXED
-			if (st->ma.debug) {
-				char tmp[64];
-				sprintf(tmp, "fixed assign tei %d", TEI_FIXED);
-				st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
-			}
-			st->ma.manl2(st, MDL_ASSIGN_REQ, (void *) (int) TEI_FIXED);
-#else
+		case (MDL_ASSIGN | INDICATION):
 			FsmEvent(&st->ma.tei_m, EV_IDREQ, arg);
-#endif
 			break;
-		case (MDL_ERROR_REQ):
+		case (MDL_ERROR | REQUEST):
 			FsmEvent(&st->ma.tei_m, EV_VERIFY, arg);
 			break;
 		default:
@@ -412,14 +423,14 @@
 }
 
 static void
-tei_debug(struct FsmInst *fi, char *s)
+tei_debug(struct FsmInst *fi, char *fmt, ...)
 {
+	va_list args;
 	struct PStack *st = fi->userdata;
-	char tm[32], str[256];
 
-	jiftime(tm, jiffies);
-	sprintf(str, "%s Tei %s\n", tm, s);
-	HiSax_putstatus(st->l1.hardware, str);
+	va_start(args, fmt);
+	VHiSax_putstatus(st->l1.hardware, "tei ", fmt, args);
+	va_end(args);
 }
 
 void
@@ -439,9 +450,8 @@
 }
 
 void
-init_tei(struct IsdnCardState *sp, int protocol)
+init_tei(struct IsdnCardState *cs, int protocol)
 {
-
 }
 
 void
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/teleint.c linux/drivers/isdn/hisax/teleint.c
--- v2.3.3/linux/drivers/isdn/hisax/teleint.c	Thu Nov  5 09:58:44 1998
+++ linux/drivers/isdn/hisax/teleint.c	Sun May 23 10:03:41 1999
@@ -1,11 +1,17 @@
-/* $Id: teleint.c,v 1.5 1998/02/02 13:40:47 keil Exp $
+/* $Id: teleint.c,v 1.7 1998/11/15 23:55:26 keil Exp $
 
  * teleint.c     low level stuff for TeleInt isdn cards
  *
- * Author     Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author     Karsten Keil (keil@isdn4linux.de)
  *
  *
  * $Log: teleint.c,v $
+ * Revision 1.7  1998/11/15 23:55:26  keil
+ * changes from 2.0
+ *
+ * Revision 1.6  1998/04/15 16:45:31  keil
+ * new init code
+ *
  * Revision 1.5  1998/02/02 13:40:47  keil
  * fast io
  *
@@ -32,7 +38,7 @@
 
 extern const char *CardType[];
 
-const char *TeleInt_revision = "$Revision: 1.5 $";
+const char *TeleInt_revision = "$Revision: 1.7 $";
 
 #define byteout(addr,val) outb(val,addr)
 #define bytein(addr) inb(addr)
@@ -64,17 +70,20 @@
 readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
 {
 	register u_char ret;
-	int max_delay = 2000;
+	register int max_delay = 20000;
+	register int i;
+	
 	byteout(ale, off);
-
-	ret = HFC_BUSY & bytein(ale);
-	while (ret && --max_delay)
+	for (i = 0; i<size; i++) {
 		ret = HFC_BUSY & bytein(ale);
-	if (!max_delay) {
-		printk(KERN_WARNING "TeleInt Busy not inaktive\n");
-		return;
+		while (ret && --max_delay)
+			ret = HFC_BUSY & bytein(ale);
+		if (!max_delay) {
+			printk(KERN_WARNING "TeleInt Busy not inaktive\n");
+			return;
+		}
+		data[i] = bytein(adr);
 	}
-	insb(adr, data, size);
 }
 
 
@@ -104,18 +113,21 @@
 writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
 {
 	register u_char ret;
-	int max_delay = 2000;
-
+	register int max_delay = 20000;
+	register int i;
+	
 	/* fifo write without cli because it's allready done  */
 	byteout(ale, off);
-	ret = HFC_BUSY & bytein(ale);
-	while (ret && --max_delay)
+	for (i = 0; i<size; i++) {
 		ret = HFC_BUSY & bytein(ale);
-	if (!max_delay) {
-		printk(KERN_WARNING "TeleInt Busy not inaktive\n");
-		return;
+		while (ret && --max_delay)
+			ret = HFC_BUSY & bytein(ale);
+		if (!max_delay) {
+			printk(KERN_WARNING "TeleInt Busy not inaktive\n");
+			return;
+		}
+		byteout(adr, data[i]);
 	}
-	outsb(adr, data, size);
 }
 
 /* Interface functions */
@@ -157,11 +169,8 @@
 		cs->hw.hfc.cip = reg;
 		byteout(cs->hw.hfc.addr | 1, reg);
 		ret = bytein(cs->hw.hfc.addr);
-		if (cs->debug & L1_DEB_HSCX_FIFO && (data != 2)) {
-			char tmp[32];
-			sprintf(tmp, "hfc RD %02x %02x", reg, ret);
-			debugl1(cs, tmp);
-		}
+		if (cs->debug & L1_DEB_HSCX_FIFO && (data != 2))
+			debugl1(cs, "hfc RD %02x %02x", reg, ret);
 	} else
 		ret = bytein(cs->hw.hfc.addr | 1);
 	return (ret);
@@ -174,11 +183,8 @@
 	cs->hw.hfc.cip = reg;
 	if (data)
 		byteout(cs->hw.hfc.addr, value);
-	if (cs->debug & L1_DEB_HSCX_FIFO && (data != 2)) {
-		char tmp[32];
-		sprintf(tmp, "hfc W%c %02x %02x", data ? 'D' : 'C', reg, value);
-		debugl1(cs, tmp);
-	}
+	if (cs->debug & L1_DEB_HSCX_FIFO && (data != 2))
+		debugl1(cs, "hfc W%c %02x %02x", data ? 'D' : 'C', reg, value);
 }
 
 static void
@@ -271,6 +277,9 @@
 			inithfc(cs);
 			clear_pending_isac_ints(cs);
 			initisac(cs);
+			/* Reenable all IRQ */
+			cs->writeisac(cs, ISAC_MASK, 0);
+			cs->writeisac(cs, ISAC_CMDR, 0x41);
 			cs->hw.hfc.timer.expires = jiffies + 1;
 			add_timer(&cs->hw.hfc.timer);
 			return(0);
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/teles0.c linux/drivers/isdn/hisax/teles0.c
--- v2.3.3/linux/drivers/isdn/hisax/teles0.c	Wed Apr  1 16:21:02 1998
+++ linux/drivers/isdn/hisax/teles0.c	Sun May 23 10:03:41 1999
@@ -1,4 +1,4 @@
-/* $Id: teles0.c,v 2.6 1998/02/03 23:27:47 keil Exp $
+/* $Id: teles0.c,v 2.8 1998/04/15 16:44:28 keil Exp $
 
  * teles0.c     low level stuff for Teles Memory IO isdn cards
  *              based on the teles driver from Jan den Ouden
@@ -10,6 +10,12 @@
  *              Beat Doebeli
  *
  * $Log: teles0.c,v $
+ * Revision 2.8  1998/04/15 16:44:28  keil
+ * new init code
+ *
+ * Revision 2.7  1998/03/07 22:57:08  tsbogend
+ * made HiSax working on Linux/Alpha
+ *
  * Revision 2.6  1998/02/03 23:27:47  keil
  * IRQ 9
  *
@@ -48,7 +54,7 @@
 
 extern const char *CardType[];
 
-const char *teles0_revision = "$Revision: 2.6 $";
+const char *teles0_revision = "$Revision: 2.8 $";
 
 #define byteout(addr,val) outb(val,addr)
 #define bytein(addr) inb(addr)
@@ -62,7 +68,7 @@
 static inline void
 writeisac(unsigned int adr, u_char off, u_char data)
 {
-	writeb(data, adr + ((off & 1) ? 0x2ff : 0x100) + off);
+	writeb(data, adr + ((off & 1) ? 0x2ff : 0x100) + off); mb();
 }
 
 
@@ -77,14 +83,14 @@
 writehscx(unsigned int adr, int hscx, u_char off, u_char data)
 {
 	writeb(data, adr + (hscx ? 0x1c0 : 0x180) +
-	       ((off & 1) ? 0x1ff : 0) + off);
+	       ((off & 1) ? 0x1ff : 0) + off); mb();
 }
 
 static inline void
 read_fifo_isac(unsigned int adr, u_char * data, int size)
 {
 	register int i;
-	register u_char *ad = (u_char *) (adr + 0x100);
+	register u_char *ad = (u_char *) ((long)adr + 0x100);
 	for (i = 0; i < size; i++)
 		data[i] = readb(ad);
 }
@@ -93,16 +99,17 @@
 write_fifo_isac(unsigned int adr, u_char * data, int size)
 {
 	register int i;
-	register u_char *ad = (u_char *) (adr + 0x100);
-	for (i = 0; i < size; i++)
-		writeb(data[i], ad);
+	register u_char *ad = (u_char *) ((long)adr + 0x100);
+	for (i = 0; i < size; i++) {
+		writeb(data[i], ad); mb();
+	}
 }
 
 static inline void
 read_fifo_hscx(unsigned int adr, int hscx, u_char * data, int size)
 {
 	register int i;
-	register u_char *ad = (u_char *) (adr + (hscx ? 0x1c0 : 0x180));
+	register u_char *ad = (u_char *) ((long)adr + (hscx ? 0x1c0 : 0x180));
 	for (i = 0; i < size; i++)
 		data[i] = readb(ad);
 }
@@ -111,9 +118,10 @@
 write_fifo_hscx(unsigned int adr, int hscx, u_char * data, int size)
 {
 	int i;
-	register u_char *ad = (u_char *) (adr + (hscx ? 0x1c0 : 0x180));
-	for (i = 0; i < size; i++)
-		writeb(data[i], ad);
+	register u_char *ad = (u_char *) ((long)adr + (hscx ? 0x1c0 : 0x180));
+	for (i = 0; i < size; i++) {
+		writeb(data[i], ad); mb();
+	}
 }
 
 /* Interface functions */
@@ -264,9 +272,9 @@
 		byteout(cs->hw.teles0.cfg_reg + 4, cfval | 1);
 		HZDELAY(HZ / 10 + 1);
 	}
-	writeb(0, cs->hw.teles0.membase + 0x80);
+	writeb(0, cs->hw.teles0.membase + 0x80); mb();
 	HZDELAY(HZ / 5 + 1);
-	writeb(1, cs->hw.teles0.membase + 0x80);
+	writeb(1, cs->hw.teles0.membase + 0x80); mb();
 	HZDELAY(HZ / 5 + 1);
 	restore_flags(flags);
 	return(0);
@@ -286,10 +294,7 @@
 			return(request_irq(cs->irq, &teles0_interrupt,
 					I4L_IRQ_FLAG, "HiSax", cs));
 		case CARD_INIT:
-			clear_pending_isac_ints(cs);
-			clear_pending_hscx_ints(cs);
-			initisac(cs);
-			inithscx(cs);
+			inithscxisac(cs, 3);
 			return(0);
 		case CARD_TEST:
 			return(0);
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/teles3.c linux/drivers/isdn/hisax/teles3.c
--- v2.3.3/linux/drivers/isdn/hisax/teles3.c	Thu May 14 18:44:53 1998
+++ linux/drivers/isdn/hisax/teles3.c	Sun May 23 10:03:41 1999
@@ -1,4 +1,4 @@
-/* $Id: teles3.c,v 2.7 1998/02/02 13:29:48 keil Exp $
+/* $Id: teles3.c,v 2.10 1999/02/15 14:37:15 cpetig Exp $
 
  * teles3.c     low level stuff for Teles 16.3 & PNP isdn cards
  *
@@ -11,6 +11,15 @@
  *              Beat Doebeli
  *
  * $Log: teles3.c,v $
+ * Revision 2.10  1999/02/15 14:37:15  cpetig
+ * oops, missed something in last commit
+ *
+ * Revision 2.9  1999/02/15 14:11:02  cpetig
+ * fixed a bug with Teles PCMCIA, it doesn't have a config register
+ *
+ * Revision 2.8  1998/04/15 16:44:30  keil
+ * new init code
+ *
  * Revision 2.7  1998/02/02 13:29:48  keil
  * fast io
  *
@@ -69,7 +78,7 @@
 #include "isdnl1.h"
 
 extern const char *CardType[];
-const char *teles3_revision = "$Revision: 2.7 $";
+const char *teles3_revision = "$Revision: 2.10 $";
 
 #define byteout(addr,val) outb(val,addr)
 #define bytein(addr) inb(addr)
@@ -214,15 +223,14 @@
 release_io_teles3(struct IsdnCardState *cs)
 {
 	if (cs->typ == ISDN_CTYPE_TELESPCMCIA)
-		release_region(cs->hw.teles3.cfg_reg, 97);
+		release_region(cs->hw.teles3.hscx[0], 97);
 	else {
-		if (cs->hw.teles3.cfg_reg) {
+		if (cs->hw.teles3.cfg_reg)
 			if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
 				release_region(cs->hw.teles3.cfg_reg, 1);
 			} else {
 				release_region(cs->hw.teles3.cfg_reg, 8);
 			}
-		}
 		release_ioregs(cs, 0x7);
 	}
 }
@@ -305,10 +313,7 @@
 			return(request_irq(cs->irq, &teles3_interrupt,
 					I4L_IRQ_FLAG, "HiSax", cs));
 		case CARD_INIT:
-			clear_pending_isac_ints(cs);
-			clear_pending_hscx_ints(cs);
-			initisac(cs);
-			inithscx(cs);
+			inithscxisac(cs, 3);
 			return(0);
 		case CARD_TEST:
 			return(0);
@@ -342,7 +347,7 @@
 		cs->hw.teles3.hscx[0] = cs->hw.teles3.cfg_reg - 0xc20;
 		cs->hw.teles3.hscx[1] = cs->hw.teles3.cfg_reg - 0x820;
 	} else if (cs->typ == ISDN_CTYPE_TELESPCMCIA) {
-		cs->hw.teles3.cfg_reg = card->para[1];
+		cs->hw.teles3.cfg_reg = 0;
 		cs->hw.teles3.hscx[0] = card->para[1] - 0x20;
 		cs->hw.teles3.hscx[1] = card->para[1];
 		cs->hw.teles3.isac = card->para[1] + 0x20;
@@ -362,12 +367,12 @@
 	cs->hw.teles3.hscxfifo[0] = cs->hw.teles3.hscx[0] + 0x3e;
 	cs->hw.teles3.hscxfifo[1] = cs->hw.teles3.hscx[1] + 0x3e;
 	if (cs->typ == ISDN_CTYPE_TELESPCMCIA) {
-		if (check_region((cs->hw.teles3.cfg_reg), 97)) {
+		if (check_region((cs->hw.teles3.hscx[0]), 97)) {
 			printk(KERN_WARNING
 			       "HiSax: %s ports %x-%x already in use\n",
 			       CardType[cs->typ],
-			       cs->hw.teles3.cfg_reg,
-			       cs->hw.teles3.cfg_reg + 96);
+			       cs->hw.teles3.hscx[0],
+			       cs->hw.teles3.hscx[0] + 96);
 			return (0);
 		} else
 			request_region(cs->hw.teles3.hscx[0], 97, "HiSax Teles PCMCIA");
@@ -400,13 +405,12 @@
 			       CardType[cs->typ],
 			       cs->hw.teles3.isac + 32,
 			       cs->hw.teles3.isac + 64);
-			if (cs->hw.teles3.cfg_reg) {
+			if (cs->hw.teles3.cfg_reg)
 				if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
 					release_region(cs->hw.teles3.cfg_reg, 1);
 				} else {
 					release_region(cs->hw.teles3.cfg_reg, 8);
 				}
-			}
 			return (0);
 		} else
 			request_region(cs->hw.teles3.isac + 32, 32, "HiSax isac");
@@ -416,13 +420,12 @@
 			       CardType[cs->typ],
 			       cs->hw.teles3.hscx[0] + 32,
 			       cs->hw.teles3.hscx[0] + 64);
-			if (cs->hw.teles3.cfg_reg) {
+			if (cs->hw.teles3.cfg_reg)
 				if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
 					release_region(cs->hw.teles3.cfg_reg, 1);
 				} else {
 					release_region(cs->hw.teles3.cfg_reg, 8);
 				}
-			}
 			release_ioregs(cs, 1);
 			return (0);
 		} else
@@ -433,13 +436,12 @@
 			       CardType[cs->typ],
 			       cs->hw.teles3.hscx[1] + 32,
 			       cs->hw.teles3.hscx[1] + 64);
-			if (cs->hw.teles3.cfg_reg) {
+			if (cs->hw.teles3.cfg_reg)
 				if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
 					release_region(cs->hw.teles3.cfg_reg, 1);
 				} else {
 					release_region(cs->hw.teles3.cfg_reg, 8);
 				}
-			}
 			release_ioregs(cs, 3);
 			return (0);
 		} else
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/teles3c.c linux/drivers/isdn/hisax/teles3c.c
--- v2.3.3/linux/drivers/isdn/hisax/teles3c.c	Thu Nov  5 09:58:44 1998
+++ linux/drivers/isdn/hisax/teles3c.c	Sun May 23 10:03:41 1999
@@ -1,11 +1,14 @@
-/* $Id: teles3c.c,v 1.2 1998/02/02 13:27:07 keil Exp $
+/* $Id: teles3c.c,v 1.3 1998/11/15 23:55:27 keil Exp $
 
  * teles3c.c     low level stuff for teles 16.3c
  *
- * Author     Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author     Karsten Keil (keil@isdn4linux.de)
  *
  *
  * $Log: teles3c.c,v $
+ * Revision 1.3  1998/11/15 23:55:27  keil
+ * changes from 2.0
+ *
  * Revision 1.2  1998/02/02 13:27:07  keil
  * New
  *
@@ -20,14 +23,13 @@
 
 extern const char *CardType[];
 
-const char *teles163c_revision = "$Revision: 1.2 $";
+const char *teles163c_revision = "$Revision: 1.3 $";
 
 static void
 t163c_interrupt(int intno, void *dev_id, struct pt_regs *regs)
 {
 	struct IsdnCardState *cs = dev_id;
 	u_char val, stat;
-	char tmp[32];
 
 	if (!cs) {
 		printk(KERN_WARNING "teles3c: Spurious interrupt!\n");
@@ -36,16 +38,12 @@
 	if ((HFCD_ANYINT | HFCD_BUSY_NBUSY) & 
 		(stat = cs->BC_Read_Reg(cs, HFCD_DATA, HFCD_STAT))) {
 		val = cs->BC_Read_Reg(cs, HFCD_DATA, HFCD_INT_S1);
-		if (cs->debug & L1_DEB_ISAC) {
-			sprintf(tmp, "teles3c: stat(%02x) s1(%02x)", stat, val);
-			debugl1(cs, tmp);
-		}
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "teles3c: stat(%02x) s1(%02x)", stat, val);
 		hfc2bds0_interrupt(cs, val);
 	} else {
-		if (cs->debug & L1_DEB_ISAC) {
-			sprintf(tmp, "teles3c: irq_no_irq stat(%02x)", stat);
-			debugl1(cs, tmp);
-		}
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "teles3c: irq_no_irq stat(%02x)", stat);
 	}
 }
 
@@ -110,13 +108,9 @@
 t163c_card_msg(struct IsdnCardState *cs, int mt, void *arg)
 {
 	long flags;
-	char tmp[32];
 
-	if (cs->debug & L1_DEB_ISAC) {
-		
-		sprintf(tmp, "teles3c: card_msg %x", mt);
-		debugl1(cs, tmp);
-	}
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "teles3c: card_msg %x", mt);
 	switch (mt) {
 		case CARD_RESET:
 			reset_t163c(cs);
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/hisax/telespci.c linux/drivers/isdn/hisax/telespci.c
--- v2.3.3/linux/drivers/isdn/hisax/telespci.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/telespci.c	Sun May 23 10:03:41 1999
@@ -0,0 +1,371 @@
+/* $Id: telespci.c,v 2.5 1998/11/15 23:55:28 keil Exp $
+
+ * telespci.c     low level stuff for Teles PCI isdn cards
+ *
+ * Author       Ton van Rosmalen 
+ *              Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ *
+ * $Log: telespci.c,v $
+ * Revision 2.5  1998/11/15 23:55:28  keil
+ * changes from 2.0
+ *
+ * Revision 2.4  1998/10/05 09:38:08  keil
+ * Fix register addressing
+ *
+ * Revision 2.3  1998/05/25 12:58:26  keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
+ * Revision 2.1  1998/04/15 16:38:23  keil
+ * Add S0Box and Teles PCI support
+ *
+ *
+ */
+#define __NO_VERSION__
+#include <linux/config.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+
+extern const char *CardType[];
+
+const char *telespci_revision = "$Revision: 2.5 $";
+
+#define ZORAN_PO_RQ_PEN	0x02000000
+#define ZORAN_PO_WR	0x00800000
+#define ZORAN_PO_GID0	0x00000000
+#define ZORAN_PO_GID1	0x00100000
+#define ZORAN_PO_GREG0	0x00000000
+#define ZORAN_PO_GREG1	0x00010000
+#define ZORAN_PO_DMASK	0xFF
+
+#define WRITE_ADDR_ISAC	(ZORAN_PO_WR | ZORAN_PO_GID0 | ZORAN_PO_GREG0)
+#define READ_DATA_ISAC	(ZORAN_PO_GID0 | ZORAN_PO_GREG1)
+#define WRITE_DATA_ISAC	(ZORAN_PO_WR | ZORAN_PO_GID0 | ZORAN_PO_GREG1)
+#define WRITE_ADDR_HSCX	(ZORAN_PO_WR | ZORAN_PO_GID1 | ZORAN_PO_GREG0)
+#define READ_DATA_HSCX	(ZORAN_PO_GID1 | ZORAN_PO_GREG1)
+#define WRITE_DATA_HSCX	(ZORAN_PO_WR | ZORAN_PO_GID1 | ZORAN_PO_GREG1)
+
+#define ZORAN_WAIT_NOBUSY	do { \
+					portdata = readl(adr + 0x200); \
+				} while (portdata & ZORAN_PO_RQ_PEN)
+
+static inline u_char
+readisac(unsigned int adr, u_char off)
+{
+	register unsigned int portdata;
+
+	ZORAN_WAIT_NOBUSY;
+	
+	/* set address for ISAC */
+	writel(WRITE_ADDR_ISAC | off, adr + 0x200);
+	ZORAN_WAIT_NOBUSY;
+	
+	/* read data from ISAC */
+	writel(READ_DATA_ISAC, adr + 0x200);
+	ZORAN_WAIT_NOBUSY;
+	return((u_char)(portdata & ZORAN_PO_DMASK));
+}
+
+static inline void
+writeisac(unsigned int adr, u_char off, u_char data)
+{
+	register unsigned int portdata;
+
+	ZORAN_WAIT_NOBUSY;
+	
+	/* set address for ISAC */
+	writel(WRITE_ADDR_ISAC | off, adr + 0x200);
+	ZORAN_WAIT_NOBUSY;
+
+	/* write data to ISAC */
+	writel(WRITE_DATA_ISAC | data, adr + 0x200);
+	ZORAN_WAIT_NOBUSY;
+}
+
+static inline u_char
+readhscx(unsigned int adr, int hscx, u_char off)
+{
+	register unsigned int portdata;
+
+	ZORAN_WAIT_NOBUSY;
+	/* set address for HSCX */
+	writel(WRITE_ADDR_HSCX | ((hscx ? 0x40:0) + off), adr + 0x200);
+	ZORAN_WAIT_NOBUSY;
+	
+	/* read data from HSCX */
+	writel(READ_DATA_HSCX, adr + 0x200);
+	ZORAN_WAIT_NOBUSY;
+	return ((u_char)(portdata & ZORAN_PO_DMASK));
+}
+
+static inline void
+writehscx(unsigned int adr, int hscx, u_char off, u_char data)
+{
+	register unsigned int portdata;
+
+	ZORAN_WAIT_NOBUSY;
+	/* set address for HSCX */
+	writel(WRITE_ADDR_HSCX | ((hscx ? 0x40:0) + off), adr + 0x200);
+	ZORAN_WAIT_NOBUSY;
+
+	/* write data to HSCX */
+	writel(WRITE_DATA_HSCX | data, adr + 0x200);
+	ZORAN_WAIT_NOBUSY;
+}
+
+static inline void
+read_fifo_isac(unsigned int adr, u_char * data, int size)
+{
+	register unsigned int portdata;
+	register int i;
+
+	ZORAN_WAIT_NOBUSY;
+	/* read data from ISAC */
+	for (i = 0; i < size; i++) {
+		/* set address for ISAC fifo */
+		writel(WRITE_ADDR_ISAC | 0x1E, adr + 0x200);
+		ZORAN_WAIT_NOBUSY;
+		writel(READ_DATA_ISAC, adr + 0x200);
+		ZORAN_WAIT_NOBUSY;
+		data[i] = (u_char)(portdata & ZORAN_PO_DMASK);
+	}
+}
+
+static void
+write_fifo_isac(unsigned int adr, u_char * data, int size)
+{
+	register unsigned int portdata;
+	register int i;
+
+	ZORAN_WAIT_NOBUSY;
+	/* write data to ISAC */
+	for (i = 0; i < size; i++) {
+		/* set address for ISAC fifo */
+		writel(WRITE_ADDR_ISAC | 0x1E, adr + 0x200);
+		ZORAN_WAIT_NOBUSY;
+		writel(WRITE_DATA_ISAC | data[i], adr + 0x200);
+		ZORAN_WAIT_NOBUSY;
+	}
+}
+
+static inline void
+read_fifo_hscx(unsigned int adr, int hscx, u_char * data, int size)
+{
+	register unsigned int portdata;
+	register int i;
+
+	ZORAN_WAIT_NOBUSY;
+	/* read data from HSCX */
+	for (i = 0; i < size; i++) {
+		/* set address for HSCX fifo */
+		writel(WRITE_ADDR_HSCX |(hscx ? 0x5F:0x1F), adr + 0x200);
+		ZORAN_WAIT_NOBUSY;
+		writel(READ_DATA_HSCX, adr + 0x200);
+		ZORAN_WAIT_NOBUSY;
+		data[i] = (u_char) (portdata & ZORAN_PO_DMASK);
+	}
+}
+
+static inline void
+write_fifo_hscx(unsigned int adr, int hscx, u_char * data, int size)
+{
+	unsigned int portdata;
+	register int i;
+
+	ZORAN_WAIT_NOBUSY;
+	/* write data to HSCX */
+	for (i = 0; i < size; i++) {
+		/* set address for HSCX fifo */
+		writel(WRITE_ADDR_HSCX |(hscx ? 0x5F:0x1F), adr + 0x200);
+		ZORAN_WAIT_NOBUSY;
+		writel(WRITE_DATA_HSCX | data[i], adr + 0x200);
+		ZORAN_WAIT_NOBUSY;
+		udelay(10);
+	}
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readisac(cs->hw.teles0.membase, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writeisac(cs->hw.teles0.membase, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	read_fifo_isac(cs->hw.teles0.membase, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	write_fifo_isac(cs->hw.teles0.membase, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return (readhscx(cs->hw.teles0.membase, hscx, offset));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	writehscx(cs->hw.teles0.membase, hscx, offset, value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readhscx(cs->hw.teles0.membase, nr, reg)
+#define WRITEHSCX(cs, nr, reg, data) writehscx(cs->hw.teles0.membase, nr, reg, data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo_hscx(cs->hw.teles0.membase, nr, ptr, cnt)
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo_hscx(cs->hw.teles0.membase, nr, ptr, cnt)
+
+#include "hscx_irq.c"
+
+static void
+telespci_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+#define MAXCOUNT 20
+	struct IsdnCardState *cs = dev_id;
+	u_char val, stat = 0;
+
+	if (!cs) {
+		printk(KERN_WARNING "TelesPCI: Spurious interrupt!\n");
+		return;
+	}
+	val = readhscx(cs->hw.teles0.membase, 1, HSCX_ISTA);
+	if (val) {
+		hscx_int_main(cs, val);
+		stat |= 1;
+	}
+	val = readisac(cs->hw.teles0.membase, ISAC_ISTA);
+	if (val) {
+		isac_interrupt(cs, val);
+		stat |= 2;
+	}
+	/* Clear interrupt register for Zoran PCI controller */
+	writel(0x70000000, cs->hw.teles0.membase + 0x3C);
+
+	if (stat & 1) {
+		writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0xFF);
+		writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0xFF);
+		writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0x0);
+		writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0x0);
+	}
+	if (stat & 2) {
+		writeisac(cs->hw.teles0.membase, ISAC_MASK, 0xFF);
+		writeisac(cs->hw.teles0.membase, ISAC_MASK, 0x0);
+	}
+}
+
+void
+release_io_telespci(struct IsdnCardState *cs)
+{
+	iounmap((void *)cs->hw.teles0.membase);
+}
+
+static int
+TelesPCI_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	switch (mt) {
+		case CARD_RESET:
+			return(0);
+		case CARD_RELEASE:
+			release_io_telespci(cs);
+			return(0);
+		case CARD_SETIRQ:
+			return(request_irq(cs->irq, &telespci_interrupt,
+					I4L_IRQ_FLAG | SA_SHIRQ, "HiSax", cs));
+		case CARD_INIT:
+			inithscxisac(cs, 3);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+static 	struct pci_dev *dev_tel __initdata = NULL;
+
+__initfunc(int
+setup_telespci(struct IsdnCard *card))
+{
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, telespci_revision);
+	printk(KERN_INFO "HiSax: Teles/PCI driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_TELESPCI)
+		return (0);
+
+#if CONFIG_PCI
+	if (!pci_present()) {
+		printk(KERN_ERR "TelesPCI: no PCI bus present\n");
+		return(0);
+	}
+	if ((dev_tel = pci_find_device (0x11DE, 0x6120, dev_tel))) {
+		cs->irq = dev_tel->irq;
+		if (!cs->irq) {
+			printk(KERN_WARNING "Teles: No IRQ for PCI card found\n");
+			return(0);
+		}
+		cs->hw.teles0.membase = (u_int) ioremap(dev_tel->base_address[0],
+			PAGE_SIZE);
+		printk(KERN_INFO "Found: Zoran, base-address: 0x%lx, irq: 0x%x\n",
+			dev_tel->base_address[0], dev_tel->irq);
+	} else {
+		printk(KERN_WARNING "TelesPCI: No PCI card found\n");
+		return(0);
+	}
+#else
+	printk(KERN_WARNING "HiSax: Teles/PCI and NO_PCI_BIOS\n");
+	printk(KERN_WARNING "HiSax: Teles/PCI unable to config\n");
+	return (0);
+#endif /* CONFIG_PCI */
+
+	/* Initialize Zoran PCI controller */
+	writel(0x00000000, cs->hw.teles0.membase + 0x28);
+	writel(0x01000000, cs->hw.teles0.membase + 0x28);
+	writel(0x01000000, cs->hw.teles0.membase + 0x28);
+	writel(0x7BFFFFFF, cs->hw.teles0.membase + 0x2C);
+	writel(0x70000000, cs->hw.teles0.membase + 0x3C);
+	writel(0x61000000, cs->hw.teles0.membase + 0x40);
+	/* writel(0x00800000, cs->hw.teles0.membase + 0x200); */
+
+	printk(KERN_INFO
+	       "HiSax: %s config irq:%d mem:%x\n",
+	       CardType[cs->typ], cs->irq,
+	       cs->hw.teles0.membase);
+
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &TelesPCI_card_msg;
+	ISACVersion(cs, "TelesPCI:");
+	if (HscxVersion(cs, "TelesPCI:")) {
+		printk(KERN_WARNING
+		 "TelesPCI: wrong HSCX versions check IO/MEM addresses\n");
+		release_io_telespci(cs);
+		return (0);
+	}
+	return (1);
+}
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/icn/icn.c linux/drivers/isdn/icn/icn.c
--- v2.3.3/linux/drivers/isdn/icn/icn.c	Thu Nov  5 09:58:44 1998
+++ linux/drivers/isdn/icn/icn.c	Sun May 23 10:03:41 1999
@@ -1,4 +1,4 @@
-/* $Id: icn.c,v 1.49 1998/02/13 11:14:15 keil Exp $
+/* $Id: icn.c,v 1.56 1999/04/12 13:15:07 fritz Exp $
 
  * ISDN low-level module for the ICN active ISDN-Card.
  *
@@ -19,6 +19,30 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: icn.c,v $
+ * Revision 1.56  1999/04/12 13:15:07  fritz
+ * Fixed a cast.
+ *
+ * Revision 1.55  1999/04/12 12:34:02  fritz
+ * Changes from 2.0 tree.
+ *
+ * Revision 1.54  1999/01/05 18:29:39  he
+ * merged remaining schedule_timeout() changes from 2.1.127
+ *
+ * Revision 1.53  1998/06/17 19:51:28  he
+ * merged with 2.1.10[34] (cosmetics and udelay() -> mdelay())
+ * brute force fix to avoid Ugh's in isdn_tty_write()
+ * cleaned up some dead code
+ *
+ * Revision 1.52  1998/05/20 19:29:58  tsbogend
+ * fixed bug introduced by changes for new BSENT callback
+ *
+ * Revision 1.51  1998/03/07 22:29:55  fritz
+ * Adapted Detlef's chenges for 2.1.
+ *
+ * Revision 1.50  1998/03/07 17:41:54  detabc
+ * add d-channel connect and disconnect support statcallback
+ * from icn low-level to link->level
+ *
  * Revision 1.49  1998/02/13 11:14:15  keil
  * change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
  *
@@ -209,7 +233,7 @@
 #undef MAP_DEBUG
 
 static char
-*revision = "$Revision: 1.49 $";
+*revision = "$Revision: 1.56 $";
 
 static int icn_addcard(int, char *, char *);
 
@@ -232,10 +256,10 @@
 	cli();
 	card->xlen[channel] = 0;
 	card->sndcount[channel] = 0;
-	if (card->xskb[channel]) {
+	if ((skb = card->xskb[channel])) {
 		card->xskb[channel] = NULL;
 		restore_flags(flags);
-		dev_kfree_skb(card->xskb[channel]);
+		dev_kfree_skb(skb);
 	} else
 		restore_flags(flags);
 }
@@ -529,6 +553,11 @@
 					cmd.parm.length = card->xlen[channel];
 					card->interface.statcallb(&cmd);
 				}
+			} else {
+				save_flags(flags);
+				cli();
+				card->xskb[channel] = skb;
+				restore_flags(flags);
 			}
 			card->xmit_lock[channel] = 0;
 			if (!icn_trymaplock_channel(card, mch))
@@ -580,8 +609,11 @@
 {
 	{"BCON_",          ISDN_STAT_BCONN, 1},	/* B-Channel connected        */
 	{"BDIS_",          ISDN_STAT_BHUP,  2},	/* B-Channel disconnected     */
-	{"DCON_",          ISDN_STAT_DCONN, 0},	/* D-Channel connected        */
-	{"DDIS_",          ISDN_STAT_DHUP,  0},	/* D-Channel disconnected     */
+	/*
+	** add d-channel connect and disconnect support to link-level
+	*/
+	{"DCON_",          ISDN_STAT_DCONN, 10},	/* D-Channel connected        */
+	{"DDIS_",          ISDN_STAT_DHUP,  11},	/* D-Channel disconnected     */
 	{"DCAL_I",         ISDN_STAT_ICALL, 3},	/* Incoming call dialup-line  */
 	{"DSCA_I",         ISDN_STAT_ICALL, 3},	/* Incoming call 1TR6-SPV     */
 	{"FCALL",          ISDN_STAT_ICALL, 4},	/* Leased line connection up  */
@@ -630,7 +662,33 @@
 	cmd.driver = card->myid;
 	cmd.arg = channel;
 	switch (action) {
+	case 11:
+			save_flags(flags);
+			cli();
+			icn_free_queue(card,channel);
+			card->rcvidx[channel] = 0;
+
+			if (card->flags & 
+			    ((channel)?ICN_FLAGS_B2ACTIVE:ICN_FLAGS_B1ACTIVE)) {
+				
+				isdn_ctrl ncmd;
+				
+				card->flags &= ~((channel)?
+						 ICN_FLAGS_B2ACTIVE:ICN_FLAGS_B1ACTIVE);
+				
+				memset(&ncmd, 0, sizeof(ncmd));
+				
+				ncmd.driver = card->myid;
+				ncmd.arg = channel;
+				ncmd.command = ISDN_STAT_BHUP;
+				restore_flags(flags);
+				card->interface.statcallb(&cmd);
+			} else
+				restore_flags(flags);
+			
+			break;
 		case 1:
+			icn_free_queue(card,channel);
 			card->flags |= (channel) ?
 			    ICN_FLAGS_B2ACTIVE : ICN_FLAGS_B1ACTIVE;
 			break;
@@ -1539,7 +1597,7 @@
 						c->parm.num[0] ? "N" : "ALL", c->parm.num);
 				} else
 					sprintf(cbuf, "%02d;EAZ%s\n", (int) a,
-						c->parm.num[0] ? c->parm.num : "0123456789");
+						c->parm.num[0] ? (char *)(c->parm.num) : "0123456789");
 				i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
 			}
 			break;
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/icn/icn.h linux/drivers/isdn/icn/icn.h
--- v2.3.3/linux/drivers/isdn/icn/icn.h	Thu Jan  7 08:46:59 1999
+++ linux/drivers/isdn/icn/icn.h	Sun May 23 10:03:41 1999
@@ -318,9 +318,9 @@
 #ifdef MODULE
 MODULE_AUTHOR("Fritz Elfert");
 MODULE_PARM(portbase, "i");
-MODULE_PARM_DESC(portbase, "Port address of first card");
+MODULE_PARM_DESC(portbase, "Port adress of first card");
 MODULE_PARM(membase, "i");
-MODULE_PARM_DESC(membase, "Shared memory address of all cards");
+MODULE_PARM_DESC(membase, "Shared memory adress of all cards");
 MODULE_PARM(icn_id, "s");
 MODULE_PARM_DESC(icn_id, "ID-String of first card");
 MODULE_PARM(icn_id2, "s");
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/isdn_audio.c linux/drivers/isdn/isdn_audio.c
--- v2.3.3/linux/drivers/isdn/isdn_audio.c	Wed Apr  1 16:21:03 1998
+++ linux/drivers/isdn/isdn_audio.c	Sun May 23 10:03:42 1999
@@ -1,9 +1,10 @@
-/* $Id: isdn_audio.c,v 1.10 1998/02/20 17:09:40 fritz Exp $
+/* $Id: isdn_audio.c,v 1.13 1999/04/12 12:33:09 fritz Exp $
 
  * Linux ISDN subsystem, audio conversion and compression (linklevel).
  *
- * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1994-1999 by Fritz Elfert (fritz@isdn4linux.de)
  * DTMF code (c) 1996 by Christian Mock (cm@kukuruz.ping.at)
+ * Silence detection (c) 1998 by Armin Schindler (mac@gismo.telekom.de)
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -20,6 +21,15 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_audio.c,v $
+ * Revision 1.13  1999/04/12 12:33:09  fritz
+ * Changes from 2.0 tree.
+ *
+ * Revision 1.12  1998/07/26 18:48:43  armin
+ * Added silence detection in voice receive mode.
+ *
+ * Revision 1.11  1998/04/10 10:35:10  paul
+ * fixed (silly?) warnings from egcs on Alpha.
+ *
  * Revision 1.10  1998/02/20 17:09:40  fritz
  * Changes for recent kernels.
  *
@@ -61,7 +71,7 @@
 #include "isdn_audio.h"
 #include "isdn_common.h"
 
-char *isdn_audio_revision = "$Revision: 1.10 $";
+char *isdn_audio_revision = "$Revision: 1.13 $";
 
 /*
  * Misc. lookup-tables.
@@ -276,7 +286,7 @@
 isdn_audio_tlookup(const char *table, char *buff, unsigned long n)
 {
 	while (n--)
-		*buff++ = table[*buff];
+		*buff++ = table[*(unsigned char *)buff];
 }
 #endif
 
@@ -659,4 +669,93 @@
 		}
 		len -= c;
 	}
+}
+
+silence_state *
+isdn_audio_silence_init(silence_state * s)
+{
+	if (!s)
+		s = (silence_state *) kmalloc(sizeof(silence_state), GFP_ATOMIC);
+	if (s) {
+		s->idx = 0;
+		s->state = 0;
+	}
+	return s;
+}
+
+void
+isdn_audio_calc_silence(modem_info * info, unsigned char *buf, int len, int fmt)
+{
+	silence_state *s = info->silence_state;
+	int i;
+	signed char c;
+
+	if (!info->emu.vpar[1]) return;
+
+	for (i = 0; i < len; i++) {
+		if (fmt)
+		    c = isdn_audio_alaw_to_ulaw[*buf++];
+			else
+		    c = *buf++;
+
+		if (c > 0) c -= 128;
+		c = abs(c);
+
+		if (c > (info->emu.vpar[1] * 4)) { 
+			s->idx = 0;
+			s->state = 1; 
+		} else {
+			if (s->idx < 210000) s->idx++; 
+		}
+	}
+}
+
+void
+isdn_audio_eval_silence(modem_info * info)
+{
+	silence_state *s = info->silence_state;
+	struct sk_buff *skb;
+	unsigned long flags;
+	int di;
+	int ch;
+	char what;
+	char *p;
+
+	what = ' ';
+
+	if (s->idx > (info->emu.vpar[2] * 800)) { 
+		s->idx = 0;
+		if (!s->state) {	/* silence from beginning of rec */ 
+			what = 's';
+		} else {
+			what = 'q';
+		}
+	}
+		if ((what == 's') || (what == 'q')) {
+			printk(KERN_DEBUG "ttyI%d: %s\n", info->line,
+				(what=='s') ? "silence":"quiet");
+			skb = dev_alloc_skb(2);
+			p = (char *) skb_put(skb, 2);
+			p[0] = 0x10;
+			p[1] = what;
+			if (skb_headroom(skb) < sizeof(isdn_audio_skb)) {
+				printk(KERN_WARNING
+				       "isdn_audio: insufficient skb_headroom, dropping\n");
+				kfree_skb(skb);
+				return;
+			}
+			ISDN_AUDIO_SKB_DLECOUNT(skb) = 0;
+			ISDN_AUDIO_SKB_LOCK(skb) = 0;
+			save_flags(flags);
+			cli();
+			di = info->isdn_driver;
+			ch = info->isdn_channel;
+			__skb_queue_tail(&dev->drv[di]->rpqueue[ch], skb);
+			dev->drv[di]->rcvcount[ch] += 2;
+			restore_flags(flags);
+			/* Schedule dequeuing */
+			if ((dev->modempoll) && (info->rcvsched))
+				isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
+			wake_up_interruptible(&dev->drv[di]->rcv_waitq[ch]);
+		} 
 }
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/isdn_audio.h linux/drivers/isdn/isdn_audio.h
--- v2.3.3/linux/drivers/isdn/isdn_audio.h	Thu Feb 27 10:57:29 1997
+++ linux/drivers/isdn/isdn_audio.h	Sun May 23 10:03:42 1999
@@ -1,8 +1,8 @@
-/* $Id: isdn_audio.h,v 1.5 1997/02/03 22:45:21 fritz Exp $
+/* $Id: isdn_audio.h,v 1.7 1999/04/12 12:33:11 fritz Exp $
 
  * Linux ISDN subsystem, audio conversion and compression (linklevel).
  *
- * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1994-1999 by Fritz Elfert (fritz@isdn4linux.de)
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -19,6 +19,12 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_audio.h,v $
+ * Revision 1.7  1999/04/12 12:33:11  fritz
+ * Changes from 2.0 tree.
+ *
+ * Revision 1.6  1998/07/26 18:48:44  armin
+ * Added silence detection in voice receive mode.
+ *
  * Revision 1.5  1997/02/03 22:45:21  fritz
  * Reformatted according CodingStyle
  *
@@ -51,6 +57,11 @@
 	int buf[DTMF_NPOINTS];
 } dtmf_state;
 
+typedef struct silence_state {
+	int state;
+	unsigned int idx;
+} silence_state;
+
 extern void isdn_audio_ulaw2alaw(unsigned char *, unsigned long);
 extern void isdn_audio_alaw2ulaw(unsigned char *, unsigned long);
 extern adpcm_state *isdn_audio_adpcm_init(adpcm_state *, int);
@@ -60,3 +71,6 @@
 extern void isdn_audio_calc_dtmf(modem_info *, unsigned char *, int, int);
 extern void isdn_audio_eval_dtmf(modem_info *);
 dtmf_state *isdn_audio_dtmf_init(dtmf_state *);
+extern void isdn_audio_calc_silence(modem_info *, unsigned char *, int, int);
+extern void isdn_audio_eval_silence(modem_info *);
+silence_state *isdn_audio_silence_init(silence_state *);
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/isdn_bsdcomp.c linux/drivers/isdn/isdn_bsdcomp.c
--- v2.3.3/linux/drivers/isdn/isdn_bsdcomp.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/isdn_bsdcomp.c	Sun May 23 10:03:42 1999
@@ -0,0 +1,934 @@
+/*
+ * BSD compression module
+ *
+ * Patched version for ISDN syncPPP written 1997/1998 by Michael Hipp
+ * The whole module is now SKB based.
+ *
+ * Compile with:
+ *  gcc -O2 -I/usr/src/linux/include -D__KERNEL__ -DMODULE -c isdn_bsdcomp.c
+ */
+
+/*
+ * Original copyright notice:
+ *
+ * Copyright (c) 1985, 1986 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * James A. Woods, derived from original work by Spencer Thomas
+ * and Joseph Orost.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef MODULE
+#error This file must be compiled as a module.
+#endif
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/sched.h>	/* to get the struct task_struct */
+#include <linux/string.h>	/* used in new tty drivers */
+#include <linux/signal.h>	/* used in new tty drivers */
+
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/segment.h>
+#include <asm/byteorder.h>
+#include <asm/types.h>
+
+#include <linux/if.h>
+
+#include <linux/if_ether.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/inet.h>
+#include <linux/ioctl.h>
+
+#include <linux/ppp_defs.h>
+
+#include <linux/isdn.h>
+#include <linux/isdn_ppp.h>
+/* #include <linux/netprotocol.h> */
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/if_arp.h>
+#include <linux/ppp-comp.h>
+
+#include "isdn_ppp.h"
+
+#define BSD_VERSION(x)	((x) >> 5)
+#define BSD_NBITS(x)	((x) & 0x1F)
+
+#define BSD_CURRENT_VERSION	1
+
+#define DEBUG 1
+
+/*
+ * A dictionary for doing BSD compress.
+ */
+
+struct bsd_dict {
+	u32 fcode;
+	u16 codem1;		/* output of hash table -1 */
+	u16 cptr;		/* map code to hash table entry */
+};
+
+struct bsd_db {
+	int            totlen;		/* length of this structure */
+	unsigned int   hsize;		/* size of the hash table */
+	unsigned char  hshift;		/* used in hash function */
+	unsigned char  n_bits;		/* current bits/code */
+	unsigned char  maxbits;		/* maximum bits/code */
+	unsigned char  debug;		/* non-zero if debug desired */
+	unsigned char  unit;		/* ppp unit number */
+	u16 seqno;          		/* sequence # of next packet */
+	unsigned int   mru;		/* size of receive (decompress) bufr */
+	unsigned int   maxmaxcode;	/* largest valid code */
+	unsigned int   max_ent;		/* largest code in use */
+	unsigned int   in_count;	/* uncompressed bytes, aged */
+	unsigned int   bytes_out;	/* compressed bytes, aged */
+	unsigned int   ratio;		/* recent compression ratio */
+	unsigned int   checkpoint;	/* when to next check the ratio */
+	unsigned int   clear_count;	/* times dictionary cleared */
+	unsigned int   incomp_count;	/* incompressible packets */
+	unsigned int   incomp_bytes;	/* incompressible bytes */
+	unsigned int   uncomp_count;	/* uncompressed packets */
+	unsigned int   uncomp_bytes;	/* uncompressed bytes */
+	unsigned int   comp_count;	/* compressed packets */
+	unsigned int   comp_bytes;	/* compressed bytes */
+	unsigned short  *lens;		/* array of lengths of codes */
+	struct bsd_dict *dict;		/* dictionary */
+	int xmit;
+};
+
+#define BSD_OVHD	2		/* BSD compress overhead/packet */
+#define MIN_BSD_BITS	9
+#define BSD_INIT_BITS	MIN_BSD_BITS
+#define MAX_BSD_BITS	15
+
+/*
+ * the next two codes should not be changed lightly, as they must not
+ * lie within the contiguous general code space.
+ */
+#define CLEAR	256			/* table clear output code */
+#define FIRST	257			/* first free entry */
+#define LAST	255
+
+#define MAXCODE(b)	((1 << (b)) - 1)
+#define BADCODEM1	MAXCODE(MAX_BSD_BITS);
+
+#define BSD_HASH(prefix,suffix,hshift) ((((unsigned long)(suffix))<<(hshift)) \
+					 ^ (unsigned long)(prefix))
+#define BSD_KEY(prefix,suffix)		((((unsigned long)(suffix)) << 16) \
+					 + (unsigned long)(prefix))
+
+#define CHECK_GAP	10000		/* Ratio check interval */
+
+#define RATIO_SCALE_LOG	8
+#define RATIO_SCALE	(1<<RATIO_SCALE_LOG)
+#define RATIO_MAX	(0x7fffffff>>RATIO_SCALE_LOG)
+
+/*
+ * clear the dictionary
+ */
+
+static void bsd_clear(struct bsd_db *db)
+{
+	db->clear_count++;
+	db->max_ent      = FIRST-1;
+	db->n_bits       = BSD_INIT_BITS;
+	db->bytes_out    = 0;
+	db->in_count     = 0;
+	db->incomp_count = 0;
+	db->ratio	     = 0;
+	db->checkpoint   = CHECK_GAP;
+}
+
+/*
+ * If the dictionary is full, then see if it is time to reset it.
+ *
+ * Compute the compression ratio using fixed-point arithmetic
+ * with 8 fractional bits.
+ *
+ * Since we have an infinite stream instead of a single file,
+ * watch only the local compression ratio.
+ *
+ * Since both peers must reset the dictionary at the same time even in
+ * the absence of CLEAR codes (while packets are incompressible), they
+ * must compute the same ratio.
+ */
+static int bsd_check (struct bsd_db *db)	/* 1=output CLEAR */
+{
+    unsigned int new_ratio;
+
+    if (db->in_count >= db->checkpoint)
+      {
+	/* age the ratio by limiting the size of the counts */
+	if (db->in_count >= RATIO_MAX || db->bytes_out >= RATIO_MAX)
+	  {
+	    db->in_count  -= (db->in_count  >> 2);
+	    db->bytes_out -= (db->bytes_out >> 2);
+	  }
+	
+	db->checkpoint = db->in_count + CHECK_GAP;
+	
+	if (db->max_ent >= db->maxmaxcode)
+	  {
+	    /* Reset the dictionary only if the ratio is worse,
+	     * or if it looks as if it has been poisoned
+	     * by incompressible data.
+	     *
+	     * This does not overflow, because
+	     *	db->in_count <= RATIO_MAX.
+	     */
+
+	    new_ratio = db->in_count << RATIO_SCALE_LOG;
+	    if (db->bytes_out != 0)
+	      {
+		new_ratio /= db->bytes_out;
+	      }
+	    
+	    if (new_ratio < db->ratio || new_ratio < 1 * RATIO_SCALE)
+	      {
+		bsd_clear (db);
+		return 1;
+	      }
+	    db->ratio = new_ratio;
+	  }
+      }
+    return 0;
+}
+
+/*
+ * Return statistics.
+ */
+
+static void bsd_stats (void *state, struct compstat *stats)
+{
+	struct bsd_db *db = (struct bsd_db *) state;
+    
+	stats->unc_bytes    = db->uncomp_bytes;
+	stats->unc_packets  = db->uncomp_count;
+	stats->comp_bytes   = db->comp_bytes;
+	stats->comp_packets = db->comp_count;
+	stats->inc_bytes    = db->incomp_bytes;
+	stats->inc_packets  = db->incomp_count;
+	stats->in_count     = db->in_count;
+	stats->bytes_out    = db->bytes_out;
+}
+
+/*
+ * Reset state, as on a CCP ResetReq.
+ */
+static void bsd_reset (void *state,unsigned char code, unsigned char id,
+			unsigned char *data, unsigned len,
+			struct isdn_ppp_resetparams *rsparm)
+{
+	struct bsd_db *db = (struct bsd_db *) state;
+
+	bsd_clear(db);
+	db->seqno       = 0;
+	db->clear_count = 0;
+}
+
+/*
+ * Release the compression structure
+ */
+static void bsd_free (void *state)
+{
+	struct bsd_db *db = (struct bsd_db *) state;
+
+	if (db) {
+		/*
+		 * Release the dictionary
+		 */
+		if (db->dict) {
+			vfree (db->dict);
+			db->dict = NULL;
+		}
+
+		/*
+		 * Release the string buffer
+		 */
+		if (db->lens) {
+			vfree (db->lens);
+			db->lens = NULL;
+		}
+
+		/*
+		 * Finally release the structure itself.
+		 */
+		kfree (db);
+		MOD_DEC_USE_COUNT;
+	}
+}
+
+
+/*
+ * Allocate space for a (de) compressor.
+ */
+static void *bsd_alloc (struct isdn_ppp_comp_data *data)
+{
+	int bits;
+	unsigned int hsize, hshift, maxmaxcode;
+	struct bsd_db *db;
+	int decomp;
+
+	static unsigned int htab[][2] = {
+		{ 5003 , 4 } , { 5003 , 4 } , { 5003 , 4 } , { 5003 , 4 } , 
+		{ 9001 , 5 } , { 18013 , 6 } , { 35023 , 7 } , { 69001 , 8 } 
+	};
+		
+	if (data->optlen != 1 || data->num != CI_BSD_COMPRESS
+		|| BSD_VERSION(data->options[0]) != BSD_CURRENT_VERSION)
+		return NULL;
+
+	bits = BSD_NBITS(data->options[0]);
+
+	if(bits < 9 || bits > 15)
+		return NULL;
+
+	hsize = htab[bits-9][0];
+	hshift = htab[bits-9][1];
+	
+	/*
+	 * Allocate the main control structure for this instance.
+	 */
+	maxmaxcode = MAXCODE(bits);
+	db = (struct bsd_db *) kmalloc (sizeof (struct bsd_db),GFP_KERNEL);
+	if (!db)
+		return NULL;
+
+	memset (db, 0, sizeof(struct bsd_db));
+
+	db->xmit = data->flags & IPPP_COMP_FLAG_XMIT;
+	decomp = db->xmit ? 0 : 1;
+
+	/*
+	 * Allocate space for the dictionary. This may be more than one page in
+	 * length.
+	 */
+	db->dict = (struct bsd_dict *) vmalloc (hsize * sizeof (struct bsd_dict));
+	if (!db->dict) {
+		bsd_free (db);
+		return NULL;
+	}
+
+	MOD_INC_USE_COUNT;
+
+	/*
+	 * If this is the compression buffer then there is no length data.
+	 * For decompression, the length information is needed as well.
+	 */
+	if (!decomp)
+		db->lens = NULL;
+	else {
+		db->lens = (unsigned short *) vmalloc ((maxmaxcode + 1) *
+			sizeof (db->lens[0]));
+		if (!db->lens) {
+			bsd_free (db); /* calls MOD_DEC_USE_COUNT; */
+			return (NULL);
+		}
+	}
+
+	/*
+	 * Initialize the data information for the compression code
+	 */
+	db->totlen     = sizeof (struct bsd_db) + (sizeof (struct bsd_dict) * hsize);
+	db->hsize      = hsize;
+	db->hshift     = hshift;
+	db->maxmaxcode = maxmaxcode;
+	db->maxbits    = bits;
+
+	return (void *) db;
+}
+
+/*
+ * Initialize the database.
+ */
+static int bsd_init (void *state, struct isdn_ppp_comp_data *data, int unit, int debug)
+{
+	struct bsd_db *db = state;
+	int indx;
+	int decomp;
+
+	if(!state || !data) {
+		printk(KERN_ERR "isdn_bsd_init: [%d] ERR, state %lx data %lx\n",unit,(long)state,(long)data);
+		return 0;
+	}
+
+	decomp = db->xmit ? 0 : 1;
+    
+	if (data->optlen != 1 || data->num != CI_BSD_COMPRESS
+		|| (BSD_VERSION(data->options[0]) != BSD_CURRENT_VERSION)
+		|| (BSD_NBITS(data->options[0]) != db->maxbits)
+		|| (decomp && db->lens == NULL)) {
+		printk(KERN_ERR "isdn_bsd: %d %d %d %d %lx\n",data->optlen,data->num,data->options[0],decomp,(unsigned long)db->lens);
+		return 0;
+	}
+
+	if (decomp)
+		for(indx=LAST;indx>=0;indx--)
+			db->lens[indx] = 1;
+
+	indx = db->hsize;
+	while (indx-- != 0) {
+		db->dict[indx].codem1 = BADCODEM1;
+		db->dict[indx].cptr   = 0;
+	}
+
+	db->unit = unit;
+	db->mru  = 0;
+
+	db->debug = 1;
+    
+	bsd_reset(db,0,0,NULL,0,NULL);
+    
+	return 1;
+}
+
+/*
+ * Obtain pointers to the various structures in the compression tables
+ */
+
+#define dict_ptrx(p,idx) &(p->dict[idx])
+#define lens_ptrx(p,idx) &(p->lens[idx])
+
+#ifdef DEBUG
+static unsigned short *lens_ptr(struct bsd_db *db, int idx)
+{
+	if ((unsigned int) idx > (unsigned int) db->maxmaxcode) {
+		printk (KERN_DEBUG "<9>ppp: lens_ptr(%d) > max\n", idx);
+		idx = 0;
+	}
+	return lens_ptrx (db, idx);
+}
+
+static struct bsd_dict *dict_ptr(struct bsd_db *db, int idx)
+{
+	if ((unsigned int) idx >= (unsigned int) db->hsize) {
+		printk (KERN_DEBUG "<9>ppp: dict_ptr(%d) > max\n", idx);
+		idx = 0;
+	}
+	return dict_ptrx (db, idx);
+}
+
+#else
+#define lens_ptr(db,idx) lens_ptrx(db,idx)
+#define dict_ptr(db,idx) dict_ptrx(db,idx)
+#endif
+
+/*
+ * compress a packet
+ */
+static int bsd_compress (void *state, struct sk_buff *skb_in, struct sk_buff *skb_out,int proto)
+{
+	struct bsd_db *db;
+	int hshift;
+	unsigned int max_ent;
+	unsigned int n_bits;
+	unsigned int bitno;
+	unsigned long accm;
+	int ent;
+	unsigned long fcode;
+	struct bsd_dict *dictp;
+	unsigned char c;
+	int hval,disp,ilen,mxcode;
+	unsigned char *rptr = skb_in->data;
+	int isize = skb_in->len;
+
+#define OUTPUT(ent)			\
+  {					\
+    bitno -= n_bits;			\
+    accm |= ((ent) << bitno);		\
+    do	{				\
+        if(skb_out && skb_tailroom(skb_out) > 0) 	\
+      		*(skb_put(skb_out,1)) = (unsigned char) (accm>>24); \
+	accm <<= 8;			\
+	bitno += 8;			\
+    } while (bitno <= 24);		\
+  }
+
+	/*
+	 * If the protocol is not in the range we're interested in,
+	 * just return without compressing the packet.  If it is,
+	 * the protocol becomes the first byte to compress.
+	 */
+	printk(KERN_DEBUG "bsd_compress called with %x\n",proto);
+	
+	ent = proto;
+	if (proto < 0x21 || proto > 0xf9 || !(proto & 0x1) )
+		return 0;
+
+	db      = (struct bsd_db *) state;
+	hshift  = db->hshift;
+	max_ent = db->max_ent;
+	n_bits  = db->n_bits;
+	bitno   = 32;
+	accm    = 0;
+	mxcode  = MAXCODE (n_bits);
+	
+	/* This is the PPP header information */
+	if(skb_out && skb_tailroom(skb_out) >= 2) {
+		char *v = skb_put(skb_out,2);
+		/* we only push our own data on the header,
+		  AC,PC and protos is pushed by caller  */
+		v[0] = db->seqno >> 8;
+		v[1] = db->seqno;
+	}
+
+	ilen   = ++isize; /* This is off by one, but that is what is in draft! */
+
+	while (--ilen > 0) {
+		c     = *rptr++;
+		fcode = BSD_KEY  (ent, c);
+		hval  = BSD_HASH (ent, c, hshift);
+		dictp = dict_ptr (db, hval);
+	
+		/* Validate and then check the entry. */
+		if (dictp->codem1 >= max_ent)
+			goto nomatch;
+
+		if (dictp->fcode == fcode) {
+			ent = dictp->codem1 + 1;
+			continue;	/* found (prefix,suffix) */
+		}
+	
+		/* continue probing until a match or invalid entry */
+		disp = (hval == 0) ? 1 : hval;
+
+		do {
+			hval += disp;
+			if (hval >= db->hsize)
+				hval -= db->hsize;
+			dictp = dict_ptr (db, hval);
+			if (dictp->codem1 >= max_ent)
+				goto nomatch;
+		} while (dictp->fcode != fcode);
+
+		ent = dictp->codem1 + 1;	/* finally found (prefix,suffix) */
+		continue;
+	
+nomatch:
+		OUTPUT(ent);		/* output the prefix */
+	
+		/* code -> hashtable */
+		if (max_ent < db->maxmaxcode) {
+			struct bsd_dict *dictp2;
+			struct bsd_dict *dictp3;
+			int indx;
+
+			/* expand code size if needed */
+			if (max_ent >= mxcode) {
+				db->n_bits = ++n_bits;
+				mxcode = MAXCODE (n_bits);
+			}
+	    
+			/* 
+			 * Invalidate old hash table entry using
+			 * this code, and then take it over.
+			 */
+			dictp2 = dict_ptr (db, max_ent + 1);
+			indx   = dictp2->cptr;
+			dictp3 = dict_ptr (db, indx);
+
+			if (dictp3->codem1 == max_ent)
+				dictp3->codem1 = BADCODEM1;
+
+			dictp2->cptr   = hval;
+			dictp->codem1  = max_ent;
+			dictp->fcode = fcode;
+			db->max_ent    = ++max_ent;
+
+			if (db->lens) {
+				unsigned short *len1 = lens_ptr (db, max_ent);
+				unsigned short *len2 = lens_ptr (db, ent);
+				*len1 = *len2 + 1;
+			}
+		}
+		ent = c;
+	}
+    
+	OUTPUT(ent);		/* output the last code */
+
+	if(skb_out)
+		db->bytes_out    += skb_out->len; /* Do not count bytes from here */
+	db->uncomp_bytes += isize;
+	db->in_count     += isize;
+	++db->uncomp_count;
+	++db->seqno;
+
+	if (bitno < 32)
+		++db->bytes_out; /* must be set before calling bsd_check */
+
+	/*
+	 * Generate the clear command if needed
+	 */
+
+	if (bsd_check(db))
+		OUTPUT (CLEAR);
+
+	/*
+	 * Pad dribble bits of last code with ones.
+	 * Do not emit a completely useless byte of ones.
+	 */
+	if (bitno < 32 && skb_out && skb_tailroom(skb_out) > 0) 
+		*(skb_put(skb_out,1)) = (unsigned char) ((accm | (0xff << (bitno-8))) >> 24);
+    
+	/*
+	 * Increase code size if we would have without the packet
+	 * boundary because the decompressor will do so.
+	 */
+	if (max_ent >= mxcode && max_ent < db->maxmaxcode)
+		db->n_bits++;
+
+	/* If output length is too large then this is an incompressible frame. */
+	if (!skb_out || (skb_out && skb_out->len >= skb_in->len) ) {
+		++db->incomp_count;
+		db->incomp_bytes += isize;
+		return 0;
+	}
+
+	/* Count the number of compressed frames */
+	++db->comp_count;
+	db->comp_bytes += skb_out->len;
+	return skb_out->len;
+
+#undef OUTPUT
+}
+
+/*
+ * Update the "BSD Compress" dictionary on the receiver for
+ * incompressible data by pretending to compress the incoming data.
+ */
+static void bsd_incomp (void *state, struct sk_buff *skb_in,int proto)
+{
+	bsd_compress (state, skb_in, NULL, proto);
+}
+
+/*
+ * Decompress "BSD Compress".
+ */
+static int bsd_decompress (void *state, struct sk_buff *skb_in, struct sk_buff *skb_out,
+			   struct isdn_ppp_resetparams *rsparm)
+{
+	struct bsd_db *db;
+	unsigned int max_ent;
+	unsigned long accm;
+	unsigned int bitno;		/* 1st valid bit in accm */
+	unsigned int n_bits;
+	unsigned int tgtbitno;	/* bitno when we have a code */
+	struct bsd_dict *dictp;
+	int seq;
+	unsigned int incode;
+	unsigned int oldcode;
+	unsigned int finchar;
+	unsigned char *p,*ibuf;
+	int ilen;
+	int codelen;
+	int extra;
+
+	db       = (struct bsd_db *) state;
+	max_ent  = db->max_ent;
+	accm     = 0;
+	bitno    = 32;		/* 1st valid bit in accm */
+	n_bits   = db->n_bits;
+	tgtbitno = 32 - n_bits;	/* bitno when we have a code */
+
+	printk(KERN_DEBUG "bsd_decompress called\n");
+
+	if(!skb_in || !skb_out) {
+		printk(KERN_ERR "bsd_decompress called with NULL parameter\n");
+		return DECOMP_ERROR;
+	}
+    
+	/*
+	 * Get the sequence number.
+	 */
+	if( (p = skb_pull(skb_in,2)) == NULL) {
+		return DECOMP_ERROR;
+	}
+	p-=2;
+	seq   = (p[0] << 8) + p[1];
+	ilen  = skb_in->len;
+	ibuf = skb_in->data;
+
+	/*
+	 * Check the sequence number and give up if it differs from
+	 * the value we're expecting.
+	 */
+	if (seq != db->seqno) {
+		if (db->debug) {
+			printk(KERN_DEBUG "bsd_decomp%d: bad sequence # %d, expected %d\n",
+				db->unit, seq, db->seqno - 1);
+		}
+		return DECOMP_ERROR;
+	}
+
+	++db->seqno;
+	db->bytes_out += ilen;
+
+	if(skb_tailroom(skb_out) > 0)
+		*(skb_put(skb_out,1)) = 0;
+	else
+		return DECOMP_ERR_NOMEM;
+    
+	oldcode = CLEAR;
+
+	/*
+	 * Keep the checkpoint correctly so that incompressible packets
+	 * clear the dictionary at the proper times.
+	 */
+
+	for (;;) {
+		if (ilen-- <= 0) {
+			db->in_count += (skb_out->len - 1); /* don't count the header */
+			break;
+		}
+
+		/*
+		 * Accumulate bytes until we have a complete code.
+		 * Then get the next code, relying on the 32-bit,
+		 * unsigned accm to mask the result.
+		 */
+
+		bitno -= 8;
+		accm  |= *ibuf++ << bitno;
+		if (tgtbitno < bitno)
+			continue;
+
+		incode = accm >> tgtbitno;
+		accm <<= n_bits;
+		bitno += n_bits;
+
+		/*
+		 * The dictionary must only be cleared at the end of a packet.
+		 */
+	
+		if (incode == CLEAR) {
+			if (ilen > 0) {
+				if (db->debug)
+					printk(KERN_DEBUG "bsd_decomp%d: bad CLEAR\n", db->unit);
+				return DECOMP_FATALERROR;	/* probably a bug */
+			}
+			bsd_clear(db);
+			break;
+		}
+
+		if ((incode > max_ent + 2) || (incode > db->maxmaxcode)
+			|| (incode > max_ent && oldcode == CLEAR)) {
+			if (db->debug) {
+				printk(KERN_DEBUG "bsd_decomp%d: bad code 0x%x oldcode=0x%x ",
+					db->unit, incode, oldcode);
+				printk(KERN_DEBUG "max_ent=0x%x skb->Len=%d seqno=%d\n",
+					max_ent, skb_out->len, db->seqno);
+			}
+			return DECOMP_FATALERROR;	/* probably a bug */
+		}
+	
+		/* Special case for KwKwK string. */
+		if (incode > max_ent) {
+			finchar = oldcode;
+			extra   = 1;
+		} else {
+			finchar = incode;
+			extra   = 0;
+		}
+
+		codelen = *(lens_ptr (db, finchar));
+		if( skb_tailroom(skb_out) < codelen + extra) {
+			if (db->debug) {
+				printk(KERN_DEBUG "bsd_decomp%d: ran out of mru\n", db->unit);
+#ifdef DEBUG
+				printk(KERN_DEBUG "  len=%d, finchar=0x%x, codelen=%d,skblen=%d\n",
+					ilen, finchar, codelen, skb_out->len);
+#endif
+			}
+			return DECOMP_FATALERROR;
+		}
+
+		/*
+		 * Decode this code and install it in the decompressed buffer.
+		 */
+
+		p     = skb_put(skb_out,codelen);
+		p += codelen;
+		while (finchar > LAST) {
+			struct bsd_dict *dictp2 = dict_ptr (db, finchar);
+	    
+			dictp = dict_ptr (db, dictp2->cptr);
+
+#ifdef DEBUG
+			if (--codelen <= 0 || dictp->codem1 != finchar-1) {
+				if (codelen <= 0) {
+					printk(KERN_ERR "bsd_decomp%d: fell off end of chain ", db->unit);
+					printk(KERN_ERR "0x%x at 0x%x by 0x%x, max_ent=0x%x\n", incode, finchar, dictp2->cptr, max_ent);
+				} else {
+					if (dictp->codem1 != finchar-1) {
+						printk(KERN_ERR "bsd_decomp%d: bad code chain 0x%x finchar=0x%x ",db->unit, incode, finchar);
+						printk(KERN_ERR "oldcode=0x%x cptr=0x%x codem1=0x%x\n", oldcode, dictp2->cptr, dictp->codem1);
+					}
+				}
+				return DECOMP_FATALERROR;
+			}
+#endif
+
+			{
+				u32 fcode = dictp->fcode;
+				*--p    = (fcode >> 16) & 0xff;
+				finchar = fcode & 0xffff;
+			}
+		}
+		*--p = finchar;
+	
+#ifdef DEBUG
+		if (--codelen != 0)
+			printk(KERN_ERR "bsd_decomp%d: short by %d after code 0x%x, max_ent=0x%x\n", db->unit, codelen, incode, max_ent);
+#endif
+	
+		if (extra)		/* the KwKwK case again */
+			*(skb_put(skb_out,1)) = finchar;
+	
+		/*
+		 * If not first code in a packet, and
+		 * if not out of code space, then allocate a new code.
+		 *
+		 * Keep the hash table correct so it can be used
+		 * with uncompressed packets.
+		 */
+		if (oldcode != CLEAR && max_ent < db->maxmaxcode) {
+			struct bsd_dict *dictp2, *dictp3;
+			u16  *lens1,  *lens2;
+			unsigned long fcode;
+			int hval, disp, indx;
+	    
+			fcode = BSD_KEY(oldcode,finchar);
+			hval  = BSD_HASH(oldcode,finchar,db->hshift);
+			dictp = dict_ptr (db, hval);
+	    
+			/* look for a free hash table entry */
+			if (dictp->codem1 < max_ent) {
+				disp = (hval == 0) ? 1 : hval;
+				do {
+					hval += disp;
+					if (hval >= db->hsize)
+						hval -= db->hsize;
+					dictp = dict_ptr (db, hval);
+				} while (dictp->codem1 < max_ent);
+			}
+	    
+			/*
+			 * Invalidate previous hash table entry
+			 * assigned this code, and then take it over
+			 */
+
+			dictp2 = dict_ptr (db, max_ent + 1);
+			indx   = dictp2->cptr;
+			dictp3 = dict_ptr (db, indx);
+
+			if (dictp3->codem1 == max_ent)
+				dictp3->codem1 = BADCODEM1;
+
+			dictp2->cptr   = hval;
+			dictp->codem1  = max_ent;
+			dictp->fcode = fcode;
+			db->max_ent    = ++max_ent;
+
+			/* Update the length of this string. */
+			lens1  = lens_ptr (db, max_ent);
+			lens2  = lens_ptr (db, oldcode);
+			*lens1 = *lens2 + 1;
+	    
+			/* Expand code size if needed. */
+			if (max_ent >= MAXCODE(n_bits) && max_ent < db->maxmaxcode) {
+				db->n_bits = ++n_bits;
+				tgtbitno   = 32-n_bits;
+			}
+		}
+		oldcode = incode;
+	}
+
+	++db->comp_count;
+	++db->uncomp_count;
+	db->comp_bytes   += skb_in->len - BSD_OVHD;
+	db->uncomp_bytes += skb_out->len;
+
+	if (bsd_check(db)) {
+		if (db->debug)
+			printk(KERN_DEBUG "bsd_decomp%d: peer should have cleared dictionary on %d\n",
+				db->unit, db->seqno - 1);
+	}
+	return skb_out->len;
+}
+
+/*************************************************************
+ * Table of addresses for the BSD compression module
+ *************************************************************/
+
+static struct isdn_ppp_compressor ippp_bsd_compress = {
+	NULL,NULL,		/* prev,next: overwritten by isdn_ppp */
+	CI_BSD_COMPRESS,	/* compress_proto */
+	bsd_alloc,		/* alloc */
+	bsd_free,		/* free */
+	bsd_init,		/* init */
+	bsd_reset,		/* reset */
+	bsd_compress,		/* compress */
+	bsd_decompress,		/* decompress */
+	bsd_incomp,		/* incomp */
+	bsd_stats		/* comp_stat */
+};
+
+/*************************************************************
+ * Module support routines
+ *************************************************************/
+
+int init_module(void)
+{
+	int answer = isdn_ppp_register_compressor (&ippp_bsd_compress);
+	if (answer == 0)
+		printk (KERN_INFO "PPP BSD Compression module registered\n");
+	return answer;
+}
+
+void cleanup_module(void)
+{
+	isdn_ppp_unregister_compressor (&ippp_bsd_compress);
+}
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/isdn_budget.c linux/drivers/isdn/isdn_budget.c
--- v2.3.3/linux/drivers/isdn/isdn_budget.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/isdn_budget.c	Sun May 23 10:03:42 1999
@@ -0,0 +1,206 @@
+/* $Id: isdn_budget.c,v 1.3 1998/10/23 10:18:39 paul Exp $
+ *
+ * Linux ISDN subsystem, budget-accounting for network interfaces.
+ *
+ * Copyright 1997       by Christian Lademann <cal@zls.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_budget.c,v $
+ * Revision 1.3  1998/10/23 10:18:39  paul
+ * Implementation of "dialmode" (successor of "status")
+ * You also need current isdnctrl for this!
+ *
+ * Revision 1.2  1998/03/07 23:17:30  fritz
+ * Added RCS keywords
+ * Bugfix: Did not compile without isdn_dumppkt beeing enabled.
+ *
+ */
+
+/*
+30.06.97:cal:angelegt
+04.11.97:cal:budget.period: int --> time_t
+*/
+
+#include <linux/config.h>
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/isdn.h>
+#include "isdn_common.h"
+#include "isdn_net.h"
+
+#ifdef CONFIG_ISDN_BUDGET
+
+#define	VERBOSE_PRINTK(v, l, p...)	{ \
+	if(dev->net_verbose >= (v)) { \
+		printk(l ## p); \
+	} else { ; } \
+}
+
+
+int
+isdn_net_budget(int type, struct device *ndev) {
+	isdn_net_local		*lp = (isdn_net_local *)ndev->priv;
+	int			i, ret = 0;
+
+	switch(type) {
+	case ISDN_BUDGET_INIT:
+		for(i = 0; i < ISDN_BUDGET_NUM_BUDGET; i++) {
+			lp->budget [i] .amount = -1;
+			lp->budget [i] .used = 0;
+			lp->budget [i] .period = (time_t)0;
+			lp->budget [i] .period_started = (time_t)0;
+			lp->budget [i] .last_check = CURRENT_TIME;
+			lp->budget [i] .notified = 0;
+		}
+
+		return(0);
+		break;
+
+	case ISDN_BUDGET_CHECK_DIAL:
+	case ISDN_BUDGET_CHECK_CHARGE:
+	case ISDN_BUDGET_CHECK_ONLINE:
+		ret = 0;
+
+		for(i = 0; i < ISDN_BUDGET_NUM_BUDGET; i++) {
+			if(lp->budget [i] .amount < 0)
+				continue;
+
+			if(lp->budget [i] .period_started + lp->budget [i] .period < CURRENT_TIME) {
+				lp->budget [i] .used = 0;
+				lp->budget [i] .period_started = CURRENT_TIME;
+				lp->budget [i] .notified = 0;
+			}
+
+			if(lp->budget [i] .used >= lp->budget [i] .amount)
+				ret |= (1 << i);
+		}
+
+		switch(type) {
+		case ISDN_BUDGET_CHECK_DIAL:
+			if(! ret) {
+				lp->budget [ISDN_BUDGET_DIAL] .used++;
+				lp->budget [ISDN_BUDGET_DIAL] .last_check = CURRENT_TIME;
+			}
+			break;
+
+		case ISDN_BUDGET_CHECK_CHARGE:
+			lp->budget [ISDN_BUDGET_CHARGE] .used++;
+			lp->budget [ISDN_BUDGET_CHARGE] .last_check = CURRENT_TIME;
+			break;
+
+		case ISDN_BUDGET_CHECK_ONLINE:
+			if(lp->budget [ISDN_BUDGET_ONLINE] .last_check) {
+				lp->budget [ISDN_BUDGET_ONLINE] .used += (CURRENT_TIME - lp->budget [ISDN_BUDGET_ONLINE] .last_check);
+			}
+
+			lp->budget [ISDN_BUDGET_ONLINE] .last_check = CURRENT_TIME;
+			break;
+		}
+
+/*
+		if(ret)
+			lp->flags |= ISDN_NET_DM_OFF;
+*/
+		for(i = 0; i < ISDN_BUDGET_NUM_BUDGET; i++) {
+			if(ret & (1 << i) && ! lp->budget [i] .notified) {
+				switch(i) {
+				case ISDN_BUDGET_DIAL:
+					printk(KERN_WARNING "isdn_budget: dial budget used up.\n");
+					break;
+
+				case ISDN_BUDGET_CHARGE:
+					printk(KERN_WARNING "isdn_budget: charge budget used up.\n");
+					break;
+
+				case ISDN_BUDGET_ONLINE:
+					printk(KERN_WARNING "isdn_budget: online budget used up.\n");
+					break;
+
+				default:
+					printk(KERN_WARNING "isdn_budget: budget #%d used up.\n", i);
+					break;
+				}
+
+				lp->budget [i] .notified = 1;
+			}
+		}
+
+		return(ret);
+		break;
+
+	case ISDN_BUDGET_START_ONLINE:
+		lp->budget [ISDN_BUDGET_ONLINE] .last_check = CURRENT_TIME;
+		return(0);
+
+		break;
+	}
+
+	return(-1);
+}
+
+
+int
+isdn_budget_ioctl(isdn_ioctl_budget *iocmd) {
+	isdn_net_dev		*p = isdn_net_findif(iocmd->name);
+
+	if(p) {
+		switch(iocmd->command) {
+		case ISDN_BUDGET_SET_BUDGET:
+			if(! suser())
+				return(-EPERM);
+
+			if(iocmd->budget < 0 || iocmd->budget > ISDN_BUDGET_NUM_BUDGET)
+				return(-EINVAL);
+
+			if(iocmd->amount < 0)
+				iocmd->amount = -1;
+
+			p->local->budget [iocmd->budget] .amount = iocmd->amount;
+			p->local->budget [iocmd->budget] .period = iocmd->period;
+
+			if(iocmd->used <= 0)
+				p->local->budget [iocmd->budget] .used = 0;
+			else
+				p->local->budget [iocmd->budget] .used = iocmd->used;
+
+			if(iocmd->period_started == (time_t)0)
+				p->local->budget [iocmd->budget] .period_started = CURRENT_TIME;
+			else
+				p->local->budget [iocmd->budget] .period_started = iocmd->period_started;
+
+			return(0);
+			break;
+
+		case ISDN_BUDGET_GET_BUDGET:
+			if(iocmd->budget < 0 || iocmd->budget > ISDN_BUDGET_NUM_BUDGET)
+				return(-EINVAL);
+
+			iocmd->amount = p->local->budget [iocmd->budget] .amount;
+			iocmd->used = p->local->budget [iocmd->budget] .used;
+			iocmd->period = p->local->budget [iocmd->budget] .period;
+			iocmd->period_started = p->local->budget [iocmd->budget] .period_started;
+
+			return(0);
+			break;
+
+		default:
+			return(-EINVAL);
+			break;
+		}
+	}
+	return(-ENODEV);
+}
+#endif
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/isdn_cards.c linux/drivers/isdn/isdn_cards.c
--- v2.3.3/linux/drivers/isdn/isdn_cards.c	Wed Apr  1 16:21:03 1998
+++ linux/drivers/isdn/isdn_cards.c	Sun May 23 10:03:42 1999
@@ -1,8 +1,8 @@
-/* $Id: isdn_cards.c,v 1.7 1998/02/20 17:24:28 fritz Exp $
+/* $Id: isdn_cards.c,v 1.9 1999/04/12 12:33:11 fritz Exp $
 
  * Linux ISDN subsystem, initialization for non-modularized drivers.
  *
- * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1994,95,96 by Fritz Elfert (fritz@isdn4linux.de)
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -19,6 +19,12 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_cards.c,v $
+ * Revision 1.9  1999/04/12 12:33:11  fritz
+ * Changes from 2.0 tree.
+ *
+ * Revision 1.8  1999/03/29 11:13:23  armin
+ * Added eicon driver init.
+ *
  * Revision 1.7  1998/02/20 17:24:28  fritz
  * Added ACT2000 init.
  *
@@ -56,6 +62,10 @@
 extern void pcbit_init(void);
 #endif
 
+#ifdef CONFIG_ISDN_DRV_EICON
+extern void eicon_init(void);
+#endif
+
 #ifdef CONFIG_ISDN_DRV_AVMB1
 extern void avmb1_init(void);
 extern void capi_init(void);
@@ -87,5 +97,8 @@
 #endif
 #if CONFIG_ISDN_DRV_ACT2000
 	act2000_init();
+#endif
+#if CONFIG_ISDN_DRV_EICON
+	eicon_init();
 #endif
 }
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/isdn_cards.h linux/drivers/isdn/isdn_cards.h
--- v2.3.3/linux/drivers/isdn/isdn_cards.h	Thu Feb 27 10:57:29 1997
+++ linux/drivers/isdn/isdn_cards.h	Sun May 23 10:03:42 1999
@@ -1,8 +1,8 @@
-/* $Id: isdn_cards.h,v 1.2 1997/02/03 23:31:55 fritz Exp $
+/* $Id: isdn_cards.h,v 1.3 1999/04/12 12:33:13 fritz Exp $
 
  * Linux ISDN subsystem, initialization for non-modularized drivers.
  *
- * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1994-1999 by Fritz Elfert (fritz@isdn4linux.de)
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -19,6 +19,9 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_cards.h,v $
+ * Revision 1.3  1999/04/12 12:33:13  fritz
+ * Changes from 2.0 tree.
+ *
  * Revision 1.2  1997/02/03 23:31:55  fritz
  * Reformatted according CodingStyle
  *
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/isdn_common.c linux/drivers/isdn/isdn_common.c
--- v2.3.3/linux/drivers/isdn/isdn_common.c	Fri May 14 18:55:18 1999
+++ linux/drivers/isdn/isdn_common.c	Sun May 23 10:03:42 1999
@@ -1,8 +1,8 @@
-/* $Id: isdn_common.c,v 1.55 1998/02/23 23:35:32 fritz Exp $
+/* $Id: isdn_common.c,v 1.75 1999/04/18 14:06:47 fritz Exp $
 
  * Linux ISDN subsystem, common used functions (linklevel).
  *
- * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1994-1999  by Fritz Elfert (fritz@isdn4linux.de)
  * Copyright 1995,96    Thinking Objects Software GmbH Wuerzburg
  * Copyright 1995,96    by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
  *
@@ -20,11 +20,85 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * Note: This file differs from the corresponding revision as present in the
- * isdn4linux CVS repository because some later bug fixes have been extracted
- * from the repository and merged into this file. -- Henner Eisen
- *
  * $Log: isdn_common.c,v $
+ * Revision 1.75  1999/04/18 14:06:47  fritz
+ * Removed TIMRU stuff.
+ *
+ * Revision 1.74  1999/04/12 13:16:45  fritz
+ * Changes from 2.0 tree.
+ *
+ * Revision 1.73  1999/04/12 12:33:15  fritz
+ * Changes from 2.0 tree.
+ *
+ * Revision 1.72  1999/03/02 12:04:44  armin
+ * -added ISDN_STAT_ADDCH to increase supported channels after
+ *  register_isdn().
+ * -ttyI now goes on-hook on ATZ when B-Ch is connected.
+ * -added timer-function for register S7 (Wait for Carrier).
+ * -analog modem (ISDN_PROTO_L2_MODEM) implementations.
+ * -on L2_MODEM a string will be appended to the CONNECT-Message,
+ *  which is provided by the HL-Driver in parm.num in ISDN_STAT_BCONN.
+ * -variable "dialing" used for ATA also, for interrupting call
+ *  establishment and register S7.
+ *
+ * Revision 1.71  1999/01/28 09:10:43  armin
+ * Fixed bad while-loop in isdn_readbch().
+ *
+ * Revision 1.70  1999/01/15 19:58:54  he
+ * removed compatibiltity macro
+ *
+ * Revision 1.69  1998/09/07 21:59:58  he
+ * flush method for 2.1.118 and above
+ * updated IIOCTLNETGPN
+ *
+ * Revision 1.68  1998/08/31 21:09:45  he
+ * new ioctl IIOCNETGPN for /dev/isdninfo (get network interface'
+ *     peer phone number)
+ *
+ * Revision 1.67  1998/06/26 15:12:21  fritz
+ * Added handling of STAT_ICALL with incomplete CPN.
+ * Added AT&L for ttyI emulator.
+ * Added more locking stuff in tty_write.
+ *
+ * Revision 1.66  1998/06/17 19:50:41  he
+ * merged with 2.1.10[34] (cosmetics and udelay() -> mdelay())
+ * brute force fix to avoid Ugh's in isdn_tty_write()
+ * cleaned up some dead code
+ *
+ * Revision 1.65  1998/06/07 00:20:00  fritz
+ * abc cleanup.
+ *
+ * Revision 1.64  1998/06/02 12:10:03  detabc
+ * wegen einer einstweiliger verfuegung gegen DW ist zur zeit
+ * die abc-extension bis zur klaerung der rechtslage nicht verfuegbar
+ *
+ * Revision 1.63  1998/05/03 17:40:38  detabc
+ * Include abc-extension-support for >= 2.1.x Kernels in
+ * isdn_net.c and isdn_common.c. alpha-test OK and running !
+ *
+ * Revision 1.62  1998/04/14 16:28:43  he
+ * Fixed user space access with interrupts off and remaining
+ * copy_{to,from}_user() -> -EFAULT return codes
+ *
+ * Revision 1.61  1998/03/22 18:50:46  hipp
+ * Added BSD Compression for syncPPP .. UNTESTED at the moment
+ *
+ * Revision 1.60  1998/03/19 13:18:18  keil
+ * Start of a CAPI like interface for supplementary Service
+ * first service: SUSPEND
+ *
+ * Revision 1.59  1998/03/09 17:46:23  he
+ * merged in 2.1.89 changes
+ *
+ * Revision 1.58  1998/03/07 22:35:24  fritz
+ * Starting generic module support (Nothing usable yet).
+ *
+ * Revision 1.57  1998/03/07 18:21:01  cal
+ * Dynamic Timeout-Rule-Handling vs. 971110 included
+ *
+ * Revision 1.56  1998/02/25 17:49:38  he
+ * Changed return codes caused be failing copy_{to,from}_user to -EFAULT
+ *
  * Revision 1.55  1998/02/23 23:35:32  fritz
  * Eliminated some compiler warnings.
  *
@@ -269,7 +343,7 @@
 
 isdn_dev *dev = (isdn_dev *) 0;
 
-static char *isdn_revision = "$Revision: 1.55 $";
+static char *isdn_revision = "$Revision: 1.75 $";
 
 extern char *isdn_net_revision;
 extern char *isdn_tty_revision;
@@ -290,13 +364,36 @@
 void
 isdn_MOD_INC_USE_COUNT(void)
 {
+	int i;
+
 	MOD_INC_USE_COUNT;
+	for (i = 0; i < dev->drivers; i++) {
+		isdn_ctrl cmd;
+
+		cmd.driver = i;
+		cmd.arg = 0;
+		cmd.command = ISDN_CMD_LOCK;
+		isdn_command(&cmd);
+		dev->drv[i]->locks++;
+	}
 }
 
 void
 isdn_MOD_DEC_USE_COUNT(void)
 {
+	int i;
+
 	MOD_DEC_USE_COUNT;
+	for (i = 0; i < dev->drivers; i++)
+		if (dev->drv[i]->locks > 0) {
+			isdn_ctrl cmd;
+
+			cmd.driver = i;
+			cmd.arg = 0;
+			cmd.command = ISDN_CMD_UNLOCK;
+			isdn_command(&cmd);
+			dev->drv[i]->locks--;
+		}
 }
 
 #if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP)
@@ -312,6 +409,82 @@
 }
 #endif
 
+/*
+ * I picked the pattern-matching-functions from an old GNU-tar version (1.10)
+ * It was originally written and put to PD by rs@mirror.TMC.COM (Rich Salz)
+ */
+static int
+isdn_star(char *s, char *p)
+{
+	while (isdn_wildmat(s, p)) {
+		if (*++s == '\0')
+			return (2);
+	}
+	return (0);
+}
+
+/*
+ * 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.
+ *
+ * Return:
+ *   0 = match.
+ *   1 = no match.
+ *   2 = no match. Would eventually match, if s would be longer.
+ *
+ * Possible Patterns:
+ *
+ * '?'     matches one character
+ * '*'     matches zero or more characters
+ * [xyz]   matches the set of characters in brackets.
+ * [^xyz]  matches any single character not in the set of characters
+ */
+
+int
+isdn_wildmat(char *s, char *p)
+{
+	register int last;
+	register int matched;
+	register int reverse;
+	register int nostar = 1;
+
+	for (; *p; s++, p++)
+		switch (*p) {
+			case '\\':
+				/*
+				 * Literal match with following character,
+				 * fall through.
+				 */
+				p++;
+			default:
+				if (*s != *p)
+					return (*s == '\0')?2:1;
+				continue;
+			case '?':
+				/* Match anything. */
+				if (*s == '\0')
+					return (2);
+				continue;
+			case '*':
+				nostar = 0;	
+				/* Trailing star matches everything. */
+				return (*++p ? isdn_star(s, p) : 0);
+			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 (1);
+				continue;
+		}
+	return (*s == '\0')?0:nostar;
+}
+
 static void
 isdn_free_queue(struct sk_buff_head *queue)
 {
@@ -345,7 +518,6 @@
 isdn_timer_funct(ulong dummy)
 {
 	int tf = dev->tflags;
-
 	if (tf & ISDN_TIMER_FAST) {
 		if (tf & ISDN_TIMER_MODEMREAD)
 			isdn_tty_readmodem();
@@ -374,13 +546,16 @@
 				if (tf & ISDN_TIMER_KEEPALIVE)
 					isdn_net_slarp_out();
 			}
+			if (tf & ISDN_TIMER_CARRIER)
+				isdn_tty_carrier_timeout();
 #if (defined CONFIG_ISDN_PPP) && (defined CONFIG_ISDN_MPP)
 			if (tf & ISDN_TIMER_IPPP)
 				isdn_ppp_timer_timeout();
 #endif
 		}
 	}
-	if (tf) {
+	if (tf) 
+	{
 		int flags;
 
 		save_flags(flags);
@@ -506,6 +681,29 @@
 	isdn_command(&cmd);
 }
 
+/*
+ * Begin of a CAPI like LL<->HL interface, currently used only for 
+ * supplementary service (CAPI 2.0 part III)
+ */
+#include "avmb1/capicmd.h"  /* this should be moved in a common place */
+
+int
+isdn_capi_rec_hl_msg(capi_msg *cm) {
+	
+	int di;
+	int ch;
+	
+	di = (cm->adr.Controller & 0x7f) -1;
+	ch = isdn_dc2minor(di, (cm->adr.Controller>>8)& 0x7f);
+	switch(cm->Command) {
+		case CAPI_FACILITY:
+			/* in the moment only handled in tty */
+			return(isdn_tty_capi_facility(cm));
+		default:
+			return(-1);
+	}
+}
+
 static int
 isdn_status_callback(isdn_ctrl * c)
 {
@@ -540,13 +738,13 @@
 			wake_up_interruptible(&dev->drv[di]->st_waitq);
 			break;
 		case ISDN_STAT_RUN:
-			dev->drv[di]->running = 1;
+			dev->drv[di]->flags |= DRV_FLAG_RUNNING;
 			for (i = 0; i < ISDN_MAX_CHANNELS; i++)
 				if (dev->drvmap[i] == di)
 					isdn_all_eaz(di, dev->chanmap[i]);
 			break;
 		case ISDN_STAT_STOP:
-			dev->drv[di]->running = 0;
+			dev->drv[di]->flags &= ~DRV_FLAG_RUNNING;
 			break;
 		case ISDN_STAT_ICALL:
 			if (i < 0)
@@ -562,19 +760,17 @@
 				return 0;
 			}
 			/* Try to find a network-interface which will accept incoming call */
-			cmd.driver = di;
-			cmd.arg = c->arg;
-			cmd.command = ISDN_CMD_LOCK;
-			isdn_command(&cmd);
 			r = isdn_net_find_icall(di, c->arg, i, c->parm.setup);
 			switch (r) {
 				case 0:
 					/* No network-device replies.
-					 * Try ttyI's
+					 * Try ttyI's.
+					 * These return 0 on no match, 1 on match and
+					 * 3 on eventually match, if CID is longer.
 					 */
-					if (isdn_tty_find_icall(di, c->arg, c->parm.setup) >= 0)
-						retval = 1;
-					else if (dev->drv[di]->reject_bus) {
+					retval = isdn_tty_find_icall(di, c->arg, c->parm.setup);
+					if ((!retval) && (dev->drv[di]->flags & DRV_FLAG_REJBUS)) {
+						/* No tty responding */
 						cmd.driver = di;
 						cmd.arg = c->arg;
 						cmd.command = ISDN_CMD_HANGUP;
@@ -606,13 +802,14 @@
 					/* ... then start callback. */
 					isdn_net_dial();
 					break;
+				case 5:
+					/* Number would eventually match, if longer */
+					retval = 3;
+					break;
 			}
-			if (retval != 1) {
-				cmd.driver = di;
-				cmd.arg = c->arg;
-				cmd.command = ISDN_CMD_UNLOCK;
-				isdn_command(&cmd);
-			}
+#ifdef ISDN_DEBUG_STATCALLB
+			printk(KERN_DEBUG "ICALL: ret=%d\n", retval);
+#endif
 			return retval;
 			break;
 		case ISDN_STAT_CINF:
@@ -664,7 +861,7 @@
 #endif
 			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
 				return 0;
-			dev->drv[di]->flags &= ~(1 << (c->arg));
+			dev->drv[di]->online &= ~(1 << (c->arg));
 			isdn_info_update();
 			/* Signal hangup to network-devices */
 			if (isdn_net_stat_callback(i, c))
@@ -682,7 +879,7 @@
 			/* Signal B-channel-connect to network-devices */
 			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
 				return 0;
-			dev->drv[di]->flags |= (1 << (c->arg));
+			dev->drv[di]->online |= (1 << (c->arg));
 			isdn_info_update();
 			if (isdn_net_stat_callback(i, c))
 				break;
@@ -698,7 +895,7 @@
 #endif
 			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
 				return 0;
-			dev->drv[di]->flags &= ~(1 << (c->arg));
+			dev->drv[di]->online &= ~(1 << (c->arg));
 			isdn_info_update();
 #ifdef CONFIG_ISDN_X25
 			/* Signal hangup to network-devices */
@@ -723,6 +920,9 @@
 				break;
 			break;
 		case ISDN_STAT_ADDCH:
+			if (isdn_add_channels(dev->drv[di], di, c->arg, 1))
+				return -1;
+			isdn_info_update();
 			break;
 		case ISDN_STAT_UNLOAD:
 			save_flags(flags);
@@ -741,6 +941,9 @@
 				isdn_free_queue(&dev->drv[di]->rpqueue[i]);
 			kfree(dev->drv[di]->rpqueue);
 			kfree(dev->drv[di]->rcv_waitq);
+#if LINUX_VERSION_CODE < 131841
+			kfree(dev->drv[di]->snd_waitq);
+#endif
 			kfree(dev->drv[di]);
 			dev->drv[di] = NULL;
 			dev->drvid[di][0] = '\0';
@@ -749,6 +952,8 @@
 			return 0;
 		case ISDN_STAT_L1ERR:
 			break;
+		case CAPI_PUT_MESSAGE:
+			return(isdn_capi_rec_hl_msg(&c->parm.cmsg));
 		default:
 			return -1;
 	}
@@ -784,7 +989,11 @@
  * of the mapping (di,ch)<->minor, happen during the sleep? --he 
  */
 int
+#if LINUX_VERSION_CODE < 131841
+isdn_readbchan(int di, int channel, u_char * buf, u_char * fp, int len, struct wait_queue **sleep)
+#else
 isdn_readbchan(int di, int channel, u_char * buf, u_char * fp, int len, wait_queue_head_t *sleep)
+#endif
 {
 	int left;
 	int count;
@@ -818,7 +1027,8 @@
 
 			dflag = 0;
 			count_pull = count_put = 0;
-			while ((count_pull < skb->len) && (left-- > 0)) {
+			while ((count_pull < skb->len) && (left > 0)) {
+				left--;
 				if (dev->drv[di]->DLEflag & DLEmask) {
 					*cp++ = DLE;
 					dev->drv[di]->DLEflag &= ~DLEmask;
@@ -893,8 +1103,6 @@
 	return (dev->chanmap[minor]);
 }
 
-#define INF_DV 0x01             /* Data version for /dev/isdninfo */
-
 static char *
 isdn_statstr(void)
 {
@@ -930,7 +1138,7 @@
 	p = istatbuf + strlen(istatbuf);
 	for (i = 0; i < ISDN_MAX_DRIVERS; i++) {
 		if (dev->drv[i]) {
-			sprintf(p, "%ld ", dev->drv[i]->flags);
+			sprintf(p, "%ld ", dev->drv[i]->online);
 			p = istatbuf + strlen(istatbuf);
 		} else {
 			sprintf(p, "? ");
@@ -996,7 +1204,7 @@
 		drvidx = isdn_minor2drv(minor);
 		if (drvidx < 0)
 			return -ENODEV;
-		if (!dev->drv[drvidx]->running)
+		if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING))
 			return -ENODEV;
 		chidx = isdn_minor2chan(minor);
 		if(  ! (p = kmalloc(count,GFP_KERNEL))  ) return -ENOMEM;
@@ -1066,7 +1274,7 @@
 		drvidx = isdn_minor2drv(minor);
 		if (drvidx < 0)
 			return -ENODEV;
-		if (!dev->drv[drvidx]->running)
+		if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING))
 			return -ENODEV;
 		chidx = isdn_minor2chan(minor);
 		while (isdn_writebuf_stub(drvidx, chidx, buf, count, 1) != count)
@@ -1080,7 +1288,7 @@
 		/*
 		 * We want to use the isdnctrl device to load the firmware
 		 *
-		 if (!dev->drv[drvidx]->running)
+		 if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING))
 		 return -ENODEV;
 		 */
 		if (dev->drv[drvidx]->interface->writecmd)
@@ -1131,143 +1339,6 @@
 	return POLLERR;
 }
 
-/* 
- * This accesses user space with interrupts off, but is not needed by
- * any of the isdn4k-util programs anyway. Thus, in contrast to your
- * first impression after looking at the code, fixing is trival!*/
-#if 0 
-static int
-isdn_set_allcfg(char *src)
-{
-	int ret;
-	int i;
-	ulong flags;
-	isdn_net_ioctl_cfg cfg;
-	isdn_net_ioctl_phone phone;
-
-	if ((ret = isdn_net_rmall()))
-		return ret;
-	if (copy_from_user((char *) &i, src, sizeof(int))) return -EFAULT;
-	save_flags(flags);
-	cli();
-	src += sizeof(int);
-	while (i) {
-		int phone_len;
-		int out_flag;
-
-		if (copy_from_user((char *) &cfg, src, sizeof(cfg))) {
-			restore_flags(flags);
-			return -EFAULT;
-		}
-		src += sizeof(cfg);
-		if (!isdn_net_new(cfg.name, NULL)) {
-			restore_flags(flags);
-			return -EIO;
-		}
-		if ((ret = isdn_net_setcfg(&cfg))) {
-			restore_flags(flags);
-			return ret;
-		}
-		phone_len = out_flag = 0;
-		while (out_flag < 2) {
-			if ((ret = verify_area(VERIFY_READ, src, 1))) {
-				restore_flags(flags);
-				return ret;
-			}
-			get_user(phone.phone[phone_len], src++);
-			if ((phone.phone[phone_len] == ' ') ||
-			    (phone.phone[phone_len] == '\0')) {
-				if (phone_len) {
-					phone.phone[phone_len] = '\0';
-					strcpy(phone.name, cfg.name);
-					phone.outgoing = out_flag;
-					if ((ret = isdn_net_addphone(&phone))) {
-						restore_flags(flags);
-						return ret;
-					}
-				} else
-					out_flag++;
-				phone_len = 0;
-			}
-			if (++phone_len >= sizeof(phone.phone))
-				printk(KERN_WARNING
-				       "%s: IIOCSETSET phone number too long, ignored\n",
-				       cfg.name);
-		}
-		i--;
-	}
-	restore_flags(flags);
-	return 0;
-}
-
-static int
-isdn_get_allcfg(char *dest)
-{
-	isdn_net_ioctl_cfg cfg;
-	isdn_net_ioctl_phone phone;
-	isdn_net_dev *p;
-	ulong flags;
-	int ret;
-
-	/* Walk through netdev-chain */
-	save_flags(flags);
-	cli();
-	p = dev->netdev;
-	while (p) {
-		isdn_net_local *lp = p->local;
-
-		if ((ret = verify_area(VERIFY_WRITE, (void *) dest, sizeof(cfg) + 200))) {
-			restore_flags(flags);
-			return ret;
-		}
-		strcpy(cfg.eaz, lp->msn);
-		cfg.exclusive = lp->exclusive;
-		if (lp->pre_device >= 0) {
-			sprintf(cfg.drvid, "%s,%d", dev->drvid[lp->pre_device],
-				lp->pre_channel);
-		} else
-			cfg.drvid[0] = '\0';
-		cfg.onhtime = lp->onhtime;
-		cfg.charge = lp->charge;
-		cfg.l2_proto = lp->l2_proto;
-		cfg.l3_proto = lp->l3_proto;
-		cfg.p_encap = lp->p_encap;
-		cfg.secure = (lp->flags & ISDN_NET_SECURE) ? 1 : 0;
-		cfg.callback = (lp->flags & ISDN_NET_CALLBACK) ? 1 : 0;
-		cfg.chargehup = (lp->hupflags & ISDN_CHARGEHUP) ? 1 : 0;
-		cfg.ihup = (lp->hupflags & ISDN_INHUP) ? 1 : 0;
-		cfg.chargeint = lp->chargeint;
-		if (copy_to_user(dest, lp->name, 10)) {
-			restore_flags(flags);
-			return -EFAULT;
-		}
-		dest += 10;
-		if (copy_to_user(dest, (char *) &cfg, sizeof(cfg))) {
-			restore_flags(flags);
-			return -EFAULT;
-		}
-		dest += sizeof(cfg);
-		strcpy(phone.name, lp->name);
-		phone.outgoing = 0;
-		if ((ret = isdn_net_getphones(&phone, dest)) < 0) {
-			restore_flags(flags);
-			return ret;
-		} else
-			dest += ret;
-		strcpy(phone.name, lp->name);
-		phone.outgoing = 1;
-		if ((ret = isdn_net_getphones(&phone, dest)) < 0) {
-			restore_flags(flags);
-			return ret;
-		} else
-			dest += ret;
-		put_user(0, dest);
-		p = p->next;
-	}
-	restore_flags(flags);
-	return 0;
-}
-#endif
 
 static int
 isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
@@ -1315,6 +1386,17 @@
 				} else
 					return -EINVAL;
 				break;
+#ifdef CONFIG_NETDEVICES
+			case IIOCNETGPN:
+				/* Get peer phone number of a connected 
+				 * isdn network interface */
+				if (arg) {
+					if (copy_from_user((char *) &phone, (char *) arg, sizeof(phone)))
+						return -EFAULT;
+					return isdn_net_getpeer(&phone, (isdn_net_ioctl_phone *) arg);
+				} else
+					return -EINVAL;
+#endif
 			default:
 				return -EINVAL;
 		}
@@ -1326,7 +1408,7 @@
 		if (drvidx < 0)
 			return -ENODEV;
 		chidx = isdn_minor2chan(minor);
-		if (!dev->drv[drvidx]->running)
+		if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING))
 			return -ENODEV;
 		return 0;
 	}
@@ -1512,26 +1594,11 @@
 				}
 				if (drvidx == -1)
 					return -ENODEV;
-				dev->drv[drvidx]->reject_bus = iocts.arg;
-				return 0;
-#if 0
-			case IIOCGETSET:
-				/* Get complete setup (all network-interfaces and profile-
-				   settings of all tty-devices */
-				if (arg)
-					return (isdn_get_allcfg((char *) arg));
+				if (iocts.arg)
+					dev->drv[drvidx]->flags |= DRV_FLAG_REJBUS;
 				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;
-#endif
+					dev->drv[drvidx]->flags &= ~DRV_FLAG_REJBUS;
+				return 0;
 			case IIOCSIGPRF:
 				dev->profd = current;
 				return 0;
@@ -1543,7 +1610,7 @@
 					int i;
 
 					if ((ret = verify_area(VERIFY_WRITE, (void *) arg,
-					(ISDN_MODEM_ANZREG + ISDN_MSNLEN)
+					(ISDN_MODEM_ANZREG + ISDN_MSNLEN + ISDN_LMSNLEN)
 						   * ISDN_MAX_CHANNELS)))
 						return ret;
 
@@ -1555,8 +1622,11 @@
 						if (copy_to_user(p, dev->mdm.info[i].emu.pmsn, ISDN_MSNLEN))
 							return -EFAULT;
 						p += ISDN_MSNLEN;
+						if (copy_to_user(p, dev->mdm.info[i].emu.plmsn, ISDN_LMSNLEN))
+							return -EFAULT;
+						p += ISDN_LMSNLEN;
 					}
-					return (ISDN_MODEM_ANZREG + ISDN_MSNLEN) * ISDN_MAX_CHANNELS;
+					return (ISDN_MODEM_ANZREG + ISDN_MSNLEN + ISDN_LMSNLEN) * ISDN_MAX_CHANNELS;
 				} else
 					return -EINVAL;
 				break;
@@ -1720,7 +1790,6 @@
 	uint minor = MINOR(ino->i_rdev);
 	int drvidx;
 	int chidx;
-	isdn_ctrl c;
 
 	if (minor == ISDN_MINOR_STATUS) {
 		infostruct *p;
@@ -1743,31 +1812,25 @@
 		if (drvidx < 0)
 			return -ENODEV;
 		chidx = isdn_minor2chan(minor);
-		if (!dev->drv[drvidx]->running)
+		if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING))
 			return -ENODEV;
-		if (!(dev->drv[drvidx]->flags & (1 << chidx)))
+		if (!(dev->drv[drvidx]->online & (1 << chidx)))
 			return -ENODEV;
-		c.command = ISDN_CMD_LOCK;
-		c.driver = drvidx;
-		isdn_command(&c);
-		MOD_INC_USE_COUNT;
+		isdn_MOD_INC_USE_COUNT();
 		return 0;
 	}
 	if (minor <= ISDN_MINOR_CTRLMAX) {
 		drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
 		if (drvidx < 0)
 			return -ENODEV;
-		c.command = ISDN_CMD_LOCK;
-		c.driver = drvidx;
-		MOD_INC_USE_COUNT;
-		isdn_command(&c);
+		isdn_MOD_INC_USE_COUNT();
 		return 0;
 	}
 #ifdef CONFIG_ISDN_PPP
 	if (minor <= ISDN_MINOR_PPPMAX) {
 		int ret;
 		if (!(ret = isdn_ppp_open(minor - ISDN_MINOR_PPP, filep)))
-			MOD_INC_USE_COUNT;
+			isdn_MOD_INC_USE_COUNT();
 		return ret;
 	}
 #endif
@@ -1778,13 +1841,12 @@
 isdn_close(struct inode *ino, struct file *filep)
 {
 	uint minor = MINOR(ino->i_rdev);
-	int drvidx;
-	isdn_ctrl c;
 
-	MOD_DEC_USE_COUNT;
 	if (minor == ISDN_MINOR_STATUS) {
 		infostruct *p = dev->infochain;
 		infostruct *q = NULL;
+
+		MOD_DEC_USE_COUNT;
 		while (p) {
 			if (p->private == (char *) &(filep->private_data)) {
 				if (q)
@@ -1800,24 +1862,12 @@
 		printk(KERN_WARNING "isdn: No private data while closing isdnctrl\n");
 		return 0;
 	}
-	if (minor < ISDN_MINOR_CTRL) {
-		drvidx = isdn_minor2drv(minor);
-		if (drvidx < 0)
-			return 0;
-		c.command = ISDN_CMD_UNLOCK;
-		c.driver = drvidx;
-		isdn_command(&c);
+	isdn_MOD_DEC_USE_COUNT();
+	if (minor < ISDN_MINOR_CTRL)
 		return 0;
-	}
 	if (minor <= ISDN_MINOR_CTRLMAX) {
-		drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
-		if (drvidx < 0)
-			return 0;
 		if (dev->profd == current)
 			dev->profd = NULL;
-		c.command = ISDN_CMD_UNLOCK;
-		c.driver = drvidx;
-		isdn_command(&c);
 		return 0;
 	}
 #ifdef CONFIG_ISDN_PPP
@@ -1871,7 +1921,6 @@
 	ulong flags;
 	ulong features;
 	ulong vfeatures;
-	isdn_ctrl cmd;
 
 	save_flags(flags);
 	cli();
@@ -1889,7 +1938,7 @@
 			if ((dev->usage[i] & ISDN_USAGE_EXCLUSIVE) &&
 			((pre_dev != d) || (pre_chan != dev->chanmap[i])))
 				continue;
-			if ((dev->drv[d]->running)) {
+			if (dev->drv[d]->flags & DRV_FLAG_RUNNING) {
 				if (((dev->drv[d]->interface->features & features) == features) ||
 				    (((dev->drv[d]->interface->features & vfeatures) == vfeatures) &&
 				     (dev->drv[d]->interface->features & ISDN_FEATURE_L2_TRANS))) {
@@ -1897,10 +1946,6 @@
 						dev->usage[i] &= ISDN_USAGE_EXCLUSIVE;
 						dev->usage[i] |= usage;
 						isdn_info_update();
-						cmd.driver = d;
-						cmd.arg = 0;
-						cmd.command = ISDN_CMD_LOCK;
-						isdn_command(&cmd);
 						restore_flags(flags);
 						return i;
 					} else {
@@ -1908,10 +1953,6 @@
 							dev->usage[i] &= ISDN_USAGE_EXCLUSIVE;
 							dev->usage[i] |= usage;
 							isdn_info_update();
-							cmd.driver = d;
-							cmd.arg = 0;
-							cmd.command = ISDN_CMD_LOCK;
-							isdn_command(&cmd);
 							restore_flags(flags);
 							return i;
 						}
@@ -1931,7 +1972,6 @@
 {
 	int i;
 	ulong flags;
-	isdn_ctrl cmd;
 
 	save_flags(flags);
 	cli();
@@ -1945,12 +1985,6 @@
 			dev->obytes[i] = 0;
 			isdn_info_update();
 			isdn_free_queue(&dev->drv[di]->rpqueue[ch]);
-			cmd.driver = di;
-			cmd.arg = ch;
-			cmd.command = ISDN_CMD_UNLOCK;
-			restore_flags(flags);
-			isdn_command(&cmd);
-			return;
 		}
 	restore_flags(flags);
 }
@@ -1995,7 +2029,6 @@
 		copy_from_user(skb_put(skb, len), buf, len);
 	else
 		memcpy(skb_put(skb, len), buf, len);
-
 	ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, 1, skb);
 	if (ret <= 0)
 		dev_kfree_skb(skb);
@@ -2051,19 +2084,180 @@
 	return ret;
 }
 
+int
+register_isdn_module(isdn_module *m) {
+#if 0
+	isdn_module_list **pp = &dev->modules;
+	isdn_module *new = kmalloc(sizeof(isdn_module_list), GFP_KERNEL);
+
+	if (!new) {
+		printk(KERN_WARNING "isdn: Out of memory in register_isdn_module\n");
+		return -1;
+	}
+	while (*pp && (*pp)->orig != m)
+		pp = &(*pp)->next;
+	if (*pp != NULL) {
+		printk(KERN_WARNING "isdn: Module %s already registered\n", m->name);
+		return -1;
+	}
+	while (*pp && ((*pp)->module.priority < m->priority))
+		pp = &(*pp)->next;
+	new->next = *pp;
+	new->orig = m;
+	new->module = *m;
+ 
+	*pp = new;
+#endif
+	return 0;
+}
+
+int
+unregister_isdn_module(isdn_module *m) {
+#if 0
+	isdn_module_list **pp = &dev->modules;
+
+	while (*pp && *pp != m)
+		pp = &(*pp)->next;
+	if (*pp == NULL) {
+		printk(KERN_WARNING "isdn: Module %s not found\n", m->name);
+		return -1;
+	}
+#endif
+	return 0;
+}
+
+int
+isdn_add_channels(driver *d, int drvidx, int n, int adding)
+{
+	int j, k, m;
+	ulong flags;
+
+#if LINUX_VERSION_CODE >= 131841
+	init_waitqueue_head(&d->st_waitq);
+#endif
+	if (d->flags & DRV_FLAG_RUNNING)
+		return -1;
+	if (n < 1)
+		return 0;
+
+	m = (adding) ? d->channels + n : n;
+
+	if (dev->channels + n > ISDN_MAX_CHANNELS) {
+		printk(KERN_WARNING "register_isdn: Max. %d channels supported\n",
+		       ISDN_MAX_CHANNELS);
+		return -1;
+	}
+
+	if ((adding) && (d->rcverr))
+		kfree(d->rcverr);
+	if (!(d->rcverr = (int *) kmalloc(sizeof(int) * m, GFP_KERNEL))) {
+		printk(KERN_WARNING "register_isdn: Could not alloc rcverr\n");
+		return -1;
+	}
+	memset((char *) d->rcverr, 0, sizeof(int) * m);
+
+	if ((adding) && (d->rcvcount))
+		kfree(d->rcvcount);
+	if (!(d->rcvcount = (int *) kmalloc(sizeof(int) * m, GFP_KERNEL))) {
+		printk(KERN_WARNING "register_isdn: Could not alloc rcvcount\n");
+		if (!adding) kfree(d->rcverr);
+		return -1;
+	}
+	memset((char *) d->rcvcount, 0, sizeof(int) * m);
+
+	if ((adding) && (d->rpqueue)) {
+		for (j = 0; j < d->channels; j++)
+			isdn_free_queue(&d->rpqueue[j]);
+		kfree(d->rpqueue);
+	}
+	if (!(d->rpqueue =
+	      (struct sk_buff_head *) kmalloc(sizeof(struct sk_buff_head) * m, GFP_KERNEL))) {
+		printk(KERN_WARNING "register_isdn: Could not alloc rpqueue\n");
+		if (!adding) {
+			kfree(d->rcvcount);
+			kfree(d->rcverr);
+		}
+		return -1; 
+	}
+	for (j = 0; j < m; j++) {
+		skb_queue_head_init(&d->rpqueue[j]);
+	}
+
+	if ((adding) && (d->rcv_waitq))
+		kfree(d->rcv_waitq);
+#if LINUX_VERSION_CODE < 131841
+	if (!(d->rcv_waitq = (struct wait_queue **)
+	      kmalloc(sizeof(struct wait_queue *) * m, GFP_KERNEL))) {
+#else
+	d->rcv_waitq = (wait_queue_head_t *)
+		kmalloc(sizeof(wait_queue_head_t) * 2 * m, GFP_KERNEL);
+	if (!d->rcv_waitq) {
+#endif
+		printk(KERN_WARNING "register_isdn: Could not alloc rcv_waitq\n");
+		if (!adding) {
+			kfree(d->rpqueue);
+			kfree(d->rcvcount);
+			kfree(d->rcverr);
+		}
+		return -1;
+	}
+#if LINUX_VERSION_CODE < 131841
+	memset((char *) d->rcv_waitq, 0, sizeof(struct wait_queue *) * m);
+
+	if ((adding) && (d->snd_waitq))
+		kfree(d->snd_waitq);
+	if (!(d->snd_waitq = (struct wait_queue **)
+	      kmalloc(sizeof(struct wait_queue *) * m, GFP_KERNEL))) {
+		printk(KERN_WARNING "register_isdn: Could not alloc snd_waitq\n");
+		if (!adding) {
+			kfree(d->rcv_waitq);
+			kfree(d->rpqueue);
+			kfree(d->rcvcount);
+			kfree(d->rcverr);
+		}
+		return -1;
+	}
+	memset((char *) d->snd_waitq, 0, sizeof(struct wait_queue *) * m);
+#else
+	d->snd_waitq = d->rcv_waitq + m;
+	for (j = 0; j < m; j++) {
+		init_waitqueue_head(&d->rcv_waitq[m]);
+		init_waitqueue_head(&d->snd_waitq[m]);
+	}
+#endif
+
+	dev->channels += n;
+	save_flags(flags);
+	cli();
+	for (j = d->channels; j < m; j++)
+		for (k = 0; k < ISDN_MAX_CHANNELS; k++)
+			if (dev->chanmap[k] < 0) {
+				dev->chanmap[k] = j;
+				dev->drvmap[k] = drvidx;
+				break;
+			}
+	restore_flags(flags);
+	d->channels = m;
+	return 0;
+}
+
 /*
  * Low-level-driver registration
  */
 
 EXPORT_SYMBOL(register_isdn);
+EXPORT_SYMBOL(register_isdn_module);
+EXPORT_SYMBOL(unregister_isdn_module);
+#ifdef CONFIG_ISDN_PPP
+EXPORT_SYMBOL(isdn_ppp_register_compressor);
+EXPORT_SYMBOL(isdn_ppp_unregister_compressor);
+#endif
 
 int
 register_isdn(isdn_if * i)
 {
 	driver *d;
-	int n,
-	 j,
-	 k;
+	int j;
 	ulong flags;
 	int drvidx;
 
@@ -2072,12 +2266,6 @@
 		       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 (!i->writebuf_skb) {
 		printk(KERN_WARNING "register_isdn: No write routine given.\n");
 		return 0;
@@ -2087,59 +2275,22 @@
 		return 0;
 	}
 	memset((char *) d, 0, sizeof(driver));
-	init_waitqueue_head(&d->st_waitq); 
-	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 =
-	      (struct sk_buff_head *) kmalloc(sizeof(struct sk_buff_head) * n, GFP_KERNEL))) {
-		printk(KERN_WARNING "register_isdn: Could not alloc rpqueue\n");
-		kfree(d->rcvcount);
-		kfree(d->rcverr);
-		kfree(d);
-		return 0;
-	}
-	for (j = 0; j < n; j++) {
-		skb_queue_head_init(&d->rpqueue[j]);
-	}
-	d->rcv_waitq = (wait_queue_head_t *)
-		kmalloc(sizeof(wait_queue_head_t) * 2 * n, GFP_KERNEL);
-	if (!d->rcv_waitq) { 
-		printk(KERN_WARNING "register_isdn: Could not alloc rcv_waitq\n");
-		kfree(d->rpqueue);
-		kfree(d->rcvcount);
-		kfree(d->rcverr);
-		kfree(d);
-		return 0;
-	}
-	d->snd_waitq = d->rcv_waitq + n; 		
-	for (j = 0; j < n; j++) { 
-		init_waitqueue_head(&d->rcv_waitq[n]); 
-		init_waitqueue_head(&d->snd_waitq[n]); 
-	}
-	d->channels = n;
-	d->loaded = 1;
+
 	d->maxbufsize = i->maxbufsize;
 	d->pktcount = 0;
 	d->stavail = 0;
-	d->running = 0;
-	d->flags = 0;
+	d->flags = DRV_FLAG_LOADED;
+	d->online = 0;
 	d->interface = i;
+	d->channels = 0;
 	for (drvidx = 0; drvidx < ISDN_MAX_DRIVERS; drvidx++)
 		if (!dev->drv[drvidx])
 			break;
+	if (isdn_add_channels(d, drvidx, i->channels, 0)) {
+		kfree(d);
+		return 0;
+	}
 	i->channels = drvidx;
-
 	i->rcvcallb_skb = isdn_receive_skb_callback;
 	i->statcallb = isdn_status_callback;
 	if (!strlen(i->id))
@@ -2149,15 +2300,7 @@
 	for (j = 0; j < drvidx; j++)
 		if (!strcmp(i->id, dev->drvid[j]))
 			sprintf(i->id, "line%d", drvidx);
-	for (j = 0; j < n; j++)
-		for (k = 0; k < ISDN_MAX_CHANNELS; k++)
-			if (dev->chanmap[k] < 0) {
-				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++;
@@ -2209,15 +2352,21 @@
 	memset((char *) dev, 0, sizeof(isdn_dev));
 	init_timer(&dev->timer);
 	dev->timer.function = isdn_timer_funct;
+#if LINUX_VERSION_CODE < 131841
+	dev->sem = MUTEX;
+#else
 	init_MUTEX(&dev->sem);
-	init_waitqueue_head(&dev->info_waitq); 
+	init_waitqueue_head(&dev->info_waitq);
+#endif
 	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
 		dev->drvmap[i] = -1;
 		dev->chanmap[i] = -1;
 		dev->m_idx[i] = -1;
 		strcpy(dev->num[i], "???");
+#if LINUX_VERSION_CODE >= 131841
 		init_waitqueue_head(&dev->mdm.info[i].open_wait);
 		init_waitqueue_head(&dev->mdm.info[i].close_wait);
+#endif
 	}
 	if (register_chrdev(ISDN_MAJOR, "isdn", &isdn_fops)) {
 		printk(KERN_WARNING "isdn: Could not register control devices\n");
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/isdn_common.h linux/drivers/isdn/isdn_common.h
--- v2.3.3/linux/drivers/isdn/isdn_common.h	Fri May 14 18:55:18 1999
+++ linux/drivers/isdn/isdn_common.h	Sun May 23 10:03:42 1999
@@ -1,8 +1,8 @@
-/* $Id: isdn_common.h,v 1.9 1998/02/20 17:19:01 fritz Exp $
+/* $Id: isdn_common.h,v 1.15 1999/04/18 14:06:50 fritz Exp $
 
  * header for Linux ISDN subsystem, common used functions and debugging-switches (linklevel).
  *
- * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1994-1999  by Fritz Elfert (fritz@isdn4linux.de)
  * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
  * Copyright 1995,96    by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
  *
@@ -20,11 +20,36 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * Note: This file differs from the corresponding revision as present in the
- * isdn4linux CVS repository because some later bug fixes have been extracted
- * from the repository and merged into this file. -- Henner Eisen
- *
  * $Log: isdn_common.h,v $
+ * Revision 1.15  1999/04/18 14:06:50  fritz
+ * Removed TIMRU stuff.
+ *
+ * Revision 1.14  1999/04/12 12:33:18  fritz
+ * Changes from 2.0 tree.
+ *
+ * Revision 1.13  1999/03/02 12:04:47  armin
+ * -added ISDN_STAT_ADDCH to increase supported channels after
+ *  register_isdn().
+ * -ttyI now goes on-hook on ATZ when B-Ch is connected.
+ * -added timer-function for register S7 (Wait for Carrier).
+ * -analog modem (ISDN_PROTO_L2_MODEM) implementations.
+ * -on L2_MODEM a string will be appended to the CONNECT-Message,
+ *  which is provided by the HL-Driver in parm.num in ISDN_STAT_BCONN.
+ * -variable "dialing" used for ATA also, for interrupting call
+ *  establishment and register S7.
+ *
+ * Revision 1.12  1998/06/26 15:12:27  fritz
+ * Added handling of STAT_ICALL with incomplete CPN.
+ * Added AT&L for ttyI emulator.
+ * Added more locking stuff in tty_write.
+ *
+ * Revision 1.11  1998/04/14 16:28:47  he
+ * Fixed user space access with interrupts off and remaining
+ * copy_{to,from}_user() -> -EFAULT return codes
+ *
+ * Revision 1.10  1998/03/07 18:21:03  cal
+ * Dynamic Timeout-Rule-Handling vs. 971110 included
+ *
  * Revision 1.9  1998/02/20 17:19:01  fritz
  * Added common stub for sending commands to lowlevel.
  *
@@ -90,10 +115,16 @@
 extern void isdn_timer_ctrl(int tf, int onoff);
 extern void isdn_unexclusive_channel(int di, int ch);
 extern int isdn_getnum(char **);
+#if LINUX_VERSION_CODE < 131841
+extern int isdn_readbchan(int, int, u_char *, u_char *, int, struct wait_queue**);
+#else
 extern int isdn_readbchan(int, int, u_char *, u_char *, int, wait_queue_head_t *);
+#endif
 extern int isdn_get_free_channel(int, int, int, int, int);
 extern int isdn_writebuf_skb_stub(int, int, int, struct sk_buff *);
 extern int register_isdn(isdn_if * i);
+extern int isdn_wildmat(char *, char *);
+extern int isdn_add_channels(driver *, int, int, int);
 #if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP)
 extern void isdn_dumppkt(char *, u_char *, int, int);
 #endif
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/isdn_concap.c linux/drivers/isdn/isdn_concap.c
--- v2.3.3/linux/drivers/isdn/isdn_concap.c	Wed Apr  1 16:21:03 1998
+++ linux/drivers/isdn/isdn_concap.c	Sun May 23 10:03:42 1999
@@ -1,10 +1,21 @@
-/* $Id: isdn_concap.c,v 1.2 1998/01/31 22:49:21 keil Exp $
+/* $Id: isdn_concap.c,v 1.5 1998/10/30 18:44:48 he Exp $
  
  * Stuff to support the concap_proto by isdn4linux. isdn4linux - specific
  * stuff goes here. Stuff that depends only on the concap protocol goes to
  * another -- protocol specific -- source file.
  *
  * $Log: isdn_concap.c,v $
+ * Revision 1.5  1998/10/30 18:44:48  he
+ * pass return value from isdn_net_dial_req for dialmode change
+ *
+ * Revision 1.4  1998/10/30 17:55:24  he
+ * dialmode for x25iface and multulink ppp
+ *
+ * Revision 1.3  1998/05/26 22:39:22  he
+ * sync'ed with 2.1.102 where appropriate (CAPABILITY changes)
+ * concap typo
+ * cleared dev.tbusy in isdn_net BCONN status callback
+ *
  * Revision 1.2  1998/01/31 22:49:21  keil
  * correct comments
  *
@@ -20,14 +31,9 @@
 #include <linux/concap.h>
 #include "isdn_concap.h"
 
-/* The declaration of this (or a plublic variant thereof) should really go
-   in linux/isdn.h. But we really need it here (and isdn_ppp, like us, also
-   refers to that private function currently owned by isdn_net.c) */
-extern int isdn_net_force_dial_lp(isdn_net_local *);
-
 
 /* The following set of device service operations are for encapsulation
-   protocols that require for reliable datalink sematics. That means:
+   protocols that require for reliable datalink semantics. That means:
 
    - before any data is to be submitted the connection must explicitly
      be set up.
@@ -66,9 +72,9 @@
 	IX25DEBUG( "isdn_concap_dl_connect_req: %s \n", ndev -> name);
 
 	/* dial ... */
-	ret = isdn_net_force_dial_lp( lp );
+	ret = isdn_net_dial_req( lp );
 	if ( ret ) IX25DEBUG("dialing failed\n");
-	return 0;
+	return ret;
 }
 
 int isdn_concap_dl_disconn_req(struct concap_proto *concap)
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/isdn_net.c linux/drivers/isdn/isdn_net.c
--- v2.3.3/linux/drivers/isdn/isdn_net.c	Tue Jan 19 11:06:52 1999
+++ linux/drivers/isdn/isdn_net.c	Sun May 23 10:03:42 1999
@@ -1,8 +1,8 @@
-/* $Id: isdn_net.c,v 1.55 1998/02/23 19:38:22 fritz Exp $
+/* $Id: isdn_net.c,v 1.84 1999/04/18 14:06:55 fritz Exp $
 
  * Linux ISDN subsystem, network interfaces and related functions (linklevel).
  *
- * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1994-1998  by Fritz Elfert (fritz@isdn4linux.de)
  * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
  * Copyright 1995,96    by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
  *
@@ -20,11 +20,119 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * Note: This file differs from the corresponding revision as present in the
- * isdn4linux CVS repository because some later bug fixes have been extracted
- * from the repository and merged into this file. -- Henner Eisen
- *
  * $Log: isdn_net.c,v $
+ * Revision 1.84  1999/04/18 14:06:55  fritz
+ * Removed TIMRU stuff.
+ *
+ * Revision 1.83  1999/04/12 12:33:23  fritz
+ * Changes from 2.0 tree.
+ *
+ * Revision 1.82  1999/01/17 00:55:58  he
+ * added mark_bh in BCONN statcallb and cleaned up some dead code
+ *
+ * Revision 1.81  1999/01/15 16:36:52  he
+ * replaced icmp_send() by dst_link_failure()
+ *
+ * Revision 1.80  1998/12/01 13:06:22  paul
+ * Also huptimeout with dialmode == manual
+ *
+ * Revision 1.79  1998/10/30 17:55:27  he
+ * dialmode for x25iface and multulink ppp
+ *
+ * Revision 1.78  1998/10/26 18:20:46  he
+ * re-inserted p=p->next in isdn_net_find_icall() (fixes kernel lock up
+ * on incoming call not matching the first interface)
+ *
+ * Revision 1.77  1998/10/23 10:18:44  paul
+ * Implementation of "dialmode" (successor of "status")
+ * You also need current isdnctrl for this!
+ *
+ * Revision 1.76  1998/09/07 22:00:05  he
+ * flush method for 2.1.118 and above
+ * updated IIOCTLNETGPN
+ *
+ * Revision 1.75  1998/08/31 21:09:50  he
+ * new ioctl IIOCNETGPN for /dev/isdninfo (get network interface'
+ *     peer phone number)
+ *
+ * Revision 1.74  1998/07/30 11:28:32  paul
+ * printk message only appeared when status is off and interface is rawIP,
+ * which is confusing for people who don't know about "isdnctrl status <if> on".
+ *
+ * Revision 1.73  1998/06/26 22:01:37  keil
+ * tx_queue_len = 5 was too small
+ *
+ * Revision 1.72  1998/06/26 15:12:31  fritz
+ * Added handling of STAT_ICALL with incomplete CPN.
+ * Added AT&L for ttyI emulator.
+ * Added more locking stuff in tty_write.
+ *
+ * Revision 1.71  1998/06/18 22:43:08  fritz
+ * Bugfix: Setting ndev->do_ioctl had beed accidetly removed at abc-cleanup.
+ *
+ * Revision 1.70  1998/06/17 19:50:49  he
+ * merged with 2.1.10[34] (cosmetics and udelay() -> mdelay())
+ * brute force fix to avoid Ugh's in isdn_tty_write()
+ * cleaned up some dead code
+ *
+ * Revision 1.69  1998/06/09 12:27:37  cal
+ * Changed default of local netdev flags: ISDN_NET_STOPPED is default now,
+ * so autodial is suppressed for that device until it is switched on using
+ * 'isdnctrl status dev-name on'.
+ *
+ * Revision 1.68  1998/06/07 00:20:05  fritz
+ * abc cleanup.
+ *
+ * Revision 1.67  1998/06/02 12:10:08  detabc
+ * wegen einer einstweiliger verfuegung gegen DW ist zur zeit
+ * die abc-extension bis zur klaerung der rechtslage nicht verfuegbar
+ *
+ * Revision 1.66  1998/05/26 22:39:24  he
+ * sync'ed with 2.1.102 where appropriate (CAPABILITY changes)
+ * concap typo
+ * cleared dev.tbusy in isdn_net BCONN status callback
+ *
+ * Revision 1.65  1998/05/22 10:01:11  detabc
+ * in case of a icmp-unreach condition the tcp-keepalive-entrys
+ * will be dropped from the internal double-link-list (only abc-extension).
+ * send icmp unreach only if the skb->protocol == ETH_P_IP
+ * speedup abc-no-dchan  redial
+ *
+ * Revision 1.64  1998/05/07 19:58:39  detabc
+ * bugfix in abc_delayed_hangup
+ * optimize keepalive-tests for abc_rawip
+ *
+ * Revision 1.63  1998/05/05 23:23:36  detabc
+ * change ICMP_HOST_UNREACH to ICMP_NET_UNREACH (only abc-ext.)
+ * set dev->tbusy to zero in isdn_net_unreachable() (only abc-ext.)
+ * drop all new packets and send ICMP_NET_UNREACH for
+ * min. dialwait to max. dialwait * 6 time. (only abc-ext.)
+ * change random-deliver of packets (small first) from all emcapsulation
+ * to only rawip with ABC-Router-Flag enabled.
+ *
+ * Revision 1.62  1998/05/03 17:40:42  detabc
+ * Include abc-extension-support for >= 2.1.x Kernels in
+ * isdn_net.c and isdn_common.c. alpha-test OK and running !
+ *
+ * Revision 1.61  1998/04/16 19:19:42  keil
+ * Fix from vger (tx max qlength)
+ *
+ * Revision 1.60  1998/04/14 16:28:49  he
+ * Fixed user space access with interrupts off and remaining
+ * copy_{to,from}_user() -> -EFAULT return codes
+ *
+ * Revision 1.59  1998/03/07 22:37:33  fritz
+ * Bugfix: restore_flags missing.
+ *
+ * Revision 1.58  1998/03/07 18:21:05  cal
+ * Dynamic Timeout-Rule-Handling vs. 971110 included
+ *
+ * Revision 1.57  1998/02/25 18:31:13  fritz
+ * Added debugging output in adjust_header.
+ *
+ * Revision 1.56  1998/02/25 17:49:42  he
+ * Changed return codes caused be failing copy_{to,from}_user to -EFAULT
+ *
  * Revision 1.55  1998/02/23 19:38:22  fritz
  * Corrected check for modified feature-flags.
  *
@@ -248,9 +356,7 @@
 #include <linux/isdn.h>
 #include <net/arp.h>
 #include <net/dst.h>
-#ifndef DEV_NUMBUFFS
 #include <net/pkt_sched.h>
-#endif
 #include <linux/inetdevice.h>
 #include "isdn_common.h"
 #include "isdn_net.h"
@@ -265,14 +371,10 @@
 /* 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 *);
-#ifdef DEV_NUMBUFFS
-static void dev_purge_queues(struct device *dev);	/* move this to net/core/dev.c */
-#endif
 
-char *isdn_net_revision = "$Revision: 1.55 $";
+char *isdn_net_revision = "$Revision: 1.84 $";
 
  /*
   * Code for raw-networking over ISDN
@@ -293,16 +395,21 @@
 		
 		dst_link_failure(skb);
 	}
+	else {  /* dial not triggered by rawIP packet */
+		printk(KERN_DEBUG "isdn_net: %s: %s\n",
+			   dev->name,
+			   (reason != NULL) ? reason : "reason unknown");
+	}
 }
 
 static void
 isdn_net_reset(struct device *dev)
 {
 #ifdef CONFIG_ISDN_X25
-	struct concap_device_ops * dops = 
+	struct concap_device_ops * dops =
 		( (isdn_net_local *) dev->priv ) -> dops;
-	struct concap_proto * cprot = 
-		( (isdn_net_local *) dev->priv ) -> netdev -> cprot; 
+	struct concap_proto * cprot =
+		( (isdn_net_local *) dev->priv ) -> netdev -> cprot;
 #endif
 	ulong flags;
 
@@ -311,7 +418,7 @@
 	dev->interrupt = 0;
 	dev->tbusy = 0;
 #ifdef CONFIG_ISDN_X25
-	if( cprot && cprot -> pops && dops ) 
+	if( cprot && cprot -> pops && dops )
 		cprot -> pops -> restart ( cprot, dev, dops );
 #endif
 	restore_flags(flags);
@@ -338,7 +445,7 @@
 		if (ifa != NULL)
 			memcpy(dev->dev_addr+2, &ifa->ifa_local, 4);
 	}
- 
+
 	/* If this interface has slaves, start them also */
 
 	if ((p = (((isdn_net_local *) dev->priv)->slave))) {
@@ -362,6 +469,7 @@
 
 	save_flags(flags);
 	cli();
+	lp->flags |= ISDN_NET_CONNECTED;
 	lp->isdn_device = dev->drvmap[idx];
 	lp->isdn_channel = dev->chanmap[idx];
 	dev->rx_netdev[idx] = lp->netdev;
@@ -387,10 +495,6 @@
 		dev_kfree_skb(lp->sav_skb);
 		lp->sav_skb = NULL;
 	}
-#ifdef DEV_NUMBUFFS
-	if (!lp->master)        /* purge only for master device */
-		dev_purge_queues(&lp->netdev->dev);
-#else
 	if (!lp->master) {	/* reset only master device */
 		/* Moral equivalent of dev_purge_queues():
 		   BEWARE! This chunk of code cannot be called from hardware
@@ -398,7 +502,6 @@
 		 */
 		qdisc_reset(lp->netdev->dev.qdisc);
 	}
-#endif
 	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;
@@ -444,7 +547,13 @@
 		if ((l->flags & ISDN_NET_CONNECTED) && (!l->dialstate)) {
 			anymore = 1;
 			l->huptimer++;
-			if ((l->onhtime) && (l->huptimer > l->onhtime)) {
+			/*
+			 * if there is some dialmode where timeout-hangup
+			 * should _not_ be done, check for that here
+			 */
+			if ((l->onhtime) &&
+			    (l->huptimer > l->onhtime))
+			{
 				if (l->hupflags & ISDN_MANCHARGE &&
 				    l->hupflags & ISDN_CHARGEHUP) {
 					while (jiffies - l->chargetime > l->chargeint)
@@ -469,6 +578,11 @@
 				} else if (l->hupflags & ISDN_INHUP)
 					isdn_net_hangup(&p->dev);
 			}
+
+			if(dev->global_flags & ISDN_GLOBAL_STOPPED || (ISDN_NET_DIALMODE(*l) == ISDN_NET_DM_OFF)) {
+				isdn_net_hangup(&p->dev);
+				break;
+			}
 		}
 		p = (isdn_net_dev *) p->next;
 	}
@@ -487,7 +601,7 @@
 {
 	isdn_net_dev *p = dev->st_netdev[idx];
 	int cmd = c->command;
-	
+
 	if (p) {
 		isdn_net_local *lp = p->local;
 #ifdef CONFIG_ISDN_X25
@@ -538,23 +652,12 @@
 				   failed. If there are generic encap protocol
 				   receiver routines signal the closure of
 				   the link*/
-				
-				if( !(lp->flags & ISDN_NET_CONNECTED) 
+
+				if( !(lp->flags & ISDN_NET_CONNECTED)
 				    && pops && pops -> disconn_ind )
 					pops -> disconn_ind(cprot);
 #endif /* CONFIG_ISDN_X25 */
 				if ((!lp->dialstate) && (lp->flags & ISDN_NET_CONNECTED)) {
-					lp->flags &= ~ISDN_NET_CONNECTED;
-					if (lp->first_skb) {
-						dev_kfree_skb(lp->first_skb);
-						lp->first_skb = NULL;
-					}
-					if (lp->sav_skb) {
-						dev_kfree_skb(lp->sav_skb);
-						lp->sav_skb = NULL;
-					}
-					isdn_free_channel(lp->isdn_device, lp->isdn_channel,
-							  ISDN_USAGE_NET);
 #ifdef CONFIG_ISDN_PPP
 					isdn_ppp_free(lp);
 #endif
@@ -562,10 +665,7 @@
 					printk(KERN_INFO "%s: remote hangup\n", lp->name);
 					printk(KERN_INFO "%s: Chargesum is %d\n", lp->name,
 					       lp->charge);
-					lp->isdn_device = -1;
-					lp->isdn_channel = -1;
-					dev->st_netdev[idx] = NULL;
-					dev->rx_netdev[idx] = NULL;
+					isdn_net_unbind_channel(lp);
 					return 1;
 				}
 				break;
@@ -607,6 +707,11 @@
 						lp->chargetime = jiffies;
 						printk(KERN_DEBUG "isdn_net: chargetime of %s now %d\n",
 						lp->name, lp->chargetime);
+
+						/* reset dial-timeout */
+						lp->dialstarted = 0;
+						lp->dialwait_timer = 0;
+
 						/* Immediately send first skb to speed up arp */
 #ifdef CONFIG_ISDN_PPP
 						if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
@@ -617,13 +722,13 @@
 						if( pops )
 							if( pops->connect_ind)
 								pops->connect_ind(cprot);
-
 #endif /* CONFIG_ISDN_X25 */
 						if (lp->first_skb) {
-							
+
 							if (!(isdn_net_xmit(&p->dev, lp, lp->first_skb)))
 								lp->first_skb = NULL;
-						} else {
+						}
+						else {
 							/*
 							 * dev.tbusy is usually cleared implicitly by isdn_net_xmit(,,lp->first_skb).
 							 * With an empty lp->first_skb, we need to do this ourselves
@@ -721,6 +826,13 @@
 					break;
 				}
 				anymore = 1;
+
+				if(lp->dialtimeout > 0)
+					if(lp->dialstarted == 0 || jiffies > (lp->dialstarted + lp->dialtimeout + lp->dialwait)) {
+						lp->dialstarted = jiffies;
+						lp->dialwait_timer = 0;
+					}
+
 				lp->dialstate++;
 				/* Fall through */
 			case 2:
@@ -735,12 +847,22 @@
 				lp->dialretry = 0;
 				anymore = 1;
 				lp->dialstate++;
-				/* Falls through */
+				/* Fall through */
 			case 3:
 				/* Setup interface, dial current phone-number, switch to next number.
 				 * If list of phone-numbers is exhausted, increment
 				 * retry-counter.
 				 */
+				if(dev->global_flags & ISDN_GLOBAL_STOPPED || (ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_OFF)) {
+					char *s;
+					if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+						s = "dial suppressed: isdn system stopped";
+					else
+						s = "dial suppressed: dialmode `off'";
+					isdn_net_unreachable(&p->dev, lp->first_skb, s);
+					isdn_net_hangup(&p->dev);
+					break;
+				}
 				cmd.driver = lp->isdn_device;
 				cmd.command = ISDN_CMD_SETL2;
 				cmd.arg = lp->isdn_channel + (lp->l2_proto << 8);
@@ -765,6 +887,16 @@
 					lp->dialstate = 4;
 					printk(KERN_INFO "%s: Open leased line ...\n", lp->name);
 				} else {
+					if(lp->dialtimeout > 0)
+						if(jiffies > (lp->dialstarted + lp->dialtimeout)) {
+							restore_flags(flags);
+							lp->dialwait_timer = jiffies + lp->dialwait;
+							lp->dialstarted = 0;
+							isdn_net_unreachable(&p->dev, lp->first_skb, "dial: timed out");
+							isdn_net_hangup(&p->dev);
+							break;
+						}
+
 					sprintf(cmd.parm.setup.phone, "%s", lp->dial->num);
 					/*
 					 * Switch to next number or back to start if at end of list.
@@ -772,6 +904,17 @@
 					if (!(lp->dial = (isdn_net_phone *) lp->dial->next)) {
 						lp->dial = lp->phone[1];
 						lp->dialretry++;
+
+						if (lp->dialretry > lp->dialmax) {
+							restore_flags(flags);
+							if (lp->dialtimeout == 0) {
+								lp->dialwait_timer = jiffies + lp->dialwait;
+								lp->dialstarted = 0;
+								isdn_net_unreachable(&p->dev, lp->first_skb, "dial: tried all numbers dialmax times");
+							}
+							isdn_net_hangup(&p->dev);
+							break;
+						}
 					}
 					restore_flags(flags);
 					cmd.driver = lp->isdn_device;
@@ -786,7 +929,7 @@
 						isdn_info_update();
 					}
 					printk(KERN_INFO "%s: dialing %d %s...\n", lp->name,
-					       lp->dialretry - 1, cmd.parm.setup.phone);
+					       lp->dialretry, cmd.parm.setup.phone);
 					lp->dtimer = 0;
 #ifdef ISDN_DEBUG_NET_DIAL
 					printk(KERN_DEBUG "dial: d=%d c=%d\n", lp->isdn_device,
@@ -810,15 +953,11 @@
 				break;
 			case 4:
 				/* Wait for D-Channel-connect.
-				 * If timeout and max retries not
-				 * reached, switch back to state 3.
+				 * If timeout, switch back to state 3.
+				 * Dialmax-handling moved to state 3.
 				 */
-				if (lp->dtimer++ > ISDN_TIMER_DTIMEOUT10) {
-					if (lp->dialretry < lp->dialmax) {
-						lp->dialstate = 3;
-					} else
-						isdn_net_hangup(&p->dev);
-				}
+				if (lp->dtimer++ > ISDN_TIMER_DTIMEOUT10)
+					lp->dialstate = 3;
 				anymore = 1;
 				break;
 			case 5:
@@ -895,7 +1034,8 @@
 				/* Remote does callback. Hangup after cbdelay, then wait for incoming
 				 * call (in state 4).
 				 */
-				if (lp->dtimer++ > lp->cbdelay) {
+				if (lp->dtimer++ > lp->cbdelay)
+				{
 					printk(KERN_INFO "%s: hangup waiting for callback ...\n", lp->name);
 					lp->dtimer = 0;
 					lp->dialstate = 4;
@@ -930,7 +1070,6 @@
 #endif
 
 	if (lp->flags & ISDN_NET_CONNECTED) {
-		lp->flags &= ~ISDN_NET_CONNECTED;
 		printk(KERN_INFO "isdn_net: local hangup %s\n", lp->name);
 #ifdef CONFIG_ISDN_PPP
 		isdn_ppp_free(lp);
@@ -938,7 +1077,7 @@
 #ifdef CONFIG_ISDN_X25
 		/* try if there are generic encap protocol
 		   receiver routines and signal the closure of
-		   the link */		
+		   the link */
 		if( pops && pops -> disconn_ind )
 		  pops -> disconn_ind(cprot);
 #endif /* CONFIG_ISDN_X25 */
@@ -968,7 +1107,7 @@
 	char addinfo[100];
 
 	addinfo[0] = '\0';
-	/* This check stolen from 2.1.72 dev_queue_xmit_nit() */ 
+	/* This check stolen from 2.1.72 dev_queue_xmit_nit() */
 	if (skb->nh.raw < skb->data || skb->nh.raw >= skb->tail) {
 		/* fall back to old isdn_net_log_packet method() */
 		char * buf = skb->data;
@@ -1062,20 +1201,17 @@
 	if (ret == len) {
 		lp->transcount += len;
 		clear_bit(0, (void *) &(ndev->tbusy));
-		mark_bh(NET_BH);
 		return 0;
 	}
 	if (ret < 0) {
 		dev_kfree_skb(skb);
 		lp->stats.tx_errors++;
 		clear_bit(0, (void *) &(ndev->tbusy));
-		mark_bh(NET_BH);
 		return 0;
 	}
 	return 1;
 }
 
-
 /*
  *  Helper function for isdn_net_start_xmit.
  *  When called, the connection is already established.
@@ -1150,9 +1286,11 @@
 	if (!skb)
 		return;
 	if (lp->p_encap == ISDN_NET_ENCAP_ETHER) {
-		ulong pullsize = (ulong)skb->nh.raw - (ulong)skb->data - ETH_HLEN;
-		if (pullsize)
+		int pullsize = (ulong)skb->nh.raw - (ulong)skb->data - ETH_HLEN;
+		if (pullsize > 0) {
+			printk(KERN_DEBUG "isdn_net: Pull junk %d\n", pullsize);
 			skb_pull(skb, pullsize);
+		}
 	}
 }
 
@@ -1166,7 +1304,7 @@
 {
 	isdn_net_local *lp = (isdn_net_local *) ndev->priv;
 #ifdef CONFIG_ISDN_X25
-	struct concap_proto * cprot = lp -> netdev -> cprot; 
+	struct concap_proto * cprot = lp -> netdev -> cprot;
 #endif
 
 	if (ndev->tbusy) {
@@ -1179,7 +1317,7 @@
 	ndev->tbusy = 1; /* left instead of obsolete test_and_set_bit() */
 #ifdef CONFIG_ISDN_X25
 /* At this point hard_start_xmit() passes control to the encapsulation
-   protocol (if present). 
+   protocol (if present).
    For X.25 auto-dialing is completly bypassed because:
    - It does not conform with the semantics of a reliable datalink
      service as needed by X.25 PLP.
@@ -1205,17 +1343,46 @@
 #endif
 		if (!(lp->flags & ISDN_NET_CONNECTED)) {
 			int chi;
+			/* only do autodial if allowed by config */
+			if (!(ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_AUTO)) {
+				isdn_net_unreachable(ndev, skb, "dial rejected: interface not in dialmode `auto'");
+				dev_kfree_skb(skb);
+				ndev->tbusy = 0;
+				return 0;
+			}
 			if (lp->phone[1]) {
 				ulong flags;
 				save_flags(flags);
 				cli();
+
+				if(lp->dialwait_timer <= 0)
+					if(lp->dialstarted > 0 && lp->dialtimeout > 0 && jiffies < lp->dialstarted + lp->dialtimeout + lp->dialwait)
+						lp->dialwait_timer = lp->dialstarted + lp->dialtimeout + lp->dialwait;
+
+				if(lp->dialwait_timer > 0) {
+					if(jiffies < lp->dialwait_timer) {
+						isdn_net_unreachable(ndev, skb, "dial rejected: retry-time not reached");
+						dev_kfree_skb(skb);
+						ndev->tbusy = 0;
+						restore_flags(flags);
+						return 0;
+					} else
+						lp->dialwait_timer = 0;
+				}
+
 				/* Grab a free ISDN-Channel */
-				if ((chi =
+				if (((chi =
 				     isdn_get_free_channel(ISDN_USAGE_NET,
 							   lp->l2_proto,
 							   lp->l3_proto,
 							   lp->pre_device,
-						 lp->pre_channel)) < 0) {
+						 lp->pre_channel)) < 0) &&
+					((chi =
+				     isdn_get_free_channel(ISDN_USAGE_NET,
+							   lp->l2_proto,
+							   lp->l3_proto,
+							   lp->pre_device,
+						 lp->pre_channel^1)) < 0)) {
 					restore_flags(flags);
 					isdn_net_unreachable(ndev, skb,
 							   "No channel");
@@ -1227,7 +1394,6 @@
 				if (dev->net_verbose)
 					isdn_net_log_skb(skb, lp);
 				lp->dialstate = 1;
-				lp->flags |= ISDN_NET_CONNECTED;
 				/* Connect interface with channel */
 				isdn_net_bind_channel(lp, chi);
 #ifdef CONFIG_ISDN_PPP
@@ -1290,8 +1456,8 @@
 {
 	struct device *p;
 #ifdef CONFIG_ISDN_X25
-	struct concap_proto * cprot = 
-		( (isdn_net_local *) dev->priv ) -> netdev -> cprot; 
+	struct concap_proto * cprot =
+		( (isdn_net_local *) dev->priv ) -> netdev -> cprot;
 	/* printk(KERN_DEBUG "isdn_net_close %s\n" , dev-> name ); */
 #endif
 
@@ -1304,9 +1470,9 @@
 		/* If this interface has slaves, stop them also */
 		while (p) {
 #ifdef CONFIG_ISDN_X25
-			cprot = ( (isdn_net_local *) p->priv ) 
-				-> netdev -> cprot; 
-			if( cprot && cprot -> pops ) 
+			cprot = ( (isdn_net_local *) p->priv )
+				-> netdev -> cprot;
+			if( cprot && cprot -> pops )
 				cprot -> pops -> close( cprot );
 #endif
 			isdn_net_hangup(p);
@@ -1392,7 +1558,7 @@
 	int len;
 	cisco_hdr *ch;
 	cisco_slarp *s;
-	
+
 	if (!skb) {
 		printk(KERN_WARNING
 		       "%s: Could not allocate SLARP reply\n", lp->name);
@@ -1406,7 +1572,7 @@
 	s = (cisco_slarp *)skb_put(skb, sizeof(cisco_slarp));
 	if (is_reply) {
 		s->code = htonl(CISCO_SLARP_REPLY);
- 		memset(&s->slarp.reply.ifaddr, 0, sizeof(__u32));
+		memset(&s->slarp.reply.ifaddr, 0, sizeof(__u32));
 		memset(&s->slarp.reply.netmask, 0, sizeof(__u32));
 	} else {
 		lp->cisco_myseq++;
@@ -1589,7 +1755,7 @@
 		default:
 #ifdef CONFIG_ISDN_X25
 		  /* try if there are generic sync_device receiver routines */
-			if(cprot) if(cprot -> pops) 
+			if(cprot) if(cprot -> pops)
 				if( cprot -> pops -> data_ind){
 					cprot -> pops -> data_ind(cprot,skb);
 					return;
@@ -1600,6 +1766,7 @@
 			kfree_skb(skb);
 			return;
 	}
+
 	netif_rx(skb);
 	return;
 }
@@ -1788,16 +1955,12 @@
 	ndev->type = ARPHRD_ETHER;
 	ndev->addr_len = ETH_ALEN;
 
-	ndev->tx_queue_len = 10; /* for clients without MPPP 5 is better.  */
+	/* for clients with MPPP maybe higher values better */
+	ndev->tx_queue_len = 30;
 
 	for (i = 0; i < ETH_ALEN; i++)
 		ndev->broadcast[i] = 0xff;
 
-#ifdef DEV_NUMBUFFS
-	for (i = 0; i < DEV_NUMBUFFS; i++)
-		skb_queue_head_init(&ndev->buffs[i]);
-#endif
-
 	/* The ISDN-specific entries in the device structure. */
 	ndev->open = &isdn_net_open;
 	ndev->hard_start_xmit = &isdn_net_start_xmit;
@@ -1813,86 +1976,15 @@
 				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 originally 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.
- *
- * Possible Patterns:
- *
- * '?'     matches one character
- * '*'     matches zero or more characters
- * [xyz]   matches the set of characters 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)
 {
@@ -1945,6 +2037,8 @@
  *               2 = Reject call, wait cbdelay, then call back
  *               3 = Reject call
  *               4 = Wait cbdelay, then call back
+ *               5 = No appropriate interface for this call,
+ *                   would eventually match if CID was longer.
  */
 int
 isdn_net_find_icall(int di, int ch, int idx, setup_parm setup)
@@ -1953,6 +2047,7 @@
 	int si1;
 	int si2;
 	int ematch;
+	int wret;
 	int swapped;
 	int sidx = 0;
 	isdn_net_dev *p;
@@ -1987,13 +2082,13 @@
 	}
 	n = (isdn_net_phone *) 0;
 	p = dev->netdev;
-	ematch = 0;
+	ematch = wret = swapped = 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) {
+		int matchret;
 		isdn_net_local *lp = p->local;
 
 		/* If last check has triggered as binding-swap, revert it */
@@ -2006,18 +2101,22 @@
 				break;
 		}
 		swapped = 0;
-		if (!strcmp(isdn_map_eaz2msn(lp->msn, di), eaz))
+		if (!(matchret = isdn_wildmat(eaz, isdn_map_eaz2msn(lp->msn, di))))
 			ematch = 1;
+		/* Remember if more numbers eventually can match */
+		if (matchret > wret)
+			wret = matchret;
 #ifdef ISDN_DEBUG_NET_ICALL
 		printk(KERN_DEBUG "n_fi: if='%s', l.msn=%s, l.flags=%d, l.dstate=%d\n",
 		       lp->name, lp->msn, lp->flags, lp->dialstate);
 #endif
-		if ((!strcmp(isdn_map_eaz2msn(lp->msn, di), eaz)) &&	/* EAZ is matching   */
-		    (((!(lp->flags & ISDN_NET_CONNECTED)) &&	/* but not connected */
-		      (USG_NONE(dev->usage[idx]))) ||	/* and ch. unused or */
-		     ((((lp->dialstate == 4) || (lp->dialstate == 12)) &&	/* if dialing        */
-		       (!(lp->flags & ISDN_NET_CALLBACK)))	/* but no callback   */
-		     ))) {
+		if ((!matchret) &&                                        /* EAZ is matching   */
+		    (((!(lp->flags & ISDN_NET_CONNECTED)) &&              /* but not connected */
+		      (USG_NONE(dev->usage[idx]))) ||                     /* and ch. unused or */
+		     ((((lp->dialstate == 4) || (lp->dialstate == 12)) && /* if dialing        */
+		       (!(lp->flags & ISDN_NET_CALLBACK)))                /* but no callback   */
+		     )))
+			 {
 #ifdef ISDN_DEBUG_NET_ICALL
 			printk(KERN_DEBUG "n_fi: match1, pdev=%d pch=%d\n",
 			       lp->pre_device, lp->pre_channel);
@@ -2085,8 +2184,6 @@
 #ifdef ISDN_DEBUG_NET_ICALL
 						printk(KERN_DEBUG "n_fi: already on 2nd channel\n");
 #endif
-						p = (isdn_net_dev *) p->next;
-						continue;
 					}
 				}
 			}
@@ -2096,7 +2193,7 @@
 			n = lp->phone[0];
 			if (lp->flags & ISDN_NET_SECURE) {
 				while (n) {
-					if (isdn_net_wildmat(nr, n->num))
+					if (!isdn_wildmat(nr, n->num))
 						break;
 					n = (isdn_net_phone *) n->next;
 				}
@@ -2105,7 +2202,21 @@
 #ifdef ISDN_DEBUG_NET_ICALL
 				printk(KERN_DEBUG "n_fi: match3\n");
 #endif
-				/* Here we got an interface matched, now see if it is up.
+				/* matching interface found */
+
+				/*
+				 * Is the state STOPPED?
+				 * If so, no dialin is allowed,
+				 * so reject actively.
+				 * */
+				if (ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_OFF) {
+					restore_flags(flags);
+					printk(KERN_INFO "incoming call, interface %s `stopped' -> rejected\n",
+					       lp->name);
+					return 3;
+				}
+				/*
+				 * Is the interface up?
 				 * If not, reject the call actively.
 				 */
 				if (!p->dev.start) {
@@ -2140,6 +2251,17 @@
 				}
 				if (lp->flags & ISDN_NET_CALLBACK) {
 					int chi;
+					/*
+					 * Is the state MANUAL?
+					 * If so, no callback can be made,
+					 * so reject actively.
+					 * */
+					if (ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_OFF) {
+						restore_flags(flags);
+						printk(KERN_INFO "incoming call for callback, interface %s `off' -> rejected\n",
+						       lp->name);
+						return 3;
+					}
 					printk(KERN_DEBUG "%s: call from %s -> %s, start callback\n",
 					       lp->name, nr, eaz);
 					if (lp->phone[1]) {
@@ -2155,7 +2277,6 @@
 						/* Setup dialstate. */
 						lp->dtimer = 0;
 						lp->dialstate = 11;
-						lp->flags |= ISDN_NET_CONNECTED;
 						/* Connect interface with channel */
 						isdn_net_bind_channel(lp, chi);
 #ifdef CONFIG_ISDN_PPP
@@ -2217,10 +2338,10 @@
 		p = (isdn_net_dev *) p->next;
 	}
 	/* If none of configured EAZ/MSN matched and not verbose, be silent */
-	if (ematch || dev->net_verbose)
+	if (!ematch || dev->net_verbose)
 		printk(KERN_INFO "isdn_net: call from %s -> %d %s ignored\n", nr, di, eaz);
 	restore_flags(flags);
-	return 0;
+	return (wret == 2)?5:0;
 }
 
 /*
@@ -2253,6 +2374,7 @@
 			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,
@@ -2263,7 +2385,6 @@
 				return -EAGAIN;
 			}
 			lp->dialstate = 1;
-			lp->flags |= ISDN_NET_CONNECTED;
 			/* Connect interface with channel */
 			isdn_net_bind_channel(lp, chi);
 #ifdef CONFIG_ISDN_PPP
@@ -2285,6 +2406,20 @@
 }
 
 /*
+ * This is called from certain upper protocol layers (multilink ppp
+ * and x25iface encapsulation module) that want to initiate dialing
+ * themselves.
+ */
+int
+isdn_net_dial_req(isdn_net_local * lp)
+{
+	/* is there a better error code? */
+	if (!(ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_AUTO)) return -EBUSY;
+
+	return isdn_net_force_dial_lp(lp);
+}
+
+/*
  * Force a net-interface to dial out.
  * This is always called from within userspace (ISDN_IOCTL_NET_DIAL).
  */
@@ -2383,8 +2518,13 @@
 	netdev->local->onhtime = 10;	/* Default hangup-time for saving costs
 	   of those who forget configuring this */
 	netdev->local->dialmax = 1;
-	netdev->local->flags = ISDN_NET_CBHUP;	/* Hangup before Callback */
+	netdev->local->flags = ISDN_NET_CBHUP | ISDN_NET_DM_MANUAL;	/* Hangup before Callback, manual dial */
 	netdev->local->cbdelay = 25;	/* Wait 5 secs before Callback */
+	netdev->local->dialtimeout = -1;  /* Infinite Dial-Timeout */
+	netdev->local->dialwait = 5 * HZ; /* Wait 5 sec. after failed dial */
+	netdev->local->dialstarted = 0;   /* Jiffies of last dial-start */
+	netdev->local->dialwait_timer = 0;  /* Jiffies of earliest next dial-start */
+
 	/* Put into to netdev-chain */
 	netdev->next = (void *) dev->netdev;
 	dev->netdev = netdev;
@@ -2465,7 +2605,7 @@
 			save_flags(flags);
 			cli(); /* avoid races with incoming events trying to
 				  call cprot->pops methods */
-			if( cprot && cprot -> pops ) 
+			if( cprot && cprot -> pops )
 				cprot -> pops -> proto_del ( cprot );
 			p -> cprot = NULL;
 			lp -> dops = NULL;
@@ -2479,7 +2619,7 @@
 			p -> cprot = isdn_concap_new( cfg -> p_encap );
 			/* p -> cprot == NULL now if p_encap is not supported
 			   by means of the concap_proto mechanism */
-			/* the protocol is not configured yet; this will 
+			/* the protocol is not configured yet; this will
 			   happen later when isdn_net_reset() is called */
 #endif
 		}
@@ -2508,7 +2648,7 @@
 			if( cfg->p_encap >= 0 &&
 			    cfg->p_encap <= ISDN_NET_ENCAP_MAX_ENCAP )
 				break;
-			printk(KERN_WARNING 
+			printk(KERN_WARNING
 			       "%s: encapsulation protocol %d not supported\n",
 			       p->local->name, cfg->p_encap);
 			return -EINVAL;
@@ -2583,6 +2723,8 @@
 		lp->triggercps = cfg->triggercps;
 		lp->slavedelay = cfg->slavedelay * HZ;
 		lp->pppbind = cfg->pppbind;
+		lp->dialtimeout = cfg->dialtimeout >= 0 ? cfg->dialtimeout * HZ : -1;
+		lp->dialwait = cfg->dialwait * HZ;
 		if (cfg->secure)
 			lp->flags |= ISDN_NET_SECURE;
 		else
@@ -2604,6 +2746,16 @@
 				lp->flags &= ~ISDN_NET_CALLBACK;
 				break;
 		}
+		lp->flags &= ~ISDN_NET_DIALMODE_MASK;	/* first all bits off */
+		if (cfg->dialmode && !(cfg->dialmode & ISDN_NET_DIALMODE_MASK)) {
+			/* old isdnctrl version, where only 0 or 1 is given */
+			printk(KERN_WARNING
+			     "Old isdnctrl version detected! Please update.\n");
+			lp->flags |= ISDN_NET_DM_OFF; /* turn on `off' bit */
+		}
+		else {
+			lp->flags |= cfg->dialmode;  /* turn on selected bits */
+		}
 		if (cfg->chargehup)
 			lp->hupflags |= ISDN_CHARGEHUP;
 		else
@@ -2671,6 +2823,7 @@
 		if (lp->flags & ISDN_NET_CBOUT)
 			cfg->callback = 2;
 		cfg->cbhup = (lp->flags & ISDN_NET_CBHUP) ? 1 : 0;
+		cfg->dialmode = lp->flags & ISDN_NET_DIALMODE_MASK;
 		cfg->chargehup = (lp->hupflags & 4) ? 1 : 0;
 		cfg->ihup = (lp->hupflags & 8) ? 1 : 0;
 		cfg->cbdelay = lp->cbdelay;
@@ -2680,6 +2833,8 @@
 		cfg->chargeint = (lp->hupflags & ISDN_CHARGEHUP) ?
 		    (lp->chargeint / HZ) : 0;
 		cfg->pppbind = lp->pppbind;
+		cfg->dialtimeout = lp->dialtimeout >= 0 ? lp->dialtimeout / HZ : -1;
+		cfg->dialwait = lp->dialwait / HZ;
 		if (lp->slave)
 			strcpy(cfg->slave, ((isdn_net_local *) lp->slave->priv)->name);
 		else
@@ -2749,9 +2904,37 @@
 }
 
 /*
- * Delete a phone-number from an interface.
+ * Copy a string containing the peer's phone number of a connected interface
+ * to user space.
  */
+int
+isdn_net_getpeer(isdn_net_ioctl_phone *phone, isdn_net_ioctl_phone *peer)
+{
+	isdn_net_dev *p = isdn_net_findif(phone->name);
+	int ch, dv, idx;
 
+	if (!p) return -ENODEV;
+	/*
+	 * Theoretical race: while this executes, the remote number might
+	 * become invalid (hang up) or change (new connection), resulting
+         * in (partially) wrong number copied to user. This race
+	 * currently ignored.
+	 */
+	ch = p->local->isdn_channel;
+	dv = p->local->isdn_device;
+	if(ch<0 && dv<0) return -ENOTCONN;
+	idx = isdn_dc2minor(dv, ch);
+	if (idx<0) return -ENODEV;
+	/* for pre-bound channels, we need this extra check */
+	if ( strncmp(dev->num[idx],"???",3) == 0 ) return -ENOTCONN;
+	strncpy(phone->phone,dev->num[idx],ISDN_MSNLEN);
+	phone->outgoing=USG_OUTGOING(idx);
+	if ( copy_to_user(peer,phone,sizeof(*peer)) ) return -EFAULT;
+	return 0;
+}
+/*
+ * Delete a phone-number from an interface.
+ */
 int
 isdn_net_delphone(isdn_net_ioctl_phone * phone)
 {
@@ -2957,21 +3140,3 @@
 	restore_flags(flags);
 	return 0;
 }
-
-#ifdef DEV_NUMBUFFS
-/*
- * helper function to flush device queues
- * the better place would be net/core/dev.c
- */
-static void
-dev_purge_queues(struct device *dev)
-{
-	int i;
-	for (i = 0; i < DEV_NUMBUFFS; i++) {
-		struct sk_buff *skb;
-		while ((skb = skb_dequeue(&dev->buffs[i])))
-			dev_kfree_skb(skb);
-	}
-
-}
-#endif
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/isdn_net.h linux/drivers/isdn/isdn_net.h
--- v2.3.3/linux/drivers/isdn/isdn_net.h	Wed Apr  1 16:21:03 1998
+++ linux/drivers/isdn/isdn_net.h	Sun May 23 10:03:42 1999
@@ -1,8 +1,8 @@
-/* $Id: isdn_net.h,v 1.6 1997/10/09 21:28:54 fritz Exp $
+/* $Id: isdn_net.h,v 1.9 1999/04/12 12:33:27 fritz Exp $
 
  * header for Linux ISDN subsystem, network related functions (linklevel).
  *
- * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1994-1999  by Fritz Elfert (fritz@isdn4linux.de)
  * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
  * Copyright 1995,96    by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
  *
@@ -21,6 +21,16 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_net.h,v $
+ * Revision 1.9  1999/04/12 12:33:27  fritz
+ * Changes from 2.0 tree.
+ *
+ * Revision 1.8  1998/10/30 17:55:33  he
+ * dialmode for x25iface and multulink ppp
+ *
+ * Revision 1.7  1998/08/31 21:09:55  he
+ * new ioctl IIOCNETGPN for /dev/isdninfo (get network interface'
+ *     peer phone number)
+ *
  * Revision 1.6  1997/10/09 21:28:54  fritz
  * New HL<->LL interface:
  *   New BSENT callback with nr. of bytes included.
@@ -99,6 +109,7 @@
 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_getpeer(isdn_net_ioctl_phone *, isdn_net_ioctl_phone *);
 extern int isdn_net_delphone(isdn_net_ioctl_phone *);
 extern int isdn_net_find_icall(int, int, int, setup_parm);
 extern void isdn_net_hangup(struct device *);
@@ -111,3 +122,4 @@
 			     struct sk_buff *);
 extern int isdn_net_rcv_skb(int, struct sk_buff *);
 extern void isdn_net_slarp_out(void);
+extern int isdn_net_dial_req(isdn_net_local *);
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/isdn_ppp.c linux/drivers/isdn/isdn_ppp.c
--- v2.3.3/linux/drivers/isdn/isdn_ppp.c	Fri May 14 18:55:18 1999
+++ linux/drivers/isdn/isdn_ppp.c	Sun May 23 10:03:42 1999
@@ -1,4 +1,4 @@
-/* $Id: isdn_ppp.c,v 1.33 1998/02/20 17:11:54 fritz Exp $
+/* $Id: isdn_ppp.c,v 1.47 1999/04/18 14:06:59 fritz Exp $
  *
  * Linux ISDN subsystem, functions for synchronous PPP (linklevel).
  *
@@ -18,11 +18,50 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * Note: This file differs from the corresponding revision as present in the
- * isdn4linux CVS repository because some later bug fixes have been extracted
- * from the repository and merged into this file. -- Henner Eisen
- *
  * $Log: isdn_ppp.c,v $
+ * Revision 1.47  1999/04/18 14:06:59  fritz
+ * Removed TIMRU stuff.
+ *
+ * Revision 1.46  1999/04/12 12:33:35  fritz
+ * Changes from 2.0 tree.
+ *
+ * Revision 1.45  1998/12/30 17:48:24  paul
+ * fixed syncPPP callback out
+ *
+ * Revision 1.44  1998/10/30 17:55:34  he
+ * dialmode for x25iface and multulink ppp
+ *
+ * Revision 1.43  1998/10/29 17:23:54  hipp
+ * Minor MPPP fixes, verboser logging.
+ *
+ * Revision 1.42  1998/07/20 11:30:07  hipp
+ * Readded compression check
+ *
+ * Revision 1.41  1998/07/08 16:50:57  hipp
+ * Compression changes
+ *
+ * Revision 1.40  1998/04/06 19:07:27  hipp
+ * added check, whether compression is enabled.
+ *
+ * Revision 1.39  1998/03/25 22:46:53  hipp
+ * Some additional CCP changes.
+ *
+ * Revision 1.38  1998/03/24 16:33:06  hipp
+ * More CCP changes. BSD compression now "works" on a local loopback link.
+ * Moved some isdn_ppp stuff from isdn.h to isdn_ppp.h
+ *
+ * Revision 1.37  1998/03/22 18:50:49  hipp
+ * Added BSD Compression for syncPPP .. UNTESTED at the moment
+ *
+ * Revision 1.36  1998/03/09 17:46:30  he
+ * merged in 2.1.89 changes
+ *
+ * Revision 1.35  1998/03/07 18:21:11  cal
+ * Dynamic Timeout-Rule-Handling vs. 971110 included
+ *
+ * Revision 1.34  1998/02/25 17:49:48  he
+ * Changed return codes caused be failing copy_{to,from}_user to -EFAULT
+ *
  * Revision 1.33  1998/02/20 17:11:54  fritz
  * Changes for recent kernels.
  *
@@ -157,13 +196,16 @@
  * experimental for dynamic addressing: readdress IP frames
  */
 #undef ISDN_SYNCPPP_READDRESS
+#define CONFIG_ISDN_CCP 1
 
 #include <linux/config.h>
 #define __NO_VERSION__
 #include <linux/module.h>
 #include <linux/version.h>
-#include <linux/isdn.h>
 #include <linux/poll.h>
+#include <linux/isdn.h>
+#include <linux/ppp-comp.h>
+
 #include "isdn_common.h"
 #include "isdn_ppp.h"
 #include "isdn_net.h"
@@ -180,13 +222,33 @@
 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);
-static int isdn_ppp_set_compressor(struct ippp_struct *is,int num);
+static int isdn_ppp_set_compressor(struct ippp_struct *is,struct isdn_ppp_comp_data *);
 static struct sk_buff *isdn_ppp_decompress(struct sk_buff *,
-				struct ippp_struct *,struct ippp_struct *);
+				struct ippp_struct *,struct ippp_struct *,int proto);
 static void isdn_ppp_receive_ccp(isdn_net_dev * net_dev, isdn_net_local * lp,
-				struct sk_buff *skb);
+				struct sk_buff *skb,int proto);
 static struct sk_buff *isdn_ppp_compress(struct sk_buff *skb_in,int *proto,
 	struct ippp_struct *is,struct ippp_struct *master,int type);
+static void isdn_ppp_send_ccp(isdn_net_dev *net_dev, isdn_net_local *lp,
+	 struct sk_buff *skb);
+
+/* New CCP stuff */
+static void isdn_ppp_ccp_kickup(struct ippp_struct *is);
+static void isdn_ppp_ccp_xmit_reset(struct ippp_struct *is, int proto,
+				    unsigned char code, unsigned char id,
+				    unsigned char *data, int len);
+static struct ippp_ccp_reset *isdn_ppp_ccp_reset_alloc(struct ippp_struct *is);
+static void isdn_ppp_ccp_reset_free_state(struct ippp_struct *is,
+					  unsigned char id);
+static void isdn_ppp_ccp_timer_callback(unsigned long closure);
+static struct ippp_ccp_reset_state *isdn_ppp_ccp_reset_alloc_state(struct ippp_struct *is,
+						      unsigned char id);
+static void isdn_ppp_ccp_reset_trans(struct ippp_struct *is,
+				     struct isdn_ppp_resetparams *rp);
+static void isdn_ppp_ccp_reset_ack_rcvd(struct ippp_struct *is,
+					unsigned char id);
+
+
 
 #ifdef CONFIG_ISDN_MPP
 static int isdn_ppp_bundle(struct ippp_struct *, int unit);
@@ -199,18 +261,16 @@
 static void isdn_ppp_free_mpqueue(isdn_net_dev *);
 #endif
 
-char *isdn_ppp_revision = "$Revision: 1.33 $";
+char *isdn_ppp_revision = "$Revision: 1.47 $";
 
 static struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS];
 static struct isdn_ppp_compressor *ipc_head = NULL;
 
-extern int isdn_net_force_dial_lp(isdn_net_local *);
-
 /*
  * frame log (debug)
  */
 static void
-isdn_ppp_frame_log(char *info, char *data, int len, int maxlen)
+isdn_ppp_frame_log(char *info, char *data, int len, int maxlen,int unit,int slot)
 {
 	int cnt,
 	 j,
@@ -223,13 +283,14 @@
 	for (i = 0, cnt = 0; cnt < maxlen; i++) {
 		for (j = 0; j < 16 && cnt < maxlen; j++, cnt++)
 			sprintf(buf + j * 3, "%02x ", (unsigned char) data[cnt]);
-		printk(KERN_DEBUG "%s[%d]: %s\n", info, i, buf);
+		printk(KERN_DEBUG "[%d/%d].%s[%d]: %s\n",unit,slot, info, i, buf);
 	}
 }
 
 /*
  * unbind isdn_net_local <=> ippp-device
  * note: it can happen, that we hangup/free the master before the slaves
+ *       in this case we bind another lp to the master device
  */
 int
 isdn_ppp_free(isdn_net_local * lp)
@@ -267,8 +328,7 @@
 	if ((is->state & IPPP_CONNECT))
 		isdn_ppp_closewait(lp->ppp_slot);	/* force wakeup on ippp device */
 	else if (is->state & IPPP_ASSIGNED)
-		is->state = IPPP_OPEN;	/* fallback to 'OPEN but not ASSIGEND' staet */
-
+		is->state = IPPP_OPEN;	/* fallback to 'OPEN but not ASSIGNED' state */
 
 	if (is->debug & 0x1)
 		printk(KERN_DEBUG "isdn_ppp_free %d %lx %lx\n", lp->ppp_slot, (long) lp, (long) is->lp);
@@ -320,14 +380,16 @@
 			}
 		}
 	} else {
-		for (i = 0; i < ISDN_MAX_CHANNELS; i++)
-			if (ippp_table[i]->minor == lp->pppbind && ippp_table[i]->state == IPPP_OPEN)
+		for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+			if (ippp_table[i]->minor == lp->pppbind &&
+			    (ippp_table[i]->state & IPPP_OPEN) == IPPP_OPEN)
 				break;
+		}
 	}
 
 	if (i >= ISDN_MAX_CHANNELS) {
 		restore_flags(flags);
-		printk(KERN_WARNING "isdn_ppp_bind: Can't find usable ippp device.\n");
+		printk(KERN_WARNING "isdn_ppp_bind: Can't find a (free) connection to the ipppd daemon.\n");
 		return -1;
 	}
 	unit = isdn_ppp_if_get_unit(lp->name);	/* get unit number from interface name .. ugly! */
@@ -336,6 +398,15 @@
 		return -1;
 	}
 	lp->ppp_slot = i;
+
+	/* reset some values */
+	lp->netdev->ib.bundled = 0;
+	lp->netdev->ib.next_num = 0;
+	lp->netdev->ib.modify = 0;
+	lp->netdev->ib.last = NULL;
+	lp->netdev->ib.min = 0;
+	lp->netdev->ib.sq = NULL;
+
 	is = ippp_table[i];
 	is->lp = lp;
 	is->unit = unit;
@@ -359,7 +430,10 @@
 
 	ippp_table[lp->ppp_slot]->state = IPPP_OPEN | IPPP_CONNECT | IPPP_NOBLOCK;
 
-	wake_up_interruptible(&ippp_table[lp->ppp_slot]->wq);
+#if LINUX_VERSION_CODE < 131841
+	if (ippp_table[lp->ppp_slot]->wq)
+#endif
+		wake_up_interruptible(&ippp_table[lp->ppp_slot]->wq);
 }
 
 /*
@@ -376,7 +450,11 @@
 		return 0;
 	is = ippp_table[slot];
 
+#if LINUX_VERSION_CODE < 131841
+	if (is->state && is->wq)
+#else
 	if (is->state)
+#endif
 		wake_up_interruptible(&is->wq);
 
 	is->state = IPPP_CLOSEWAIT;
@@ -417,14 +495,19 @@
 	}
 	is = file->private_data = ippp_table[slot];
 
+#if 0
 	if (is->debug & 0x1)
+#endif
 		printk(KERN_DEBUG "ippp, open, slot: %d, minor: %d, state: %04x\n", slot, min, is->state);
 
 	/* compression stuff */
-	is->compressor = NULL;
-	is->decomp_stat = is->comp_stat = NULL;
-	is->link_compressor = NULL;
-	is->link_decomp_stat = is->link_comp_stat = NULL;
+	is->link_compressor   = is->compressor = NULL;
+	is->link_decompressor = is->decompressor = NULL;
+	is->link_comp_stat    = is->comp_stat = NULL;
+	is->link_decomp_stat  = is->decomp_stat = NULL;
+	is->compflags = 0;
+
+	is->reset = isdn_ppp_ccp_reset_alloc(is);
 
 	is->lp = NULL;
 	is->mp_seqno = 0;       /* MP sequence number */
@@ -436,9 +519,11 @@
 	is->mru = 1524;         /* MRU, default 1524 */
 	is->maxcid = 16;        /* VJ: maxcid */
 	is->tk = current;
-	/* next two are redundant, but be paranoid */ 
-	init_waitqueue_head(&is->wq);  /* read() wait queue */
-	init_waitqueue_head(&is->wql);  /* select() wait queue */
+#if LINUX_VERSION_CODE < 131841
+	is->wq = NULL;          /* read() wait queue */
+#else
+	init_waitqueue_head(&is->wq);
+#endif
 	is->first = is->rq + NUM_RCV_BUFFS - 1;	/* receive queue */
 	is->last = is->rq;
 	is->minor = min;
@@ -491,10 +576,30 @@
 	is->last = is->rq;
 
 #ifdef CONFIG_ISDN_PPP_VJ
+/* TODO: if this was the previous master: link the slcomp to the new master */
 	slhc_free(is->slcomp);
 	is->slcomp = NULL;
 #endif
 
+/* TODO: if this was the previous master: link the the stuff to the new master */
+	if(is->comp_stat)
+		is->compressor->free(is->comp_stat);
+	if(is->link_comp_stat)
+		is->link_compressor->free(is->link_comp_stat);
+	if(is->link_decomp_stat)
+		is->link_decompressor->free(is->link_decomp_stat);
+	if(is->decomp_stat)
+		is->decompressor->free(is->decomp_stat);
+        is->compressor   = is->link_compressor   = NULL;
+        is->decompressor = is->link_decompressor = NULL;
+	is->comp_stat    = is->link_comp_stat    = NULL;
+        is->decomp_stat  = is->link_decomp_stat  = NULL;
+
+	if(is->reset)
+		kfree(is->reset);
+	is->reset = NULL;
+
+	/* this slot is ready for new connections */
 	is->state = 0;
 }
 
@@ -505,7 +610,7 @@
 get_arg(void *b, void *val, int len)
 {
 	if (len <= 0)
-		len = sizeof(unsigned long);
+		len = sizeof(void *);
 	if (copy_from_user((void *) val, b, len))
 		return -EFAULT;
 	return 0;
@@ -515,15 +620,12 @@
  * set arg .. ioctl helper
  */
 static int
-set_arg(void *b, unsigned long val, void *str)
+set_arg(void *b, void *val,int len)
 {
-	if (!str) {
-		if (copy_to_user(b, (void *) &val, 4))
-			return -EFAULT;
-	} else {
-		if (copy_to_user(b, str, val))
-			return -EFAULT;
-	}
+	if(len <= 0)
+		len = sizeof(void *);
+	if (copy_to_user(b, (void *) val, len))
+		return -EFAULT;
 	return 0;
 }
 
@@ -534,9 +636,10 @@
 isdn_ppp_ioctl(int min, struct file *file, unsigned int cmd, unsigned long arg)
 {
 	unsigned long val;
-	int num,r;
+	int r,i,j;
 	struct ippp_struct *is;
 	isdn_net_local *lp;
+	struct isdn_ppp_comp_data data;
 
 	is = (struct ippp_struct *) file->private_data;
 	lp = is->lp;
@@ -552,7 +655,7 @@
 #ifdef CONFIG_ISDN_MPP
 			if (!(is->state & IPPP_CONNECT))
 				return -EINVAL;
-			if ((r = get_arg((void *) arg, &val, 0)))
+			if ((r = get_arg((void *) arg, &val, sizeof(val) )))
 				return r;
 			printk(KERN_DEBUG "iPPP-bundle: minor: %d, slave unit: %d, master unit: %d\n",
 			       (int) min, (int) is->unit, (int) val);
@@ -562,24 +665,30 @@
 #endif
 			break;
 		case PPPIOCGUNIT:	/* get ppp/isdn unit number */
-			if ((r = set_arg((void *) arg, is->unit, NULL)))
+			if ((r = set_arg((void *) arg, &is->unit, sizeof(is->unit) )))
+				return r;
+			break;
+		case PPPIOCGIFNAME:
+			if(!lp)
+				return -EINVAL;
+			if ((r = set_arg((void *) arg, lp->name,strlen(lp->name))))
 				return r;
 			break;
 		case PPPIOCGMPFLAGS:	/* get configuration flags */
-			if ((r = set_arg((void *) arg, is->mpppcfg, NULL)))
+			if ((r = set_arg((void *) arg, &is->mpppcfg, sizeof(is->mpppcfg) )))
 				return r;
 			break;
 		case PPPIOCSMPFLAGS:	/* set configuration flags */
-			if ((r = get_arg((void *) arg, &val, 0)))
+			if ((r = get_arg((void *) arg, &val, sizeof(val) )))
 				return r;
 			is->mpppcfg = val;
 			break;
 		case PPPIOCGFLAGS:	/* get configuration flags */
-			if ((r = set_arg((void *) arg, is->pppcfg, NULL)))
+			if ((r = set_arg((void *) arg, &is->pppcfg,sizeof(is->pppcfg) )))
 				return r;
 			break;
 		case PPPIOCSFLAGS:	/* set configuration flags */
-			if ((r = get_arg((void *) arg, &val, 0))) {
+			if ((r = get_arg((void *) arg, &val, sizeof(val) ))) {
 				return r;
 			}
 			if (val & SC_ENABLE_IP && !(is->pppcfg & SC_ENABLE_IP) && (is->state & IPPP_CONNECT)) {
@@ -598,12 +707,12 @@
 			if (lp) {
 				struct ppp_idle pidle;
 				pidle.xmit_idle = pidle.recv_idle = lp->huptimer;
-				if ((r = set_arg((void *) arg, sizeof(struct ppp_idle), &pidle)))
+				if ((r = set_arg((void *) arg, &pidle,sizeof(struct ppp_idle))))
 					 return r;
 			}
 			break;
 		case PPPIOCSMRU:	/* set receive unit size for PPP */
-			if ((r = get_arg((void *) arg, &val, 0)))
+			if ((r = get_arg((void *) arg, &val, sizeof(val) )))
 				return r;
 			is->mru = val;
 			break;
@@ -612,7 +721,7 @@
 		case PPPIOCSMPMTU:
 			break;
 		case PPPIOCSMAXCID:	/* set the maximum compression slot id */
-			if ((r = get_arg((void *) arg, &val, 0)))
+			if ((r = get_arg((void *) arg, &val, sizeof(val) )))
 				return r;
 			val++;
 			if (is->maxcid != val) {
@@ -635,31 +744,33 @@
 			}
 			break;
 		case PPPIOCGDEBUG:
-			if ((r = set_arg((void *) arg, is->debug, 0)))
+			if ((r = set_arg((void *) arg, &is->debug, sizeof(is->debug) )))
 				return r;
 			break;
 		case PPPIOCSDEBUG:
-			if ((r = get_arg((void *) arg, &val, 0)))
+			if ((r = get_arg((void *) arg, &val, sizeof(val) )))
 				return r;
 			is->debug = val;
 			break;
 		case PPPIOCGCOMPRESSORS:
 			{
-				unsigned long protos = 0;
+				unsigned long protos[8] = {0,};
 				struct isdn_ppp_compressor *ipc = ipc_head;
 				while(ipc) {
-					protos |= (0x1<<ipc->num);
+					j = ipc->num / (sizeof(long)*8);
+					i = ipc->num % (sizeof(long)*8);
+					if(j < 8)
+						protos[j] |= (0x1<<i);
 					ipc = ipc->next;
 				}
-				if ((r = set_arg((void *) arg, protos, 0)))
+				if ((r = set_arg((void *) arg,protos,8*sizeof(long) )))
 					return r;
 			}
 			break;
 		case PPPIOCSCOMPRESSOR:
-			if ((r = get_arg((void *) arg, &num, sizeof(int))))
+			if ((r = get_arg((void *) arg, &data, sizeof(struct isdn_ppp_comp_data))))
 				return r;
-			return isdn_ppp_set_compressor(is, num);
-			break;
+			return isdn_ppp_set_compressor(is, &data);
 		case PPPIOCGCALLINFO:
 			{
 				struct pppcallinfo pci;
@@ -678,7 +789,7 @@
 					if(lp->flags & ISDN_NET_CALLBACK)
 						pci.calltype |= CALLTYPE_CALLBACK;
 				}
-				return set_arg((void *)arg,sizeof(struct pppcallinfo),&pci);
+				return set_arg((void *)arg,&pci,sizeof(struct pppcallinfo));
 			}
 		default:
 			break;
@@ -701,9 +812,12 @@
 		printk(KERN_DEBUG "isdn_ppp_poll: minor: %d\n",
 				MINOR(file->f_dentry->d_inode->i_rdev));
 
+	/* just registers wait_queue hook. This doesn't really wait. */
 	poll_wait(file, &is->wq, wait);
 
 	if (!(is->state & IPPP_OPEN)) {
+		if(is->state == IPPP_CLOSEWAIT)
+			return POLLHUP;
 		printk(KERN_DEBUG "isdn_ppp: device not open\n");
 		return POLLERR;
 	}
@@ -777,7 +891,10 @@
 	is->last = bl->next;
 	restore_flags(flags);
 
-	wake_up_interruptible(&is->wq);
+#if LINUX_VERSION_CODE < 131841
+	if (is->wq)
+#endif
+		wake_up_interruptible(&is->wq);
 
 	return len;
 }
@@ -863,7 +980,8 @@
 		if (lp->isdn_device < 0 || lp->isdn_channel < 0)
 			return 0;
 
-		if (dev->drv[lp->isdn_device]->running && lp->dialstate == 0 &&
+		if ((dev->drv[lp->isdn_device]->flags & DRV_FLAG_RUNNING) &&
+			lp->dialstate == 0 &&
 		    (lp->flags & ISDN_NET_CONNECTED)) {
 			int cnt;
 			struct sk_buff *skb;
@@ -876,8 +994,11 @@
 				return -EFAULT;
 			if (is->debug & 0x40) {
 				printk(KERN_DEBUG "ppp xmit: len %d\n", (int) skb->len);
-				isdn_ppp_frame_log("xmit", skb->data, skb->len, 32);
+				isdn_ppp_frame_log("xmit", skb->data, skb->len, 32,is->unit,lp->ppp_slot);
 			}
+
+			isdn_ppp_send_ccp(lp->netdev,lp,skb); /* keeps CCP/compression states in sync */
+
 			if ((cnt = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, 1, skb)) != count) {
 				if (lp->sav_skb) {
 					dev_kfree_skb(lp->sav_skb);
@@ -910,8 +1031,6 @@
 			return -1;
 		}
 		memset((char *) ippp_table[i], 0, sizeof(struct ippp_struct));
-		init_waitqueue_head(&ippp_table[i]->wq); 
-		init_waitqueue_head(&ippp_table[i]->wql); 
 		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;
@@ -963,8 +1082,9 @@
 	is = ippp_table[lp->ppp_slot];
 
 	if (is->debug & 0x4) {
-		printk(KERN_DEBUG "ippp_receive: len: %d\n", (int) skb->len);
-		isdn_ppp_frame_log("receive", skb->data, skb->len, 32);
+		printk(KERN_DEBUG "ippp_receive: is:%08lx lp:%08lx slot:%d unit:%d len:%d\n",
+		       (long)is,(long)lp,lp->ppp_slot,is->unit,(int) skb->len);
+		isdn_ppp_frame_log("receive", skb->data, skb->len, 32,is->unit,lp->ppp_slot);
 	}
 	if (net_dev->local->master) {
 		printk(KERN_WARNING "isdn_ppp_receice: net_dev != master\n");
@@ -982,13 +1102,18 @@
 #ifdef CONFIG_ISDN_MPP
 	if (!(is->mpppcfg & SC_REJ_MP_PROT)) {
 		int sqno_end;
-		
-		if(proto == PPP_LINK_COMP) {
-			printk(KERN_DEBUG "received single link compressed frame\n");
-			skb = isdn_ppp_decompress(skb,is,NULL);
-			if(!skb)
-				return;
-			proto = isdn_ppp_strip_proto(skb);
+
+		if(is->compflags & SC_LINK_DECOMP_ON) {	
+			if(proto == PPP_LINK_COMP) {
+				if(is->debug & 0x10)
+					printk(KERN_DEBUG "received single link compressed frame\n");
+				skb = isdn_ppp_decompress(skb,is,NULL,proto);
+				if(!skb)
+					return;
+				proto = isdn_ppp_strip_proto(skb);
+			}
+			else
+				isdn_ppp_decompress(skb,is,NULL,proto);
 		}
 
 		if (proto == PPP_MP) {
@@ -1055,7 +1180,8 @@
 				}
 				min_sqno &= mask;
 				for (lpq = net_dev->queue;;) {
-					ippp_table[lpq->ppp_slot]->last_link_seqno &= mask;
+					if(ippp_table[lpq->ppp_slot]->last_link_seqno >= 0)
+						ippp_table[lpq->ppp_slot]->last_link_seqno &= mask;
 					lpq = lpq->next;
 					if (lpq == net_dev->queue)
 						break;
@@ -1150,17 +1276,31 @@
 
 	if (is->debug & 0x10) {
 		printk(KERN_DEBUG "push, skb %d %04x\n", (int) skb->len, proto);
-		isdn_ppp_frame_log("rpush", skb->data, skb->len, 32);
+		isdn_ppp_frame_log("rpush", skb->data, skb->len, 32,is->unit,lp->ppp_slot);
 	}
 
 	if(proto == PPP_COMP) {
 		if(!lp->master)
-			skb = isdn_ppp_decompress(skb,is,is);
+			skb = isdn_ppp_decompress(skb,is,is,proto);
 		else
-			skb = isdn_ppp_decompress(skb,is,ippp_table[((isdn_net_local *) (lp->master->priv))->ppp_slot]);
-		if(!skb)
+			skb = isdn_ppp_decompress(skb,is,ippp_table[((isdn_net_local *) (lp->master->priv))->ppp_slot],proto);
+
+		if(!skb) {
+			printk(KERN_DEBUG "ippp: compressed frame discarded!\n");
 			return;
+		}
+
 		proto = isdn_ppp_strip_proto(skb);
+		if (is->debug & 0x10) {
+			printk(KERN_DEBUG "RPostDecomp, skb %d %04x\n", (int) skb->len, proto);
+			isdn_ppp_frame_log("R-Decomp", skb->data, skb->len, 32,is->unit,lp->ppp_slot);
+		}
+	}
+	else if(is->compflags & SC_DECOMP_ON)  { /* If decomp is ON */
+		if(!lp->master)
+			isdn_ppp_decompress(skb,is,is,proto);
+		else
+			isdn_ppp_decompress(skb,is,ippp_table[((isdn_net_local *) (lp->master->priv))->ppp_slot],proto);
 	}
 
 	switch (proto) {
@@ -1227,7 +1367,13 @@
 #endif
 			break;
 		case PPP_CCP:
-			isdn_ppp_receive_ccp(net_dev,lp,skb);
+		case PPP_LINK_CCP:
+			isdn_ppp_receive_ccp(net_dev,lp,skb,proto);
+			/* Dont pop up ResetReq/Ack stuff to the daemon any
+			   longer - the job is done already */
+			if(skb->data[0] == CCP_RESETREQ ||
+			   skb->data[0] == CCP_RESETACK)
+				break;
 			/* fall through */
 		default:
 			isdn_ppp_fill_rq(skb->data, skb->len, proto, lp->ppp_slot);	/* push data to pppd device */
@@ -1235,10 +1381,10 @@
 			return;
 	}
 
+ 	/* Reset hangup-timer */
+ 	lp->huptimer = 0;
 	netif_rx(skb);
 	/* net_dev->local->stats.rx_packets++; *//* done in isdn_net.c */
-	/* Reset hangup-timer */
-	lp->huptimer = 0;
 
 	return;
 }
@@ -1339,7 +1485,6 @@
 		lp = nlp;
 	}
 	ipt = ippp_table[lp->ppp_slot];
-
 	lp->huptimer = 0;
 
 	/*
@@ -1355,6 +1500,8 @@
 
 	if (ipt->debug & 0x4)
 		printk(KERN_DEBUG "xmit skb, len %d\n", (int) skb->len);
+        if (ipts->debug & 0x40)
+                isdn_ppp_frame_log("xmit0", skb->data, skb->len, 32,ipts->unit,lp->ppp_slot);
 
 #ifdef CONFIG_ISDN_PPP_VJ
 	if (proto == PPP_IP && ipts->pppcfg & SC_COMP_TCP) {	/* ipts here? probably yes, but check this again */
@@ -1394,10 +1541,11 @@
 	}
 #endif
 
-    /*
-     * normal or bundle compression
-     */
-	skb = isdn_ppp_compress(skb,&proto,ipt,ipts,0);
+	/*
+	 * normal (single link) or bundle compression
+	 */
+	if(ipts->compflags & SC_COMP_ON)
+		skb = isdn_ppp_compress(skb,&proto,ipt,ipts,0);
 
 	if (ipt->debug & 0x24)
 		printk(KERN_DEBUG "xmit2 skb, len %d, proto %04x\n", (int) skb->len, proto);
@@ -1431,9 +1579,10 @@
 #endif
 
 	/*
-	 * 'link' compression 
+	 * 'link in bundle' compression  ...
 	 */
-	skb = isdn_ppp_compress(skb,&proto,ipt,ipts,1);
+	if(ipt->compflags & SC_LINK_COMP_ON)
+		skb = isdn_ppp_compress(skb,&proto,ipt,ipts,1);
 
 	if( (ipt->pppcfg & SC_COMP_PROT) && (proto <= 0xff) ) {
 		unsigned char *data = isdn_ppp_skb_push(&skb,1);
@@ -1460,7 +1609,7 @@
 
 	if (ipts->debug & 0x40) {
 		printk(KERN_DEBUG "skb xmit: len: %d\n", (int) skb->len);
-		isdn_ppp_frame_log("xmit", skb->data, skb->len, 32);
+		isdn_ppp_frame_log("xmit", skb->data, skb->len, 32,ipt->unit,lp->ppp_slot);
 	}
 	if (isdn_net_send_skb(dev, lp, skb)) {
 		if (lp->sav_skb) {	/* whole sav_skb processing with disabled IRQs ?? */
@@ -1474,6 +1623,12 @@
 
 #ifdef CONFIG_ISDN_MPP
 
+/*
+ * free SQ queue
+ * -------------
+ * Note: We need two queues for MPPP. The SQ queue holds fully (re)assembled frames,
+ * that can't be delivered, because there is an outstanding earlier frame
+ */
 static void
 isdn_ppp_free_sqqueue(isdn_net_dev * p)
 {
@@ -1490,6 +1645,12 @@
 
 }
 
+/*
+ * free MP queue
+ * -------------
+ * Note: The MP queue holds all frame fragments of frames, that can't be
+ * reassembled, because there is at least one missing fragment.
+ */
 static void 
 isdn_ppp_free_mpqueue(isdn_net_dev * p)
 {
@@ -1551,7 +1712,9 @@
 	return 0;
 }
 
-
+/*
+ * Mask sequence numbers in MP queue
+ */
 static void
 isdn_ppp_mask_queue(isdn_net_dev * dev, long mask)
 {
@@ -1562,6 +1725,11 @@
 	}
 }
 
+/*
+ * put a fragment at the right place into the MP queue 
+ * Also checks, whether this fragment completes a frame. In this case
+ * the fragments are copied together into one SKB
+ */
 static int
 isdn_ppp_fill_mpqueue(isdn_net_dev * dev, struct sk_buff **skb, int BEbyte, long *sqnop, int min_sqno)
 {
@@ -1762,13 +1930,11 @@
 		slhc_toss(ippp_table[dev->local->ppp_slot]->slcomp);
 #endif
 }
+#endif
 
 /*
  * a buffered packet timed-out?
  */
-
-#endif
-
 void
 isdn_ppp_timer_timeout(void)
 {
@@ -1940,7 +2106,7 @@
 	if (!sdev)
 		return 2;
 
-	isdn_net_force_dial_lp((isdn_net_local *) sdev->priv);
+	isdn_net_dial_req((isdn_net_local *) sdev->priv);
 	return 0;
 #else
 	return -1;
@@ -1981,47 +2147,440 @@
 /*
  * PPP compression stuff
  */
-static struct sk_buff *isdn_ppp_decompress(struct sk_buff *skb,struct ippp_struct *is,struct ippp_struct *master)
+
+
+/* Push an empty CCP Data Frame up to the daemon to wake it up and let it
+   generate a CCP Reset-Request or tear down CCP altogether */
+
+static void isdn_ppp_ccp_kickup(struct ippp_struct *is)
+{
+	isdn_ppp_fill_rq(NULL, 0, PPP_COMP, is->lp->ppp_slot);
+}
+
+/* In-kernel handling of CCP Reset-Request and Reset-Ack is necessary,
+   but absolutely nontrivial. The most abstruse problem we are facing is
+   that the generation, reception and all the handling of timeouts and
+   resends including proper request id management should be entirely left
+   to the (de)compressor, but indeed is not covered by the current API to
+   the (de)compressor. The API is a prototype version from PPP where only
+   some (de)compressors have yet been implemented and all of them are
+   rather simple in their reset handling. Especially, their is only one
+   outstanding ResetAck at a time with all of them and ResetReq/-Acks do
+   not have parameters. For this very special case it was sufficient to
+   just return an error code from the decompressor and have a single
+   reset() entry to communicate all the necessary information between
+   the framework and the (de)compressor. Bad enough, LZS is different
+   (and any other compressor may be different, too). It has multiple
+   histories (eventually) and needs to Reset each of them independently
+   and thus uses multiple outstanding Acks and history numbers as an
+   additional parameter to Reqs/Acks.
+   All that makes it harder to port the reset state engine into the
+   kernel because it is not just the same simple one as in (i)pppd but
+   it must be able to pass additional parameters and have multiple out-
+   standing Acks. We are trying to achieve the impossible by handling
+   reset transactions independent by their id. The id MUST change when
+   the data portion changes, thus any (de)compressor who uses more than
+   one resettable state must provide and recognize individual ids for
+   each individual reset transaction. The framework itself does _only_
+   differentiate them by id, because it has no other semantics like the
+   (de)compressor might.
+   This looks like a major redesign of the interface would be nice,
+   but I don't have an idea how to do it better. */
+
+/* Send a CCP Reset-Request or Reset-Ack directly from the kernel. This is
+   getting that lengthy because there is no simple "send-this-frame-out"
+   function above but every wrapper does a bit different. Hope I guess
+   correct in this hack... */
+
+static void isdn_ppp_ccp_xmit_reset(struct ippp_struct *is, int proto,
+				    unsigned char code, unsigned char id,
+				    unsigned char *data, int len)
+{
+	struct sk_buff *skb;
+	unsigned char *p;
+	int count;
+	int cnt = 0;
+	isdn_net_local *lp = is->lp;
+
+	/* Alloc large enough skb */
+	skb = dev_alloc_skb(len + 16);
+	if(!skb) {
+		printk(KERN_WARNING
+		       "ippp: CCP cannot send reset - out of memory\n");
+		return;
+	}
+
+	/* We may need to stuff an address and control field first */
+	if(!(is->pppcfg & SC_COMP_AC)) {
+		p = skb_put(skb, 2);
+		*p++ = 0xff;
+		*p++ = 0x03;
+	}
+
+	/* Stuff proto, code, id and length */
+	p = skb_put(skb, 6);
+	*p++ = (proto >> 8);
+	*p++ = (proto & 0xff);
+	*p++ = code;
+	*p++ = id;
+	cnt = 4 + len;
+	*p++ = (cnt >> 8);
+	*p++ = (cnt & 0xff);
+
+	/* Now stuff remaining bytes */
+	if(len) {
+		p = skb_put(skb, len);
+		memcpy(p, data, len);
+	}
+
+	/* skb is now ready for xmit */
+	printk(KERN_DEBUG "Sending CCP Frame:\n");
+	isdn_ppp_frame_log("ccp-xmit", skb->data, skb->len, 32, is->unit,lp->ppp_slot);
+
+	/* Just ripped from isdn_ppp_write. Dunno whether it makes sense,
+	   especially dunno what the sav_skb stuff is good for. */
+
+	count = skb->len;
+	if ((cnt = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel,
+					  1, skb)) != count) {
+		if (lp->sav_skb) {
+			dev_kfree_skb(lp->sav_skb);
+			printk(KERN_INFO
+			       "isdn_ppp_write: freeing sav_skb (%d,%d)!\n",
+			       cnt, count);
+		} else
+			printk(KERN_INFO
+			       "isdn_ppp_write: Can't write PPP frame to LL (%d,%d)!\n",
+			       cnt, count);
+		lp->sav_skb = skb;
+	}
+}
+
+/* Allocate the reset state vector */
+static struct ippp_ccp_reset *isdn_ppp_ccp_reset_alloc(struct ippp_struct *is)
+{
+	struct ippp_ccp_reset *r;
+	printk(KERN_DEBUG "ippp_ccp: allocating reset data structure\n");
+	r = kmalloc(sizeof(struct ippp_ccp_reset), GFP_KERNEL);
+	if(!r)
+		return NULL;
+	memset(r, 0, sizeof(struct ippp_ccp_reset));
+	is->reset = r;
+	return r;
+}
+
+/* Free a given state and clear everything up for later reallocation */
+static void isdn_ppp_ccp_reset_free_state(struct ippp_struct *is,
+					  unsigned char id)
+{
+	struct ippp_ccp_reset_state *rs;
+
+	if(is->reset->rs[id]) {
+		printk(KERN_DEBUG "ippp_ccp: freeing state for id %d\n", id);
+		rs = is->reset->rs[id];
+		/* Make sure the kernel will not call back later */
+		if(rs->ta)
+			del_timer(&rs->timer);
+		is->reset->rs[id] = NULL;
+		kfree(rs);
+	} else {
+		printk(KERN_WARNING "ippp_ccp: id %d is not allocated\n", id);
+	}
+}
+
+/* The timer callback function which is called when a ResetReq has timed out,
+   aka has never been answered by a ResetAck */
+static void isdn_ppp_ccp_timer_callback(unsigned long closure)
+{
+	struct ippp_ccp_reset_state *rs =
+		(struct ippp_ccp_reset_state *)closure;
+
+	if(!rs) {
+		printk(KERN_ERR "ippp_ccp: timer cb with zero closure.\n");
+		return;
+	}
+	if(rs->ta && rs->state == CCPResetSentReq) {
+		/* We are correct here */
+		printk(KERN_DEBUG "ippp_ccp: CCP Reset timed out for id %d\n",
+		       rs->id);
+		if(!rs->expra) {
+			/* Hmm, there is no Ack really expected. We can clean
+			   up the state now, it will be reallocated if the
+			   decompressor insists on another reset */
+			rs->ta = 0;
+			isdn_ppp_ccp_reset_free_state(rs->is, rs->id);
+			return;
+		}
+		/* Push it again */
+		isdn_ppp_ccp_xmit_reset(rs->is, PPP_CCP, CCP_RESETREQ, rs->id,
+					rs->data, rs->dlen);
+		/* Restart timer */
+		rs->timer.expires = jiffies + HZ*5;
+		add_timer(&rs->timer);
+	} else {
+		printk(KERN_WARNING "ippp_ccp: timer cb in wrong state %d\n",
+		       rs->state);
+	}
+}
+
+/* Allocate a new reset transaction state */
+static struct ippp_ccp_reset_state *isdn_ppp_ccp_reset_alloc_state(struct ippp_struct *is,
+						      unsigned char id)
+{
+	struct ippp_ccp_reset_state *rs;
+	if(is->reset->rs[id]) {
+		printk(KERN_WARNING "ippp_ccp: old state exists for id %d\n",
+		       id);
+		return NULL;
+	} else {
+		rs = kmalloc(sizeof(struct ippp_ccp_reset_state), GFP_KERNEL);
+		if(!rs)
+			return NULL;
+		memset(rs, 0, sizeof(struct ippp_ccp_reset_state));
+		rs->state = CCPResetIdle;
+		rs->is = is;
+		rs->id = id;
+		rs->timer.data = (unsigned long)rs;
+		rs->timer.function = isdn_ppp_ccp_timer_callback;
+		is->reset->rs[id] = rs;
+	}
+	return rs;
+}
+
+
+/* A decompressor wants a reset with a set of parameters - do what is
+   necessary to fulfill it */
+static void isdn_ppp_ccp_reset_trans(struct ippp_struct *is,
+				     struct isdn_ppp_resetparams *rp)
+{
+	struct ippp_ccp_reset_state *rs;
+
+	if(rp->valid) {
+		/* The decompressor defines parameters by itself */
+		if(rp->rsend) {
+			/* And he wants us to send a request */
+			if(!(rp->idval)) {
+				printk(KERN_ERR "ippp_ccp: decompressor must"
+				       " specify reset id\n");
+				return;
+			}
+			if(is->reset->rs[rp->id]) {
+				/* There is already a transaction in existence
+				   for this id. May be still waiting for a
+				   Ack or may be wrong. */
+				rs = is->reset->rs[rp->id];
+				if(rs->state == CCPResetSentReq && rs->ta) {
+					printk(KERN_DEBUG "ippp_ccp: reset"
+					       " trans still in progress"
+					       " for id %d\n", rp->id);
+				} else {
+					printk(KERN_WARNING "ippp_ccp: reset"
+					       " trans in wrong state %d for"
+					       " id %d\n", rs->state, rp->id);
+				}
+			} else {
+				/* Ok, this is a new transaction */
+				printk(KERN_DEBUG "ippp_ccp: new trans for id"
+				       " %d to be started\n", rp->id);
+				rs = isdn_ppp_ccp_reset_alloc_state(is, rp->id);
+				if(!rs) {
+					printk(KERN_ERR "ippp_ccp: out of mem"
+					       " allocing ccp trans\n");
+					return;
+				}
+				rs->state = CCPResetSentReq;
+				rs->expra = rp->expra;
+				if(rp->dtval) {
+					rs->dlen = rp->dlen;
+					memcpy(rs->data, rp->data, rp->dlen);
+				}
+				/* HACK TODO - add link comp here */
+				isdn_ppp_ccp_xmit_reset(is, PPP_CCP,
+							CCP_RESETREQ, rs->id,
+							rs->data, rs->dlen);
+				/* Start the timer */
+				rs->timer.expires = jiffies + 5*HZ;
+				add_timer(&rs->timer);
+				rs->ta = 1;
+			}
+		} else {
+			printk(KERN_DEBUG "ippp_ccp: no reset sent\n");
+		}
+	} else {
+		/* The reset params are invalid. The decompressor does not
+		   care about them, so we just send the minimal requests
+		   and increase ids only when an Ack is received for a
+		   given id */
+		if(is->reset->rs[is->reset->lastid]) {
+			/* There is already a transaction in existence
+			   for this id. May be still waiting for a
+			   Ack or may be wrong. */
+			rs = is->reset->rs[is->reset->lastid];
+			if(rs->state == CCPResetSentReq && rs->ta) {
+				printk(KERN_DEBUG "ippp_ccp: reset"
+				       " trans still in progress"
+				       " for id %d\n", rp->id);
+			} else {
+				printk(KERN_WARNING "ippp_ccp: reset"
+				       " trans in wrong state %d for"
+				       " id %d\n", rs->state, rp->id);
+			}
+		} else {
+			printk(KERN_DEBUG "ippp_ccp: new trans for id"
+			       " %d to be started\n", is->reset->lastid);
+			rs = isdn_ppp_ccp_reset_alloc_state(is,
+							    is->reset->lastid);
+			if(!rs) {
+				printk(KERN_ERR "ippp_ccp: out of mem"
+				       " allocing ccp trans\n");
+				return;
+			}
+			rs->state = CCPResetSentReq;
+			/* We always expect an Ack if the decompressor doesnt
+			   know	better */
+			rs->expra = 1;
+			rs->dlen = 0;
+			/* HACK TODO - add link comp here */
+			isdn_ppp_ccp_xmit_reset(is, PPP_CCP, CCP_RESETREQ,
+						rs->id, NULL, 0);
+			/* Start the timer */
+			rs->timer.expires = jiffies + 5*HZ;
+			add_timer(&rs->timer);
+			rs->ta = 1;
+		}
+	}
+}
+
+/* An Ack was received for this id. This means we stop the timer and clean
+   up the state prior to calling the decompressors reset routine. */
+static void isdn_ppp_ccp_reset_ack_rcvd(struct ippp_struct *is,
+					unsigned char id)
+{
+	struct ippp_ccp_reset_state *rs = is->reset->rs[id];
+
+	if(rs) {
+		if(rs->ta && rs->state == CCPResetSentReq) {
+			/* Great, we are correct */
+			if(!rs->expra)
+				printk(KERN_DEBUG "ippp_ccp: ResetAck received"
+				       " for id %d but not expected\n", id);
+		} else {
+			printk(KERN_INFO "ippp_ccp: ResetAck received out of"
+			       "sync for id %d\n", id);
+		}
+		if(rs->ta) {
+			rs->ta = 0;
+			del_timer(&rs->timer);
+		}
+		isdn_ppp_ccp_reset_free_state(is, id);
+	} else {
+		printk(KERN_INFO "ippp_ccp: ResetAck received for unknown id"
+		       " %d\n", id);
+	}
+	/* Make sure the simple reset stuff uses a new id next time */
+	is->reset->lastid++;
+}
+
+static struct sk_buff *isdn_ppp_decompress(struct sk_buff *skb,struct ippp_struct *is,struct ippp_struct *master,
+	int proto)
 {
-#if 1
-	printk(KERN_ERR "compression not included!\n");
-	dev_kfree_skb(skb);
-	return NULL;
+#ifndef CONFIG_ISDN_CCP
+	if(proto == PPP_COMP || proto == PPP_LINK_COMP) {
+		printk(KERN_ERR "isdn_ppp: Ouch! Compression not included!\n");
+		dev_kfree_skb(skb);
+		return NULL;
+	}
+	return skb;
 #else
+	void *stat = NULL;
+	struct isdn_ppp_compressor *ipc = NULL;
+	struct sk_buff *skb_out;
+	int len;
+	struct ippp_struct *ri;
+	struct isdn_ppp_resetparams rsparm;
+	unsigned char rsdata[IPPP_RESET_MAXDATABYTES];
+
 	if(!master) {
 		/* 
-		 * single link compression 
+		 * single link decompression 
 		 */
-		if(!is->link_compressor) {
-			printk(KERN_ERR "ippp: no (link) compressor defined!\n");
+		if(!is->link_decompressor) {
+			printk(KERN_ERR "ippp: no link decompressor defined!\n");
 			dev_kfree_skb(skb);
 			return NULL;
 		}
 		if(!is->link_decomp_stat) {
-			printk(KERN_DEBUG "ippp: initialize link compressor\n");
+			printk(KERN_DEBUG "ippp: no link decompressor data allocated\n");
+			dev_kfree_skb(skb);
+			return NULL;
 		}
-/*
-		-> decompress link
-*/
-    }
+		stat = is->link_decomp_stat;
+		ipc = is->link_decompressor;
+		ri = is;
+	}
 	else {
 		/*
 		 * 'normal' or bundle-compression 
 		 */
-		if(!master->compressor) {
-			printk(KERN_ERR "ippp: no (link) compressor defined!\n");
+		if(!master->decompressor) {
+			printk(KERN_ERR "ippp: no decompressor defined!\n");
 			dev_kfree_skb(skb);
 			return NULL;
 		}
 		if(!master->decomp_stat) {
-#if 0
-			master->decomp_stat = (master->compressor->decomp_alloc)( .. );
-#endif
-			printk(KERN_DEBUG "ippp: initialize compressor\n");
+			printk(KERN_DEBUG "ippp: no decompressor data allocated\n");
+			dev_kfree_skb(skb);
+			return NULL;
 		}
+		stat = master->decomp_stat;
+		ipc = master->decompressor;
+		ri = master;
+	}
+
+	/*
+	printk(KERN_DEBUG "ippp: Decompress valid!\n");
+	*/
+
+	if((master && proto == PPP_COMP) || (!master && proto == PPP_LINK_COMP) ) {
+		/* Set up reset params for the decompressor */
+		memset(&rsparm, 0, sizeof(rsparm));
+		rsparm.data = rsdata;
+		rsparm.maxdlen = IPPP_RESET_MAXDATABYTES;
+
+/* !!!HACK,HACK,HACK!!! 2048 is only assumed */
+		skb_out = dev_alloc_skb(2048);
+		len = ipc->decompress(stat,skb,skb_out, &rsparm);
+		dev_kfree_skb(skb);
+		if(len <= 0) {
+		  /* Ok, some error */
+		  switch(len) {
+		  case DECOMP_ERROR:
+		    ri->pppcfg |= SC_DC_ERROR;
+		    printk(KERN_INFO "ippp: decomp wants reset %s params\n",
+			   rsparm.valid ? "with" : "without");
+
+		    isdn_ppp_ccp_reset_trans(ri, &rsparm);
+
+		    break;
+		  case DECOMP_FATALERROR:
+		    ri->pppcfg |= SC_DC_FERROR;
+		    /* Kick ipppd to recognize the error */
+		    isdn_ppp_ccp_kickup(ri);
+		    break;
+		  }
+		  /* Did I see a leak here ? */
+		  dev_kfree_skb(skb_out);
+		  return NULL;
+		}
+		return skb_out;
+	}
+	else {
+		/*
+		printk(KERN_DEBUG "isdn_ppp: [%d] Calling incomp with this frame!\n",is->unit);
+		*/
+		ipc->incomp(stat,skb,proto);
+		return skb;
 	}
-	
-	return skb;
 #endif
 }
 
@@ -2035,19 +2594,29 @@
 static struct sk_buff *isdn_ppp_compress(struct sk_buff *skb_in,int *proto,
 	struct ippp_struct *is,struct ippp_struct *master,int type)
 {
-#if 1	
-	return skb_in;
-#else
     int ret;
     int new_proto;
     struct isdn_ppp_compressor *compressor;
     void *stat;
     struct sk_buff *skb_out;
 
+#ifdef CONFIG_ISDN_CCP
+	/* we do not compress control protocols */
+    if(*proto < 0 || *proto > 0x3fff) {
+#else
+    {
+#endif
+      return skb_in;
+    }
+
 	if(type) { /* type=1 => Link compression */
+#if 0
 		compressor = is->link_compressor;
 		stat = is->link_comp_stat;
 		new_proto = PPP_LINK_COMP;
+#else
+		return skb_in;
+#endif
 	}
 	else {
 		if(!master) {
@@ -2062,15 +2631,16 @@
 	}
 
 	if(!compressor) {
-		printk(KERN_ERR "No compressor set!\n");
+		printk(KERN_ERR "isdn_ppp: No compressor set!\n");
 		return skb_in;
 	}
 	if(!stat) {
-		/* init here ? */
+		printk(KERN_ERR "isdn_ppp: Compressor not initialized?\n");
 		return skb_in;
 	}
 
-	skb_out = dev_alloc_skb(skb_in->len);
+	/* Allow for at least 150 % expansion (for now) */
+	skb_out = dev_alloc_skb(skb_in->len + skb_in->len/2 + 32);
 	if(!skb_out)
 		return skb_in;
 
@@ -2083,24 +2653,225 @@
 	dev_kfree_skb(skb_in);
 	*proto = new_proto;
 	return skb_out;
-#endif
-
 }
 
 /*
  * we received a CCP frame .. 
- * not a clean solution, but we SHOULD handle a few cased in the kernel
+ * not a clean solution, but we MUST handle a few cases in the kernel
  */
 static void isdn_ppp_receive_ccp(isdn_net_dev *net_dev, isdn_net_local *lp,
-	 struct sk_buff *skb)
+	 struct sk_buff *skb,int proto)
 {
-#if 0
-	printk(KERN_DEBUG "isdn_ppp_receive_cpp: %02x %02x %02x %02x %02x %02x %02x %02x\n",
-		skb->data[0],skb->data[1],skb->data[2],skb->data[3],
-		skb->data[4],skb->data[5],skb->data[6],skb->data[7] );
-#endif
+	struct ippp_struct *is = ippp_table[lp->ppp_slot];
+	struct ippp_struct *mis;
+	int len;
+	struct isdn_ppp_resetparams rsparm;
+	unsigned char rsdata[IPPP_RESET_MAXDATABYTES];	
+
+	printk(KERN_DEBUG "Received CCP frame from peer\n");
+	isdn_ppp_frame_log("ccp-rcv", skb->data, skb->len, 32, is->unit,lp->ppp_slot);
+
+	if(lp->master)
+		mis = ippp_table[((isdn_net_local *) (lp->master->priv))->ppp_slot];
+	else
+		mis = is;
+
+	switch(skb->data[0]) {
+	case CCP_CONFREQ:
+	case CCP_TERMREQ:
+	case CCP_TERMACK:
+		if(is->debug & 0x10)
+			printk(KERN_DEBUG "Disable (de)compression here!\n");
+		if(proto == PPP_CCP)
+			mis->compflags &= ~(SC_DECOMP_ON|SC_COMP_ON);		
+		else
+			is->compflags &= ~(SC_LINK_DECOMP_ON|SC_LINK_COMP_ON);		
+		break;
+	case CCP_CONFACK:
+		/* if we RECEIVE an ackowledge we enable the decompressor */
+		if(is->debug & 0x10)
+			printk(KERN_DEBUG "Enable decompression here!\n");
+		if(proto == PPP_CCP)
+			mis->compflags |= SC_DECOMP_ON;
+		else
+			is->compflags |= SC_LINK_DECOMP_ON;
+		break;
+
+	case CCP_RESETACK:
+		printk(KERN_DEBUG "Received ResetAck from peer\n");
+		len = (skb->data[2] << 8) | skb->data[3];
+		len -= 4;
+
+		if(proto == PPP_CCP) {
+			/* If a reset Ack was outstanding for this id, then
+			   clean up the state engine */
+			isdn_ppp_ccp_reset_ack_rcvd(mis, skb->data[1]);
+			if(mis->decompressor && mis->decomp_stat)
+				mis->decompressor->
+					reset(mis->decomp_stat,
+					      skb->data[0],
+					      skb->data[1],
+					      len ? &skb->data[4] : NULL,
+					      len, NULL);
+			/* TODO: This is not easy to decide here */
+			mis->compflags &= ~SC_DECOMP_DISCARD;
+			mis->pppcfg &= ~SC_DC_ERROR;
+		}
+		else {
+			isdn_ppp_ccp_reset_ack_rcvd(is, skb->data[1]);
+			if(is->link_decompressor && is->link_decomp_stat)
+				is->link_decompressor->
+					reset(is->link_decomp_stat,
+					      skb->data[0],
+					      skb->data[1],
+					      len ? &skb->data[4] : NULL,
+					      len, NULL);
+			/* TODO: neither here */
+			is->compflags &= ~SC_LINK_DECOMP_DISCARD;
+			is->pppcfg &= ~SC_DC_ERROR;
+		}
+		break;
+
+	case CCP_RESETREQ:
+		printk(KERN_DEBUG "Received ResetReq from peer\n");
+		/* Receiving a ResetReq means we must reset our compressor */
+		/* Set up reset params for the reset entry */
+		memset(&rsparm, 0, sizeof(rsparm));
+		rsparm.data = rsdata;
+		rsparm.maxdlen = IPPP_RESET_MAXDATABYTES; 
+		/* Isolate data length */
+		len = (skb->data[2] << 8) | skb->data[3];
+		len -= 4;
+		if(proto == PPP_CCP) {
+			if(mis->compressor && mis->comp_stat)
+				mis->compressor->
+					reset(mis->comp_stat,
+					      skb->data[0],
+					      skb->data[1],
+					      len ? &skb->data[4] : NULL,
+					      len, &rsparm);
+		}
+		else {
+			if(is->link_compressor && is->link_comp_stat)
+				is->link_compressor->
+					reset(is->link_comp_stat,
+					      skb->data[0],
+					      skb->data[1],
+					      len ? &skb->data[4] : NULL,
+					      len, &rsparm);
+		}
+		/* Ack the Req as specified by rsparm */
+		if(rsparm.valid) {
+			/* Compressor reset handler decided how to answer */
+			if(rsparm.rsend) {
+				/* We should send a Frame */
+				isdn_ppp_ccp_xmit_reset(is, proto, CCP_RESETACK,
+							rsparm.idval ? rsparm.id
+							: skb->data[1],
+							rsparm.dtval ?
+							rsparm.data : NULL,
+							rsparm.dtval ?
+							rsparm.dlen : 0);
+			} else {
+				printk(KERN_DEBUG "ResetAck suppressed\n");
+			}
+		} else {
+			/* We answer with a straight reflected Ack */
+			isdn_ppp_ccp_xmit_reset(is, proto, CCP_RESETACK,
+						skb->data[1],
+						len ? &skb->data[4] : NULL,
+						len);
+		}
+		break;
+	}
 }
 
+
+/*
+ * Daemon sends a CCP frame ...
+ */
+
+/* TODO: Clean this up with new Reset semantics */
+
+static void isdn_ppp_send_ccp(isdn_net_dev *net_dev, isdn_net_local *lp, struct sk_buff *skb)
+{
+	struct ippp_struct *mis,*is = ippp_table[lp->ppp_slot];
+	int proto;
+	unsigned char *data;
+
+	if(!skb || skb->len < 3)
+		return;
+
+	/* Daemon may send with or without address and control field comp */
+	data = skb->data;
+	if(!(is->pppcfg & SC_COMP_AC) && data[0] == 0xff && data[1] == 0x03) {
+		data += 2;
+		if(skb->len < 5)
+			return;
+	}
+
+	proto = ((int)data[0]<<8)+data[1];
+	if(proto != PPP_CCP && proto != PPP_LINK_CCP)
+		return;
+
+	printk(KERN_DEBUG "Received CCP frame from daemon:\n");
+	isdn_ppp_frame_log("ccp-xmit", skb->data, skb->len, 32, is->unit,lp->ppp_slot);
+
+        if(lp->master)
+                mis = ippp_table[((isdn_net_local *) (lp->master->priv))->ppp_slot];
+        else
+                mis = is;
+	
+	if(mis != is)
+		printk(KERN_DEBUG "isdn_ppp: Ouch! Master CCP sends on slave slot!\n");
+	
+        switch(data[2]) {
+	case CCP_CONFREQ:
+	case CCP_TERMREQ:
+	case CCP_TERMACK:
+		if(is->debug & 0x10)
+			printk(KERN_DEBUG "Disable (de)compression here!\n");
+		if(proto == PPP_CCP)
+			is->compflags &= ~(SC_DECOMP_ON|SC_COMP_ON);
+		else
+			is->compflags &= ~(SC_LINK_DECOMP_ON|SC_LINK_COMP_ON);
+		break;
+	case CCP_CONFACK:
+		/* if we SEND an ackowledge we can/must enable the compressor */
+		if(is->debug & 0x10)
+			printk(KERN_DEBUG "Enable compression here!\n");
+		if(proto == PPP_CCP)
+			is->compflags |= SC_COMP_ON;
+		else
+			is->compflags |= SC_LINK_COMP_ON;
+		break;
+	case CCP_RESETACK:
+		/* If we send a ACK we should reset our compressor */
+		if(is->debug & 0x10)
+			printk(KERN_DEBUG "Reset decompression state here!\n");
+		printk(KERN_DEBUG "ResetAck from daemon passed by\n");
+		if(proto == PPP_CCP) {
+			/* link to master? */
+			if(is->compressor && is->comp_stat)
+				is->compressor->reset(is->comp_stat, 0, 0,
+						      NULL, 0, NULL);
+			is->compflags &= ~SC_COMP_DISCARD;	
+		}
+		else {
+			if(is->link_compressor && is->link_comp_stat)
+				is->link_compressor->reset(is->link_comp_stat,
+							   0, 0, NULL, 0, NULL);
+			is->compflags &= ~SC_LINK_COMP_DISCARD;	
+		}
+		break;
+	case CCP_RESETREQ:
+		/* Just let it pass by */
+		printk(KERN_DEBUG "ResetReq from daemon passed by\n");
+		break;
+	}
+}
+
+
 int isdn_ppp_register_compressor(struct isdn_ppp_compressor *ipc)
 {
 	ipc->next = ipc_head;
@@ -2124,32 +2895,67 @@
 	return 0;
 }
 
-static int isdn_ppp_set_compressor(struct ippp_struct *is,int num)
+static int isdn_ppp_set_compressor(struct ippp_struct *is, struct isdn_ppp_comp_data *data)
 {
 	struct isdn_ppp_compressor *ipc = ipc_head;
+	int ret;
+	void *stat;
+	int num = data->num;
+
+	if(is->debug & 0x10)
+		printk(KERN_DEBUG "[%d] Set %s type %d\n",is->unit,
+			(data->flags&IPPP_COMP_FLAG_XMIT)?"compressor":"decompressor",num);
 
 	while(ipc) {
 		if(ipc->num == num) {
-			return 0;	
-			is->compressor = ipc;
-			is->link_compressor = ipc;
+			stat = ipc->alloc(data);
+			if(stat) {
+				ret = ipc->init(stat,data,is->unit,0);
+				if(!ret) {
+					printk(KERN_ERR "Can't init (de)compression!\n");
+					ipc->free(stat);
+					stat = NULL;
+					break;
+				}
+			}
+			else {
+				printk(KERN_ERR "Can't alloc (de)compression!\n");
+				break;
+			}
+
+                        if(data->flags & IPPP_COMP_FLAG_XMIT) {
+				if(data->flags & IPPP_COMP_FLAG_LINK) {
+					if(is->link_comp_stat)
+						is->link_compressor->free(is->link_comp_stat);
+					is->link_comp_stat = stat;
+                                	is->link_compressor = ipc;
+				}
+				else {
+					if(is->comp_stat)
+						is->compressor->free(is->comp_stat);
+					is->comp_stat = stat;
+                                	is->compressor = ipc;
+				}
+			}
+                        else {
+				if(data->flags & IPPP_COMP_FLAG_LINK) {
+					if(is->link_decomp_stat)
+						is->link_decompressor->free(is->link_decomp_stat);
+					is->link_decomp_stat = stat;
+        	                        is->link_decompressor = ipc;
+				}
+				else {
+					if(is->decomp_stat)
+						is->decompressor->free(is->decomp_stat);
+					is->decomp_stat = stat;
+        	                        is->decompressor = ipc;
+				}
+			}
+			return 0;
 		}
 		ipc = ipc->next;
 	}
 	return -EINVAL;
 }
-
-
-#if 0
-static struct symbol_table isdn_ppp_syms =
-{
-#include <linux/symtab_begin.h>
-	X(isdn_ppp_register_compressor),
-	X(isdn_ppp_unregister_compressor),
-#include <linux/symtab_end.h>
-};
-#endif
-
-
 
 
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/isdn_ppp.h linux/drivers/isdn/isdn_ppp.h
--- v2.3.3/linux/drivers/isdn/isdn_ppp.h	Wed Apr  1 16:21:03 1998
+++ linux/drivers/isdn/isdn_ppp.h	Sun May 23 10:03:42 1999
@@ -1,4 +1,4 @@
-/* $Id: isdn_ppp.h,v 1.12 1998/01/31 22:07:48 keil Exp $
+/* $Id: isdn_ppp.h,v 1.13 1998/03/22 18:50:50 hipp Exp $
 
  * header for Linux ISDN subsystem, functions for synchronous PPP (linklevel).
  *
@@ -19,6 +19,9 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_ppp.h,v $
+ * Revision 1.13  1998/03/22 18:50:50  hipp
+ * Added BSD Compression for syncPPP .. UNTESTED at the moment
+ *
  * Revision 1.12  1998/01/31 22:07:48  keil
  * changes for newer kernels
  *
@@ -83,6 +86,9 @@
 extern void isdn_ppp_release(int, struct file *);
 extern int isdn_ppp_dial_slave(char *);
 extern void isdn_ppp_wakeup_daemon(isdn_net_local *);
+
+extern int isdn_ppp_register_compressor(struct isdn_ppp_compressor *ipc);
+extern int isdn_ppp_unregister_compressor(struct isdn_ppp_compressor *ipc);
 
 #define IPPP_OPEN	0x01
 #define IPPP_CONNECT	0x02
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/isdn_tty.c linux/drivers/isdn/isdn_tty.c
--- v2.3.3/linux/drivers/isdn/isdn_tty.c	Fri May 14 18:55:19 1999
+++ linux/drivers/isdn/isdn_tty.c	Sun May 23 10:03:42 1999
@@ -1,8 +1,8 @@
-/* $Id: isdn_tty.c,v 1.47 1998/02/22 19:44:14 fritz Exp $
+/* $Id: isdn_tty.c,v 1.63 1999/04/12 12:33:39 fritz Exp $
 
  * Linux ISDN subsystem, tty functions and AT-command emulator (linklevel).
  *
- * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1994-1999  by Fritz Elfert (fritz@isdn4linux.de)
  * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
  *
  * This program is free software; you can redistribute it and/or modify
@@ -20,6 +20,73 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_tty.c,v $
+ * Revision 1.63  1999/04/12 12:33:39  fritz
+ * Changes from 2.0 tree.
+ *
+ * Revision 1.62  1999/03/02 12:04:48  armin
+ * -added ISDN_STAT_ADDCH to increase supported channels after
+ *  register_isdn().
+ * -ttyI now goes on-hook on ATZ when B-Ch is connected.
+ * -added timer-function for register S7 (Wait for Carrier).
+ * -analog modem (ISDN_PROTO_L2_MODEM) implementations.
+ * -on L2_MODEM a string will be appended to the CONNECT-Message,
+ *  which is provided by the HL-Driver in parm.num in ISDN_STAT_BCONN.
+ * -variable "dialing" used for ATA also, for interrupting call
+ *  establishment and register S7.
+ *
+ * Revision 1.61  1999/01/27 22:53:11  he
+ * minor updates (spellings, jiffies wrap around in isdn_tty)
+ *
+ * Revision 1.60  1998/11/15 23:57:32  keil
+ * changes for 2.1.127
+ *
+ * Revision 1.59  1998/08/20 13:50:15  keil
+ * More support for hybrid modem (not working yet)
+ *
+ * Revision 1.58  1998/07/26 18:48:45  armin
+ * Added silence detection in voice receive mode.
+ *
+ * Revision 1.57  1998/06/26 15:12:36  fritz
+ * Added handling of STAT_ICALL with incomplete CPN.
+ * Added AT&L for ttyI emulator.
+ * Added more locking stuff in tty_write.
+ *
+ * Revision 1.56  1998/06/18 23:31:51  fritz
+ * Replaced cli()/restore_flags() in isdn_tty_write() by locking.
+ * Removed direct-senddown feature in isdn_tty_write because it will
+ * never succeed with locking and is useless anyway.
+ *
+ * Revision 1.55  1998/06/17 19:50:55  he
+ * merged with 2.1.10[34] (cosmetics and udelay() -> mdelay())
+ * brute force fix to avoid Ugh's in isdn_tty_write()
+ * cleaned up some dead code
+ *
+ * Revision 1.54  1998/06/07 00:20:13  fritz
+ * abc cleanup.
+ *
+ * Revision 1.53  1998/06/02 12:10:16  detabc
+ * wegen einer einstweiliger verfuegung gegen DW ist zur zeit
+ * die abc-extension bis zur klaerung der rechtslage nicht verfuegbar
+ *
+ * Revision 1.52  1998/03/19 13:18:21  keil
+ * Start of a CAPI like interface for supplementary Service
+ * first service: SUSPEND
+ *
+ * Revision 1.51  1998/03/08 14:26:11  detabc
+ * change kfree_skb to dev_kfree_skb
+ * remove SET_SKB_FREE
+ *
+ * Revision 1.50  1998/03/08 13:14:28  detabc
+ * abc-extension support for kernels > 2.1.x
+ * first try (sorry experimental)
+ *
+ * Revision 1.49  1998/03/08 00:01:59  fritz
+ * Bugfix: Lowlevel module usage and channel usage were not
+ *         reset on NO DCHANNEL.
+ *
+ * Revision 1.48  1998/03/07 12:28:15  tsbogend
+ * fixed kernel unaligned traps on Linux/Alpha
+ *
  * Revision 1.47  1998/02/22 19:44:14  fritz
  * Bugfixes and improvements regarding V.110, V.110 now running.
  *
@@ -254,7 +321,7 @@
 static int si2bit[8] =
 {4, 1, 4, 4, 4, 4, 4, 4};
 
-char *isdn_tty_revision = "$Revision: 1.47 $";
+char *isdn_tty_revision = "$Revision: 1.63 $";
 
 #define DLE 0x10
 #define ETX 0x03
@@ -270,6 +337,8 @@
 #define REG_LF        4
 #define REG_BS        5
 
+#define REG_WAITC     7
+
 #define REG_RESP     12
 #define BIT_RESP      1
 #define REG_RESPNUM  12
@@ -287,8 +356,6 @@
 #define REG_CPPP     12
 #define BIT_CPPP    128
 
-#define REG_DELXMT   13
-#define BIT_DELXMT    1
 #define REG_T70      13
 #define BIT_T70       2
 #define BIT_T70_EXT  32
@@ -300,6 +367,8 @@
 #define BIT_CIDONCE  16
 #define REG_RUNG     13
 #define BIT_RUNG     64
+#define REG_RESRXT   13
+#define BIT_RESRXT  128
 
 #define REG_L2PROT   14
 #define REG_L3PROT   15
@@ -389,6 +458,8 @@
 				r = 0;
 #ifdef CONFIG_ISDN_AUDIO
 				isdn_audio_eval_dtmf(info);
+				if ((info->vonline & 1) && (info->emu.vpar[1]))
+					isdn_audio_eval_silence(info);
 #endif
 				if ((tty = info->tty)) {
 					if (info->mcr & UART_MCR_RTS) {
@@ -445,6 +516,8 @@
 	
 	if (info->vonline)
 		isdn_audio_calc_dtmf(info, skb->data, skb->len, ifmt);
+	if ((info->vonline & 1) && (info->emu.vpar[1]))
+		isdn_audio_calc_silence(info, skb->data, skb->len, ifmt);
 #endif
 	if ((info->online < 2)
 #ifdef CONFIG_ISDN_AUDIO
@@ -566,8 +639,8 @@
 					   info->isdn_channel, 1, skb)) == len) {
 		struct tty_struct *tty = info->tty;
 		info->send_outstanding++;
-		info->msr |= UART_MSR_CTS;
-		info->lsr |= UART_LSR_TEMT;
+		info->msr &= ~UART_MSR_CTS;
+		info->lsr &= ~UART_LSR_TEMT;
 		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
 		    tty->ldisc.write_wakeup)
 			(tty->ldisc.write_wakeup) (tty);
@@ -579,8 +652,6 @@
 		dev_kfree_skb(skb);
 		return;
 	}
-	if (slen)
-		skb_pull(skb, slen);
 	skb_queue_head(&info->xmit_queue, skb);
 }
 
@@ -702,7 +773,6 @@
 	int audio_len;
 #endif
 	struct sk_buff *skb;
-	unsigned long flags;
 
 #ifdef CONFIG_ISDN_AUDIO
 	if (info->vonline & 4) {
@@ -717,18 +787,20 @@
 		}
 	}
 #endif
-	save_flags(flags);
-	cli();
-	if (!(buflen = info->xmit_count)) {
-		restore_flags(flags);
+	if (!(buflen = info->xmit_count))
 		return;
-	}
-	if ((info->emu.mdmreg[REG_CTS] & BIT_CTS) != 0)
+ 	if ((info->emu.mdmreg[REG_CTS] & BIT_CTS) != 0)
 		info->msr &= ~UART_MSR_CTS;
-	info->lsr &= ~UART_LSR_TEMT;
+	info->lsr &= ~UART_LSR_TEMT;	
+	/* info->xmit_count is modified here and in isdn_tty_write().
+	 * So we return here if isdn_tty_write() is in the
+	 * critical section.
+	 */
+	atomic_inc(&info->xmit_lock);
+	if (!(atomic_dec_and_test(&info->xmit_lock)))
+		return;
 	if (info->isdn_driver < 0) {
 		info->xmit_count = 0;
-		restore_flags(flags);
 		return;
 	}
 	skb_res = dev->drv[info->isdn_driver]->interface->hl_hdrlen + 4;
@@ -742,7 +814,6 @@
 	skb = dev_alloc_skb(skb_res + buflen);
 #endif
 	if (!skb) {
-		restore_flags(flags);
 		printk(KERN_WARNING
 		       "isdn_tty: Out of memory in ttyI%d senddown\n",
 		       info->line);
@@ -751,7 +822,6 @@
 	skb_reserve(skb, skb_res);
 	memcpy(skb_put(skb, buflen), info->xmit_buf, buflen);
 	info->xmit_count = 0;
-	restore_flags(flags);
 #ifdef CONFIG_ISDN_AUDIO
 	if (info->vonline & 2) {
 		/* For now, ifmt is fixed to 1 (alaw), since this
@@ -859,7 +929,7 @@
 			break;
 		}
 #ifdef CONFIG_ISDN_AUDIO
-	if (si == 1) {
+	if ((si == 1) && (l2 != ISDN_PROTO_L2_MODEM)) {
 		l2 = ISDN_PROTO_L2_TRANS;
 		usg = ISDN_USAGE_VOICE;
 	}
@@ -907,9 +977,11 @@
 		cmd.parm.setup.si2 = m->mdmreg[REG_SI2];
 		cmd.command = ISDN_CMD_DIAL;
 		info->dialing = 1;
+		info->emu.carrierwait = 0;
 		strcpy(dev->num[i], n);
 		isdn_info_update();
 		isdn_command(&cmd);
+		isdn_timer_ctrl(ISDN_TIMER_CARRIER, 1);
 	}
 }
 
@@ -942,6 +1014,10 @@
 		kfree(info->dtmf_state);
 		info->dtmf_state = NULL;
 	}
+	if (info->silence_state) {
+		kfree(info->silence_state);
+		info->silence_state = NULL;
+	}
 	if (info->adpcms) {
 		kfree(info->adpcms);
 		info->adpcms = NULL;
@@ -965,8 +1041,9 @@
 		}
 		isdn_all_eaz(info->isdn_driver, info->isdn_channel);
 		info->emu.mdmreg[REG_RINGCNT] = 0;
-		usage = (info->emu.mdmreg[REG_SI1I] == 1) ?
-		    ISDN_USAGE_VOICE : ISDN_USAGE_MODEM;
+		usage = ((info->emu.mdmreg[REG_SI1I] != 1) || 
+		    (info->emu.mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM)) ?
+			ISDN_USAGE_MODEM : ISDN_USAGE_VOICE;
 		isdn_free_channel(info->isdn_driver, info->isdn_channel,
 				  usage);
 	}
@@ -978,6 +1055,226 @@
 	}
 }
 
+/*
+ * Begin of a CAPI like interface, currently used only for 
+ * supplementary service (CAPI 2.0 part III)
+ */
+#include "avmb1/capicmd.h"  /* this should be moved in a common place */
+
+int
+isdn_tty_capi_facility(capi_msg *cm) {
+	return(-1); /* dummy */
+}
+
+/* isdn_tty_suspend() tries to suspend the current tty connection
+ */
+static void
+isdn_tty_suspend(char *id, modem_info * info, atemu * m)
+{
+	isdn_ctrl cmd;
+	
+	int l;
+
+	if (!info)
+		return;
+
+#ifdef ISDN_DEBUG_MODEM_SERVICES
+	printk(KERN_DEBUG "Msusp ttyI%d\n", info->line);
+#endif
+	l = strlen(id);
+	if ((info->isdn_driver >= 0) && l) {
+		cmd.parm.cmsg.Length = l+17;
+		cmd.parm.cmsg.Command = CAPI_FACILITY;
+		cmd.parm.cmsg.Subcommand = CAPI_REQ;
+		cmd.parm.cmsg.adr.Controller = info->isdn_driver + 1;
+		cmd.parm.cmsg.para[0] = 3; /* 16 bit 0x0003 suplementary service */
+		cmd.parm.cmsg.para[1] = 0;
+		cmd.parm.cmsg.para[2] = l + 3;
+		cmd.parm.cmsg.para[3] = 4; /* 16 bit 0x0004 Suspend */
+		cmd.parm.cmsg.para[4] = 0;
+		cmd.parm.cmsg.para[5] = l;
+		strncpy(&cmd.parm.cmsg.para[6], id, l);
+		cmd.command = CAPI_PUT_MESSAGE;
+		cmd.driver = info->isdn_driver;
+		cmd.arg = info->isdn_channel;
+		isdn_command(&cmd);
+	}
+}
+
+/* isdn_tty_resume() tries to resume a suspended call
+ * setup of the lower levels before that. unfortunatly here is no
+ * checking for compatibility of used protocols implemented by Q931
+ * It does the same things like isdn_tty_dial, the last command
+ * is different, may be we can merge it.
+ */
+
+static void
+isdn_tty_resume(char *id, modem_info * info, atemu * m)
+{
+	int usg = ISDN_USAGE_MODEM;
+	int si = 7;
+	int l2 = m->mdmreg[REG_L2PROT];
+	isdn_ctrl cmd;
+	ulong flags;
+	int i;
+	int j;
+	int l;
+
+	l = strlen(id);
+	if (!l) {
+		isdn_tty_modem_result(4, info);
+		return;
+	}
+	for (j = 7; j >= 0; j--)
+		if (m->mdmreg[REG_SI1] & (1 << j)) {
+			si = bit2si[j];
+			break;
+		}
+#ifdef CONFIG_ISDN_AUDIO
+	if ((si == 1) && (l2 != ISDN_PROTO_L2_MODEM)) {
+		l2 = ISDN_PROTO_L2_TRANS;
+		usg = ISDN_USAGE_VOICE;
+	}
+#endif
+	m->mdmreg[REG_SI1I] = si2bit[si];
+	save_flags(flags);
+	cli();
+	i = isdn_get_free_channel(usg, l2, m->mdmreg[REG_L3PROT], -1, -1);
+	if (i < 0) {
+		restore_flags(flags);
+		isdn_tty_modem_result(6, info);
+	} else {
+		info->isdn_driver = dev->drvmap[i];
+		info->isdn_channel = dev->chanmap[i];
+		info->drv_index = i;
+		dev->m_idx[i] = info->line;
+		dev->usage[i] |= ISDN_USAGE_OUTGOING;
+		info->last_dir = 1;
+//		strcpy(info->last_num, n);
+		isdn_info_update();
+		restore_flags(flags);
+		cmd.driver = info->isdn_driver;
+		cmd.arg = info->isdn_channel;
+		cmd.command = ISDN_CMD_CLREAZ;
+		isdn_command(&cmd);
+		strcpy(cmd.parm.num, isdn_map_eaz2msn(m->msn, info->isdn_driver));
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETEAZ;
+		isdn_command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETL2;
+		info->last_l2 = l2;
+		cmd.arg = info->isdn_channel + (l2 << 8);
+		isdn_command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETL3;
+		cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8);
+		isdn_command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.arg = info->isdn_channel;
+		cmd.parm.cmsg.Length = l+17;
+		cmd.parm.cmsg.Command = CAPI_FACILITY;
+		cmd.parm.cmsg.Subcommand = CAPI_REQ;
+		cmd.parm.cmsg.adr.Controller = info->isdn_driver + 1;
+		cmd.parm.cmsg.para[0] = 3; /* 16 bit 0x0003 suplementary service */
+		cmd.parm.cmsg.para[1] = 0;
+		cmd.parm.cmsg.para[2] = l+3;
+		cmd.parm.cmsg.para[3] = 5; /* 16 bit 0x0005 Resume */
+		cmd.parm.cmsg.para[4] = 0;
+		cmd.parm.cmsg.para[5] = l;
+		strncpy(&cmd.parm.cmsg.para[6], id, l);
+		cmd.command =CAPI_PUT_MESSAGE;
+/*		info->dialing = 1;
+		strcpy(dev->num[i], n);
+		isdn_info_update();
+*/
+		isdn_command(&cmd);
+	}
+}
+
+/* isdn_tty_send_msg() sends a message to a HL driver
+ * This is used for hybrid modem cards to send AT commands to it
+ */
+
+static void
+isdn_tty_send_msg(modem_info * info, atemu * m, char *msg)
+{
+	int usg = ISDN_USAGE_MODEM;
+	int si = 7;
+	int l2 = m->mdmreg[REG_L2PROT];
+	isdn_ctrl cmd;
+	ulong flags;
+	int i;
+	int j;
+	int l;
+
+	l = strlen(msg);
+	if (!l) {
+		isdn_tty_modem_result(4, info);
+		return;
+	}
+	for (j = 7; j >= 0; j--)
+		if (m->mdmreg[REG_SI1] & (1 << j)) {
+			si = bit2si[j];
+			break;
+		}
+#ifdef CONFIG_ISDN_AUDIO
+	if ((si == 1) && (l2 != ISDN_PROTO_L2_MODEM)) {
+		l2 = ISDN_PROTO_L2_TRANS;
+		usg = ISDN_USAGE_VOICE;
+	}
+#endif
+	m->mdmreg[REG_SI1I] = si2bit[si];
+	save_flags(flags);
+	cli();
+	i = isdn_get_free_channel(usg, l2, m->mdmreg[REG_L3PROT], -1, -1);
+	if (i < 0) {
+		restore_flags(flags);
+		isdn_tty_modem_result(6, info);
+	} else {
+		info->isdn_driver = dev->drvmap[i];
+		info->isdn_channel = dev->chanmap[i];
+		info->drv_index = i;
+		dev->m_idx[i] = info->line;
+		dev->usage[i] |= ISDN_USAGE_OUTGOING;
+		info->last_dir = 1;
+		isdn_info_update();
+		restore_flags(flags);
+		cmd.driver = info->isdn_driver;
+		cmd.arg = info->isdn_channel;
+		cmd.command = ISDN_CMD_CLREAZ;
+		isdn_command(&cmd);
+		strcpy(cmd.parm.num, isdn_map_eaz2msn(m->msn, info->isdn_driver));
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETEAZ;
+		isdn_command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETL2;
+		info->last_l2 = l2;
+		cmd.arg = info->isdn_channel + (l2 << 8);
+		isdn_command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETL3;
+		cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8);
+		isdn_command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.arg = info->isdn_channel;
+		cmd.parm.cmsg.Length = l+14;
+		cmd.parm.cmsg.Command = CAPI_MANUFACTURER;
+		cmd.parm.cmsg.Subcommand = CAPI_REQ;
+		cmd.parm.cmsg.adr.Controller = info->isdn_driver + 1;
+		cmd.parm.cmsg.para[0] = l+1;
+		strncpy(&cmd.parm.cmsg.para[1], msg, l);
+		cmd.parm.cmsg.para[l+1] = 0xd;
+		cmd.command =CAPI_PUT_MESSAGE;
+/*		info->dialing = 1;
+		strcpy(dev->num[i], n);
+		isdn_info_update();
+*/
+		isdn_command(&cmd);
+	}
+}
+
 static inline int
 isdn_tty_paranoia_check(modem_info * info, kdev_t device, const char *routine)
 {
@@ -1138,15 +1435,16 @@
 {
 	int c;
 	int total = 0;
-	ulong flags;
 	modem_info *info = (modem_info *) tty->driver_data;
 
 	if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_write"))
 		return 0;
 	if (!tty)
 		return 0;
-	save_flags(flags);
-	cli();
+	if (from_user)
+		down(&info->write_sem);
+	/* See isdn_tty_senddown() */
+	atomic_inc(&info->xmit_lock);
 	while (1) {
 		c = MIN(count, info->xmit_size - info->xmit_count);
 		if (info->isdn_driver >= 0)
@@ -1205,10 +1503,6 @@
 			} else
 #endif
 				info->xmit_count += c;
-			if (m->mdmreg[REG_DELXMT] & BIT_DELXMT) {
-				isdn_tty_senddown(info);
-				isdn_tty_tint(info);
-			}
 		} else {
 			info->msr |= UART_MSR_CTS;
 			info->lsr |= UART_LSR_TEMT;
@@ -1228,7 +1522,9 @@
 	}
 	if ((info->xmit_count) || (skb_queue_len(&info->xmit_queue)))
 		isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, 1);
-	restore_flags(flags);
+	atomic_dec(&info->xmit_lock);
+	if (from_user)
+		up(&info->write_sem);
 	return total;
 }
 
@@ -1359,7 +1655,7 @@
 	status = info->lsr;
 	restore_flags(flags);
 	result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0);
-	put_user(result, (ulong *) value);
+	put_user(result, (uint *) value);
 	return 0;
 }
 
@@ -1383,7 +1679,7 @@
 	    | ((status & UART_MSR_RI) ? TIOCM_RNG : 0)
 	    | ((status & UART_MSR_DSR) ? TIOCM_DSR : 0)
 	    | ((status & UART_MSR_CTS) ? TIOCM_CTS : 0);
-	put_user(result, (ulong *) value);
+	put_user(result, (uint *) value);
 	return 0;
 }
 
@@ -1567,7 +1863,12 @@
 static int
 isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info * info)
 {
-	DECLARE_WAITQUEUE(wait, current); 
+#if LINUX_VERSION_CODE < 131841
+	struct wait_queue wait =
+	{current, NULL};
+#else
+	DECLARE_WAITQUEUE(wait, NULL);
+#endif
 	int do_clocal = 0;
 	unsigned long flags;
 	int retval;
@@ -1891,6 +2192,7 @@
 	m->profile[19] = 0;
 	m->profile[20] = 0;
 	m->pmsn[0] = '\0';
+	m->plmsn[0] = '\0';
 }
 
 #ifdef CONFIG_ISDN_AUDIO
@@ -1911,6 +2213,7 @@
 	if ((m->mdmreg[REG_DTRR] & BIT_DTRR) || force) {
 		memcpy(m->mdmreg, m->profile, ISDN_MODEM_ANZREG);
 		memcpy(m->msn, m->pmsn, ISDN_MSNLEN);
+		memcpy(m->lmsn, m->plmsn, ISDN_LMSNLEN);
 		info->xmit_size = m->mdmreg[REG_PSIZE] * 16;
 	}
 #ifdef CONFIG_ISDN_AUDIO
@@ -1924,6 +2227,7 @@
 {
 	memcpy(m->profile, m->mdmreg, ISDN_MODEM_ANZREG);
 	memcpy(m->pmsn, m->msn, ISDN_MSNLEN);
+	memcpy(m->plmsn, m->lmsn, ISDN_LMSNLEN);
 	if (dev->profd)
 		send_sig(SIGIO, dev->profd, 1);
 }
@@ -1987,6 +2291,11 @@
 	}
 	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
 		info = &m->info[i];
+#if LINUX_VERSION_CODE < 131841
+		info->write_sem = MUTEX;
+#else
+		init_MUTEX(&info->write_sem);
+#endif
 		sprintf(info->last_cause, "0000");
 		sprintf(info->last_num, "none");
 		info->last_dir = 0;
@@ -2003,8 +2312,13 @@
 		info->blocked_open = 0;
 		info->callout_termios = m->cua_modem.init_termios;
 		info->normal_termios = m->tty_modem.init_termios;
+#if LINUX_VERSION_CODE < 131841
+		info->open_wait = 0;
+		info->close_wait = 0;
+#else
 		init_waitqueue_head(&info->open_wait);
 		init_waitqueue_head(&info->close_wait);
+#endif
 		info->isdn_driver = -1;
 		info->isdn_channel = -1;
 		info->drv_index = -1;
@@ -2023,25 +2337,67 @@
 	return 0;
 }
 
+static int
+isdn_tty_match_icall(char *cid, atemu *emu, int di)
+{
+#ifdef ISDN_DEBUG_MODEM_ICALL
+	printk(KERN_DEBUG "m_fi: msn=%s lmsn=%s mmsn=%s mreg[SI1]=%d mreg[SI2]=%d\n",
+	       emu->msn, emu->lmsn, isdn_map_eaz2msn(emu->msn, di),
+	       emu->mdmreg[REG_SI1], emu->mdmreg[REG_SI2]);
+#endif
+	if (strlen(emu->lmsn)) {
+		char *p = emu->lmsn;
+		char *q;
+		int  tmp;
+		int  ret = 0;
+
+		while (1) {
+			if ((q = strchr(p, ';')))
+				*q = '\0';
+			if ((tmp = isdn_wildmat(cid, isdn_map_eaz2msn(p, di))) > ret)
+				ret = tmp;
+#ifdef ISDN_DEBUG_MODEM_ICALL
+			printk(KERN_DEBUG "m_fi: lmsnX=%s mmsn=%s -> tmp=%d\n",
+			       p, isdn_map_eaz2msn(emu->msn, di), tmp);
+#endif
+			if (q) {
+				*q = ';';
+				p = q;
+				p++;
+			}
+			if (!tmp)
+				return 0;
+			if (!q)
+				break;
+		}
+		return ret;
+	} else
+		return isdn_wildmat(cid, isdn_map_eaz2msn(emu->msn, di));
+}
+
 /*
  * An incoming call-request has arrived.
  * Search the tty-devices for an appropriate device and bind
  * it to the ISDN-Channel.
- * Return Index to dev->mdm or -1 if none found.
+ * Return:
+ *
+ *  0 = No matching device found.
+ *  1 = A matching device found.
+ *  3 = No match found, but eventually would match, if
+ *      CID is longer.
  */
 int
 isdn_tty_find_icall(int di, int ch, setup_parm setup)
 {
 	char *eaz;
 	int i;
+	int wret;
 	int idx;
 	int si1;
 	int si2;
 	char nr[32];
 	ulong flags;
 
-	save_flags(flags);
-	cli();
 	if (!setup.phone[0]) {
 		nr[0] = '0';
 		nr[1] = '\0';
@@ -2058,17 +2414,14 @@
 #ifdef ISDN_DEBUG_MODEM_ICALL
 	printk(KERN_DEBUG "m_fi: eaz=%s si1=%d si2=%d\n", eaz, si1, si2);
 #endif
+	wret = 0;
+	save_flags(flags);
+	cli();
 	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
 		modem_info *info = &dev->mdm.info[i];
-#ifdef ISDN_DEBUG_MODEM_ICALL
-		printk(KERN_DEBUG "m_fi: i=%d msn=%s mmsn=%s mreg18=%d mreg19=%d\n", i,
-		       info->emu.msn, isdn_map_eaz2msn(info->emu.msn, di),
-		       info->emu.mdmreg[REG_SI1], info->emu.mdmreg[REG_SI2]);
-#endif
-		if ((!strcmp(isdn_map_eaz2msn(info->emu.msn, di),
-			     eaz)) &&	                          /* EAZ is matching */
-		    (info->emu.mdmreg[REG_SI1] & si2bit[si1]) &&  /* SI1 is matching */
-		    (info->emu.mdmreg[REG_SI2] == si2)) {	  /* SI2 is matching */
+
+		if ((info->emu.mdmreg[REG_SI1] & si2bit[si1]) &&  /* SI1 is matching */
+		    (info->emu.mdmreg[REG_SI2] == si2))	{         /* SI2 is matching */
 			idx = isdn_dc2minor(di, ch);
 #ifdef ISDN_DEBUG_MODEM_ICALL
 			printk(KERN_DEBUG "m_fi: match1\n");
@@ -2080,34 +2433,42 @@
 #ifndef FIX_FILE_TRANSFER
 				(info->flags & ISDN_ASYNC_NORMAL_ACTIVE) &&
 #endif
-			    (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] |= (si1 == 1) ? ISDN_USAGE_VOICE : ISDN_USAGE_MODEM;
-				strcpy(dev->num[idx], nr);
-				info->emu.mdmreg[REG_SI1I] = si2bit[si1];
-				info->emu.mdmreg[REG_PLAN] = setup.plan;
-				info->emu.mdmreg[REG_SCREEN] = setup.screen;
-				isdn_info_update();
-				restore_flags(flags);
-				printk(KERN_INFO "isdn_tty: call from %s, -> RING on ttyI%d\n", nr,
-				       info->line);
-				info->msr |= UART_MSR_RI;
-				isdn_tty_modem_result(2, info);
-				isdn_timer_ctrl(ISDN_TIMER_MODEMRING, 1);
-				return info->line;
+				(info->isdn_driver == -1) &&
+				(info->isdn_channel == -1) &&
+				(USG_NONE(dev->usage[idx]))) {
+				int matchret;
+
+				if ((matchret = isdn_tty_match_icall(eaz, &info->emu, di)) > wret)
+					wret = matchret;
+				if (!matchret) {                  /* EAZ is matching */
+					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] |= ((si1 != 1) || (info->emu.mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM)) ? 
+						ISDN_USAGE_MODEM : ISDN_USAGE_VOICE;
+					strcpy(dev->num[idx], nr);
+					strcpy(info->emu.cpn, eaz);
+					info->emu.mdmreg[REG_SI1I] = si2bit[si1];
+					info->emu.mdmreg[REG_PLAN] = setup.plan;
+					info->emu.mdmreg[REG_SCREEN] = setup.screen;
+					isdn_info_update();
+					restore_flags(flags);
+					printk(KERN_INFO "isdn_tty: call from %s, -> RING on ttyI%d\n", nr,
+					       info->line);
+					info->msr |= UART_MSR_RI;
+					isdn_tty_modem_result(2, info);
+					isdn_timer_ctrl(ISDN_TIMER_MODEMRING, 1);
+					return 1;
+				}
 			}
 		}
 	}
-	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;
+	printk(KERN_INFO "isdn_tty: call from %s -> %s %s\n", nr, eaz,
+	       ((dev->drv[di]->flags & DRV_FLAG_REJBUS) && (wret != 2))? "rejected" : "ignored");
+	return (wret == 2)?3:0;
 }
 
 #define TTY_IS_ACTIVE(info) \
@@ -2128,7 +2489,7 @@
                         case ISDN_STAT_CINF:
                                 printk(KERN_DEBUG "CHARGEINFO on ttyI%d: %ld %s\n", info->line, c->arg, c->parm.num);
                                 info->emu.charge = (unsigned) simple_strtoul(c->parm.num, &e, 10);
-                                if (e == c->parm.num)
+                                if (e == (char *)c->parm.num)
 					info->emu.charge = 0;
 				
                                 break;			
@@ -2169,10 +2530,11 @@
 				printk(KERN_DEBUG "tty_STAT_DHUP ttyI%d\n", info->line);
 #endif
 				if (TTY_IS_ACTIVE(info)) {
-					if (info->dialing == 1) {
-						info->dialing = 0;
+					if (info->dialing == 1) 
 						isdn_tty_modem_result(7, info);
-					}
+					if (info->dialing > 1) 
+						isdn_tty_modem_result(3, info);
+					info->dialing = 0;
 #ifdef ISDN_DEBUG_MODEM_HUP
 					printk(KERN_DEBUG "Mhup in ISDN_STAT_DHUP\n");
 #endif
@@ -2191,14 +2553,20 @@
 				if (TTY_IS_ACTIVE(info)) {
 					info->msr |= UART_MSR_DCD;
 					info->emu.charge = 0;
-					if (info->dialing) {
-						info->dialing = 0;
+					if (info->dialing & 0xf)
 						info->last_dir = 1;
-					} else
+					else
 						info->last_dir = 0;
+					info->dialing = 0;
 					info->rcvsched = 1;
-					if (USG_MODEM(dev->usage[i]))
-						isdn_tty_modem_result(5, info);
+					if (USG_MODEM(dev->usage[i])) {
+						if (info->emu.mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM) {
+							strcpy(info->emu.connmsg, c->parm.num);
+							isdn_tty_modem_result(1, info);
+						}
+						else
+							isdn_tty_modem_result(5, info);
+					}
 					if (USG_VOICE(dev->usage[i]))
 						isdn_tty_modem_result(11, info);
 					return 1;
@@ -2228,11 +2596,7 @@
 						sprintf(info->last_cause, "0000");
 						isdn_tty_modem_result(6, info);
 					}
-					info->msr &= ~UART_MSR_DCD;
-					if (info->online) {
-						isdn_tty_modem_result(3, info);
-						info->online = 0;
-					}
+					isdn_tty_modem_hup(info, 0);
 					return 1;
 				}
 				break;
@@ -2393,7 +2757,7 @@
 	 "CONNECT 64000", "NO DIALTONE", "BUSY", "NO ANSWER",
 	 "RINGING", "NO MSN/EAZ", "VCON", "RUNG"};
 	ulong flags;
-	char s[10];
+	char s[ISDN_MSNLEN+10];
 
 	switch (code) {
 		case 2:
@@ -2473,7 +2837,20 @@
 			}
 			isdn_tty_at_cout(msg[code], info);
 			switch (code) {
+				case 1:
+					switch (m->mdmreg[REG_L2PROT]) {
+						case ISDN_PROTO_L2_MODEM:
+							isdn_tty_at_cout(" ", info);
+							isdn_tty_at_cout(m->connmsg, info);
+							break;
+					}
+					break;
 				case 2:
+					/* Append CPN, if enabled */
+					if ((m->mdmreg[REG_RESRXT] & BIT_RESRXT)) {
+						sprintf(s, "/%s", m->cpn);
+						isdn_tty_at_cout(s, info);
+					}
 					/* Print CID only once, _after_ 1.st RING */
 					if ((m->mdmreg[REG_CIDONCE] & BIT_CIDONCE) &&
 					    (m->mdmreg[REG_RINGCNT] == 1)) {
@@ -2560,7 +2937,9 @@
 static void
 isdn_tty_get_msnstr(char *n, char **p)
 {
-	while ((*p[0] >= '0' && *p[0] <= '9') || (*p[0] == ','))
+	while ((*p[0] >= '0' && *p[0] <= '9') ||
+	       /* Why a comma ??? */
+	       (*p[0] == ','))
 		*n++ = *p[0]++;
 	*n = '\0';
 }
@@ -2626,6 +3005,9 @@
 		case ISDN_PROTO_L2_TRANS:
 			isdn_tty_at_cout("transparent", info);
 			break;
+		case ISDN_PROTO_L2_MODEM:
+			isdn_tty_at_cout("modem", info);
+			break;
 		default:
 			isdn_tty_at_cout("unknown", info);
 			break;
@@ -2668,6 +3050,8 @@
 	int i;
 	char rb[100];
 
+#define MAXRB (sizeof(rb) - 1)
+
 	switch (*p[0]) {
 		case 'B':
 			/* &B - Set Buffersize */
@@ -2719,6 +3103,15 @@
 			isdn_tty_reset_profile(m);
 			isdn_tty_modem_reset_regs(info, 1);
 			break;
+		case 'L':
+			/* &L -Set Numbers to listen on */
+			p[0]++;
+			i = 0;
+			while ((strchr("0123456789,*[]?;", *p[0])) &&
+			       (i < ISDN_LMSNLEN))
+				m->lmsn[i++] = *p[0]++;
+			m->lmsn[i] = '\0';
+			break;
 		case 'R':
 			/* &R - Set V.110 bitrate adaption */
 			p[0]++;
@@ -2774,6 +3167,11 @@
 			sprintf(rb, "\r\nEAZ/MSN: %.50s\r\n",
 				strlen(m->msn) ? m->msn : "None");
 			isdn_tty_at_cout(rb, info);
+			if (strlen(m->lmsn)) {
+				isdn_tty_at_cout("\r\nListen: ", info);
+				isdn_tty_at_cout(m->lmsn, info);
+				isdn_tty_at_cout("\r\n", info);
+			}
 			break;
 		case 'W':
 			/* &W - Write Profile */
@@ -2939,9 +3337,10 @@
 #ifdef CONFIG_ISDN_AUDIO
 		/* If more than one bit set in reg18, autoselect Layer2 */
 		if ((m->mdmreg[REG_SI1] & m->mdmreg[REG_SI1I]) != m->mdmreg[REG_SI1]) {
-			if (m->mdmreg[REG_SI1I] == 1)
-				l2 = ISDN_PROTO_L2_TRANS;
-			else
+			if (m->mdmreg[REG_SI1I] == 1) {
+				if (l2 != ISDN_PROTO_L2_MODEM)
+					l2 = ISDN_PROTO_L2_TRANS;
+			} else
 				l2 = ISDN_PROTO_L2_X75I;
 		}
 #endif
@@ -2957,7 +3356,10 @@
 		cmd.driver = info->isdn_driver;
 		cmd.arg = info->isdn_channel;
 		cmd.command = ISDN_CMD_ACCEPTD;
+		info->dialing = 16;
+		info->emu.carrierwait = 0;
 		isdn_command(&cmd);
+		isdn_timer_ctrl(ISDN_TIMER_CARRIER, 1);
 	} else
 		isdn_tty_modem_result(8, info);
 }
@@ -3221,6 +3623,11 @@
 				printk(KERN_WARNING "isdn_tty: Couldn't malloc dtmf state\n");
 				PARSE_ERROR1;
 			}
+			info->silence_state = isdn_audio_silence_init(info->silence_state);
+			if (!info->silence_state) {
+				printk(KERN_WARNING "isdn_tty: Couldn't malloc silence state\n");
+				PARSE_ERROR1;
+			}
 			if (m->vpar[3] < 5) {
 				info->adpcmr = isdn_audio_adpcm_init(info->adpcmr, m->vpar[3]);
 				if (!info->adpcmr) {
@@ -3247,31 +3654,27 @@
 					break;
 				case '=':
 					p[0]++;
-					switch (*p[0]) {
-						case '0':
-						case '1':
-						case '2':
-						case '3':
-							par1 = isdn_getnum(p);
-							if ((par1 < 0) || (par1 > 31))
-								PARSE_ERROR1;
-							if (*p[0] != ',')
-								PARSE_ERROR1;
-							p[0]++;
-							par2 = isdn_getnum(p);
-							if ((par2 < 0) || (par2 > 255))
-								PARSE_ERROR1;
-							m->vpar[1] = par1;
-							m->vpar[2] = par2;
-							break;
-						case '?':
-							p[0]++;
-							isdn_tty_at_cout("\r\n<0-31>,<0-255>",
-								   info);
-							break;
-						default:
+					if ((*p[0]>='0') && (*p[0]<='9')) {
+						par1 = isdn_getnum(p);
+						if ((par1 < 0) || (par1 > 31))
 							PARSE_ERROR1;
-					}
+						if (*p[0] != ',')
+							PARSE_ERROR1;
+						p[0]++;
+						par2 = isdn_getnum(p);
+						if ((par2 < 0) || (par2 > 255))
+							PARSE_ERROR1;
+						m->vpar[1] = par1;
+						m->vpar[2] = par2;
+						break;
+					} else 
+					if (*p[0] == '?') {
+						p[0]++;
+						isdn_tty_at_cout("\r\n<0-31>,<0-255>",
+							   info);
+						break;
+					} else
+					PARSE_ERROR1;
 					break;
 				default:
 					PARSE_ERROR1;
@@ -3445,7 +3848,7 @@
 				p++;
 				if (info->msr & UART_MSR_DCD)
 					/* if B-Channel is up */
-					isdn_tty_modem_result(5, info);
+					isdn_tty_modem_result((m->mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM) ? 1:5, info);
 				else
 					isdn_tty_modem_result(3, info);
 				return;
@@ -3486,29 +3889,46 @@
 			case 'Z':
 				/* Z - Load Registers from Profile */
 				p++;
+				if (info->msr & UART_MSR_DCD) {
+					info->online = 0;
+					isdn_tty_on_hook(info);
+				}
 				isdn_tty_modem_reset_regs(info, 1);
 				break;
-#ifdef CONFIG_ISDN_AUDIO
 			case '+':
 				p++;
 				switch (*p) {
+#ifdef CONFIG_ISDN_AUDIO
 					case 'F':
 						p++;
 						if (isdn_tty_cmd_PLUSF(&p, info))
 							return;
 						break;
 					case 'V':
-						if (!(m->mdmreg[REG_SI1] & 1))
+						if ((!(m->mdmreg[REG_SI1] & 1)) ||
+							(m->mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM))
 							PARSE_ERROR;
 						p++;
 						if (isdn_tty_cmd_PLUSV(&p, info))
 							return;
 						break;
+#endif                          /* CONFIG_ISDN_AUDIO */
+					case 'S':	/* SUSPEND */
+						p++;
+						isdn_tty_get_msnstr(ds, &p);
+						isdn_tty_suspend(ds, info, m);
+						break;
+					case 'R':	/* RESUME */
+						isdn_tty_get_msnstr(ds, &p);
+						isdn_tty_resume(ds, info, m);
+					case 'M':	/* MESSAGE */
+						p++;
+						isdn_tty_send_msg(info, m, p);
+						break;
 					default:
 						PARSE_ERROR;
 				}
 				break;
-#endif                          /* CONFIG_ISDN_AUDIO */
 			case '&':
 				p++;
 				if (isdn_tty_cmd_ATand(&p, info))
@@ -3672,4 +4092,29 @@
 		}
 	}
 	isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, ton);
+}
+
+/*
+ * Check all channels if we have a 'no carrier' timeout.
+ * Timeout value is set by Register S7.
+ */
+void
+isdn_tty_carrier_timeout(void)
+{
+	int ton = 0;
+	int i;
+
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		modem_info *info = &dev->mdm.info[i];
+		if (info->dialing) {
+			if (info->emu.carrierwait++ > info->emu.mdmreg[REG_WAITC]) {
+				info->dialing = 0;
+				isdn_tty_modem_result(3, info);
+				isdn_tty_modem_hup(info, 1);
+			}
+			else
+				ton = 1;
+		}
+	}
+	isdn_timer_ctrl(ISDN_TIMER_CARRIER, ton);
 }
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/isdn_tty.h linux/drivers/isdn/isdn_tty.h
--- v2.3.3/linux/drivers/isdn/isdn_tty.h	Thu May 29 21:53:06 1997
+++ linux/drivers/isdn/isdn_tty.h	Sun May 23 10:03:42 1999
@@ -1,8 +1,8 @@
-/* $Id: isdn_tty.h,v 1.10 1997/03/02 14:29:26 fritz Exp $
+/* $Id: isdn_tty.h,v 1.13 1999/04/12 12:33:46 fritz Exp $
 
  * header for Linux ISDN subsystem, tty related functions (linklevel).
  *
- * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1994-1999  by Fritz Elfert (fritz@isdn4linux.de)
  * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
  *
  * This program is free software; you can redistribute it and/or modify
@@ -20,6 +20,24 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_tty.h,v $
+ * Revision 1.13  1999/04/12 12:33:46  fritz
+ * Changes from 2.0 tree.
+ *
+ * Revision 1.12  1999/03/02 12:04:51  armin
+ * -added ISDN_STAT_ADDCH to increase supported channels after
+ *  register_isdn().
+ * -ttyI now goes on-hook on ATZ when B-Ch is connected.
+ * -added timer-function for register S7 (Wait for Carrier).
+ * -analog modem (ISDN_PROTO_L2_MODEM) implementations.
+ * -on L2_MODEM a string will be appended to the CONNECT-Message,
+ *  which is provided by the HL-Driver in parm.num in ISDN_STAT_BCONN.
+ * -variable "dialing" used for ATA also, for interrupting call
+ *  establishment and register S7.
+ *
+ * Revision 1.11  1998/03/19 13:18:27  keil
+ * Start of a CAPI like interface for supplementary Service
+ * first service: SUSPEND
+ *
  * Revision 1.10  1997/03/02 14:29:26  fritz
  * More ttyI related cleanup.
  *
@@ -56,6 +74,7 @@
 
 extern void isdn_tty_modem_escape(void);
 extern void isdn_tty_modem_ring(void);
+extern void isdn_tty_carrier_timeout(void);
 extern void isdn_tty_modem_xmit(void);
 extern int isdn_tty_modem_init(void);
 extern void isdn_tty_readmodem(void);
@@ -63,3 +82,4 @@
 extern void isdn_tty_cleanup_xmit(modem_info *);
 extern int isdn_tty_stat_callback(int, isdn_ctrl *);
 extern int isdn_tty_rcv_skb(int, int, int, struct sk_buff *);
+extern int isdn_tty_capi_facility(capi_msg *cm); 
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/isdn_x25iface.c linux/drivers/isdn/isdn_x25iface.c
--- v2.3.3/linux/drivers/isdn/isdn_x25iface.c	Thu Jan  7 08:46:59 1999
+++ linux/drivers/isdn/isdn_x25iface.c	Sun May 23 10:03:42 1999
@@ -1,4 +1,4 @@
-/* $Id: isdn_x25iface.c,v 1.3 1998/02/20 17:25:20 fritz Exp $
+/* $Id: isdn_x25iface.c,v 1.6 1999/01/27 22:53:19 he Exp $
  * stuff needed to support the Linux X.25 PLP code on top of devices that
  * can provide a lab_b service using the concap_proto mechanism.
  * This module supports a network interface wich provides lapb_sematics
@@ -10,6 +10,17 @@
  * goes to another -- device related -- concap_proto support source file.
  *
  * $Log: isdn_x25iface.c,v $
+ * Revision 1.6  1999/01/27 22:53:19  he
+ * minor updates (spellings, jiffies wrap around in isdn_tty)
+ *
+ * Revision 1.5  1998/10/30 17:55:39  he
+ * dialmode for x25iface and multulink ppp
+ *
+ * Revision 1.4  1998/06/17 19:51:00  he
+ * merged with 2.1.10[34] (cosmetics and udelay() -> mdelay())
+ * brute force fix to avoid Ugh's in isdn_tty_write()
+ * cleaned up some dead code
+ *
  * Revision 1.3  1998/02/20 17:25:20  fritz
  * Changes for recent kernels.
  *
@@ -302,7 +313,12 @@
 	case 0x01: /* dl_connect request */
 		if( *state == WAN_DISCONNECTED ){
 			*state = WAN_CONNECTING;
-		        cprot -> dops -> connect_req(cprot);
+		        ret = cprot -> dops -> connect_req(cprot);
+			if(ret){
+				/* reset state and notify upper layer about
+				 * immidiatly failed attempts */
+				isdn_x25iface_disconn_ind(cprot);
+			}
 		} else {
 			illegal_state_warn( *state, firstbyte );
 		}
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/isdnloop/isdnloop.c linux/drivers/isdn/isdnloop/isdnloop.c
--- v2.3.3/linux/drivers/isdn/isdnloop/isdnloop.c	Sat Oct 31 10:37:14 1998
+++ linux/drivers/isdn/isdnloop/isdnloop.c	Sun May 23 10:03:42 1999
@@ -1,4 +1,4 @@
-/* $Id: isdnloop.c,v 1.4 1998/02/24 21:39:05 he Exp $
+/* $Id: isdnloop.c,v 1.8 1998/11/18 18:59:43 armin Exp $
 
  * ISDN low-level module implementing a dummy loop driver.
  *
@@ -19,6 +19,20 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdnloop.c,v $
+ * Revision 1.8  1998/11/18 18:59:43  armin
+ * changes for 2.1.127
+ *
+ * Revision 1.7  1998/10/30 18:58:03  he
+ * typecast to suppress a compiler warning
+ *
+ * Revision 1.6  1998/06/17 19:51:37  he
+ * merged with 2.1.10[34] (cosmetics and udelay() -> mdelay())
+ * brute force fix to avoid Ugh's in isdn_tty_write()
+ * cleaned up some dead code
+ *
+ * Revision 1.5  1998/04/14 20:59:32  he
+ * merged 2.1.94 changes
+ *
  * Revision 1.4  1998/02/24 21:39:05  he
  * L2_PROT_X25DTE / DCE
  * additional state 17 and new internal signal messages "BCON_I"
@@ -42,7 +56,7 @@
 #include "isdnloop.h"
 
 static char
-*revision = "$Revision: 1.4 $";
+*revision = "$Revision: 1.8 $";
 
 static int isdnloop_addcard(char *);
 
@@ -1312,7 +1326,7 @@
 							c->parm.num[0] ? "N" : "ALL", c->parm.num);
 					} else
 						sprintf(cbuf, "%02d;EAZ%s\n", (int) a,
-							c->parm.num[0] ? c->parm.num : "0123456789");
+							c->parm.num[0] ? c->parm.num : (u_char *) "0123456789");
 					i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card);
 				}
 				break;
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/isdnloop/isdnloop.h linux/drivers/isdn/isdnloop/isdnloop.h
--- v2.3.3/linux/drivers/isdn/isdnloop/isdnloop.h	Tue Apr  7 07:52:04 1998
+++ linux/drivers/isdn/isdnloop/isdnloop.h	Sun May 23 10:03:42 1999
@@ -1,4 +1,4 @@
-/* $Id: isdnloop.h,v 1.2 1997/10/01 09:22:07 fritz Exp $
+/* $Id: isdnloop.h,v 1.3 1998/04/14 20:59:35 he Exp $
 
  * Loopback lowlevel module for testing of linklevel.
  *
@@ -19,6 +19,9 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdnloop.h,v $
+ * Revision 1.3  1998/04/14 20:59:35  he
+ * merged 2.1.94 changes
+ *
  * Revision 1.2  1997/10/01 09:22:07  fritz
  * Removed old compatibility stuff for 2.0.X kernels.
  * From now on, this code is for 2.1.X ONLY!
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/pcbit/callbacks.c linux/drivers/isdn/pcbit/callbacks.c
--- v2.3.3/linux/drivers/isdn/pcbit/callbacks.c	Thu Feb 27 10:57:30 1997
+++ linux/drivers/isdn/pcbit/callbacks.c	Sun May 23 10:03:42 1999
@@ -11,6 +11,13 @@
  *        callbacks for the FSM
  */
 
+/*
+ * Fix: 19981230 - Carlos Morgado <chbm@techie.com>
+ * Port of Nelson Escravana's <nelson.escravana@usa.net> fix to CalledPN 
+ * NULL pointer dereference in cb_in_1 (originally fixed in 2.0)
+ */
+
+
 #define __NO_VERSION__
 
 #include <linux/module.h>
@@ -164,12 +171,24 @@
          *  ictl.num >= strlen() + strlen() + 5
          */
 
+	if (cbdata->data.setup.CallingPN == NULL) {
+		printk(KERN_DEBUG "NULL CallingPN to phone; using 0\n");
+		strcpy(ictl.parm.setup.phone, "0");
+	}
+	else {
 		strcpy(ictl.parm.setup.phone, cbdata->data.setup.CallingPN);
+	}
+	if (cbdata->data.setup.CalledPN == NULL) {
+		printk(KERN_DEBUG "NULL CalledPN to eazmsn; using 0\n");
+		strcpy(ictl.parm.setup.eazmsn, "0");
+	}
+	else {
 		strcpy(ictl.parm.setup.eazmsn, cbdata->data.setup.CalledPN);
-		ictl.parm.setup.si1 = 7;
-		ictl.parm.setup.si2 = 0;
-		ictl.parm.setup.plan = 0;
-		ictl.parm.setup.screen = 0;
+	}
+	ictl.parm.setup.si1 = 7;
+	ictl.parm.setup.si2 = 0;
+	ictl.parm.setup.plan = 0;
+	ictl.parm.setup.screen = 0;
 
 #ifdef DEBUG
 	printk(KERN_DEBUG "statstr: %s\n", ictl.num);
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/pcbit/drv.c linux/drivers/isdn/pcbit/drv.c
--- v2.3.3/linux/drivers/isdn/pcbit/drv.c	Wed Apr  1 16:21:03 1998
+++ linux/drivers/isdn/pcbit/drv.c	Sun May 23 10:03:42 1999
@@ -86,6 +86,9 @@
 
 	dev_pcbit[board] = dev;
 	memset(dev, 0, sizeof(struct pcbit_dev));
+#if LINUX_VERSION_CODE >= 131841
+	init_waitqueue_head(&dev->set_running_wq);
+#endif
 
 	if (mem_base >= 0xA0000 && mem_base <= 0xFFFFF )
 		dev->sh_mem = (unsigned char*) mem_base;
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/pcbit/pcbit.h linux/drivers/isdn/pcbit/pcbit.h
--- v2.3.3/linux/drivers/isdn/pcbit/pcbit.h	Mon May 17 09:55:22 1999
+++ linux/drivers/isdn/pcbit/pcbit.h	Sun May 23 10:03:42 1999
@@ -68,7 +68,11 @@
 	struct frame_buf *write_queue;
 
 	/* Protocol start */
+#if LINUX_VERSION_CODE < 131841
+	struct wait_queue *set_running_wq;
+#else
 	wait_queue_head_t set_running_wq;
+#endif
 	struct timer_list set_running_timer;
 
 	struct timer_list error_recover_timer;
diff -u --recursive --new-file v2.3.3/linux/drivers/isdn/sc/message.c linux/drivers/isdn/sc/message.c
--- v2.3.3/linux/drivers/isdn/sc/message.c	Thu Nov  5 09:58:44 1998
+++ linux/drivers/isdn/sc/message.c	Sun May 23 10:03:42 1999
@@ -1,5 +1,5 @@
 /*
- *  $Id: message.c,v 1.3 1998/01/31 22:10:55 keil Exp $
+ *  $Id: message.c,v 1.4 1999/01/05 18:29:47 he Exp $
  *  Copyright (C) 1996  SpellCaster Telecommunications Inc.
  *
  *  message.c - functions for sending and receiving control messages
diff -u --recursive --new-file v2.3.3/linux/drivers/misc/parport_init.c linux/drivers/misc/parport_init.c
--- v2.3.3/linux/drivers/misc/parport_init.c	Mon May 17 09:55:22 1999
+++ linux/drivers/misc/parport_init.c	Sat May 22 15:02:48 1999
@@ -22,7 +22,7 @@
 static int io[PARPORT_MAX+1] __initdata = { [0 ... PARPORT_MAX] = 0 };
 static int io_hi[PARPORT_MAX+1] __initdata = { [0 ... PARPORT_MAX] = 0 };
 static int irq[PARPORT_MAX] __initdata = { [0 ... PARPORT_MAX-1] = PARPORT_IRQ_PROBEONLY };
-static int dma[PARPORT_MAX] __initdata = { [0 ... PARPORT_MAX-1] = PARPORT_DMA_NONE };
+static int dma[PARPORT_MAX] __initdata = { [0 ... PARPORT_MAX-1] = PARPORT_DMA_AUTO };
 
 extern int parport_pc_init(int *io, int *io_hi, int *irq, int *dma);
 extern int parport_ax_init(void);
@@ -146,8 +146,11 @@
 EXPORT_SYMBOL(parport_claim_or_block);
 EXPORT_SYMBOL(parport_release);
 EXPORT_SYMBOL(parport_register_port);
+EXPORT_SYMBOL(parport_announce_port);
 EXPORT_SYMBOL(parport_unregister_port);
 EXPORT_SYMBOL(parport_quiesce);
+EXPORT_SYMBOL(parport_register_driver);
+EXPORT_SYMBOL(parport_unregister_driver);
 EXPORT_SYMBOL(parport_register_device);
 EXPORT_SYMBOL(parport_unregister_device);
 EXPORT_SYMBOL(parport_enumerate);
@@ -157,6 +160,7 @@
 EXPORT_SYMBOL(parport_proc_unregister);
 EXPORT_SYMBOL(parport_probe_hook);
 EXPORT_SYMBOL(parport_parse_irqs);
+EXPORT_SYMBOL(parport_parse_dmas);
 
 void inc_parport_count(void)
 {
diff -u --recursive --new-file v2.3.3/linux/drivers/misc/parport_pc.c linux/drivers/misc/parport_pc.c
--- v2.3.3/linux/drivers/misc/parport_pc.c	Mon May 17 09:55:22 1999
+++ linux/drivers/misc/parport_pc.c	Sat May 22 15:02:48 1999
@@ -44,6 +44,7 @@
 #include <linux/ioport.h>
 #include <linux/kernel.h>
 #include <linux/malloc.h>
+#include <linux/pci.h>
 
 #include <asm/io.h>
 
@@ -847,9 +848,59 @@
 	if (parport_probe_hook)
 		(*parport_probe_hook)(p);
 
+	/* Now that we've told the sharing engine about the port, and
+	   found out its characteristics, let the high-level drivers
+	   know about it. */
+	parport_announce_port (p);
+
 	return 1;
 }
 
+/* Look for PCI parallel port cards. */
+static int __init parport_pc_init_pci (int irq, int dma)
+{
+	int count = 0;
+#ifdef CONFIG_PCI
+	int i;
+	struct {
+		unsigned int vendor;
+		unsigned int device;
+		unsigned int numports;
+		struct {
+			unsigned int lo;
+			unsigned int hi; /* -ve if not there */
+		} addr[4];
+	} cards[] = {
+		{ 0, }
+	};
+
+	if (!pci_present ())
+		return 0;
+
+	for (i = 0; cards[i].vendor; i++) {
+		struct pci_dev *pcidev = NULL;
+		while ((pcidev = pci_find_device (cards[i].vendor,
+						  cards[i].device,
+						  pcidev)) != NULL) {
+			int n;
+			for (n = 0; n < cards[i].numports; n++) {
+				int lo = cards[i].addr[n].lo;
+				int hi = cards[i].addr[n].hi;
+				int io_lo = pcidev->base_address[lo];
+				int io_hi = ((hi < 0) ? 0 :
+					     pcidev->base_address[hi]);
+				io_lo &= PCI_BASE_ADDRESS_IO_MASK;
+				io_hi &= PCI_BASE_ADDRESS_IO_MASK;
+				count += probe_one_port (io_lo, io_hi,
+							 irq, dma);
+			}
+		}
+	}
+#endif /* CONFIG_PCI */
+
+	return count;
+}
+
 int __init parport_pc_init(int *io, int *io_hi, int *irq, int *dma)
 {
 	int count = 0, i = 0;
@@ -866,6 +917,7 @@
 		count += probe_one_port(0x3bc, 0x7bc, irq[0], dma[0]);
 		count += probe_one_port(0x378, 0x778, irq[0], dma[0]);
 		count += probe_one_port(0x278, 0x678, irq[0], dma[0]);
+		count += parport_pc_init_pci (irq[0], dma[0]);
 	}
 
 	return count;
@@ -874,13 +926,22 @@
 #ifdef MODULE
 static int io[PARPORT_PC_MAX_PORTS+1] = { [0 ... PARPORT_PC_MAX_PORTS] = 0 };
 static int io_hi[PARPORT_PC_MAX_PORTS+1] = { [0 ... PARPORT_PC_MAX_PORTS] = 0 };
-static int dma[PARPORT_PC_MAX_PORTS] = { [0 ... PARPORT_PC_MAX_PORTS-1] = PARPORT_DMA_NONE };
+static int dmaval[PARPORT_PC_MAX_PORTS] = { [0 ... PARPORT_PC_MAX_PORTS-1] = PARPORT_DMA_AUTO };
 static int irqval[PARPORT_PC_MAX_PORTS] = { [0 ... PARPORT_PC_MAX_PORTS-1] = PARPORT_IRQ_PROBEONLY };
 static const char *irq[PARPORT_PC_MAX_PORTS] = { NULL, };
+static const char *dma[PARPORT_PC_MAX_PORTS] = { NULL, };
+
+MODULE_PARM_DESC(io, "base address");
 MODULE_PARM(io, "1-" __MODULE_STRING(PARPORT_PC_MAX_PORTS) "i");
+
+MODULE_PARM_DESC(io_hi, "base address for ECR");
 MODULE_PARM(io_hi, "1-" __MODULE_STRING(PARPORT_PC_MAX_PORTS) "i");
+
+MODULE_PARM_DESC(irq, "irq line to use (or 'auto' or 'none')");
 MODULE_PARM(irq, "1-" __MODULE_STRING(PARPORT_PC_MAX_PORTS) "s");
-MODULE_PARM(dma, "1-" __MODULE_STRING(PARPORT_PC_MAX_PORTS) "i");
+
+MODULE_PARM_DESC(dma, "dma channel to use (or 'auto' or 'none')");
+MODULE_PARM(dma, "1-" __MODULE_STRING(PARPORT_PC_MAX_PORTS) "s");
 
 int init_module(void)
 {	
@@ -888,9 +949,30 @@
 	   the irq values. */
 	unsigned int i;
 	for (i = 0; i < PARPORT_PC_MAX_PORTS && io[i]; i++);
-	parport_parse_irqs(i, irq, irqval);
+	if (i) {
+		if (parport_parse_irqs(i, irq, irqval)) return 1;
+		if (parport_parse_dmas(i, dma, dmaval)) return 1;
+	}
+	else {
+		/* The user can make us use any IRQs or DMAs we find. */
+		int val;
+
+		if (irq[0] && !parport_parse_irqs (1, irq, &val))
+			switch (val) {
+			case PARPORT_IRQ_NONE:
+			case PARPORT_IRQ_AUTO:
+				irqval[0] = val;
+			}
+
+		if (dma[0] && !parport_parse_dmas (1, dma, &val))
+			switch (val) {
+			case PARPORT_DMA_NONE:
+			case PARPORT_DMA_AUTO:
+				dmaval[0] = val;
+			}
+	}
 
-	return (parport_pc_init(io, io_hi, irqval, dma)?0:1);
+	return (parport_pc_init(io, io_hi, irqval, dmaval)?0:1);
 }
 
 void cleanup_module(void)
diff -u --recursive --new-file v2.3.3/linux/drivers/misc/parport_share.c linux/drivers/misc/parport_share.c
--- v2.3.3/linux/drivers/misc/parport_share.c	Fri May 14 18:55:19 1999
+++ linux/drivers/misc/parport_share.c	Sat May 22 15:02:48 1999
@@ -40,6 +40,55 @@
 static struct parport *portlist = NULL, *portlist_tail = NULL;
 spinlock_t parportlist_lock = SPIN_LOCK_UNLOCKED;
 
+static struct parport_driver *driver_chain = NULL;
+spinlock_t driverlist_lock = SPIN_LOCK_UNLOCKED;
+
+static void call_driver_chain (int attach, struct parport *port)
+{
+	struct parport_driver *drv;
+
+	for (drv = driver_chain; drv; drv = drv->next) {
+		if (attach)
+			drv->attach (port);
+		else
+			drv->detach (port);
+	}
+}
+
+int parport_register_driver (struct parport_driver *drv)
+{
+	struct parport *port;
+
+	spin_lock (&driverlist_lock);
+	drv->next = driver_chain;
+	driver_chain = drv;
+	spin_unlock (&driverlist_lock);
+
+	for (port = portlist; port; port = port->next)
+		drv->attach (port);
+
+	return 0;
+}
+
+void parport_unregister_driver (struct parport_driver *arg)
+{
+	struct parport_driver *drv = driver_chain, *olddrv = NULL;
+
+	while (drv) {
+		if (drv == arg) {
+			spin_lock (&driverlist_lock);
+			if (olddrv)
+				olddrv->next = drv->next;
+			else
+				driver_chain = drv->next;
+			spin_unlock (&driverlist_lock);
+			return;
+		}
+		olddrv = drv;
+		drv = drv->next;
+	}
+}
+
 void (*parport_probe_hook)(struct parport *port) = NULL;
 
 /* Return a list of all the ports we know about. */
@@ -138,10 +187,19 @@
 	return tmp;
 }
 
+void parport_announce_port (struct parport *port)
+{
+	/* Let drivers know that a new port has arrived. */
+	call_driver_chain (1, port);
+}
+
 void parport_unregister_port(struct parport *port)
 {
 	struct parport *p;
 
+	/* Spread the word. */
+	call_driver_chain (0, port);
+
 	spin_lock(&parportlist_lock);
 	if (portlist == port) {
 		if ((portlist = port->next) == NULL)
@@ -517,23 +575,38 @@
 	}
 }
 
-void parport_parse_irqs(int nports, const char *irqstr[], int irqval[])
+static int parport_parse_params (int nports, const char *str[], int val[],
+				 int automatic, int none)
 {
 	unsigned int i;
-	for (i = 0; i < nports && irqstr[i]; i++) {
-		if (!strncmp(irqstr[i], "auto", 4))
-			irqval[i] = PARPORT_IRQ_AUTO;
-		else if (!strncmp(irqstr[i], "none", 4))
-			irqval[i] = PARPORT_IRQ_NONE;
+	for (i = 0; i < nports && str[i]; i++) {
+		if (!strncmp(str[i], "auto", 4))
+			val[i] = automatic;
+		else if (!strncmp(str[i], "none", 4))
+			val[i] = none;
 		else {
 			char *ep;
-			unsigned long r = simple_strtoul(irqstr[i], &ep, 0);
-			if (ep != irqstr[i])
-				irqval[i] = r;
+			unsigned long r = simple_strtoul(str[i], &ep, 0);
+			if (ep != str[i])
+				val[i] = r;
 			else {
-				printk("parport: bad irq specifier `%s'\n", irqstr[i]);
-				return;
+				printk("parport: bad specifier `%s'\n", str[i]);
+				return -1;
 			}
 		}
 	}
+
+	return 0;
+}
+
+int parport_parse_irqs(int nports, const char *irqstr[], int irqval[])
+{
+	return parport_parse_params (nports, irqstr, irqval, PARPORT_IRQ_AUTO,
+				     PARPORT_IRQ_NONE);
+}
+
+int parport_parse_dmas(int nports, const char *dmastr[], int dmaval[])
+{
+	return parport_parse_params (nports, dmastr, dmaval, PARPORT_DMA_AUTO,
+				     PARPORT_DMA_NONE);
 }
diff -u --recursive --new-file v2.3.3/linux/drivers/net/Space.c linux/drivers/net/Space.c
--- v2.3.3/linux/drivers/net/Space.c	Mon Mar 15 16:11:30 1999
+++ linux/drivers/net/Space.c	Tue May 25 13:06:34 1999
@@ -805,3 +805,4 @@
 };
 
 struct device *dev_base = &loopback_dev;
+rwlock_t dev_base_lock = RW_LOCK_UNLOCKED;
diff -u --recursive --new-file v2.3.3/linux/drivers/net/arcnet.c linux/drivers/net/arcnet.c
--- v2.3.3/linux/drivers/net/arcnet.c	Mon Sep 14 11:32:22 1998
+++ linux/drivers/net/arcnet.c	Tue May 25 13:06:34 1999
@@ -2023,8 +2023,10 @@
   for (c=0; c< (arcnet_num_devs-1); c++)
     arcnet_devs[c].next=&arcnet_devs[c+1];
 
+  write_lock_bh(&dev_base_lock);
   arcnet_devs[c].next=dev_base;
   dev_base=&arcnet_devs[0];
+  write_unlock_bh(&dev_base_lock);
 
   /* Give names to those without them */
 
@@ -2078,9 +2080,11 @@
   for (;;)
     {
       sprintf(device, "arc%d", arcnum);
+      read_lock_bh(&dev_base_lock);
       for (dev = dev_base; dev; dev=dev->next)
 	if (dev->name != device && !strcmp(dev->name, device))
 	  break;
+      read_unlock_bh(&dev_base_lock);
       if (!dev)
 	return;
       arcnum++;
diff -u --recursive --new-file v2.3.3/linux/drivers/net/cosa.c linux/drivers/net/cosa.c
--- v2.3.3/linux/drivers/net/cosa.c	Mon May 17 09:55:22 1999
+++ linux/drivers/net/cosa.c	Sun May 30 10:18:20 1999
@@ -2,6 +2,9 @@
 
 /*
  *  Copyright (C) 1995-1997  Jan "Yenya" Kasprzak <kas@fi.muni.cz>
+ * 
+ * 	5/25/1999 : Marcelo Tosatti <marcelo@conectiva.com.br>
+ * 		fixed a deadlock in cosa_sppp_open 
  *
  *  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
@@ -599,6 +602,7 @@
 	if (chan->usage != 0) {
 		printk(KERN_WARNING "%s: sppp_open called with usage count %d\n",
 			chan->name, chan->usage);
+		spin_unlock_irqrestore(&chan->cosa->lock, flags);
 		return -EBUSY;
 	}
 	chan->setup_rx = sppp_setup_rx;
@@ -750,8 +754,8 @@
 
 static void chardev_channel_init(struct channel_data *chan)
 {
-	chan->rsem = MUTEX;
-	chan->wsem = MUTEX;
+	init_MUTEX(&chan->rsem);
+	init_MUTEX(&chan->wsem);
 }
 
 static long long cosa_lseek(struct file * file,
diff -u --recursive --new-file v2.3.3/linux/drivers/net/eexpress.c linux/drivers/net/eexpress.c
--- v2.3.3/linux/drivers/net/eexpress.c	Thu May  6 23:14:36 1999
+++ linux/drivers/net/eexpress.c	Sun May 30 10:18:49 1999
@@ -81,7 +81,20 @@
  * ftp's, which is significantly better than I get in DOS, so the overhead of
  * stopping and restarting the CU with each transmit is not prohibitive in
  * practice.
+ *
+ * Update by David Woodhouse 11/5/99:
+ *
+ * I've seen "CU wedged" messages in 16-bit mode, on the Alpha architecture.
+ * I assume that this is because 16-bit accesses are actually handled as two
+ * 8-bit accesses.
  */
+
+#ifdef __alpha__
+#define LOCKUP16 1
+#endif
+#ifndef LOCKUP16
+#define LOCKUP16 0
+#endif
   
 #include <linux/config.h>
 #include <linux/module.h>
@@ -297,7 +310,7 @@
 	outb(inb(dev->base_addr + Config) & ~2, dev->base_addr + Config);
 }
 
-static inline short int SHADOW(short int addr)
+static inline unsigned short int SHADOW(short int addr)
 {
 	addr &= 0x1f;
 	if (addr > 0xf) addr += 0x3ff0;
@@ -400,7 +413,10 @@
 	outb(0,ioaddr+SIGNAL_CA);
 	free_irq(irq,dev);
 	outb(i586_RST,ioaddr+EEPROM_Ctrl);
-	release_region(ioaddr,16);
+	release_region(ioaddr, EEXP_IO_EXTENT);
+	release_region(ioaddr+0x4000, 16);
+	release_region(ioaddr+0x8000, 16);
+	release_region(ioaddr+0xc000, 16);
 
 	MOD_DEC_USE_COUNT;
 	return 0;
@@ -887,7 +903,7 @@
 	struct net_local *lp = (struct net_local *)dev->priv;
 	unsigned short ioaddr = dev->base_addr;
 
-	if (lp->width) {
+	if (LOCKUP16 || lp->width) {
 		/* Stop the CU so that there is no chance that it
 		   jumps off to a bogus address while we are writing the
 		   pointer to the next transmit packet in 8-bit mode -- 
@@ -927,7 +943,7 @@
 	if (lp->tx_head != lp->tx_reap)
 		dev->tbusy = 0;
 
-	if (lp->width) {
+	if (LOCKUP16 || lp->width) {
 		/* Restart the CU so that the packet can actually
 		   be transmitted. (Zoltan Szilagyi 10-12-96) */
 		scb_command(dev, SCB_CUresume);
diff -u --recursive --new-file v2.3.3/linux/drivers/net/hamradio/bpqether.c linux/drivers/net/hamradio/bpqether.c
--- v2.3.3/linux/drivers/net/hamradio/bpqether.c	Sat Jul 18 14:16:03 1998
+++ linux/drivers/net/hamradio/bpqether.c	Tue May 25 13:06:34 1999
@@ -641,11 +641,16 @@
 	});
 #endif
 
+	read_lock_bh(&dev_base_lock);
 	for (dev = dev_base; dev != NULL; dev = dev->next) {
-		if (dev_is_ethdev(dev))
+		if (dev_is_ethdev(dev)) {
+			read_unlock_bh(&dev_base_lock);
 			bpq_new_device(dev);
+			read_lock_bh(&dev_base_lock);
+		}
 	}
-
+	read_unlock_bh(&dev_base_lock);
+out:
 	return 0;
 }
 
diff -u --recursive --new-file v2.3.3/linux/drivers/net/irda/Config.in linux/drivers/net/irda/Config.in
--- v2.3.3/linux/drivers/net/irda/Config.in	Sun Mar  7 15:26:43 1999
+++ linux/drivers/net/irda/Config.in	Sun May 30 10:27:03 1999
@@ -1,18 +1,24 @@
 mainmenu_option next_comment
 comment 'Infrared-port device drivers'
 
-dep_tristate 'IrTTY (uses serial driver)' CONFIG_IRTTY_SIR $CONFIG_IRDA
-if [ "$CONFIG_IRTTY_SIR" != "n" ]; then
-  comment '   Dongle support' 
-  bool '   Serial dongle support' CONFIG_DONGLE
-  if [ "$CONFIG_DONGLE" != "n" ]; then
-    dep_tristate '   ESI JetEye PC dongle' CONFIG_ESI_DONGLE $CONFIG_IRTTY_SIR
-    dep_tristate '   ACTiSYS IR-220L and IR220L+ dongle' CONFIG_ACTISYS_DONGLE $CONFIG_IRTTY_SIR
-    dep_tristate '   Tekram IrMate 210B dongle' CONFIG_TEKRAM_DONGLE $CONFIG_IRTTY_SIR
-    dep_tristate '   Greenwich GIrBIL dongle' CONFIG_GIRBIL_DONGLE $CONFIG_IRTTY_SIR
-  fi
+comment 'SIR device drivers'
+dep_tristate 'IrTTY (uses Linux serial driver)' CONFIG_IRTTY_SIR $CONFIG_IRDA
+dep_tristate 'IrPORT (IrDA serial driver)' CONFIG_IRPORT_SIR $CONFIG_IRDA
+
+comment 'FIR device drivers'
+dep_tristate 'NSC PC87108' CONFIG_NSC_FIR  $CONFIG_IRDA
+dep_tristate 'Winbond W83977AF (IR)' CONFIG_WINBOND_FIR $CONFIG_IRDA
+dep_tristate 'Sharp UIRCC' CONFIG_SHARP_FIR $CONFIG_IRDA
+dep_tristate 'Toshiba Type-O IR Port' CONFIG_TOSHIBA_FIR $CONFIG_IRDA
+
+comment 'Dongle support' 
+bool 'Serial dongle support' CONFIG_DONGLE
+if [ "$CONFIG_DONGLE" != "n" ]; then
+  dep_tristate '   ESI JetEye PC dongle' CONFIG_ESI_DONGLE $CONFIG_IRDA
+  dep_tristate '   ACTiSYS IR-220L and IR220L+ dongle' CONFIG_ACTISYS_DONGLE $CONFIG_IRDA
+  dep_tristate '   Tekram IrMate 210B dongle' CONFIG_TEKRAM_DONGLE $CONFIG_IRDA
+  dep_tristate '   Greenwich GIrBIL dongle' CONFIG_GIRBIL_DONGLE $CONFIG_IRDA
+  dep_tristate '   Parallax LiteLink dongle' CONFIG_LITELINK_DONGLE $CONFIG_IRDA
 fi
-dep_tristate '  NSC PC87108' CONFIG_NSC_FIR  $CONFIG_IRDA
-dep_tristate '  Winbond W83977AF (IR)' CONFIG_WINBOND_FIR $CONFIG_IRDA
-dep_tristate '  Sharp UIRCC' CONFIG_SHARP_FIR $CONFIG_IRDA
+
 endmenu
diff -u --recursive --new-file v2.3.3/linux/drivers/net/irda/Makefile linux/drivers/net/irda/Makefile
--- v2.3.3/linux/drivers/net/irda/Makefile	Thu Apr 15 05:42:41 1999
+++ linux/drivers/net/irda/Makefile	Sun May 30 10:27:03 1999
@@ -20,6 +20,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_IRPORT_SIR),y)
+L_OBJS += irport.o
+else
+  ifeq ($(CONFIG_IRPORT_SIR),m)
+  M_OBJS += irport.o
+  endif
+endif
+
 ifeq ($(CONFIG_NSC_FIR),y)
 L_OBJS += pc87108.o
 else
@@ -44,6 +52,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_TOSHIBA_FIR),y)
+L_OBJS += toshoboe.o
+else
+  ifeq ($(CONFIG_TOSHIBA_FIR),m)
+  M_OBJS += toshoboe.o
+  endif
+endif
+
 ifeq ($(CONFIG_ESI_DONGLE),y)
 L_OBJS += esi.o
 else
@@ -73,6 +89,14 @@
 else
   ifeq ($(CONFIG_GIRBIL_DONGLE),m)
   M_OBJS += girbil.o
+  endif
+endif
+
+ifeq ($(CONFIG_LITELINK_DONGLE),y)
+L_OBJS += litelink.o
+else
+  ifeq ($(CONFIG_LITELINK_DONGLE),m)
+  M_OBJS += litelink.o
   endif
 endif
 
diff -u --recursive --new-file v2.3.3/linux/drivers/net/irda/actisys.c linux/drivers/net/irda/actisys.c
--- v2.3.3/linux/drivers/net/irda/actisys.c	Sat Apr 24 17:49:37 1999
+++ linux/drivers/net/irda/actisys.c	Sun May 30 10:27:03 1999
@@ -1,16 +1,16 @@
 /*********************************************************************
  *                
  * Filename:      actisys.c
- * Version:       0.5
+ * Version:       0.8
  * Description:   Implementation for the ACTiSYS IR-220L and IR-220L+ 
  *                dongles
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Wed Oct 21 20:02:35 1998
- * Modified at:   Mon Apr 12 11:56:35 1999
+ * Modified at:   Mon May 10 15:12:54 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1998 Dag Brattli, All Rights Reserved.
+ *     Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
  *      
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
@@ -36,15 +36,14 @@
 #include <net/irda/irda.h>
 #include <net/irda/irmod.h>
 #include <net/irda/irda_device.h>
-#include <net/irda/irtty.h>
 #include <net/irda/dongle.h>
 
-static void actisys_reset( struct irda_device *dev, int unused);
-static void actisys_open( struct irda_device *idev, int type);
-static void actisys_close( struct irda_device *dev);
+static void actisys_reset(struct irda_device *dev, int unused);
+static void actisys_open(struct irda_device *idev, int type);
+static void actisys_close(struct irda_device *dev);
 static void actisys_change_speed( struct irda_device *dev, int baudrate);
-static void actisys_reset( struct irda_device *dev, int unused);
-static void actisys_init_qos( struct irda_device *idev, struct qos_info *qos);
+static void actisys_reset(struct irda_device *dev, int unused);
+static void actisys_init_qos(struct irda_device *idev, struct qos_info *qos);
 
 /* These are the baudrates supported */
 static int baud_rates[] = { 9600, 19200, 57600, 115200, 38400};
@@ -58,17 +57,37 @@
 	actisys_init_qos,
 };
 
-__initfunc(void actisys_init(void))
+static struct dongle dongle_plus = {
+	ACTISYS_PLUS_DONGLE,
+	actisys_open,
+	actisys_close,
+	actisys_reset,
+	actisys_change_speed,
+	actisys_init_qos,
+};
+
+__initfunc(int actisys_init(void))
 {
-	irtty_register_dongle(&dongle);
+	int ret;
+
+	ret = irda_device_register_dongle(&dongle);
+	if (ret < 0)
+		return ret;
+	ret = irda_device_register_dongle(&dongle_plus);
+	if (ret < 0) {
+		irda_device_unregister_dongle(&dongle);
+		return ret;
+	}	
+	return 0;
 }
 
 void actisys_cleanup(void)
 {
-	irtty_unregister_dongle(&dongle);
+	irda_device_unregister_dongle(&dongle);
+	irda_device_unregister_dongle(&dongle_plus);
 }
 
-static void actisys_open( struct irda_device *idev, int type)
+static void actisys_open(struct irda_device *idev, int type)
 {
 	strcat(idev->description, " <-> actisys");
 
@@ -78,8 +97,11 @@
 	MOD_INC_USE_COUNT;
 }
 
-static void actisys_close( struct irda_device *dev)
+static void actisys_close(struct irda_device *idev)
 {
+	/* Power off dongle */
+	irda_device_set_dtr_rts(idev, FALSE, FALSE);
+
 	MOD_DEC_USE_COUNT;
 }
 
@@ -90,25 +112,16 @@
  *    To cycle through the available baud rates, pulse RTS low for a few
  *    ms.  
  */
-static void actisys_change_speed( struct irda_device *idev, int baudrate)
+static void actisys_change_speed(struct irda_device *idev, int baudrate)
 {
-        struct irtty_cb *self;
-        struct tty_struct *tty;
-        struct termios old_termios;
-	int cflag;
         int current_baudrate;
         int index = 0;
 	
-	DEBUG( 4, __FUNCTION__ "()\n");
+	DEBUG(4, __FUNCTION__ "()\n");
 
-	ASSERT( idev != NULL, return;);
-	ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return;);
-	
-	self = (struct irtty_cb *) idev->priv;
+	ASSERT(idev != NULL, return;);
+	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return;);
 	
-	ASSERT( self != NULL, return;);
-	ASSERT( self->magic == IRTTY_MAGIC, return;);
-
 	current_baudrate = idev->qos.baud_rate.value;
 
 	/* Find the correct baudrate index for the currently used baudrate */
@@ -117,69 +130,34 @@
 
         DEBUG( 4, __FUNCTION__ "(), index=%d\n", index);
 
-	if ( !self->tty)
-		return;
-
-	tty = self->tty;
-
 	/* Cycle through avaiable baudrates until we reach the correct one */
-        while ( current_baudrate != baudrate) {	
-                DEBUG( 4, __FUNCTION__ "(), current baudrate = %d\n",
-                       baud_rates[index]);
+        while (current_baudrate != baudrate) {	
+                DEBUG(4, __FUNCTION__ "(), current baudrate = %d\n",
+		      baud_rates[index]);
 		
 		/* Set DTR, clear RTS */
-		irtty_set_dtr_rts(tty, TRUE, FALSE);
+		irda_device_set_dtr_rts(idev, TRUE, FALSE);
 		
 		/* Wait at a few ms */
 		current->state = TASK_INTERRUPTIBLE;
 		schedule_timeout(2);
 
 		/* Set DTR, Set RTS */
-		irtty_set_dtr_rts(tty, TRUE, TRUE);
+		irda_device_set_dtr_rts(idev, TRUE, TRUE);
 		
 		/* Wait at a few ms again */
 		current->state = TASK_INTERRUPTIBLE;
-		schedule_timeout( 2);
+		schedule_timeout(2);
 
                 /* Go to next baudrate */
-		if ( idev->io.dongle_id == ACTISYS_DONGLE)
+		if (idev->io.dongle_id == ACTISYS_DONGLE)
                         index = (index+1) % 4; /* IR-220L */
 		else
                         index = (index+1) % 5; /* IR-220L+ */
 
                 current_baudrate = baud_rates[index];
         }
-	DEBUG(4, __FUNCTION__ "(), current baudrate = %d\n", 
-	      baud_rates[index]);
-
-	/* Now change the speed of the serial port */
-	old_termios = *(tty->termios);
-	cflag = tty->termios->c_cflag;
-
-	cflag &= ~CBAUD;
-
-        switch ( baudrate) {
-        case 9600:
-        default:
-		cflag |= B9600;
-		break;
-	case 19200:
-		cflag |= B19200;
-		break;
-	case 38400:
-		cflag |= B38400;
-		break;
-	case 57600:
-		cflag |= B57600;
-		break;
-	case 115200:
-		cflag |= B115200;
-		break;
-	}
-
-	/* Change speed of serial port */
-	tty->termios->c_cflag = cflag;
-	tty->driver.set_termios( tty, &old_termios);
+	DEBUG(4, __FUNCTION__ "(), current baudrate = %d\n",baud_rates[index]);
 }
 
 /*
@@ -191,32 +169,20 @@
  *    	1. Clear DTR for a few ms.
  *
  */
-static void actisys_reset( struct irda_device *idev, int unused)
+static void actisys_reset(struct irda_device *idev, int unused)
 {
-	struct irtty_cb *self;
-        struct tty_struct *tty;
-
-	ASSERT( idev != NULL, return;);
-	ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return;);
-	
-	self = (struct irtty_cb *) idev->priv;
+	ASSERT(idev != NULL, return;);
+	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return;);
 	
-	ASSERT( self != NULL, return;);
-	ASSERT( self->magic == IRTTY_MAGIC, return;);
-
-	tty = self->tty;
-	if ( !tty)
-		return;
-
 	/* Clear DTR */
-	irtty_set_dtr_rts(tty, FALSE, TRUE);
+	irda_device_set_dtr_rts(idev, FALSE, TRUE);
 
 	/* Sleep 10-20 ms*/
 	current->state = TASK_INTERRUPTIBLE;
 	schedule_timeout(2);
 	
 	/* Go back to normal mode */
-	irtty_set_dtr_rts(tty, TRUE, TRUE);
+	irda_device_set_dtr_rts(idev, TRUE, TRUE);
 	
 	idev->qos.baud_rate.value = 9600;
 }
@@ -227,12 +193,12 @@
  *    Initialize QoS capabilities
  *
  */
-static void actisys_init_qos( struct irda_device *idev, struct qos_info *qos)
+static void actisys_init_qos(struct irda_device *idev, struct qos_info *qos)
 {
 	qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200;
 
 	/* Remove support for 38400 if this is not a 220L+ dongle */
-	if ( idev->io.dongle_id == ACTISYS_DONGLE)
+	if (idev->io.dongle_id == ACTISYS_DONGLE)
 		qos->baud_rate.bits &= ~IR_38400;
 	
 	qos->min_turn_time.bits &= 0x40; /* Needs 0.01 ms */
@@ -251,8 +217,7 @@
  */
 int init_module(void)
 {
-	actisys_init();
-	return(0);
+	return actisys_init();
 }
 
 /*
diff -u --recursive --new-file v2.3.3/linux/drivers/net/irda/esi.c linux/drivers/net/irda/esi.c
--- v2.3.3/linux/drivers/net/irda/esi.c	Sat Apr 24 17:49:37 1999
+++ linux/drivers/net/irda/esi.c	Sun May 30 10:27:03 1999
@@ -1,17 +1,17 @@
 /*********************************************************************
  *                
  * Filename:      esi.c
- * Version:       1.2
+ * Version:       1.4
  * Description:   Driver for the Extended Systems JetEye PC dongle
  * Status:        Experimental.
  * Author:        Thomas Davis, <ratbert@radiks.net>
  * Created at:    Sat Feb 21 18:54:38 1998
- * Modified at:   Mon Apr 12 11:55:30 1999
+ * Modified at:   Mon May 10 15:13:12 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * Sources:	  esi.c
  *
+ *     Copyright (c) 1998-1999, Dag Brattli, <dagb@cs.uit.no>
  *     Copyright (c) 1998, Thomas Davis, <ratbert@radiks.net>,
- *     Copyright (c) 1998, Dag Brattli,  <dagb@cs.uit.no>
  *     All Rights Reserved.
  *
  *     This program is free software; you can redistribute it and/or
@@ -41,11 +41,11 @@
 #include <net/irda/irtty.h>
 #include <net/irda/dongle.h>
 
-static void esi_open( struct irda_device *idev, int type);
-static void esi_close( struct irda_device *driver);
-static void esi_change_speed( struct irda_device *idev, int baud);
-static void esi_reset( struct irda_device *idev, int unused);
-static void esi_qos_init( struct irda_device *idev, struct qos_info *qos);
+static void esi_open(struct irda_device *idev, int type);
+static void esi_close(struct irda_device *driver);
+static void esi_change_speed(struct irda_device *idev, int baud);
+static void esi_reset(struct irda_device *idev, int unused);
+static void esi_qos_init(struct irda_device *idev, struct qos_info *qos);
 
 static struct dongle dongle = {
 	ESI_DONGLE,
@@ -58,17 +58,17 @@
 
 __initfunc(int esi_init(void))
 {
-	return irtty_register_dongle(&dongle);
+	return irda_device_register_dongle(&dongle);
 }
 
 void esi_cleanup(void)
 {
-	irtty_unregister_dongle( &dongle);
+	irda_device_unregister_dongle(&dongle);
 }
 
-static void esi_open( struct irda_device *idev, int type)
+static void esi_open(struct irda_device *idev, int type)
 {
-	strcat( idev->description, " <-> esi");
+	strcat(idev->description, " <-> esi");
 
 	idev->io.dongle_id = type;
 	idev->flags |= IFF_DONGLE;
@@ -76,8 +76,11 @@
 	MOD_INC_USE_COUNT;
 }
 
-static void esi_close( struct irda_device *driver)
-{
+static void esi_close(struct irda_device *idev)
+{		
+	/* Power off dongle */
+	irda_device_set_dtr_rts(idev, FALSE, FALSE);
+
 	MOD_DEC_USE_COUNT;
 }
 
@@ -87,54 +90,30 @@
  *    Set the speed for the Extended Systems JetEye PC ESI-9680 type dongle
  *
  */
-static void esi_change_speed( struct irda_device *idev, int baud)
+static void esi_change_speed(struct irda_device *idev, int baud)
 {
-	struct irtty_cb *self;
-	struct tty_struct *tty;
 	int dtr, rts;
-        struct termios old_termios;
-	int cflag;
-	
-	ASSERT( idev != NULL, return;);
-	ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return;);
 	
-	self = (struct irtty_cb *) idev->priv;
-	
-	ASSERT( self != NULL, return;);
-	ASSERT( self->magic == IRTTY_MAGIC, return;);
-
-	if ( !self->tty)
-		return;
-
-	tty = self->tty;
+	ASSERT(idev != NULL, return;);
+	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return;);
 	
-	old_termios = *(tty->termios);
-	cflag = tty->termios->c_cflag;
-
-	cflag &= ~CBAUD;
-
 	switch (baud) {
 	case 19200:
-		cflag |= B19200;
 		dtr = TRUE;
 		rts = FALSE;
 		break;
 	case 115200:
-		cflag |= B115200;
 		dtr = rts = TRUE;
 		break;
 	case 9600:
 	default:
-		cflag |= B9600;
 		dtr = FALSE;
 		rts = TRUE;
 		break;
 	}
-	/* Change speed of serial driver */
-	tty->termios->c_cflag = cflag;
-	tty->driver.set_termios(tty, &old_termios);
 
-	irtty_set_dtr_rts(tty, dtr, rts);
+	/* Change speed of dongle */
+	irda_device_set_dtr_rts(idev, dtr, rts);
 }
 
 static void esi_reset( struct irda_device *idev, int unused)
@@ -148,14 +127,17 @@
  *    Init QoS capabilities for the dongle
  *
  */
-static void esi_qos_init( struct irda_device *idev, struct qos_info *qos)
+static void esi_qos_init(struct irda_device *idev, struct qos_info *qos)
 {
 	qos->baud_rate.bits &= IR_9600|IR_19200|IR_115200;
 	qos->min_turn_time.bits &= 0x01; /* Needs at least 10 ms */
 }
 
 #ifdef MODULE
-		
+
+MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
+MODULE_DESCRIPTION("Extended Systems JetEye PC dongle driver");
+
 /*
  * Function init_module (void)
  *
diff -u --recursive --new-file v2.3.3/linux/drivers/net/irda/girbil.c linux/drivers/net/irda/girbil.c
--- v2.3.3/linux/drivers/net/irda/girbil.c	Sat Apr 24 17:49:37 1999
+++ linux/drivers/net/irda/girbil.c	Sun May 30 10:27:03 1999
@@ -1,12 +1,12 @@
 /*********************************************************************
  *                
  * Filename:      girbil.c
- * Version:       1.0
+ * Version:       1.1
  * Description:   Implementation for the Greenwich GIrBIL dongle
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Sat Feb  6 21:02:33 1999
- * Modified at:   Sat Apr 10 19:53:12 1999
+ * Modified at:   Mon May 10 16:01:33 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
  *     Copyright (c) 1999 Dag Brattli, All Rights Reserved.
@@ -80,19 +80,19 @@
 	girbil_init_qos,
 };
 
-__initfunc(void girbil_init(void))
+__initfunc(int girbil_init(void))
 {
-	irtty_register_dongle(&dongle);
+	return irda_device_register_dongle(&dongle);
 }
 
 void girbil_cleanup(void)
 {
-	irtty_unregister_dongle(&dongle);
+	irda_device_unregister_dongle(&dongle);
 }
 
 static void girbil_open(struct irda_device *idev, int type)
 {
-	strcat( idev->description, " <-> girbil");
+	strcat(idev->description, " <-> girbil");
 
 	idev->io.dongle_id = type;
 	idev->flags |= IFF_DONGLE;
@@ -100,8 +100,11 @@
 	MOD_INC_USE_COUNT;
 }
 
-static void girbil_close(struct irda_device *dev)
+static void girbil_close(struct irda_device *idev)
 {
+	/* Power off dongle */
+	irda_device_set_dtr_rts(idev, FALSE, FALSE);
+
 	MOD_DEC_USE_COUNT;
 }
 
@@ -114,71 +117,42 @@
  */
 static void girbil_change_speed(struct irda_device *idev, int speed)
 {
-	struct irtty_cb *self;
-	struct tty_struct *tty;
-	struct termios old_termios;
-	int cflag;
 	__u8 control[2];
 	
 	ASSERT(idev != NULL, return;);
 	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return;);
 	
-	self = (struct irtty_cb *) idev->priv;
-	
-	ASSERT(self != NULL, return;); 
-	ASSERT(self->magic == IRTTY_MAGIC, return;);
-	
-	if (!self->tty)
-		return;
-
-	tty = self->tty;
-	
-	old_termios = *(tty->termios);
-	cflag = tty->termios->c_cflag;
-
-	cflag &= ~CBAUD;
-
 	switch (speed) {
 	case 9600:
 	default:
-		cflag |= B9600;
 		control[0] = GIRBIL_9600;
 		break;
 	case 19200:
-		cflag |= B19200;
 		control[0] = GIRBIL_19200;
 		break;
 	case 34800:
-		cflag |= B38400;
 		control[0] = GIRBIL_38400;
 		break;
 	case 57600:
-		cflag |= B57600;
 		control[0] = GIRBIL_57600;
 		break;
 	case 115200:
-		cflag |= B115200;
 		control[0] = GIRBIL_115200;
 		break;
 	}
 	control[1] = GIRBIL_LOAD;
 
 	/* Set DTR and Clear RTS to enter command mode */
-	irtty_set_dtr_rts(tty, FALSE, TRUE);
+	irda_device_set_dtr_rts(idev, FALSE, TRUE);
 
 	/* Write control bytes */
-	if (tty->driver.write)
-		tty->driver.write(self->tty, 0, control, 2);
+	irda_device_raw_write(idev, control, 2);
 
 	current->state = TASK_INTERRUPTIBLE;
 	schedule_timeout(2);
 	
 	/* Go back to normal mode */
-	irtty_set_dtr_rts(tty, TRUE, TRUE);
-
-	/* Now change the speed of the serial port */
-	tty->termios->c_cflag = cflag;
-	tty->driver.set_termios(tty, &old_termios);	
+	irda_device_set_dtr_rts(idev, TRUE, TRUE);
 }
 
 /*
@@ -193,44 +167,32 @@
  */
 void girbil_reset(struct irda_device *idev, int unused)
 {
-	struct irtty_cb *self;
-	struct tty_struct *tty;
 	__u8 control = GIRBIL_TXEN | GIRBIL_RXEN;
 
 	ASSERT(idev != NULL, return;);
 	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return;);
 	
-	self = (struct irtty_cb *) idev->priv;
-	
-	ASSERT(self != NULL, return;);
-	ASSERT(self->magic == IRTTY_MAGIC, return;);
-
-	tty = self->tty;
-	if (!tty)
-		return;
-
 	/* Reset dongle */
-	irtty_set_dtr_rts(tty, TRUE, FALSE);
+	irda_device_set_dtr_rts(idev, TRUE, FALSE);
 
 	/* Sleep at least 5 ms */
 	current->state = TASK_INTERRUPTIBLE;
 	schedule_timeout(2);
 	
 	/* Set DTR and clear RTS to enter command mode */
-	irtty_set_dtr_rts(tty, FALSE, TRUE);
+	irda_device_set_dtr_rts(idev, FALSE, TRUE);
 
 	current->state = TASK_INTERRUPTIBLE;
 	schedule_timeout(2);
 
 	/* Write control byte */
-	if (tty->driver.write)
-		tty->driver.write(self->tty, 0, &control, 1);
+	irda_device_raw_write(idev, &control, 1);
 
 	current->state = TASK_INTERRUPTIBLE;
 	schedule_timeout(2);
 
 	/* Go back to normal mode */
-	irtty_set_dtr_rts(tty, TRUE, TRUE);
+	irda_device_set_dtr_rts(idev, TRUE, TRUE);
 }
 
 /*
@@ -258,8 +220,7 @@
  */
 int init_module(void)
 {
-	girbil_init();
-	return(0);
+	return girbil_init();
 }
 
 /*
diff -u --recursive --new-file v2.3.3/linux/drivers/net/irda/irport.c linux/drivers/net/irda/irport.c
--- v2.3.3/linux/drivers/net/irda/irport.c	Sat Apr 24 17:49:37 1999
+++ linux/drivers/net/irda/irport.c	Sun May 30 10:27:03 1999
@@ -64,58 +64,168 @@
 
 #define IO_EXTENT 8
 
-/* static unsigned int io[]  = { 0x3e8, ~0, ~0, ~0 }; */
-/* static unsigned int irq[] = { 11, 0, 0, 0 }; */
+/* 
+ * Currently you'll need to set these values using insmod like this:
+ * insmod irport io=0x3e8 irq=11
+ */
+static unsigned int io[]  = { ~0, ~0, ~0, ~0 };
+static unsigned int irq[] = { 0, 0, 0, 0 };
+
+static unsigned int qos_mtt_bits = 0x03;
+
+static struct irda_device *dev_self[] = { NULL, NULL, NULL, NULL};
+static char *driver_name = "irport";
+
+static int irport_open(int i, unsigned int iobase, unsigned int irq);
+static int irport_close(struct irda_device *idev);
 
 static void irport_write_wakeup(struct irda_device *idev);
 static int  irport_write(int iobase, int fifo_size, __u8 *buf, int len);
 static void irport_receive(struct irda_device *idev);
 
+static int  irport_net_init(struct device *dev);
+static int  irport_net_open(struct device *dev);
+static int  irport_net_close(struct device *dev);
+static void irport_wait_until_sent(struct irda_device *idev);
+static int  irport_is_receiving(struct irda_device *idev);
+static void irport_set_dtr_rts(struct irda_device *idev, int dtr, int rts);
+static int  irport_raw_write(struct irda_device *idev, __u8 *buf, int len);
+
 __initfunc(int irport_init(void))
 {
-/* 	int i; */
+ 	int i;
 
-/* 	for ( i=0; (io[i] < 2000) && (i < 4); i++) { */
-/* 		int ioaddr = io[i]; */
-/* 		if (check_region(ioaddr, IO_EXTENT)) */
-/* 			continue; */
-/* 		if (irport_open( i, io[i], io2[i], irq[i], dma[i]) == 0) */
-/* 			return 0; */
-/* 	} */
-/* 	return -ENODEV; */
-	return 0;
+ 	for (i=0; (io[i] < 2000) && (i < 4); i++) {
+ 		int ioaddr = io[i];
+ 		if (check_region(ioaddr, IO_EXTENT))
+ 			continue;
+ 		if (irport_open(i, io[i], irq[i]) == 0)
+ 			return 0;
+ 	}
+	/* 
+	 * Maybe something failed, but we can still be usable for FIR drivers 
+	 */
+ 	return 0;
 }
 
 /*
- * Function pc87108_cleanup ()
+ * Function irport_cleanup ()
  *
- *    Close all configured chips
+ *    Close all configured ports
  *
  */
 #ifdef MODULE
 static void irport_cleanup(void)
 {
-/* 	int i; */
+ 	int i;
 
         DEBUG( 4, __FUNCTION__ "()\n");
 
-	/* for ( i=0; i < 4; i++) { */
-/* 		if ( dev_self[i]) */
-/* 			irport_close( &(dev_self[i]->idev)); */
-/* 	} */
+	for (i=0; i < 4; i++) {
+ 		if (dev_self[i])
+ 			irport_close(dev_self[i]);
+ 	}
 }
 #endif /* MODULE */
 
-/*
- * Function irport_open (void)
- *
- *    Start IO port 
- *
- */
-int irport_open(int iobase)
+static int irport_open(int i, unsigned int iobase, unsigned int irq)
 {
-	DEBUG(4, __FUNCTION__ "(), iobase=%#x\n", iobase);
+	struct irda_device *idev;
+	int ret;
+
+	DEBUG( 0, __FUNCTION__ "()\n");
+
+/* 	if (irport_probe(iobase, irq) == -1) */
+/* 		return -1; */
+
+	/*
+	 *  Allocate new instance of the driver
+	 */
+	idev = kmalloc(sizeof(struct irda_device), GFP_KERNEL);
+	if (idev == NULL) {
+		printk( KERN_ERR "IrDA: Can't allocate memory for "
+			"IrDA control block!\n");
+		return -ENOMEM;
+	}
+	memset(idev, 0, sizeof(struct irda_device));
+   
+	/* Need to store self somewhere */
+	dev_self[i] = idev;
+
+	/* Initialize IO */
+	idev->io.iobase2   = iobase;
+        idev->io.irq2      = irq;
+        idev->io.io_ext    = IO_EXTENT;
+        idev->io.fifo_size = 16;
+
+	/* Lock the port that we need */
+	ret = check_region(idev->io.iobase2, idev->io.io_ext);
+	if (ret < 0) { 
+		DEBUG( 0, __FUNCTION__ "(), can't get iobase of 0x%03x\n",
+		       idev->io.iobase2);
+		/* w83977af_cleanup( self->idev);  */
+		return -ENODEV;
+	}
+	request_region(idev->io.iobase2, idev->io.io_ext, idev->name);
+
+	/* Initialize QoS for this device */
+	irda_init_max_qos_capabilies(&idev->qos);
+	
+	idev->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600|
+		IR_115200;
+
+	idev->qos.min_turn_time.bits = qos_mtt_bits;
+	irda_qos_bits_to_value(&idev->qos);
+	
+	idev->flags = IFF_SIR|IFF_PIO;
+
+	/* Specify which buffer allocation policy we need */
+	idev->rx_buff.flags = GFP_KERNEL;
+	idev->tx_buff.flags = GFP_KERNEL;
+
+	idev->rx_buff.truesize = 4000; 
+	idev->tx_buff.truesize = 4000;
+	
+	/* Initialize callbacks */
+	idev->change_speed    = irport_change_speed;
+	idev->wait_until_sent = irport_wait_until_sent;
+        idev->is_receiving    = irport_is_receiving;
+	idev->set_dtr_rts     = irport_set_dtr_rts;
+	idev->raw_write       = irport_raw_write;
+
+	/* Override the network functions we need to use */
+	idev->netdev.init            = irport_net_init;
+	idev->netdev.hard_start_xmit = irport_hard_xmit;
+	idev->netdev.open            = irport_net_open;
+	idev->netdev.stop            = irport_net_close;
+
+	/* Open the IrDA device */
+	irda_device_open(idev, driver_name, NULL);
+	
+	return 0;
+}
+
+static int irport_close(struct irda_device *idev)
+{
+	DEBUG(0, __FUNCTION__ "()\n");
+
+	ASSERT(idev != NULL, return -1;);
+	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return -1;);
 
+	/* Release the PORT that this driver is using */
+	DEBUG(0 , __FUNCTION__ "(), Releasing Region %03x\n", 
+	      idev->io.iobase2);
+	release_region(idev->io.iobase2, idev->io.io_ext);
+
+	irda_device_close(idev);
+
+	kfree(idev);
+
+	return 0;
+}
+
+void irport_start(int iobase)
+{
 	/* Initialize UART */
 	outb(UART_LCR_WLEN8, iobase+UART_LCR);  /* Reset DLAB */
 	outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase+UART_MCR);
@@ -123,24 +233,29 @@
 	/* Turn on interrups */
 	outb((UART_IER_RLSI | UART_IER_RDI), iobase+UART_IER);
 
-	return 0;
+}
+
+void irport_stop(int iobase)
+{
+	/* Reset UART */
+	outb(0, iobase+UART_MCR);
+	
+	/* Turn off interrupts */
+	outb(0, iobase+UART_IER);
 }
 
 /*
- * Function irport_cleanup ()
+ * Function irport_probe (void)
  *
- *    Stop IO port
+ *    Start IO port 
  *
  */
-void irport_close(int iobase) 
+int irport_probe(int iobase)
 {
-	DEBUG(4, __FUNCTION__ "()\n");
+	DEBUG(4, __FUNCTION__ "(), iobase=%#x\n", iobase);
 
-	/* Reset UART */
-	outb(0, iobase+UART_MCR);
 
-	/* Turn off interrupts */
-	outb(0, iobase+UART_IER);
+	return 0;
 }
 
 /*
@@ -149,14 +264,23 @@
  *    Set speed of port to specified baudrate
  *
  */
-void irport_change_speed( int iobase, int speed) 
+void irport_change_speed(struct irda_device *idev, int speed)
 {
+	int iobase; 
 	int fcr;    /* FIFO control reg */
 	int lcr;    /* Line control reg */
 	int divisor;
 
 	DEBUG( 0, __FUNCTION__ "(), Setting speed to: %d\n", speed);
 
+	ASSERT(idev != NULL, return;);
+	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return;);
+
+	iobase = idev->io.iobase2;
+	
+	/* Update accounting for new speed */
+	idev->io.baudrate = speed;
+
 	/* Turn off interrupts */
 	outb(0, iobase+UART_IER); 
 
@@ -373,8 +497,145 @@
 	idev->netdev.interrupt = 0;
 }
 
+static int irport_net_init(struct device *dev)
+{
+	/* Set up to be a normal IrDA network device driver */
+	irda_device_setup(dev);
+
+	/* Insert overrides below this line! */
+
+	return 0;
+}
+
+/*
+ * Function irport_net_open (dev)
+ *
+ *    
+ *
+ */
+static int irport_net_open(struct device *dev)
+{
+	struct irda_device *idev;
+	int iobase;
+
+	ASSERT(dev != NULL, return -1;);
+	idev = (struct irda_device *) dev->priv;
+
+	iobase = idev->io.iobase2;
+
+	if (request_irq(idev->io.irq2, irport_interrupt, 0, idev->name, 
+			(void *) idev)) {
+		return -EAGAIN;
+	}
+
+	/* Ready to play! */
+	dev->tbusy = 0;
+	dev->interrupt = 0;
+	dev->start = 1;
+
+	MOD_INC_USE_COUNT;
+
+	irport_start(iobase);
+
+	return 0;
+}
+
+/*
+ * Function irport_net_close (idev)
+ *
+ *    
+ *
+ */
+static int irport_net_close(struct device *dev)
+{
+	struct irda_device *idev;
+	int iobase;
+
+	ASSERT(dev != NULL, return -1;);
+	idev = (struct irda_device *) dev->priv;
+
+	DEBUG(4, __FUNCTION__ "()\n");
+
+	iobase = idev->io.iobase2;
+
+	irport_stop(iobase);
+
+	/* Stop device */
+	dev->tbusy = 1;
+	dev->start = 0;
+
+	free_irq(idev->io.irq2, idev);
+
+	MOD_DEC_USE_COUNT;
+
+	return 0;
+}
+
+static void irport_wait_until_sent(struct irda_device *idev)
+{
+	current->state = TASK_INTERRUPTIBLE;
+	schedule_timeout(60*HZ/1000);
+}
+
+static int irport_is_receiving(struct irda_device *idev)
+{
+	return (idev->rx_buff.state != OUTSIDE_FRAME);
+}
+
+/*
+ * Function irtty_set_dtr_rts (tty, dtr, rts)
+ *
+ *    This function can be used by dongles etc. to set or reset the status
+ *    of the dtr and rts lines
+ */
+static void irport_set_dtr_rts(struct irda_device *idev, int dtr, int rts)
+{
+	int iobase;
+
+	ASSERT(idev != NULL, return;);
+	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return;);
+
+	iobase = idev->io.iobase2;
+
+	if (dtr)
+		dtr = UART_MCR_DTR;
+	if (rts)
+		rts = UART_MCR_RTS;
+
+	outb(dtr|rts|UART_MCR_OUT2, iobase+UART_MCR);
+}
+
+static int irport_raw_write(struct irda_device *idev, __u8 *buf, int len)
+{
+	int iobase;
+	int actual = 0;
+
+	ASSERT(idev != NULL, return -1;);
+	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return -1;);
+
+	iobase = idev->io.iobase2;
+
+	/* Tx FIFO should be empty! */
+	if (!(inb(iobase+UART_LSR) & UART_LSR_THRE)) {
+		DEBUG( 0, __FUNCTION__ "(), failed, fifo not empty!\n");
+		return -1;
+	}
+        
+	/* Fill FIFO with current frame */
+	while (actual < len) {
+		/* Transmit next byte */
+		outb(buf[actual], iobase+UART_TX);
+		actual++;
+	}
+
+	return actual;
+}
+
 #ifdef MODULE
 
+MODULE_PARM(io, "1-4i");
+MODULE_PARM(irq, "1-4i");
+
 /*
  * Function cleanup_module (void)
  *
@@ -393,11 +654,7 @@
  */
 int init_module(void)
 {
-	if (irport_init() < 0) {
-		cleanup_module();
-		return 1;
-	}
-	return(0);
+	return irport_init();
 }
 
 #endif /* MODULE */
diff -u --recursive --new-file v2.3.3/linux/drivers/net/irda/irtty.c linux/drivers/net/irda/irtty.c
--- v2.3.3/linux/drivers/net/irda/irtty.c	Sat Apr 24 17:49:37 1999
+++ linux/drivers/net/irda/irtty.c	Sun May 30 10:27:04 1999
@@ -6,12 +6,12 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Tue Dec  9 21:18:38 1997
- * Modified at:   Thu Apr 22 09:20:24 1999
+ * Modified at:   Mon May 10 15:45:50 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * Sources:       slip.c by Laurence Culhane,   <loz@holmes.demon.co.uk>
  *                          Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
  * 
- *     Copyright (c) 1998 Dag Brattli, All Rights Reserved.
+ *     Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
  *      
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
@@ -38,19 +38,21 @@
 #include <net/irda/irlap.h>
 #include <net/irda/timer.h>
 #include <net/irda/irda_device.h>
-#include <linux/kmod.h>
 
 static hashbin_t *irtty = NULL;
-static hashbin_t *dongles = NULL;
 
 static struct tty_ldisc irda_ldisc;
 
-static int irtty_hard_xmit(struct sk_buff *skb, struct device *dev);
+static int qos_mtt_bits = 0x03;      /* 5 ms or more */
+
+static int  irtty_hard_xmit(struct sk_buff *skb, struct device *dev);
 static void irtty_wait_until_sent(struct irda_device *driver);
-static int irtty_is_receiving(struct irda_device *idev);
-static int irtty_net_init(struct device *dev);
-static int irtty_net_open(struct device *dev);
-static int irtty_net_close(struct device *dev);
+static int  irtty_is_receiving(struct irda_device *idev);
+static void irtty_set_dtr_rts(struct irda_device *idev, int dtr, int rts);
+static int  irtty_raw_write(struct irda_device *idev, __u8 *buf, int len);
+static int  irtty_net_init(struct device *dev);
+static int  irtty_net_open(struct device *dev);
+static int  irtty_net_close(struct device *dev);
 
 static int  irtty_open(struct tty_struct *tty);
 static void irtty_close(struct tty_struct *tty);
@@ -73,13 +75,6 @@
 		return -ENOMEM;
 	}
 
-	dongles = hashbin_new(HB_LOCAL);
-	if (dongles == NULL) {
-		printk(KERN_WARNING 
-		       "IrDA: Can't allocate dongles hashbin!\n");
-		return -ENOMEM;
-	}
-
 	/* Fill in our line protocol discipline, and register it */
 	memset(&irda_ldisc, 0, sizeof( irda_ldisc));
 
@@ -132,7 +127,6 @@
 	 *  function to hashbin_destroy().
 	 */
 	hashbin_delete(irtty, NULL);
-	hashbin_delete(dongles, NULL);
 }
 #endif /* MODULE */
 
@@ -201,7 +195,7 @@
 	/* The only value we must override it the baudrate */
 	self->idev.qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600|
 		IR_115200;
-	self->idev.qos.min_turn_time.bits = 0x0f;
+	self->idev.qos.min_turn_time.bits = qos_mtt_bits;
 	self->idev.flags = IFF_SIR | IFF_PIO;
 	irda_qos_bits_to_value(&self->idev.qos);
 
@@ -216,7 +210,8 @@
 	/* Initialize callbacks */
 	self->idev.change_speed    = irtty_change_speed;
  	self->idev.is_receiving    = irtty_is_receiving;
-	/* self->idev.is_tbusy        = irtty_is_tbusy; */
+	self->idev.set_dtr_rts     = irtty_set_dtr_rts;
+	self->idev.raw_write       = irtty_raw_write;
 	self->idev.wait_until_sent = irtty_wait_until_sent;
 
 	/* Override the network functions we need to use */
@@ -248,10 +243,6 @@
 	ASSERT(self != NULL, return;);
 	ASSERT(self->magic == IRTTY_MAGIC, return;);
 
-	/* We are not using any dongle anymore! */
-	if (self->dongle_q)
-		self->dongle_q->dongle->close(&self->idev);
-
 	/* Remove driver */
 	irda_device_close(&self->idev);
 
@@ -359,68 +350,6 @@
 }
 
 /*
- * Function irtty_init_dongle (self, type)
- *
- *    Initialize attached dongle. Warning, must be called with a process
- *    context!
- */
-static void irtty_init_dongle(struct irtty_cb *self, int type)
-{
-	struct dongle_q *node;
-
-	ASSERT(self != NULL, return;);
-	ASSERT(self->magic == IRTTY_MAGIC, return;);
-
-#ifdef CONFIG_KMOD
-	/* Try to load the module needed */
-	switch( type) {
-	case ESI_DONGLE:
-		MESSAGE("IrDA: Trying to initialize ESI dongle!\n");
-		request_module("esi");
-		break;
-	case TEKRAM_DONGLE:
-		MESSAGE("IrDA: Trying to initialize Tekram dongle!\n");
-		request_module("tekram");
-		break;
-	case ACTISYS_DONGLE:     /* FALLTHROUGH */
-	case ACTISYS_PLUS_DONGLE:
-		MESSAGE("IrDA: Trying to initialize ACTiSYS dongle!\n");
-		request_module("actisys");
-		break;
-	case GIRBIL_DONGLE:
-		MESSAGE("IrDA: Trying to initialize GIrBIL dongle!\n");
-		request_module("girbil");
-		break;
-	default:
-		ERROR("Unknown dongle type!\n");
-		return;
-	}
-#endif /* CONFIG_KMOD */
-
-	node = hashbin_find(dongles, type, NULL);
-	if ( !node) {
-		ERROR("Unable to find requested dongle\n");
-		return;
-	}
-	self->dongle_q = node;
-
-	/* Use this change speed function instead of the default */
-	self->idev.change_speed = node->dongle->change_speed;
-
-	/*
-	 * Now initialize the dongle!
-	 */
-	node->dongle->open(&self->idev, type);
-	node->dongle->qos_init(&self->idev, &self->idev.qos);
-	
-	/* Reset dongle */
-	node->dongle->reset(&self->idev, 0);
-
-	/* Set to default baudrate */
-	node->dongle->change_speed(&self->idev, 9600);
-}
-
-/*
  * Function irtty_ioctl (tty, file, cmd, arg)
  *
  *     The Swiss army knife of system calls :-)
@@ -452,7 +381,7 @@
 		break;
 	case IRTTY_IOCTDONGLE:
 		/* Initialize dongle */
-		irtty_init_dongle(self, (int) arg);
+		irda_device_init_dongle(&self->idev, (int) arg);
 		break;
 	default:
 		return -ENOIOCTLCMD;
@@ -645,54 +574,23 @@
 	tty_wait_until_sent(self->tty, 0);
 }
 
-int irtty_register_dongle(struct dongle *dongle)
-{
-	struct dongle_q *new;
-	
-	/* Check if this compressor has been registred before */
-	if ( hashbin_find ( dongles, dongle->type, NULL)) {
-		DEBUG( 0, __FUNCTION__ "(), Dongle already registered\n");
-                return 0;
-        }
-	
-	/* Make new IrDA dongle */
-        new = (struct dongle_q *) kmalloc(sizeof(struct dongle_q), GFP_KERNEL);
-        if (new == NULL)
-                return -1;
-		
-	memset(new, 0, sizeof( struct dongle_q));
-        new->dongle = dongle;
-
-	/* Insert IrDA dongle into hashbin */
-	hashbin_insert(dongles, (QUEUE *) new, dongle->type, NULL);
-	
-        return 0;
-}
-
-void irtty_unregister_dongle(struct dongle *dongle)
-{
-	struct dongle_q *node;
-
-	node = hashbin_remove(dongles, dongle->type, NULL);
-	if (!node) {
-		ERROR(__FUNCTION__ "(), dongle not found!\n");
-		return;
-	}
-	kfree(node);
-}
-
-
 /*
  * Function irtty_set_dtr_rts (tty, dtr, rts)
  *
  *    This function can be used by dongles etc. to set or reset the status
  *    of the dtr and rts lines
  */
-void irtty_set_dtr_rts(struct tty_struct *tty, int dtr, int rts)
+static void irtty_set_dtr_rts(struct irda_device *idev, int dtr, int rts)
 {
+	struct tty_struct *tty;
+	struct irtty_cb *self;
 	mm_segment_t fs;
 	int arg = 0;
 
+	self = (struct irtty_cb *) idev->priv;
+
+	tty = self->tty;
+
 #ifdef TIOCM_OUT2 /* Not defined for ARM */
 	arg = TIOCM_OUT2;
 #endif
@@ -718,6 +616,25 @@
 	set_fs(fs);
 }
 
+static int irtty_raw_write(struct irda_device *idev, __u8 *buf, int len)
+{
+	struct irtty_cb *self;
+	int actual = 0;
+
+	ASSERT(idev != NULL, return 0;);
+	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return -1;);
+	
+	self = (struct irtty_cb *) idev->priv;
+
+	ASSERT(self != NULL, return 0;);
+	ASSERT(self->magic == IRTTY_MAGIC, return 0;);
+
+	if (self->tty->driver.write)
+		actual = self->tty->driver.write(self->tty, 0, buf, len);
+
+	return actual;
+}
+
 static int irtty_net_init(struct device *dev)
 {
 	/* Set up to be a normal IrDA network device driver */
@@ -759,6 +676,8 @@
 
 MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
 MODULE_DESCRIPTION("IrDA TTY device driver");
+
+MODULE_PARM(qos_mtt_bits, "i");
 
 /*
  * Function init_module (void)
diff -u --recursive --new-file v2.3.3/linux/drivers/net/irda/litelink.c linux/drivers/net/irda/litelink.c
--- v2.3.3/linux/drivers/net/irda/litelink.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/irda/litelink.c	Sun May 30 10:27:04 1999
@@ -0,0 +1,209 @@
+/*********************************************************************
+ *                
+ * Filename:      litelink.c
+ * Version:       1.0
+ * Description:   Driver for the Parallax LiteLink dongle
+ * Status:        Stable
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Fri May  7 12:50:33 1999
+ * Modified at:   Mon May 10 15:12:18 1999
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ * 
+ *     Copyright (c) 1999 Dag Brattli, All Rights Reserved.
+ *     
+ *     This program is free software; you can redistribute it and/or 
+ *     modify it under the terms of the GNU General Public License as 
+ *     published by the Free Software Foundation; either version 2 of 
+ *     the License, or (at your option) any later version.
+ * 
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *     GNU General Public License for more details.
+ * 
+ *     You should have received a copy of the GNU General Public License 
+ *     along with this program; if not, write to the Free Software 
+ *     Foundation, Inc., 59 Temple Place, Suite 330, Boston, 
+ *     MA 02111-1307 USA
+ *     
+ ********************************************************************/
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/tty.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <asm/ioctls.h>
+#include <asm/uaccess.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irmod.h>
+#include <net/irda/irda_device.h>
+#include <net/irda/dongle.h>
+
+static void litelink_reset(struct irda_device *dev, int unused);
+static void litelink_open(struct irda_device *idev, int type);
+static void litelink_close(struct irda_device *dev);
+static void litelink_change_speed( struct irda_device *dev, int baudrate);
+static void litelink_reset(struct irda_device *dev, int unused);
+static void litelink_init_qos(struct irda_device *idev, struct qos_info *qos);
+
+/* These are the baudrates supported */
+static int baud_rates[] = { 115200, 57600, 38400, 19200, 9600 };
+
+static struct dongle dongle = {
+	LITELINK_DONGLE,
+	litelink_open,
+	litelink_close,
+	litelink_reset,
+	litelink_change_speed,
+	litelink_init_qos,
+};
+
+__initfunc(int litelink_init(void))
+{
+	return irda_device_register_dongle(&dongle);
+}
+
+void litelink_cleanup(void)
+{
+	irda_device_unregister_dongle(&dongle);
+}
+
+static void litelink_open(struct irda_device *idev, int type)
+{
+	strcat(idev->description, " <-> litelink");
+
+        idev->io.dongle_id = type;
+	idev->flags |= IFF_DONGLE;
+
+	MOD_INC_USE_COUNT;
+}
+
+static void litelink_close(struct irda_device *idev)
+{
+	/* Power off dongle */
+	irda_device_set_dtr_rts(idev, FALSE, FALSE);
+
+	MOD_DEC_USE_COUNT;
+}
+
+/*
+ * Function litelink_change_speed (tty, baud)
+ *
+ *    Change speed of the Litelink dongle. To cycle through the available 
+ *    baud rates, pulse RTS low for a few ms.  
+ */
+static void litelink_change_speed(struct irda_device *idev, int baudrate)
+{
+        int i;
+	
+	ASSERT(idev != NULL, return;);
+	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return;);
+	
+	/* Clear RTS to reset dongle */
+	irda_device_set_dtr_rts(idev, TRUE, FALSE);
+
+	/* Sleep a minimum of 15 us */
+	udelay(15);
+
+	/* Go back to normal mode */
+	irda_device_set_dtr_rts(idev, TRUE, TRUE);
+	
+	/* Sleep a minimum of 15 us */
+	udelay(15);
+	
+	/* Cycle through avaiable baudrates until we reach the correct one */
+	for (i=0; i<5 && baud_rates[i] != baudrate; i++) {
+
+		/* Set DTR, clear RTS */
+		irda_device_set_dtr_rts(idev, FALSE, TRUE);
+		
+		/* Sleep a minimum of 15 us */
+		udelay(15);
+		
+		/* Set DTR, Set RTS */
+		irda_device_set_dtr_rts(idev, TRUE, TRUE);
+		
+		/* Sleep a minimum of 15 us */
+		udelay(15);
+        }
+}
+
+/*
+ * Function litelink_reset (dev)
+ *
+ *      Reset the Litelink type dongle. Warning, this function must only be
+ *      called with a process context!
+ *
+ */
+static void litelink_reset(struct irda_device *idev, int unused)
+{
+	struct irtty_cb *self;
+        struct tty_struct *tty;
+
+	ASSERT(idev != NULL, return;);
+	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return;);
+	
+	/* Power on dongle */
+	irda_device_set_dtr_rts(idev, TRUE, TRUE);
+
+	/* Sleep a minimum of 15 us */
+	udelay(15);
+
+	/* Clear RTS to reset dongle */
+	irda_device_set_dtr_rts(idev, TRUE, FALSE);
+
+	/* Sleep a minimum of 15 us */
+	udelay(15);
+
+	/* Go back to normal mode */
+	irda_device_set_dtr_rts(idev, TRUE, TRUE);
+	
+	/* Sleep a minimum of 15 us */
+	udelay(15);
+
+	/* This dongles speed defaults to 115200 bps */
+	idev->qos.baud_rate.value = 115200;
+}
+
+/*
+ * Function litelink_init_qos (qos)
+ *
+ *    Initialize QoS capabilities
+ *
+ */
+static void litelink_init_qos( struct irda_device *idev, struct qos_info *qos)
+{
+	qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200;
+	qos->min_turn_time.bits &= 0x40; /* Needs 0.01 ms */
+}
+
+#ifdef MODULE
+
+MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
+MODULE_DESCRIPTION("Parallax Litelink dongle driver");	
+		
+/*
+ * Function init_module (void)
+ *
+ *    Initialize Litelink module
+ *
+ */
+int init_module(void)
+{
+	return litelink_init();
+}
+
+/*
+ * Function cleanup_module (void)
+ *
+ *    Cleanup Litelink module
+ *
+ */
+void cleanup_module(void)
+{
+	litelink_cleanup();
+}
+
+#endif
diff -u --recursive --new-file v2.3.3/linux/drivers/net/irda/pc87108.c linux/drivers/net/irda/pc87108.c
--- v2.3.3/linux/drivers/net/irda/pc87108.c	Sat Apr 24 17:49:37 1999
+++ linux/drivers/net/irda/pc87108.c	Sun May 30 10:27:04 1999
@@ -6,10 +6,10 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Sat Nov  7 21:43:15 1998
- * Modified at:   Tue Apr 20 11:11:39 1999
+ * Modified at:   Sun May  9 12:57:46 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>
+ *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>
  *     Copyright (c) 1998 Lichen Wang, <lwang@actisys.com>
  *     Copyright (c) 1998 Actisys Corp., www.actisys.com
  *     All Rights Reserved
@@ -67,6 +67,7 @@
 #define BROKEN_DONGLE_ID
 
 static char *driver_name = "pc87108";
+static int qos_mtt_bits = 0x07;  /* 1 ms or more */
 
 #define CHIP_IO_EXTENT 8
 
@@ -219,7 +220,7 @@
 	idev->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600|
 		IR_115200|IR_576000|IR_1152000|(IR_4000000 << 8);
 	
-	idev->qos.min_turn_time.bits = 0x07;
+	idev->qos.min_turn_time.bits = qos_mtt_bits;
 	irda_qos_bits_to_value( &idev->qos);
 	
 	idev->flags = IFF_FIR|IFF_MIR|IFF_SIR|IFF_DMA|IFF_PIO|IFF_DONGLE;
@@ -259,8 +260,9 @@
  *    Close driver instance
  *
  */
-static int pc87108_close( struct irda_device *idev)
+static int pc87108_close(struct irda_device *idev)
 {
+	struct pc87108 *self;
 	int iobase;
 
 	DEBUG( 4, __FUNCTION__ "()\n");
@@ -269,13 +271,16 @@
 	ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return -1;);
 
         iobase = idev->io.iobase;
+	self = (struct pc87108 *) idev->priv;
 
 	/* Release the PORT that this driver is using */
 	DEBUG( 4, __FUNCTION__ "(), Releasing Region %03x\n", 
 	       idev->io.iobase);
-	release_region( idev->io.iobase, idev->io.io_ext);
+	release_region(idev->io.iobase, idev->io.io_ext);
+
+	irda_device_close(idev);
 
-	irda_device_close( idev);
+	kfree(self);
 
 	return 0;
 }
@@ -805,7 +810,6 @@
 	setup_dma(idev->io.dma, idev->tx_buff.data, idev->tx_buff.len, 
 		  DMA_MODE_WRITE);
 	
-	/* idev->media_busy = TRUE; */
 	idev->io.direction = IO_XMIT;
 	
 	/* Choose transmit DMA channel  */ 
@@ -973,7 +977,7 @@
  *
  *    
  */
-static int pc87108_dma_receive_complete( struct irda_device *idev, int iobase)
+static int pc87108_dma_receive_complete(struct irda_device *idev, int iobase)
 {
 	struct sk_buff *skb;
 	struct pc87108 *self;
@@ -988,8 +992,6 @@
 	/* Save current bank */
 	bank = inb( iobase+BSR);
 	
-	iobase = idev->io.iobase;
-
 	/* Read status FIFO */
 	switch_bank(iobase, BANK5);
 	while (( status = inb( iobase+FRM_ST)) & FRM_ST_VLD) {
@@ -1003,18 +1005,18 @@
 	}
 	
 	/* Try to process all entries in status FIFO */
-	switch_bank( iobase, BANK0);
-	while ( st_fifo->len) {
+	switch_bank(iobase, BANK0);
+	while (st_fifo->len) {
       
 		/* Get first entry */
-		status = st_fifo->entries[ st_fifo->head].status;
-		len    = st_fifo->entries[ st_fifo->head].len;
+		status = st_fifo->entries[st_fifo->head].status;
+		len    = st_fifo->entries[st_fifo->head].len;
 		st_fifo->head++;
 		st_fifo->len--;
 
 		/* Check for errors */
-		if ( status & FRM_ST_ERR_MSK) {
-			if ( status & FRM_ST_LOST_FR) {
+		if (status & FRM_ST_ERR_MSK) {
+			if (status & FRM_ST_LOST_FR) {
 				/* Add number of lost frames to stats */
 				idev->stats.rx_errors += len;	
 			} else {
@@ -1188,8 +1190,8 @@
 	bank = inb( iobase+BSR);
 	
 	/* Status event, or end of frame detected in FIFO */
-	if ( eir & (EIR_SFIF_EV|EIR_LS_EV)) {
-		if ( pc87108_dma_receive_complete( idev, iobase)) {
+	if (eir & (EIR_SFIF_EV|EIR_LS_EV)) {
+		if (pc87108_dma_receive_complete( idev, iobase)) {
 
 			/* Wait for next status FIFO interrupt */
 			new_ier |= IER_SFIF_IE;
@@ -1459,6 +1461,11 @@
 }
 
 #ifdef MODULE
+
+MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
+MODULE_DESCRIPTION("NSC PC87108 IrDA Device Driver");
+
+MODULE_PARM(qos_mtt_bits, "i");
 
 /*
  * Function init_module (void)
diff -u --recursive --new-file v2.3.3/linux/drivers/net/irda/tekram.c linux/drivers/net/irda/tekram.c
--- v2.3.3/linux/drivers/net/irda/tekram.c	Sat Apr 24 17:49:37 1999
+++ linux/drivers/net/irda/tekram.c	Sun May 30 10:27:04 1999
@@ -1,15 +1,15 @@
 /*********************************************************************
  *                
  * Filename:      tekram.c
- * Version:       1.0
+ * Version:       1.1
  * Description:   Implementation of the Tekram IrMate IR-210B dongle
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Wed Oct 21 20:02:35 1998
- * Modified at:   Tue Apr 13 16:33:54 1999
+ * Modified at:   Mon May 10 16:10:17 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1998 Dag Brattli, All Rights Reserved.
+ *     Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
  *      
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
@@ -62,15 +62,15 @@
 
 __initfunc(int tekram_init(void))
 {
-	return irtty_register_dongle(&dongle);
+	return irda_device_register_dongle(&dongle);
 }
 
 void tekram_cleanup(void)
 {
-	irtty_unregister_dongle( &dongle);
+	irda_device_unregister_dongle(&dongle);
 }
 
-static void tekram_open( struct irda_device *idev, int type)
+static void tekram_open(struct irda_device *idev, int type)
 {
 	strcat(idev->description, " <-> tekram");
 
@@ -80,8 +80,11 @@
 	MOD_INC_USE_COUNT;
 }
 
-static void tekram_close( struct irda_device *dev)
-{
+static void tekram_close(struct irda_device *idev)
+{		
+	/* Power off dongle */
+	irda_device_set_dtr_rts(idev, FALSE, FALSE);
+
 	MOD_DEC_USE_COUNT;
 }
 
@@ -101,79 +104,49 @@
  *    6. wait at least 50 us, new setting (baud rate, etc) takes effect here 
  *       after
  */
-static void tekram_change_speed( struct irda_device *dev, int baud)
+static void tekram_change_speed(struct irda_device *idev, int baud)
 {
-	struct irtty_cb *self;
-	struct tty_struct *tty;
-	struct termios old_termios;
-	int cflag;
 	__u8 byte;
 	
 	DEBUG(4, __FUNCTION__ "()\n");
 
-	ASSERT(dev != NULL, return;);
-	ASSERT(dev->magic == IRDA_DEVICE_MAGIC, return;);
-	
-	self = (struct irtty_cb *) dev->priv;
-	
-	ASSERT(self != NULL, return;);
-	ASSERT(self->magic == IRTTY_MAGIC, return;);
-	
-	if (!self->tty)
-		return;
-
-	tty = self->tty;
+	ASSERT(idev != NULL, return;);
+	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return;);
 	
-	old_termios = *(tty->termios);
-	cflag = tty->termios->c_cflag;
-
-	cflag &= ~CBAUD;
-
 	switch (baud) {
 	default:
-		/* FALLTHROUGH */
 	case 9600:
-		cflag |= B9600;
 		byte = TEKRAM_PW|TEKRAM_9600;
 		break;
 	case 19200:
-		cflag |= B19200;
 		byte = TEKRAM_PW|TEKRAM_19200;
 		break;
 	case 34800:
-		cflag |= B38400;
 		byte = TEKRAM_PW|TEKRAM_38400;
 		break;
 	case 57600:
-		cflag |= B57600;
 		byte = TEKRAM_PW|TEKRAM_57600;
 		break;
 	case 115200:
-		cflag |= B115200;
 		byte = TEKRAM_PW|TEKRAM_115200;
 		break;
 	}
 
 	/* Set DTR, Clear RTS */
-	irtty_set_dtr_rts(tty, TRUE, FALSE);
+	irda_device_set_dtr_rts(idev, TRUE, FALSE);
 	
 	/* Wait at least 7us */
 	udelay(7);
 
 	/* Write control byte */
-	if (tty->driver.write)
-		tty->driver.write(self->tty, 0, &byte, 1);
+	irda_device_raw_write(idev, &byte, 1);
 	
 	/* Wait at least 100 ms */
 	current->state = TASK_INTERRUPTIBLE;
 	schedule_timeout(MSECS_TO_JIFFIES(100));
         
 	/* Set DTR, Set RTS */
-	irtty_set_dtr_rts(tty, TRUE, TRUE);
-
-	/* Now change the speed of the serial port */
-	tty->termios->c_cflag = cflag;
-	tty->driver.set_termios(tty, &old_termios);	
+	irda_device_set_dtr_rts(idev, TRUE, TRUE);
 }
 
 /*
@@ -189,41 +162,27 @@
  *        3. clear DTR to SPACE state, wait at least 50 us for further 
  *         operation
  */
-void tekram_reset(struct irda_device *dev, int unused)
+void tekram_reset(struct irda_device *idev, int unused)
 {
-	struct irtty_cb *self;
-	struct tty_struct *tty;
-
-	DEBUG(4, __FUNCTION__ "()\n");
-
-	ASSERT(dev != NULL, return;);
-	ASSERT(dev->magic == IRDA_DEVICE_MAGIC, return;);
-	
-	self = (struct irtty_cb *) dev->priv;
+	ASSERT(idev != NULL, return;);
+	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return;);
 	
-	ASSERT(self != NULL, return;);
-	ASSERT(self->magic == IRTTY_MAGIC, return;);
-
-	tty = self->tty;
-	if (!tty)
-		return;
-
 	/* Power off dongle */
-	irtty_set_dtr_rts(tty, FALSE, FALSE);
+	irda_device_set_dtr_rts(idev, FALSE, FALSE);
 
 	/* Sleep 50 ms */
 	current->state = TASK_INTERRUPTIBLE;
 	schedule_timeout(MSECS_TO_JIFFIES(50));
 
 	/* Clear DTR, Set RTS */
-	irtty_set_dtr_rts(tty, FALSE, TRUE); 
+	irda_device_set_dtr_rts(idev, FALSE, TRUE); 
 
 	/* Should sleep 1 ms, but 10-20 should not do any harm */
 	current->state = TASK_INTERRUPTIBLE;
 	schedule_timeout(MSECS_TO_JIFFIES(20));
 
 	/* Set DTR, Set RTS */
-	irtty_set_dtr_rts(tty, TRUE, TRUE);
+	irda_device_set_dtr_rts(idev, TRUE, TRUE);
 	
 	udelay(50);
 
diff -u --recursive --new-file v2.3.3/linux/drivers/net/irda/toshoboe.c linux/drivers/net/irda/toshoboe.c
--- v2.3.3/linux/drivers/net/irda/toshoboe.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/irda/toshoboe.c	Sun May 30 10:27:04 1999
@@ -0,0 +1,901 @@
+/*********************************************************************
+ *                
+ * Filename:      toshoboe.c
+ * Version:       0.1
+ * Description:   Driver for the Toshiba OBOE (or type-O or 700 or 701)
+ *                FIR Chipset. 
+ * Status:        Experimental.
+ * Author:        James McKenzie <james@fishsoup.dhs.org>
+ * Created at:    Sat May 8  12:35:27 1999
+ * 
+ *     Copyright (c) 1999 James McKenzie, All Rights Reserved.
+ *      
+ *     This program is free software; you can redistribute it and/or 
+ *     modify it under the terms of the GNU General Public License as 
+ *     published by the Free Software Foundation; either version 2 of 
+ *     the License, or (at your option) any later version.
+ *  
+ *     Neither James McKenzie nor Cambridge University admit liability nor
+ *     provide warranty for any of this software. This material is 
+ *     provided "AS-IS" and at no charge.
+ *
+ *     Applicable Models : Libretto 100CT. and many more
+ *     Toshiba refers to this chip as the type-O IR port.
+ *
+ ********************************************************************/
+
+/* This driver is experimental, I have only three ir devices */
+/* an olivetti notebook which doesn't have FIR, a toshiba libretto, and */
+/* an hp printer, this works fine at 4MBPS with my HP printer */
+
+static char *rcsid = "$Id: toshoboe.c,v 1.5 1999/05/12 12:24:39 root Exp root $";
+
+/* 
+ * $Log: toshoboe.c,v $
+ * Revision 1.5  1999/05/12 12:24:39  root
+ * *** empty log message ***
+ *
+ * Revision 1.4  1999/05/12 11:55:08  root
+ * *** empty log message ***
+ *
+ * Revision 1.3  1999/05/09 01:33:12  root
+ * *** empty log message ***
+ *
+ * Revision 1.2  1999/05/09 01:30:38  root
+ * *** empty log message ***
+ *
+ * Revision 1.1  1999/05/09 01:25:04  root
+ * Initial revision
+ * 
+ */
+
+/* Define this to have only one frame in the XMIT or RECV queue */
+/* Toshiba's drivers do this, but it disables back to back tansfers */
+/* I think that the chip may have some problems certainly, I have */
+/* seen it jump over tasks in the taskfile->xmit with this turned on */
+#define ONETASK
+
+/* To adjust the number of tasks in use edit toshoboe.h */
+
+/* Define this to enable FIR and MIR support */
+#define ENABLE_FAST
+
+/* Number of ports this driver can support, you also need to edit dev_self below */
+#define NSELFS 4
+
+/* Size of IO window */
+#define CHIP_IO_EXTENT	0x1f
+
+/* Transmit and receive buffer sizes, adjust at your peril */
+#define RX_BUF_SZ 	4196
+#define TX_BUF_SZ	4196
+
+/* No user servicable parts below here */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/malloc.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include <net/irda/wrapper.h>
+#include <net/irda/irda.h>
+#include <net/irda/irmod.h>
+#include <net/irda/irlap_frame.h>
+#include <net/irda/irda_device.h>
+
+#include <net/irda/toshoboe.h>
+
+static char *driver_name = "toshoboe";
+
+static struct toshoboe_cb *dev_self[NSELFS + 1] =
+{NULL, NULL, NULL, NULL, NULL};
+
+/* Shutdown the chip and point the taskfile reg somewhere else */
+static void 
+toshoboe_stopchip (struct toshoboe_cb *self)
+{
+  DEBUG (4, __FUNCTION__ "()\n");
+
+  outb_p (0x0e, OBOE_REG_11);
+
+  outb_p (0x00, OBOE_RST);
+  outb_p (0x3f, OBOE_TFP2);     /*Write the taskfile address */
+  outb_p (0xff, OBOE_TFP1);
+  outb_p (0xff, OBOE_TFP0);
+  outb_p (0x0f, OBOE_REG_1B);
+  outb_p (0xff, OBOE_REG_1A);
+  outb_p (0x00, OBOE_ISR);      /*FIXME: should i do this to disbale ints */
+  outb_p (0x80, OBOE_RST);
+  outb_p (0xe, OBOE_LOCK);
+}
+
+/*Set the baud rate */
+static void 
+toshoboe_setbaud (struct toshoboe_cb *self, int baud)
+{
+  DEBUG (4, __FUNCTION__ "()\n");
+
+  printk (KERN_WARNING "ToshOboe: seting baud to %d\n", baud);
+
+  cli ();
+  switch (baud)
+    {
+    case 2400:
+      outb_p (OBOE_PMDL_SIR, OBOE_PMDL);
+      outb_p (OBOE_SMDL_SIR, OBOE_SMDL);
+      outb_p (0xbf, OBOE_UDIV);
+      break;
+    case 4800:
+      outb_p (OBOE_PMDL_SIR, OBOE_PMDL);
+      outb_p (OBOE_SMDL_SIR, OBOE_SMDL);
+      outb_p (0x5f, OBOE_UDIV);
+      break;
+    case 9600:
+      outb_p (OBOE_PMDL_SIR, OBOE_PMDL);
+      outb_p (OBOE_SMDL_SIR, OBOE_SMDL);
+      outb_p (0x2f, OBOE_UDIV);
+      break;
+    case 19200:
+      outb_p (OBOE_PMDL_SIR, OBOE_PMDL);
+      outb_p (OBOE_SMDL_SIR, OBOE_SMDL);
+      outb_p (0x17, OBOE_UDIV);
+      break;
+    case 38400:
+      outb_p (OBOE_PMDL_SIR, OBOE_PMDL);
+      outb_p (OBOE_SMDL_SIR, OBOE_SMDL);
+      outb_p (0xb, OBOE_UDIV);
+      break;
+    case 57600:
+      outb_p (OBOE_PMDL_SIR, OBOE_PMDL);
+      outb_p (OBOE_SMDL_SIR, OBOE_SMDL);
+      outb_p (0x7, OBOE_UDIV);
+      break;
+    case 115200:
+      outb_p (OBOE_PMDL_SIR, OBOE_PMDL);
+      outb_p (OBOE_SMDL_SIR, OBOE_SMDL);
+      outb_p (0x3, OBOE_UDIV);
+      break;
+    case 1152000:
+      outb_p (OBOE_PMDL_MIR, OBOE_PMDL);
+      outb_p (OBOE_SMDL_MIR, OBOE_SMDL);
+      outb_p (0x1, OBOE_UDIV);
+      break;
+    case 4000000:
+      outb_p (OBOE_PMDL_FIR, OBOE_PMDL);
+      outb_p (OBOE_SMDL_FIR, OBOE_SMDL);
+      outb_p (0x0, OBOE_UDIV);
+      break;
+    }
+
+  sti ();
+
+  outb_p (0x00, OBOE_RST);
+  outb_p (0x80, OBOE_RST);
+  outb_p (0x01, OBOE_REG_9);
+
+}
+
+/* Wake the chip up and get it looking at the taskfile */
+static void 
+toshoboe_startchip (struct toshoboe_cb *self)
+{
+  __u32 physaddr;
+
+  DEBUG (4, __FUNCTION__ "()\n");
+
+
+  outb_p (0, OBOE_LOCK);
+  outb_p (0, OBOE_RST);
+  outb_p (OBOE_NTR_VAL, OBOE_NTR);
+  outb_p (0xf0, OBOE_REG_D);
+  outb_p (0xff, OBOE_ISR);
+  outb_p (0x0f, OBOE_REG_1A);
+  outb_p (0xff, OBOE_REG_1B);
+
+
+  physaddr = virt_to_bus (self->taskfile);
+
+  outb_p ((physaddr >> 0x0a) & 0xff, OBOE_TFP0);
+  outb_p ((physaddr >> 0x12) & 0xff, OBOE_TFP1);
+  outb_p ((physaddr >> 0x1a) & 0x3f, OBOE_TFP2);
+
+  outb_p (0x0e, OBOE_REG_11);
+  outb_p (0x80, OBOE_RST);
+
+  toshoboe_setbaud (self, 9600);
+
+}
+
+/*Let the chip look at memory */
+static void 
+toshoboe_enablebm (struct toshoboe_cb *self)
+{
+  DEBUG (4, __FUNCTION__ "()\n");
+  pci_set_master (self->pdev);
+}
+
+/*Don't let the chip look at memory */
+static void 
+toshoboe_disablebm (struct toshoboe_cb *self)
+{
+  __u8 command;
+  DEBUG (4, __FUNCTION__ "()\n");
+
+  pci_read_config_byte (self->pdev, PCI_COMMAND, &command);
+  command &= ~PCI_COMMAND_MASTER;
+  pci_write_config_byte (self->pdev, PCI_COMMAND, command);
+
+}
+
+/*setup the taskfile */
+static void 
+toshoboe_initbuffs (struct toshoboe_cb *self)
+{
+  int i;
+
+  DEBUG (4, __FUNCTION__ "()\n");
+
+  cli ();
+
+  for (i = 0; i < TX_SLOTS; ++i)
+    {
+      self->taskfile->xmit[i].len = 0;
+      self->taskfile->xmit[i].control = 0x00;
+      self->taskfile->xmit[i].buffer = virt_to_bus (self->xmit_bufs[i]);
+    }
+
+  for (i = 0; i < RX_SLOTS; ++i)
+    {
+      self->taskfile->recv[i].len = 0;
+      self->taskfile->recv[i].control = 0x83;
+      self->taskfile->recv[i].buffer = virt_to_bus (self->recv_bufs[i]);
+    }
+
+  sti ();
+}
+
+
+/*Transmit something */
+static int 
+toshoboe_hard_xmit (struct sk_buff *skb, struct device *dev)
+{
+  struct irda_device *idev;
+  struct toshoboe_cb *self;
+  int mtt, len;
+
+  idev = (struct irda_device *) dev->priv;
+  ASSERT (idev != NULL, return 0;);
+  ASSERT (idev->magic == IRDA_DEVICE_MAGIC, return 0;);
+
+  self = idev->priv;
+  ASSERT (self != NULL, return 0;);
+
+
+#ifdef ONETASK
+  if (self->txpending)
+    return -EBUSY;
+
+  self->txs = inb_p (OBOE_XMTT) - OBOE_XMTT_OFFSET;
+
+  self->txs &= 0x3f;
+
+#endif
+
+  if (self->taskfile->xmit[self->txs].control)
+    return -EBUSY;
+
+
+  if (inb_p (OBOE_RST) & OBOE_RST_WRAP)
+    {
+      len = async_wrap_skb (skb, self->xmit_bufs[self->txs], TX_BUF_SZ);
+    }
+  else
+    {
+      len = skb->len;
+      memcpy (self->xmit_bufs[self->txs], skb->data, len);
+    }
+  self->taskfile->xmit[self->txs].len = len & 0x0fff;
+
+
+
+  outb_p (0, OBOE_RST);
+  outb_p (0x1e, OBOE_REG_11);
+
+  self->taskfile->xmit[self->txs].control = 0x84;
+
+  mtt = irda_get_mtt (skb);
+  if (mtt)
+    udelay (mtt);
+
+  self->txpending++;
+
+  /*FIXME: ask about tbusy,media_busy stuff, for the moment */
+  /*tbusy means can't queue any more */
+#ifndef ONETASK
+  if (self->txpending == TX_SLOTS)
+    {
+#else
+  {
+#endif
+    if (irda_lock ((void *) &dev->tbusy) == FALSE)
+      return -EBUSY;
+  }
+
+  outb_p (0x80, OBOE_RST);
+  outb_p (1, OBOE_REG_9);
+
+  self->txs++;
+  self->txs %= TX_SLOTS;
+
+  dev_kfree_skb (skb);
+
+  return 0;
+}
+
+/*interrupt handler */
+static void 
+toshoboe_interrupt (int irq, void *dev_id, struct pt_regs *regs)
+{
+  struct irda_device *idev = (struct irda_device *) dev_id;
+  struct toshoboe_cb *self;
+  __u8 irqstat;
+  struct sk_buff *skb;
+
+  if (idev == NULL)
+    {
+      printk (KERN_WARNING "%s: irq %d for unknown device.\n",
+              driver_name, irq);
+      return;
+    }
+
+  self = idev->priv;
+
+  if (!self)
+    return;
+
+  DEBUG (4, __FUNCTION__ "()\n");
+
+  irqstat = inb_p (OBOE_ISR);
+
+/* woz it us */
+  if (!(irqstat & 0xf8))
+    return;
+
+  outb_p (irqstat, OBOE_ISR);   /*Acknologede it */
+
+
+/* Txdone */
+  if (irqstat & OBOE_ISR_TXDONE)
+    {
+      self->txpending--;
+
+      idev->stats.tx_packets++;
+
+      idev->media_busy = FALSE;
+      idev->netdev.tbusy = 0;
+
+      mark_bh (NET_BH);
+    }
+
+  if (irqstat & OBOE_ISR_RXDONE)
+    {
+
+#ifdef ONETASK
+      self->rxs = inb_p (OBOE_RCVT);
+      self->rxs += (RX_SLOTS - 1);
+      self->rxs %= RX_SLOTS;
+#else
+      while (self->taskfile->recv[self->rxs].control == 0)
+#endif
+        {
+          int len = self->taskfile->recv[self->rxs].len;
+	
+	  if (len>2) len-=2;
+
+          skb = dev_alloc_skb (len + 1);
+          if (skb)
+            {
+              skb_reserve (skb, 1);
+
+              skb_put (skb, len);
+              memcpy (skb->data, self->recv_bufs[self->rxs], len);
+
+              idev->stats.rx_packets++;
+              skb->dev = &idev->netdev;
+              skb->mac.raw = skb->data;
+              skb->protocol = htons (ETH_P_IRDA);
+            }
+          else
+            {
+              printk (KERN_INFO __FUNCTION__
+                      "(), memory squeeze, dropping frame.\n");
+            }
+
+
+
+          self->taskfile->recv[self->rxs].control = 0x83;
+          self->taskfile->recv[self->rxs].len = 0x0;
+
+          self->rxs++;
+          self->rxs %= RX_SLOTS;
+
+          if (skb)
+            netif_rx (skb);
+
+        }
+
+    }
+
+  if (irqstat & OBOE_ISR_20)
+    {
+      printk (KERN_WARNING "Oboe_irq: 20\n");
+    }
+  if (irqstat & OBOE_ISR_10)
+    {
+      printk (KERN_WARNING "Oboe_irq: 10\n");
+    }
+  if (irqstat & 0x8)
+    {
+      /*FIXME: I think this is a TX or RX error of some sort */
+
+      idev->stats.tx_errors++;
+      idev->stats.rx_errors++;
+
+    }
+
+
+}
+
+
+
+/* Change the baud rate */
+static void 
+toshoboe_change_speed (struct irda_device *idev, int speed)
+{
+  struct toshoboe_cb *self;
+  DEBUG (4, __FUNCTION__ "()\n");
+
+  ASSERT (idev != NULL, return;);
+  ASSERT (idev->magic == IRDA_DEVICE_MAGIC, return;);
+
+  self = idev->priv;
+  ASSERT (self != NULL, return;);
+
+  idev->io.baudrate = speed;
+
+  toshoboe_setbaud (self, speed);
+
+}
+
+
+/* Check all xmit_tasks finished */
+static void 
+toshoboe_wait_until_sent (struct irda_device *idev)
+{
+  struct toshoboe_cb *self;
+  int i;
+
+  DEBUG (4, __FUNCTION__ "()\n");
+
+  ASSERT (idev != NULL, return;);
+  ASSERT (idev->magic == IRDA_DEVICE_MAGIC, return;);
+
+  self = idev->priv;
+  ASSERT (self != NULL, return;);
+
+  for (i = 0; i < TX_SLOTS; ++i)
+    {
+      while (self->taskfile->xmit[i].control)
+        {
+          current->state = TASK_INTERRUPTIBLE;
+          schedule_timeout (6);
+        }
+    }
+
+}
+
+static int 
+toshoboe_is_receiving (struct irda_device *idev)
+{
+  DEBUG (4, __FUNCTION__ "()\n");
+
+/*FIXME Can't tell! */
+  return (FALSE);
+}
+
+
+static int 
+toshoboe_net_init (struct device *dev)
+{
+  DEBUG (4, __FUNCTION__ "()\n");
+
+  /* Setup to be a normal IrDA network device driver */
+  irda_device_setup (dev);
+
+  /* Insert overrides below this line! */
+  return 0;
+}
+
+
+
+
+static int 
+toshoboe_net_open (struct device *dev)
+{
+  struct irda_device *idev;
+  struct toshoboe_cb *self;
+
+  DEBUG (4, __FUNCTION__ "()\n");
+
+  ASSERT (dev != NULL, return -1;);
+  idev = (struct irda_device *) dev->priv;
+
+  ASSERT (idev != NULL, return 0;);
+  ASSERT (idev->magic == IRDA_DEVICE_MAGIC, return 0;);
+
+  self = idev->priv;
+  ASSERT (self != NULL, return 0;);
+
+  if (request_irq (idev->io.irq, toshoboe_interrupt,
+                   SA_SHIRQ | SA_INTERRUPT, idev->name, (void *) idev))
+    {
+
+      return -EAGAIN;
+    }
+
+  toshoboe_initbuffs (self);
+  toshoboe_enablebm (self);
+  toshoboe_startchip (self);
+
+
+  cli ();
+
+  /*FIXME: need to test this carefully to check which one */
+  /*of the two possible startup logics the chip uses */
+  /*although it won't make any difference if no-one xmits durining init */
+  /*and none what soever if using ONETASK */
+
+  self->rxs = inb_p (OBOE_RCVT);
+  self->txs = inb_p (OBOE_XMTT) - OBOE_XMTT_OFFSET;
+
+#ifdef 0
+  self->rxs = 0;
+  self->txs = 0;
+#endif
+#ifdef 0
+  self->rxs = RX_SLOTS - 1;
+  self->txs = 0;
+#endif
+
+
+  self->txpending = 0;
+
+  sti ();
+
+
+  dev->tbusy = 0;
+  dev->interrupt = 0;
+  dev->start = 1;
+
+  MOD_INC_USE_COUNT;
+
+  return 0;
+
+}
+
+static int 
+toshoboe_net_close (struct device *dev)
+{
+  struct irda_device *idev;
+  struct toshoboe_cb *self;
+
+  DEBUG (4, __FUNCTION__ "()\n");
+
+  ASSERT (dev != NULL, return -1;);
+  idev = (struct irda_device *) dev->priv;
+
+  ASSERT (idev != NULL, return 0;);
+  ASSERT (idev->magic == IRDA_DEVICE_MAGIC, return 0;);
+
+  dev->tbusy = 1;
+  dev->start = 0;
+
+
+  self = idev->priv;
+
+  ASSERT (self != NULL, return 0;);
+
+  free_irq (idev->io.irq, (void *) idev);
+
+  toshoboe_stopchip (self);
+  toshoboe_disablebm (self);
+
+  MOD_DEC_USE_COUNT;
+
+  return 0;
+
+}
+
+
+
+#ifdef MODULE
+
+static int 
+toshoboe_close (struct irda_device *idev)
+{
+  struct toshoboe_cb *self;
+  int i;
+
+  DEBUG (4, __FUNCTION__ "()\n");
+
+  ASSERT (idev != NULL, return -1;);
+  ASSERT (idev->magic == IRDA_DEVICE_MAGIC, return -1;);
+
+  self = idev->priv;
+
+  ASSERT (self != NULL, return -1;);
+
+  toshoboe_stopchip (self);
+
+  release_region (idev->io.iobase, idev->io.io_ext);
+
+
+  for (i = 0; i < TX_SLOTS; ++i)
+    {
+      kfree (self->xmit_bufs[i]);
+      self->xmit_bufs[i] = NULL;
+    }
+
+  for (i = 0; i < RX_SLOTS; ++i)
+    {
+      kfree (self->recv_bufs[i]);
+      self->recv_bufs[i] = NULL;
+    }
+
+
+  kfree (self->taskfilebuf);
+  self->taskfilebuf = NULL;
+  self->taskfile = NULL;
+
+
+  irda_device_close (idev);
+
+  return (0);
+
+}
+
+#endif
+
+
+
+static int 
+toshoboe_open (struct pci_dev *pci_dev)
+{
+  struct toshoboe_cb *self;
+  struct irda_device *idev;
+  int i = 0;
+  int ok=0;
+
+
+  DEBUG (4, __FUNCTION__ "()\n");
+
+  while (dev_self[i])
+    i++;
+
+  if (i == NSELFS)
+    {
+      printk (KERN_ERR "Oboe: No more instances available");
+      return -ENOMEM;
+    }
+
+  self = kmalloc (sizeof (struct toshoboe_cb), GFP_KERNEL);
+
+  if (self == NULL)
+    {
+      printk (KERN_ERR "IrDA: Can't allocate memory for "
+              "IrDA control block!\n");
+      return -ENOMEM;
+    }
+
+  memset (self, 0, sizeof (struct toshoboe_cb));
+
+
+  dev_self[i] = self;
+
+  self->pdev = pci_dev;
+  self->base = pci_dev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK;
+
+  idev = &self->idev;
+
+  /*Setup idev */
+
+  idev->io.iobase = self->base;
+  idev->io.irq = pci_dev->irq;
+  idev->io.io_ext = CHIP_IO_EXTENT;
+
+  /* Lock the port that we need */
+  i = check_region (idev->io.iobase, idev->io.io_ext);
+  if (i < 0)
+    {
+      DEBUG (0, __FUNCTION__ "(), can't get iobase of 0x%03x\n",
+             idev->io.iobase);
+
+      dev_self[i] = NULL;
+      kfree (self);
+
+      return -ENODEV;
+    }
+
+  request_region (idev->io.iobase, idev->io.io_ext, driver_name);
+
+  irda_init_max_qos_capabilies (&idev->qos);
+
+  idev->qos.baud_rate.bits = IR_2400 | /*IR_4800 | */ IR_9600 | IR_19200 |
+    IR_115200;
+#ifdef ENABLE_FAST
+ idev->qos.baud_rate.bits|= IR_576000 | IR_1152000 | (IR_4000000 << 8);
+#endif
+
+  idev->qos.min_turn_time.bits = 0xff;  /*FIXME: what does this do? */
+
+  irda_qos_bits_to_value (&idev->qos);
+
+  idev->flags = IFF_SIR | IFF_DMA | IFF_PIO;
+
+#ifdef ENABLE_FAST
+  idev->flags |= IFF_FIR;
+#endif
+
+  /* These aren't much use as we need to have a whole panoply of
+   * buffers running */
+
+  idev->rx_buff.flags = 0;
+  idev->tx_buff.flags = 0;
+  idev->rx_buff.truesize = 0;
+  idev->rx_buff.truesize = 0;
+
+  idev->change_speed = toshoboe_change_speed;
+  idev->wait_until_sent = toshoboe_wait_until_sent;
+  idev->is_receiving = toshoboe_is_receiving;
+
+  idev->netdev.init = toshoboe_net_init;
+  idev->netdev.hard_start_xmit = toshoboe_hard_xmit;
+  idev->netdev.open = toshoboe_net_open;
+  idev->netdev.stop = toshoboe_net_close;
+
+
+  /* Now setup the endless buffers we need */
+
+  self->txs = 0;
+  self->rxs = 0;
+
+  self->taskfilebuf = kmalloc (OBOE_TASK_BUF_LEN, GFP_KERNEL | GFP_DMA);
+  if (!self->taskfilebuf) {
+	printk(KERN_ERR "toshoboe: kmalloc for DMA failed()\n");
+	kfree(self);
+	return -ENOMEM;
+  }
+
+
+  memset (self->taskfilebuf, 0, OBOE_TASK_BUF_LEN);
+
+  /*We need to align the taskfile on a taskfile size boundary */
+  {
+    __u32 addr;
+
+    addr = (__u32) self->taskfilebuf;
+    addr &= ~(sizeof (struct OboeTaskFile) - 1);
+    addr += sizeof (struct OboeTaskFile);
+
+    self->taskfile = (struct OboeTaskFile *) addr;
+  }
+
+  for (i = 0; i < TX_SLOTS; ++i)
+    {
+      self->xmit_bufs[i] = kmalloc (TX_BUF_SZ, GFP_KERNEL | GFP_DMA);
+      if (self->xmit_bufs[i]) ok++;
+    }
+
+  for (i = 0; i < RX_SLOTS; ++i)
+    {
+      self->recv_bufs[i] = kmalloc (TX_BUF_SZ, GFP_KERNEL | GFP_DMA);
+      if (self->recv_bufs[i]) ok++;
+    }
+
+  if (ok!=RX_SLOTS+TX_SLOTS) {
+	printk(KERN_ERR  "toshoboe: kmalloc for buffers failed()\n");
+
+
+  for (i = 0; i < TX_SLOTS; ++i) if (self->xmit_bufs[i]) kfree(self->xmit_bufs[i]);
+  for (i = 0; i < RX_SLOTS; ++i) if (self->recv_bufs[i]) kfree(self->recv_bufs[i]);
+
+	kfree(self);
+	return -ENOMEM;
+
+  }
+
+
+  irda_device_open (idev, driver_name, self);
+
+  printk (KERN_WARNING "ToshOboe: Using ");
+#ifdef ONETASK
+  printk ("single");
+#else
+  printk ("multiple");
+#endif
+  printk (" tasks, version %s\n", rcsid);
+
+  return (0);
+}
+
+__initfunc (int toshoboe_init (void))
+{
+  struct pci_dev *pci_dev = NULL;
+  int found = 0;
+
+  do
+    {
+      pci_dev = pci_find_device (PCI_VENDOR_ID_TOSHIBA,
+                                 PCI_DEVICE_ID_FIR701, pci_dev);
+      if (pci_dev)
+        {
+          printk (KERN_WARNING "ToshOboe: Found 701 chip at 0x%0lx irq %d\n",
+                  pci_dev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK,
+                  pci_dev->irq);
+
+          if (!toshoboe_open (pci_dev))
+            found++;
+        }
+
+    }
+  while (pci_dev);
+
+  if (found)
+    return 0;
+
+  return -ENODEV;
+}
+
+#ifdef MODULE
+
+static void 
+toshoboe_cleanup (void)
+{
+  int i;
+
+  DEBUG (4, __FUNCTION__ "()\n");
+
+  for (i = 0; i < 4; i++)
+    {
+      if (dev_self[i])
+        toshoboe_close (&(dev_self[i]->idev));
+    }
+}
+
+
+
+int 
+init_module (void)
+{
+  return toshoboe_init ();
+}
+
+
+void 
+cleanup_module (void)
+{
+  toshoboe_cleanup ();
+}
+
+
+#endif
diff -u --recursive --new-file v2.3.3/linux/drivers/net/irda/uircc.c linux/drivers/net/irda/uircc.c
--- v2.3.3/linux/drivers/net/irda/uircc.c	Sat Apr 24 17:49:37 1999
+++ linux/drivers/net/irda/uircc.c	Sun May 30 10:27:04 1999
@@ -7,10 +7,10 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Sat Dec 26 10:59:03 1998
- * Modified at:   Tue Apr 20 11:15:52 1999
+ * Modified at:   Mon May 10 22:11:09 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1998 Dag Brattli, All Rights Reserved.
+ *     Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
  *      
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
@@ -216,7 +216,7 @@
 	idev->netdev.open            = uircc_net_open;
 	idev->netdev.stop            = uircc_net_close;
 
-	irport_open(iobase2);
+	irport_start(iobase2);
 
 	/* Open the IrDA device */
 	irda_device_open(idev, driver_name, self);
@@ -233,6 +233,7 @@
 #ifdef MODULE
 static int uircc_close(struct irda_device *idev)
 {
+	struct uircc_cb *self;
 	int iobase;
 	int status;
 
@@ -242,6 +243,7 @@
 	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return -1;);
 
         iobase = idev->io.iobase;
+	self = (struct uircc_cb *) idev->priv;
 
 	/* Some magic to disable FIR and enable SIR */
 	uircc_toshiba_cmd(&status, 0xffff, 0x001b, 0x0000);
@@ -249,7 +251,7 @@
 	/* Disable modem */
 	outb(0x00, iobase+UIRCC_CR10);
 
-	irport_close(idev->io.iobase2);
+	irport_stop(idev->io.iobase2);
 
 	/* Release the PORT that this driver is using */
 	DEBUG(4, __FUNCTION__ "(), Releasing Region %03x\n", idev->io.iobase);
@@ -262,6 +264,8 @@
 	}
 	irda_device_close(idev);
 
+	kfree(self);
+
 	return 0;
 }
 #endif /* MODULE */
@@ -346,8 +350,8 @@
 	case 37600:
 	case 57600:
 	case 115200:
- 		irport_open(idev->io.iobase2);
-		irport_change_speed( idev->io.iobase2, speed);
+ 		irport_start(idev->io.iobase2);
+		irport_change_speed(idev, speed);
 
 		/* Some magic to disable FIR and enable SIR */
 		uircc_toshiba_cmd(&status, 0xffff, 0x001b, 0x0000);
@@ -363,7 +367,7 @@
 		DEBUG(0, __FUNCTION__ "(), handling baud of 1152000\n");
 		break;
 	case 4000000:
-		irport_close(idev->io.iobase2);
+		irport_stop(idev->io.iobase2);
 
 		/* Some magic to disable SIR and enable FIR */
 		uircc_toshiba_cmd(&status, 0xffff, 0x001b, 0x0001);
diff -u --recursive --new-file v2.3.3/linux/drivers/net/irda/w83977af_ir.c linux/drivers/net/irda/w83977af_ir.c
--- v2.3.3/linux/drivers/net/irda/w83977af_ir.c	Sat Apr 24 17:49:37 1999
+++ linux/drivers/net/irda/w83977af_ir.c	Sun May 30 10:27:04 1999
@@ -1,16 +1,16 @@
 /*********************************************************************
  *                
  * Filename:      w83977af_ir.c
- * Version:       0.8
- * Description:   FIR/MIR driver for the Winbond W83977AF Super I/O chip
+ * Version:       1.0
+ * Description:   FIR driver for the Winbond W83977AF Super I/O chip
  * Status:        Experimental.
  * Author:        Paul VanderSpek
  * Created at:    Wed Nov  4 11:46:16 1998
- * Modified at:   Tue Apr 20 11:15:00 1999
+ * Modified at:   Thu May 13 08:03:27 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
+ *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>
  *     Copyright (c) 1998 Corel Computer Corp.
- *     Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>
  *      
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
@@ -40,7 +40,7 @@
  ********************************************************************/
 
 #include <linux/module.h>
-
+ 
 #include <linux/kernel.h>
 #include <linux/types.h>
 #include <linux/skbuff.h>
@@ -61,44 +61,45 @@
 #include <net/irda/w83977af.h>
 #include <net/irda/w83977af_ir.h>
 
-#define NETWINDER
+#define CONFIG_NETWINDER                 /* Adjust to NetWinder differences */
+#undef  CONFIG_NETWINDER_TX_DMA_PROBLEMS /* Not needed */
+#define CONFIG_NETWINDER_RX_DMA_PROBLEMS /* Must have this one! */
+#undef  CONFIG_USE_INTERNAL_TIMER        /* Just cannot make that timer work */
+#define CONFIG_USE_W977_PNP              /* Currently needed */
+#define PIO_MAX_SPEED       115200 
 
 static char *driver_name = "w83977af_ir";
+static int  qos_mtt_bits = 0x07;           /* 1 ms or more */
 
 #define CHIP_IO_EXTENT 8
 
 static unsigned int io[] = { 0x180, ~0, ~0, ~0 };
 static unsigned int irq[] = { 6, 0, 0, 0 };
-static unsigned int dma[] = { 0, 0, 0, 0 };
-
-static struct irda_device *dev_self[] = { NULL, NULL, NULL, NULL};
+static unsigned int dma[] = 
+{ 1, 0, 0, 0 };
 
-/* For storing entries in the status FIFO */
-struct st_fifo_entry {
-	int status;
-	int len;
-};
+static struct w83977af_ir *dev_self[] = { NULL, NULL, NULL, NULL};
 
 static struct st_fifo_entry prev;
 
 /* Some prototypes */
-static int  w83977af_open( int i, unsigned int iobase, unsigned int irq, 
-			   unsigned int dma);
-static int  w83977af_close( struct irda_device *idev);
-static int  w83977af_probe( int iobase, int irq, int dma);
+static int  w83977af_open(int i, unsigned int iobase, unsigned int irq, 
+                          unsigned int dma);
+static int  w83977af_close(struct irda_device *idev);
+static int  w83977af_probe(int iobase, int irq, int dma);
 static int  w83977af_dma_receive(struct irda_device *idev); 
 static int  w83977af_dma_receive_complete(struct irda_device *idev);
-static int  w83977af_hard_xmit( struct sk_buff *skb, struct device *dev);
-static int  w83977af_pio_write( int iobase, __u8 *buf, int len, int fifo_size);
-static void w83977af_dma_write( struct irda_device *idev, int iobase);
-static void w83977af_change_speed( struct irda_device *idev, int baud);
+static int  w83977af_hard_xmit(struct sk_buff *skb, struct device *dev);
+static int  w83977af_pio_write(int iobase, __u8 *buf, int len, int fifo_size);
+static void w83977af_dma_write(struct irda_device *idev, int iobase);
+static void w83977af_change_speed(struct irda_device *idev, int baud);
 static void w83977af_interrupt(int irq, void *dev_id, struct pt_regs *regs);
-static void w83977af_wait_until_sent( struct irda_device *idev);
-static int w83977af_is_receiving( struct irda_device *idev);
+static void w83977af_wait_until_sent(struct irda_device *idev);
+static int  w83977af_is_receiving(struct irda_device *idev);
 
-static int w83977af_net_init( struct device *dev);
-static int w83977af_net_open( struct device *dev);
-static int w83977af_net_close( struct device *dev);
+static int  w83977af_net_init(struct device *dev);
+static int  w83977af_net_open(struct device *dev);
+static int  w83977af_net_close(struct device *dev);
 
 /*
  * Function w83977af_init ()
@@ -108,13 +109,13 @@
  */
 __initfunc(int w83977af_init(void))
 {
-	int i;
+        int i;
 
-	DEBUG( 0, __FUNCTION__ "()\n");
+	DEBUG(0, __FUNCTION__ "()\n");
 
 	prev.status = 0;
 
-	for ( i=0; (io[i] < 2000) && (i < 4); i++) {
+	for (i=0; (io[i] < 2000) && (i < 4); i++) { 
 		int ioaddr = io[i];
 		if (check_region(ioaddr, CHIP_IO_EXTENT) < 0)
 			continue;
@@ -135,11 +136,11 @@
 {
 	int i;
 
-        DEBUG( 4, __FUNCTION__ "()\n");
+        DEBUG(4, __FUNCTION__ "()\n");
 
-	for ( i=0; i < 4; i++) {
-		if ( dev_self[i])
-			w83977af_close( dev_self[i]);
+	for (i=0; i < 4; i++) {
+		if (dev_self[i])
+			w83977af_close(&(dev_self[i]->idev));
 	}
 }
 #endif /* MODULE */
@@ -154,26 +155,29 @@
 		   unsigned int dma)
 {
 	struct irda_device *idev;
+        struct w83977af_ir *self;
 	int ret;
 
 	DEBUG( 0, __FUNCTION__ "()\n");
 
-	if ( w83977af_probe( iobase, irq, dma) == -1)
+	if (w83977af_probe(iobase, irq, dma) == -1)
 		return -1;
 
 	/*
 	 *  Allocate new instance of the driver
 	 */
-	idev = kmalloc( sizeof(struct irda_device), GFP_KERNEL);
-	if ( idev == NULL) {
+	self = kmalloc(sizeof(struct w83977af_ir), GFP_KERNEL);
+	if (self == NULL) {
 		printk( KERN_ERR "IrDA: Can't allocate memory for "
 			"IrDA control block!\n");
 		return -ENOMEM;
 	}
-	memset( idev, 0, sizeof(struct irda_device));
+	memset(self, 0, sizeof(struct w83977af_ir));
    
 	/* Need to store self somewhere */
-	dev_self[i] = idev;
+	dev_self[i] = self;
+
+	idev = &self->idev;
 
 	/* Initialize IO */
 	idev->io.iobase    = iobase;
@@ -183,17 +187,17 @@
         idev->io.fifo_size = 32;
 
 	/* Lock the port that we need */
-	ret = check_region( idev->io.iobase, idev->io.io_ext);
-	if ( ret < 0) { 
+	ret = check_region(idev->io.iobase, idev->io.io_ext);
+	if (ret < 0) { 
 		DEBUG( 0, __FUNCTION__ "(), can't get iobase of 0x%03x\n",
 		       idev->io.iobase);
 		/* w83977af_cleanup( self->idev);  */
 		return -ENODEV;
 	}
-	request_region( idev->io.iobase, idev->io.io_ext, idev->name);
+	request_region(idev->io.iobase, idev->io.io_ext, idev->name);
 
 	/* Initialize QoS for this device */
-	irda_init_max_qos_capabilies( &idev->qos);
+	irda_init_max_qos_capabilies(&idev->qos);
 	
 	/* The only value we must override it the baudrate */
 
@@ -202,8 +206,8 @@
 		IR_115200|IR_576000|IR_1152000|(IR_4000000 << 8);
 
 	/* The HP HDLS-1100 needs 1 ms according to the specs */
-	idev->qos.min_turn_time.bits = 0x03; /* 1ms and more */
-	irda_qos_bits_to_value( &idev->qos);
+	idev->qos.min_turn_time.bits = qos_mtt_bits;
+	irda_qos_bits_to_value(&idev->qos);
 	
 	idev->flags = IFF_FIR|IFF_MIR|IFF_SIR|IFF_DMA|IFF_PIO;
 
@@ -221,13 +225,13 @@
         idev->is_receiving    = w83977af_is_receiving;
 
 	/* Override the network functions we need to use */
-	idev->netdev.init = w83977af_net_init;
+	idev->netdev.init            = w83977af_net_init;
 	idev->netdev.hard_start_xmit = w83977af_hard_xmit;
-	idev->netdev.open = w83977af_net_open;
-	idev->netdev.stop = w83977af_net_close;
+	idev->netdev.open            = w83977af_net_open;
+	idev->netdev.stop            = w83977af_net_close;
 
 	/* Open the IrDA device */
-	irda_device_open( idev, driver_name, NULL);
+	irda_device_open(idev, driver_name, self);
 	
 	return 0;
 }
@@ -240,15 +244,18 @@
  */
 static int w83977af_close( struct irda_device *idev)
 {
+	struct w83977af_ir *self;
 	int iobase;
 
-	DEBUG( 0, __FUNCTION__ "()\n");
+	DEBUG(0, __FUNCTION__ "()\n");
 
-	ASSERT( idev != NULL, return -1;);
-	ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return -1;);
+	ASSERT(idev != NULL, return -1;);
+	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return -1;);
 
         iobase = idev->io.iobase;
+	self = (struct w83977af_ir *) idev->priv;
 
+#ifdef CONFIG_USE_W977_PNP
 	/* enter PnP configuration mode */
 	w977_efm_enter();
 
@@ -258,13 +265,15 @@
 	w977_write_reg(0x30, 0x00);
 
 	w977_efm_exit();
-
+#endif /* CONFIG_USE_W977_PNP */
 	/* Release the PORT that this driver is using */
 	DEBUG(0 , __FUNCTION__ "(), Releasing Region %03x\n", 
 	      idev->io.iobase);
-	release_region( idev->io.iobase, idev->io.io_ext);
+	release_region(idev->io.iobase, idev->io.io_ext);
+
+	irda_device_close(idev);
 
-	irda_device_close( idev);
+	kfree(self);
 
 	return 0;
 }
@@ -280,7 +289,7 @@
 	int version;
 	
 	DEBUG( 0, __FUNCTION__ "()\n");
-
+#ifdef CONFIG_USE_W977_PNP
 	/* Enter PnP configuration mode */
 	w977_efm_enter();
 
@@ -289,14 +298,14 @@
 	/* Configure PnP port, IRQ, and DMA channel */
 	w977_write_reg(0x60, (iobase >> 8) & 0xff);
 	w977_write_reg(0x61, (iobase) & 0xff);
-	/* w977_write_reg(0x70, 0x06); */
+
 	w977_write_reg(0x70, irq);
-#ifdef NETWINDER
-	w977_write_reg(0x74, dma+1); /* Netwinder uses one higher than Linux */
+#ifdef CONFIG_NETWINDER
+	w977_write_reg(0x74, dma+1); /* Netwinder uses 1 higher than Linux */
 #else
 	w977_write_reg(0x74, dma);   
 #endif
-	w977_write_reg(0x75, dma);   /* Disable Tx DMA */
+	w977_write_reg(0x75, 0x04);  /* Disable Tx DMA */
 	
 	/* Set append hardware CRC, enable IR bank selection */	
 	w977_write_reg(0xf0, APEDCRC|ENBNKSEL);
@@ -305,26 +314,26 @@
 	w977_write_reg(0x30, 0x01);
 
 	w977_efm_exit();
-
+#endif
  	/* Disable Advanced mode */
- 	switch_bank( iobase, SET2);
+ 	switch_bank(iobase, SET2);
  	outb(iobase+2, 0x00);  
 
 	/* Turn on UART (global) interrupts */
-	switch_bank( iobase, SET0);
-	outb( HCR_EN_IRQ, iobase+HCR);
+	switch_bank(iobase, SET0);
+	outb(HCR_EN_IRQ, iobase+HCR);
 	
 	/* Switch to advanced mode */
-	switch_bank( iobase, SET2);
-	outb( inb( iobase+ADCR1) | ADCR1_ADV_SL, iobase+ADCR1);
+	switch_bank(iobase, SET2);
+	outb(inb(iobase+ADCR1) | ADCR1_ADV_SL, iobase+ADCR1);
 
 	/* Set default IR-mode */
-	switch_bank( iobase, SET0);
-	outb( HCR_SIR, iobase+HCR);
+	switch_bank(iobase, SET0);
+	outb(HCR_SIR, iobase+HCR);
 
 	/* Read the Advanced IR ID */
 	switch_bank(iobase, SET3);
-	version =  inb( iobase+AUID);
+	version = inb(iobase+AUID);
 	
 	/* Should be 0x1? */
 	if (0x10 != (version & 0xf0)) {
@@ -333,18 +342,17 @@
 	}
 	
 	/* Set FIFO size to 32 */
-	switch_bank( iobase, SET2);
-	outb( ADCR2_RXFS32|ADCR2_TXFS32, iobase+ADCR2);	
+	switch_bank(iobase, SET2);
+	outb(ADCR2_RXFS32|ADCR2_TXFS32, iobase+ADCR2);	
 	
 	/* Set FIFO threshold to TX17, RX16 */
 	switch_bank(iobase, SET0);	
 	outb(UFR_RXTL|UFR_TXTL|UFR_TXF_RST|UFR_RXF_RST|UFR_EN_FIFO,iobase+UFR);
-/* 	outb( 0xa7, iobase+UFR); */
 
 	/* Receiver frame length */
-	switch_bank( iobase, SET4);
-	outb( 2048 & 0xff, iobase+6);
-	outb(( 2048 >> 8) & 0x1f, iobase+7);
+	switch_bank(iobase, SET4);
+	outb(2048 & 0xff, iobase+6);
+	outb((2048 >> 8) & 0x1f, iobase+7);
 
 	/* 
 	 * Init HP HSDL-1100 transceiver. 
@@ -358,8 +366,8 @@
 	 *   FIRRX pin 39 connected to receiver      (IRSL0) 
 	 *   CIRRX pin 40 connected to pin 37
 	 */
-	switch_bank( iobase, SET7);
-	outb( 0x40, iobase+7);
+	switch_bank(iobase, SET7);
+	outb(0x40, iobase+7);
 		
 	DEBUG(0, "W83977AF (IR) driver loaded. Version: 0x%02x\n", version);
 	
@@ -372,16 +380,14 @@
  *    Change the speed of the device
  *
  */
-void w83977af_change_speed( struct irda_device *idev, int speed)
+void w83977af_change_speed(struct irda_device *idev, int speed)
 {
 	int ir_mode = HCR_SIR;
 	int iobase; 
 	__u8 set;
 
-	DEBUG( 0, __FUNCTION__ "()\n");
-
-	ASSERT( idev != NULL, return;);
-	ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return;);
+	ASSERT(idev != NULL, return;);
+	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return;);
 
 	iobase = idev->io.iobase;
 
@@ -389,22 +395,22 @@
 	idev->io.baudrate = speed;
 
 	/* Save current bank */
-	set = inb( iobase+SSR);
+	set = inb(iobase+SSR);
 
 	/* Disable interrupts */
-	switch_bank( iobase, SET0);
-	outb( 0, iobase+ICR);
+	switch_bank(iobase, SET0);
+	outb(0, iobase+ICR);
 
 	/* Select Set 2 */
-	switch_bank( iobase, SET2);
+	switch_bank(iobase, SET2);
+	outb(0x00, iobase+ABHL);
 
-	outb( 0x00, iobase+ABHL);
-	switch ( speed) {
-	case 9600:   outb( 0x0c, iobase+ABLL); break;
-	case 19200:  outb( 0x06, iobase+ABLL); break;
-	case 37600:  outb( 0x03, iobase+ABLL); break;
-	case 57600:  outb( 0x02, iobase+ABLL); break;
-	case 115200: outb( 0x01, iobase+ABLL); break;
+	switch (speed) {
+	case 9600:   outb(0x0c, iobase+ABLL); break;
+	case 19200:  outb(0x06, iobase+ABLL); break;
+	case 37600:  outb(0x03, iobase+ABLL); break;
+	case 57600:  outb(0x02, iobase+ABLL); break;
+	case 115200: outb(0x01, iobase+ABLL); break;
 	case 576000:
 		ir_mode = HCR_MIR_576;
 		DEBUG(0, __FUNCTION__ "(), handling baud of 576000\n");
@@ -419,34 +425,37 @@
 		break;
 	default:
 		ir_mode = HCR_FIR;
-		DEBUG( 0, __FUNCTION__ "(), unknown baud rate of %d\n", speed);
+		DEBUG(0, __FUNCTION__ "(), unknown baud rate of %d\n", speed);
 		break;
 	}
 
 	/* Set speed mode */
 	switch_bank(iobase, SET0);
-	outb( ir_mode, iobase+HCR);
+	outb(ir_mode, iobase+HCR);
 
 	/* set FIFO size to 32 */
-	switch_bank( iobase, SET2);
-	outb( ADCR2_RXFS32|ADCR2_TXFS32, iobase+ADCR2);	
+	switch_bank(iobase, SET2);
+	outb(ADCR2_RXFS32|ADCR2_TXFS32, iobase+ADCR2);	
 	
 	/* set FIFO threshold to TX17, RX16 */
 	switch_bank(iobase, SET0);
-	outb(UFR_RXTL|UFR_TXTL|UFR_TXF_RST|UFR_RXF_RST|UFR_EN_FIFO, iobase+UFR);
-	
+
+	outb(0x00, iobase+UFR);        /* Reset */
+	outb(UFR_EN_FIFO, iobase+UFR); /* First we must enable FIFO */
+	outb(0xa7, iobase+UFR);
+
 	idev->netdev.tbusy = 0;
 	
 	/* Enable some interrupts so we can receive frames */
 	switch_bank(iobase, SET0);
-	if ( speed > 115200) {
-		outb( ICR_EFSFI, iobase+ICR);
-		w83977af_dma_receive( idev);
+	if (speed > PIO_MAX_SPEED) {
+		outb(ICR_EFSFI, iobase+ICR);
+		w83977af_dma_receive(idev);
 	} else
-		outb( ICR_ERBRI, iobase+ICR);
+		outb(ICR_ERBRI, iobase+ICR);
     	
 	/* Restore SSR */
-	outb( set, iobase+SSR);
+	outb(set, iobase+SSR);
 }
 
 /*
@@ -455,7 +464,7 @@
  *    Sets up a DMA transfer to send the current frame.
  *
  */
-int w83977af_hard_xmit( struct sk_buff *skb, struct device *dev)
+int w83977af_hard_xmit(struct sk_buff *skb, struct device *dev)
 {
 	struct irda_device *idev;
 	int iobase;
@@ -474,20 +483,21 @@
 	/* Lock transmit buffer */
 	if (irda_lock((void *) &dev->tbusy) == FALSE)
 		return -EBUSY;
-	
+
 	/* Save current set */
 	set = inb(iobase+SSR);
 	
 	/* Decide if we should use PIO or DMA transfer */
-	if (idev->io.baudrate > 115200) {
+	if (idev->io.baudrate > PIO_MAX_SPEED) {
+		idev->tx_buff.data = idev->tx_buff.head;
 		memcpy(idev->tx_buff.data, skb->data, skb->len);
 		idev->tx_buff.len = skb->len;
-		idev->tx_buff.data = idev->tx_buff.head;
 		
 		mtt = irda_get_mtt(skb);
+#ifdef CONFIG_USE_INTERNAL_TIMER
 	        if (mtt > 50) {
 			/* Adjust for timer resolution */
-			mtt = mtt / 1000 + 1;
+			mtt /= 1000+1;
 
 			/* Setup timer */
 			switch_bank(iobase, SET4);
@@ -502,6 +512,8 @@
 			switch_bank(iobase, SET0);
 			outb(ICR_ETMRI, iobase+ICR);
 		} else {
+#endif
+			DEBUG(4,__FUNCTION__ "(%ld), mtt=%d\n", jiffies, mtt);
 			if (mtt)
 				udelay(mtt);
 
@@ -509,7 +521,9 @@
 			switch_bank(iobase, SET0);
 	 		outb(ICR_EDMAI, iobase+ICR);
 	     		w83977af_dma_write(idev, iobase);
+#ifdef CONFIG_USE_INTERNAL_TIMER
 		}
+#endif
 	} else {
 		idev->tx_buff.data = idev->tx_buff.head;
 		idev->tx_buff.len = async_wrap_skb(skb, idev->tx_buff.data, 
@@ -527,41 +541,57 @@
 	return 0;
 }
 
-
 /*
  * Function w83977af_dma_write (idev, iobase)
  *
- *    
+ *    Send frame using DMA
  *
  */
-static void w83977af_dma_write( struct irda_device *idev, int iobase)
+static void w83977af_dma_write(struct irda_device *idev, int iobase)
 {
 	__u8 set;
-
-        DEBUG( 4, __FUNCTION__ "()\n");
+#ifdef CONFIG_NETWINDER_TX_DMA_PROBLEMS
+	unsigned long flags;
+	__u8 hcr;
+#endif
+        DEBUG(4, __FUNCTION__ "(), len=%d\n", idev->tx_buff.len);
 
 	/* Save current set */
-	set = inb( iobase+SSR);
+	set = inb(iobase+SSR);
 
 	/* Disable DMA */
 	switch_bank(iobase, SET0);
-	outb( inb( iobase+HCR) & ~HCR_EN_DMA, iobase+HCR);
-	
-	setup_dma(idev->io.dma, idev->tx_buff.data, idev->tx_buff.len, 
-		  DMA_MODE_WRITE);
-	
-	/* idev->media_busy = TRUE; */
-	idev->io.direction = IO_XMIT;
-	
+	outb(inb(iobase+HCR) & ~HCR_EN_DMA, iobase+HCR);
+
 	/* Choose transmit DMA channel  */ 
 	switch_bank(iobase, SET2);
-	outb(inb(iobase+ADCR1) | ADCR1_D_CHSW|ADCR1_DMA_F|ADCR1_ADV_SL, 
-	     iobase+ADCR1);
+	outb(ADCR1_D_CHSW|/*ADCR1_DMA_F|*/ADCR1_ADV_SL, iobase+ADCR1);
+#ifdef CONFIG_NETWINDER_TX_DMA_PROBLEMS
+	save_flags(flags);
+	cli();
+
+	disable_dma(idev->io.dma);
+	clear_dma_ff(idev->io.dma);
+	set_dma_mode(idev->io.dma, DMA_MODE_READ);
+	set_dma_addr(idev->io.dma, virt_to_bus(idev->tx_buff.data));
+	set_dma_count(idev->io.dma, idev->tx_buff.len);
+#else
+	setup_dma(idev->io.dma, idev->tx_buff.data, idev->tx_buff.len, 
+		  DMA_MODE_WRITE);	
+#endif
+	idev->io.direction = IO_XMIT;
 	
 	/* Enable DMA */
  	switch_bank(iobase, SET0);
-	outb(inb(iobase+HCR) | HCR_EN_DMA, iobase+HCR);
-	
+#ifdef CONFIG_NETWINDER_TX_DMA_PROBLEMS
+	hcr = inb(iobase+HCR);
+	outb(hcr | HCR_EN_DMA, iobase+HCR);
+	enable_dma(idev->io.dma);
+	restore_flags(flags);
+#else	
+	outb(inb(iobase+HCR) | HCR_EN_DMA | HCR_TX_WT, iobase+HCR);
+#endif
+
 	/* Restore set register */
 	outb(set, iobase+SSR);
 }
@@ -577,17 +607,17 @@
 	int actual = 0;
 	__u8 set;
 	
-	DEBUG( 4, __FUNCTION__ "()\n");
+	DEBUG(4, __FUNCTION__ "()\n");
 
 	/* Save current bank */
-	set = inb( iobase+SSR);
+	set = inb(iobase+SSR);
 
-	switch_bank( iobase, SET0);
+	switch_bank(iobase, SET0);
 	if (!(inb_p(iobase+USR) & USR_TSRE)) {
-		DEBUG( 4, __FUNCTION__ "(), warning, FIFO not empty yet!\n");
+		DEBUG(4, __FUNCTION__ "(), warning, FIFO not empty yet!\n");
 
 		fifo_size -= 17;
-		DEBUG( 4, __FUNCTION__ "%d bytes left in tx fifo\n", fifo_size);
+		DEBUG(4, __FUNCTION__ "%d bytes left in tx fifo\n", fifo_size);
 	}
 
 	/* Fill FIFO with current frame */
@@ -597,7 +627,7 @@
 	}
         
 	DEBUG(4, __FUNCTION__ "(), fifo_size %d ; %d sent of %d\n", 
-	      fifo_size, actual, len);
+	       fifo_size, actual, len);
 
 	/* Restore bank */
 	outb(set, iobase+SSR);
@@ -617,7 +647,7 @@
 	int iobase;
 	__u8 set;
 
-	DEBUG(4, __FUNCTION__ "()\n");
+	DEBUG(4, __FUNCTION__ "(%ld)\n", jiffies);
 
 	ASSERT(idev != NULL, return;);
 	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return;);
@@ -663,9 +693,10 @@
  */
 int w83977af_dma_receive(struct irda_device *idev) 
 {
+	struct w83977af_ir *self;
 	int iobase;
 	__u8 set;
-#ifdef NETWINDER
+#ifdef CONFIG_NETWINDER_RX_DMA_PROBLEMS
 	unsigned long flags;
 	__u8 hcr;
 #endif
@@ -673,62 +704,60 @@
 	ASSERT(idev != NULL, return -1;);
 	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return -1;);
 
-	DEBUG(0, __FUNCTION__ "\n");
+	DEBUG(4, __FUNCTION__ "\n");
 
+	self = idev->priv;
 	iobase= idev->io.iobase;
 
 	/* Save current set */
-	set = inb( iobase+SSR);
+	set = inb(iobase+SSR);
 
 	/* Disable DMA */
-	switch_bank( iobase, SET0);
-	outb( inb( iobase+HCR) & ~HCR_EN_DMA, iobase+HCR);
+	switch_bank(iobase, SET0);
+	outb(inb(iobase+HCR) & ~HCR_EN_DMA, iobase+HCR);
 
-#ifdef NETWINDER
+	/* Choose DMA Rx, DMA Fairness, and Advanced mode */
+	switch_bank(iobase, SET2);
+	outb((inb(iobase+ADCR1) & ~ADCR1_D_CHSW)/*|ADCR1_DMA_F*/|ADCR1_ADV_SL,
+	     iobase+ADCR1);
+
+	idev->io.direction = IO_RECV;
+	idev->rx_buff.data = idev->rx_buff.head;
+
+#ifdef CONFIG_NETWINDER_RX_DMA_PROBLEMS
 	save_flags(flags);
 	cli();
 
-	disable_dma( idev->io.dma);
-	clear_dma_ff( idev->io.dma);
-	set_dma_mode( idev->io.dma, DMA_MODE_READ);
-	set_dma_addr( idev->io.dma, virt_to_bus(idev->rx_buff.data));
-	set_dma_count( idev->io.dma, idev->rx_buff.truesize);
+	disable_dma(idev->io.dma);
+	clear_dma_ff(idev->io.dma);
+	set_dma_mode(idev->io.dma, DMA_MODE_READ);
+	set_dma_addr(idev->io.dma, virt_to_bus(idev->rx_buff.data));
+	set_dma_count(idev->io.dma, idev->rx_buff.truesize);
 #else
-	setup_dma(idev->io.dma, idev->rx_buff.data, 
-		  idev->rx_buff.truesize, DMA_MODE_READ);
+	setup_dma(idev->io.dma, idev->rx_buff.data, idev->rx_buff.truesize, 
+		  DMA_MODE_READ);
 #endif
-	/* driver->media_busy = FALSE; */
-	idev->io.direction = IO_RECV;
-	idev->rx_buff.data = idev->rx_buff.head;
-
 	/* 
 	 * Reset Rx FIFO. This will also flush the ST_FIFO, it's very 
 	 * important that we don't reset the Tx FIFO since it might not
 	 * be finished transmitting yet
 	 */
-	outb( UFR_RXTL|UFR_TXTL|UFR_RXF_RST|UFR_EN_FIFO, iobase+UFR);
-	prev.status = 0;
-
-	/* Choose DMA Rx, DMA Fairness, and Advanced mode */
-	switch_bank(iobase, SET2);
-	outb(( inb( iobase+ADCR1) & ~ADCR1_D_CHSW)|ADCR1_DMA_F|ADCR1_ADV_SL,
-	     iobase+ADCR1);
+	switch_bank(iobase, SET0);
+	outb(UFR_RXTL|UFR_TXTL|UFR_RXF_RST|UFR_EN_FIFO, iobase+UFR);
+	self->st_fifo.len = self->st_fifo.tail = self->st_fifo.head = 0;
 	
 	/* Enable DMA */
 	switch_bank(iobase, SET0);
-#ifdef NETWINDER
-	hcr = inb( iobase+HCR);
-	enable_dma( idev->io.dma);
-	outb( hcr | HCR_EN_DMA, iobase+HCR);
+#ifdef CONFIG_NETWINDER_RX_DMA_PROBLEMS
+	hcr = inb(iobase+HCR);
+	outb(hcr | HCR_EN_DMA, iobase+HCR);
+	enable_dma(idev->io.dma);
 	restore_flags(flags);
 #else	
-	outb( inb( iobase+HCR) | HCR_EN_DMA, iobase+HCR);
+	outb(inb(iobase+HCR) | HCR_EN_DMA, iobase+HCR);
 #endif
-	
 	/* Restore set */
-	outb( set, iobase+SSR);
-
-	DEBUG( 4, __FUNCTION__ "(), done!\n");	
+	outb(set, iobase+SSR);
 
 	return 0;
 }
@@ -742,12 +771,17 @@
 int w83977af_dma_receive_complete(struct irda_device *idev)
 {
 	struct sk_buff *skb;
+	struct w83977af_ir *self;
+	struct st_fifo *st_fifo;
 	int len;
 	int iobase;
 	__u8 set;
 	__u8 status;
 
-	DEBUG(0, __FUNCTION__ "\n");
+	DEBUG(4, __FUNCTION__ "\n");
+
+	self = idev->priv;
+	st_fifo = &self->st_fifo;
 
 	iobase = idev->io.iobase;
 
@@ -756,22 +790,28 @@
 	
 	iobase = idev->io.iobase;
 
+	/* Read status FIFO */
 	switch_bank(iobase, SET5);
-	if (prev.status & FS_FO_FSFDR) {
-		status = prev.status;
-		len = prev.len;
+	while ((status = inb(iobase+FS_FO)) & FS_FO_FSFDR) {
+		st_fifo->entries[st_fifo->tail].status = status;
 		
-		prev.status = 0;
-	} else {
-		status = inb(iobase+FS_FO);
-		len = inb(iobase+RFLFL);
-		len |= inb(iobase+RFLFH) << 8;
+		st_fifo->entries[st_fifo->tail].len  = inb(iobase+RFLFL);
+		st_fifo->entries[st_fifo->tail].len |= inb(iobase+RFLFH) << 8;
+		
+		st_fifo->tail++;
+		st_fifo->len++;
 	}
+	
+	while (st_fifo->len) {
+		/* Get first entry */
+		status = st_fifo->entries[st_fifo->head].status;
+		len    = st_fifo->entries[st_fifo->head].len;
+		st_fifo->head++;
+		st_fifo->len--;
 
-	while (status & FS_FO_FSFDR) {
 		/* Check for errors */
 		if (status & FS_FO_ERR_MSK) {
-			if ( status & FS_FO_LST_FR) {
+			if (status & FS_FO_LST_FR) {
 				/* Add number of lost frames to stats */
 				idev->stats.rx_errors += len;	
 			} else {
@@ -800,14 +840,20 @@
 			/* Check if we have transfered all data to memory */
 			switch_bank(iobase, SET0);
 			if (inb(iobase+USR) & USR_RDR) {
+#ifdef CONFIG_USE_INTERNAL_TIMER
 				/* Put this entry back in fifo */
-				prev.status = status;
-				prev.len = len;
-
+				st_fifo->head--;
+				st_fifo->len++;
+				st_fifo->entries[st_fifo->head].status = status;
+				st_fifo->entries[st_fifo->head].len = len;
+				
 				/* Restore set register */
-				outb( set, iobase+SSR);
+				outb(set, iobase+SSR);
 			
 				return FALSE; 	/* I'll be back! */
+#else
+				udelay(80); /* Should be enough!? */
+#endif
 			}
 						
 			skb = dev_alloc_skb(len+1);
@@ -824,28 +870,23 @@
 			skb_reserve(skb, 1); 
 			
 			/* Copy frame without CRC */
-			if ( idev->io.baudrate < 4000000) {
-				skb_put( skb, len-2);
-				memcpy( skb->data, idev->rx_buff.data, len-2);
+			if (idev->io.baudrate < 4000000) {
+				skb_put(skb, len-2);
+				memcpy(skb->data, idev->rx_buff.data, len-2);
 			} else {
-				skb_put( skb, len-4);
-				memcpy( skb->data, idev->rx_buff.data, len-4);
+				skb_put(skb, len-4);
+				memcpy(skb->data, idev->rx_buff.data, len-4);
 			}
 
 			/* Move to next frame */
 			idev->rx_buff.data += len;
+			idev->stats.rx_packets++;
 			
 			skb->dev = &idev->netdev;
 			skb->mac.raw  = skb->data;
 			skb->protocol = htons(ETH_P_IRDA);
-			netif_rx( skb);
-			idev->stats.rx_packets++;
+			netif_rx(skb);
 		}
-		/* Read next entry in ST_FIFO */
-		switch_bank(iobase, SET5);
-		status = inb( iobase+FS_FO);
-		len = inb( iobase+RFLFL);
-		len |= inb( iobase+RFLFH) << 8;
 	}
 	/* Restore set register */
 	outb(set, iobase+SSR);
@@ -875,7 +916,6 @@
 	do {
 		byte = inb(iobase+RBR);
 		async_unwrap_char(idev, byte);
-
 	} while (inb(iobase+USR) & USR_RDR); /* Data available */	
 }
 
@@ -889,9 +929,12 @@
 {
 	int actual;
 	__u8 new_icr = 0;
+	__u8 set;
+	int iobase;
 
 	DEBUG(4, __FUNCTION__ "(), isr=%#x\n", isr);
 	
+	iobase = idev->io.iobase;
 	/* Transmit FIFO low on data */
 	if (isr & ISR_TXTH_I) {
 		/* Write data left in transmit buffer */
@@ -899,16 +942,21 @@
 					    idev->tx_buff.data, 
 					    idev->tx_buff.len, 
 					    idev->io.fifo_size);
+
 		idev->tx_buff.data += actual;
 		idev->tx_buff.len  -= actual;
 		
 		idev->io.direction = IO_XMIT;
 
 		/* Check if finished */
-		if (idev->tx_buff.len > 0)
+		if (idev->tx_buff.len > 0) {
 			new_icr |= ICR_ETXTHI;
-		else { 
-			DEBUG( 4, __FUNCTION__ "(), finished with frame!\n");
+		} else {
+			set = inb(iobase+SSR);
+			switch_bank(iobase, SET0);
+			outb(AUDR_SFEND, iobase+AUDR);
+			outb(set, iobase+SSR); 
+
 			idev->netdev.tbusy = 0; /* Unlock */
 			idev->stats.tx_packets++;
 
@@ -917,7 +965,6 @@
 
 			new_icr |= ICR_ETBREI;
 		}
-		
 	}
 	/* Check if transmission has completed */
 	if (isr & ISR_TXEMP_I) {
@@ -943,22 +990,20 @@
  *    Handle MIR/FIR interrupt
  *
  */
-static __u8 w83977af_fir_interrupt( struct irda_device *idev, int isr)
+static __u8 w83977af_fir_interrupt(struct irda_device *idev, int isr)
 {
 	__u8 new_icr = 0;
 	__u8 set;
 	int iobase;
 
-	DEBUG( 4, __FUNCTION__ "(), isr=%#x\n", isr);
-
 	iobase = idev->io.iobase;
-
 	set = inb(iobase+SSR);
 	
 	/* End of frame detected in FIFO */
 	if (isr & (ISR_FEND_I|ISR_FSF_I)) {
 		if (w83977af_dma_receive_complete(idev)) {
 			
+			/* Wait for next status FIFO interrupt */
 			new_icr |= ICR_EFSFI;
 		} else {
 			/* DMA not finished yet */
@@ -982,7 +1027,7 @@
 
 		/* Clear timer event */
 		/* switch_bank(iobase, SET0); */
-/* 		outb( ASCR_CTE, iobase+ASCR); */
+/* 		outb(ASCR_CTE, iobase+ASCR); */
 
 		/* Check if this is a TX timer interrupt */
 		if (idev->io.direction == IO_XMIT) {
@@ -998,15 +1043,18 @@
 	}	
 	/* Finished with DMA */
 	if (isr & ISR_DMA_I) {
-		w83977af_dma_xmit_complete( idev);
-		
+		w83977af_dma_xmit_complete(idev);
+
 		/* Check if there are more frames to be transmitted */
-		if (irda_device_txqueue_empty( idev)) {
+		/* if (irda_device_txqueue_empty(idev)) { */
 		
-			/* Prepare for receive */
-			w83977af_dma_receive(idev);
-			new_icr = ICR_EFSFI;
-		}
+		/* Prepare for receive 
+		 * 
+		 * ** Netwinder Tx DMA likes that we do this anyway **
+		 */
+		w83977af_dma_receive(idev);
+		new_icr = ICR_EFSFI;
+	       /* } */
 	}
 	
 	/* Restore set */
@@ -1030,7 +1078,7 @@
 
 	if (idev == NULL) {
 		printk(KERN_WARNING "%s: irq %d for unknown device.\n", 
-		       driver_name, irq);
+			driver_name, irq);
 		return;
 	}
 
@@ -1049,7 +1097,7 @@
 	
 	if (isr) {
 		/* Dispatch interrupt handler for the current speed */
-		if ( idev->io.baudrate > 115200)
+		if (idev->io.baudrate > PIO_MAX_SPEED )
 			icr = w83977af_fir_interrupt(idev, isr);
 		else
 			icr = w83977af_sir_interrupt(idev, isr);
@@ -1070,7 +1118,7 @@
 static void w83977af_wait_until_sent(struct irda_device *idev)
 {
 	current->state = TASK_INTERRUPTIBLE;
-	schedule_timeout(6);
+	schedule_timeout(60*HZ/1000);
 }
 
 /*
@@ -1085,16 +1133,16 @@
 	int iobase;
 	__u8 set;
 
-	ASSERT( idev != NULL, return FALSE;);
-	ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return FALSE;);
+	ASSERT(idev != NULL, return FALSE;);
+	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return FALSE;);
 
-	if ( idev->io.baudrate > 115200) {
+	if (idev->io.baudrate > 115200) {
 		iobase = idev->io.iobase;
 
 		/* Check if rx FIFO is not empty */
 		set = inb(iobase+SSR);
-		switch_bank( iobase, SET2);
-		if (( inb( iobase+RXFDTH) & 0x3f) != 0) {
+		switch_bank(iobase, SET2);
+		if ((inb(iobase+RXFDTH) & 0x3f) != 0) {
 			/* We are receiving something */
 			status =  TRUE;
 		}
@@ -1111,12 +1159,12 @@
  *    
  *
  */
-static int w83977af_net_init( struct device *dev)
+static int w83977af_net_init(struct device *dev)
 {
 	DEBUG(0, __FUNCTION__ "()\n");
 
 	/* Set up to be a normal IrDA network device driver */
-	irda_device_setup( dev);
+	irda_device_setup(dev);
 
 	/* Insert overrides below this line! */
 
@@ -1130,7 +1178,7 @@
  *    Start the device
  *
  */
-static int w83977af_net_open( struct device *dev)
+static int w83977af_net_open(struct device *dev)
 {
 	struct irda_device *idev;
 	int iobase;
@@ -1147,7 +1195,7 @@
 	iobase = idev->io.iobase;
 
 	if (request_irq(idev->io.irq, w83977af_interrupt, 0, idev->name, 
-			(void *) idev)) {
+			 (void *) idev)) {
 		return -EAGAIN;
 	}
 	/*
@@ -1170,13 +1218,13 @@
  	/* Enable some interrupts so we can receive frames again */
  	switch_bank(iobase, SET0);
  	if (idev->io.baudrate > 115200) {
- 		outb( ICR_EFSFI, iobase+ICR);
- 		w83977af_dma_receive( idev);
+ 		outb(ICR_EFSFI, iobase+ICR);
+ 		w83977af_dma_receive(idev);
  	} else
- 		outb( ICR_ERBRI, iobase+ICR);
+ 		outb(ICR_ERBRI, iobase+ICR);
 
 	/* Restore bank register */
-	outb( set, iobase+SSR);
+	outb(set, iobase+SSR);
 
 	MOD_INC_USE_COUNT;
 
@@ -1195,34 +1243,34 @@
 	int iobase;
 	__u8 set;
 
-	DEBUG( 0, __FUNCTION__ "()\n");
+	DEBUG(0, __FUNCTION__ "()\n");
 	
 	/* Stop device */
 	dev->tbusy = 1;
 	dev->start = 0;
 
-	ASSERT( dev != NULL, return -1;);
+	ASSERT(dev != NULL, return -1;);
 	idev = (struct irda_device *) dev->priv;
 	
-	ASSERT( idev != NULL, return 0;);
-	ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return 0;);
+	ASSERT(idev != NULL, return 0;);
+	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return 0;);
 	
 	iobase = idev->io.iobase;
 
-	disable_dma( idev->io.dma);
+	disable_dma(idev->io.dma);
 
 	/* Save current set */
-	set = inb( iobase+SSR);
+	set = inb(iobase+SSR);
 	
 	/* Disable interrupts */
-	switch_bank( iobase, SET0);
-	outb( 0, iobase+ICR); 
+	switch_bank(iobase, SET0);
+	outb(0, iobase+ICR); 
 
-	free_irq( idev->io.irq, idev);
-	free_dma( idev->io.dma);
+	free_irq(idev->io.irq, idev);
+	free_dma(idev->io.dma);
 
 	/* Restore bank register */
-	outb( set, iobase+SSR);
+	outb(set, iobase+SSR);
 
 	MOD_DEC_USE_COUNT;
 
@@ -1230,6 +1278,11 @@
 }
 
 #ifdef MODULE
+
+MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
+MODULE_DESCRIPTION("Winbond W83977AF IrDA Device Driver");
+
+MODULE_PARM(qos_mtt_bits, "i");
 
 /*
  * Function init_module (void)
diff -u --recursive --new-file v2.3.3/linux/drivers/net/lapbether.c linux/drivers/net/lapbether.c
--- v2.3.3/linux/drivers/net/lapbether.c	Fri Feb 20 18:28:22 1998
+++ linux/drivers/net/lapbether.c	Tue May 25 13:06:34 1999
@@ -533,10 +533,15 @@
 
 	printk(KERN_INFO "LAPB Ethernet driver version 0.01\n");
 
+	read_lock_bh(&dev_base_lock);
 	for (dev = dev_base; dev != NULL; dev = dev->next) {
-		if (dev_is_ethdev(dev))
+		if (dev_is_ethdev(dev)) {
+			read_unlock_bh(&dev_base_lock);
 			lapbeth_new_device(dev);
+			read_lock_bh(&dev_base_lock);
+		}
 	}
+	read_unlock_bh(&dev_base_lock);
 
 	return 0;
 }
diff -u --recursive --new-file v2.3.3/linux/drivers/net/net_init.c linux/drivers/net/net_init.c
--- v2.3.3/linux/drivers/net/net_init.c	Sat Apr 24 17:51:48 1999
+++ linux/drivers/net/net_init.c	Tue May 25 13:06:34 1999
@@ -91,9 +91,11 @@
 		for (i = 0; i < MAX_ETH_CARDS; ++i)
 			if (ethdev_index[i] == NULL) {
 				sprintf(pname, "eth%d", i);
-				for (cur_dev = dev_base; cur_dev; cur_dev = cur_dev->next)
+				read_lock_bh(&dev_base_lock);
+				for (cur_dev = dev_base; cur_dev; cur_dev = cur_dev->next) {
 					if (strcmp(pname, cur_dev->name) == 0) {
 						dev = cur_dev;
+						read_unlock_bh(&dev_base_lock);
 						dev->init = NULL;
 						sizeof_priv = (sizeof_priv + 3) & ~3;
 						dev->priv = sizeof_priv
@@ -102,6 +104,8 @@
 						if (dev->priv) memset(dev->priv, 0, sizeof_priv);
 						goto found;
 					}
+				}
+				read_unlock_bh(&dev_base_lock);
 			}
 
 		alloc_size &= ~3;		/* Round to dword boundary. */
@@ -209,9 +213,11 @@
 		for (i = 0; i < MAX_HIP_CARDS; ++i)
 			if (hipdev_index[i] == NULL) {
 				sprintf(pname, "hip%d", i);
-				for (cur_dev = dev_base; cur_dev; cur_dev = cur_dev->next)
+				read_lock_bh(&dev_base_lock);
+				for (cur_dev = dev_base; cur_dev; cur_dev = cur_dev->next) {
 					if (strcmp(pname, cur_dev->name) == 0) {
 						dev = cur_dev;
+						read_unlock_bh(&dev_base_lock);
 						dev->init = NULL;
 						sizeof_priv = (sizeof_priv + 3) & ~3;
 						dev->priv = sizeof_priv
@@ -220,6 +226,8 @@
 						if (dev->priv) memset(dev->priv, 0, sizeof_priv);
 						goto hipfound;
 					}
+				}
+				read_unlock_bh(&dev_base_lock);
 			}
 
 		alloc_size &= ~3;		/* Round to dword boundary. */
@@ -536,9 +544,11 @@
 		for (i = 0; i < MAX_TR_CARDS; ++i)
 			if (trdev_index[i] == NULL) {
 				sprintf(pname, "tr%d", i);
-				for (cur_dev = dev_base; cur_dev; cur_dev = cur_dev->next)
+				read_lock_bh(&dev_base_lock);
+				for (cur_dev = dev_base; cur_dev; cur_dev = cur_dev->next) {
 					if (strcmp(pname, cur_dev->name) == 0) {
 						dev = cur_dev;
+						read_unlock_bh(&dev_base_lock);
 						dev->init = NULL;
 						sizeof_priv = (sizeof_priv + 3) & ~3;
 						dev->priv = sizeof_priv
@@ -547,6 +557,8 @@
 						if (dev->priv) memset(dev->priv, 0, sizeof_priv);
 						goto trfound;
 					}
+				}
+				read_unlock_bh(&dev_base_lock);
 			}
 
 		alloc_size &= ~3;		/* Round to dword boundary. */
diff -u --recursive --new-file v2.3.3/linux/drivers/net/smc-ultra.c linux/drivers/net/smc-ultra.c
--- v2.3.3/linux/drivers/net/smc-ultra.c	Wed Feb 24 16:27:54 1999
+++ linux/drivers/net/smc-ultra.c	Wed May 26 09:31:50 1999
@@ -483,9 +483,9 @@
 			/* NB: ultra_close_card() does free_irq + irq2dev */
 			int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET;
 			kfree(dev->priv);
-			dev->priv = NULL;
 			release_region(ioaddr, ULTRA_IO_EXTENT);
 			unregister_netdev(dev);
+			dev->priv = NULL;
 		}
 	}
 }
diff -u --recursive --new-file v2.3.3/linux/drivers/net/strip.c linux/drivers/net/strip.c
--- v2.3.3/linux/drivers/net/strip.c	Sun Nov  8 13:48:06 1998
+++ linux/drivers/net/strip.c	Tue May 25 13:06:34 1999
@@ -2027,7 +2027,9 @@
         !memcmp(strip_info->dev.dev_addr, zero_address.c, sizeof(zero_address)) &&
         memcmp(&strip_info->true_dev_addr, zero_address.c, sizeof(zero_address)))
     {
-        struct device *dev = dev_base;
+        struct device *dev;
+	read_lock_bh(&dev_base_lock);
+	dev = dev_base;
         while (dev)
         {
             if (dev->type == strip_info->dev.type &&
@@ -2035,10 +2037,12 @@
             {
                 printk(KERN_INFO "%s: Transferred packet ownership to %s.\n",
                     strip_info->dev.name, dev->name);
+		read_unlock_bh(&dev_base_lock);
                 return(dev);
             }
             dev = dev->next;
         }
+	read_unlock_bh(&dev_base_lock);
     }
     return(&strip_info->dev);
 }
diff -u --recursive --new-file v2.3.3/linux/drivers/net/sunhme.c linux/drivers/net/sunhme.c
--- v2.3.3/linux/drivers/net/sunhme.c	Mon May 17 09:55:22 1999
+++ linux/drivers/net/sunhme.c	Thu May 27 09:55:21 1999
@@ -1597,9 +1597,9 @@
 				      unsigned long status)
 {
 	int reset = 0;
-
+	
 	/* Only print messages for non-counter related interrupts. */
-	if(status & (GREG_STAT_RFIFOVF | GREG_STAT_STSTERR | GREG_STAT_TFIFO_UND |
+	if(status & (GREG_STAT_STSTERR | GREG_STAT_TFIFO_UND |
 		     GREG_STAT_MAXPKTERR | GREG_STAT_RXERR |
 		     GREG_STAT_RXPERR | GREG_STAT_RXTERR | GREG_STAT_EOPERR |
 		     GREG_STAT_MIFIRQ | GREG_STAT_TXEACK | GREG_STAT_TXLERR |
@@ -1609,9 +1609,9 @@
 		       hp->dev->name, status);
 
 	if(status & GREG_STAT_RFIFOVF) {
-		/* The receive FIFO overflowwed, usually a DMA error. */
-		printk("%s: Happy Meal receive FIFO overflow.\n", hp->dev->name);
-		reset = 1;
+		/* Receive FIFO overflow is harmless and the hardware will take
+		   care of it, just some packets are lost. Who cares. */
+		printk(KERN_DEBUG "%s: Happy Meal receive FIFO overflow.\n", hp->dev->name);
 	}
 
 	if(status & GREG_STAT_STSTERR) {
diff -u --recursive --new-file v2.3.3/linux/drivers/sbus/char/Config.in linux/drivers/sbus/char/Config.in
--- v2.3.3/linux/drivers/sbus/char/Config.in	Sun Oct  4 10:22:44 1998
+++ linux/drivers/sbus/char/Config.in	Mon May 31 22:08:10 1999
@@ -11,4 +11,5 @@
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
   tristate 'Bidirectional parallel port support (EXPERIMENTAL)' CONFIG_SUN_BPP
   tristate 'Videopix Frame Grabber (EXPERIMENTAL)' CONFIG_SUN_VIDEOPIX
+  tristate 'Aurora Multiboard 1600se (EXPERIMENTAL)' CONFIG_SUN_AURORA
 fi
diff -u --recursive --new-file v2.3.3/linux/drivers/sbus/char/Makefile linux/drivers/sbus/char/Makefile
--- v2.3.3/linux/drivers/sbus/char/Makefile	Mon Nov 16 10:37:28 1998
+++ linux/drivers/sbus/char/Makefile	Mon May 31 22:08:10 1999
@@ -87,6 +87,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_SUN_AURORA),y)
+O_OBJS += aurora.o
+else
+  ifeq ($(CONFIG_SUN_AURORA),m)
+  M_OBJS += aurora.o
+  endif
+endif
+
 include $(TOPDIR)/Rules.make
 
 sunkbdmap.o: sunkeymap.c
diff -u --recursive --new-file v2.3.3/linux/drivers/sbus/char/aurora.c linux/drivers/sbus/char/aurora.c
--- v2.3.3/linux/drivers/sbus/char/aurora.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sbus/char/aurora.c	Mon May 31 22:08:10 1999
@@ -0,0 +1,2373 @@
+/*
+ *	linux/drivers/sbus/char/aurora.c -- Aurora multiport driver
+ *
+ *	Copyright (c) 1999 by Oliver Aldulea (oli@bv.ro)
+ *
+ *	This code is based on the RISCom/8 multiport serial driver written
+ *	by Dmitry Gorodchanin (pgmdsg@ibi.com), based on the Linux serial
+ *	driver, written by Linus Torvalds, Theodore T'so and others.
+ *	The Aurora multiport programming info was obtained mainly from the
+ *	Cirrus Logic CD180 documentation (available on the web), and by
+ *	doing heavy tests on the board. Many thanks to Eddie C. Dost for the
+ *	help on the sbus interface.
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *	Revision 1.0
+ *
+ *	This is the first public release.
+ *
+ *	Most of the information you need is in the aurora.h file. Please
+ *	read that file before reading this one.
+ *
+ *	Several parts of the code do not have comments yet.
+ */
+
+#include <linux/module.h>
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#ifdef AURORA_INT_DEBUG
+#include <linux/timer.h>
+#endif
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/config.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/tqueue.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/oplib.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/bitops.h>
+#include <asm/delay.h>
+#include <asm/kdebug.h>
+#include <asm/sbus.h>
+#include <asm/uaccess.h>
+
+#include "aurora.h"
+#include "cd180.h"
+
+unsigned char irqs[4] = {
+	0, 0, 0, 0
+	};
+
+#ifdef AURORA_INT_DEBUG
+int irqhit=0;
+#endif
+
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+#define AURORA_TYPE_NORMAL	1
+
+static struct tty_driver aurora_driver;
+static struct Aurora_board aurora_board[AURORA_NBOARD] = {
+	{0,},
+};
+
+static struct Aurora_port aurora_port[AURORA_TNPORTS] =  {
+	{ 0, },
+};
+
+/* no longer used. static struct Aurora_board * IRQ_to_board[16] = { NULL, } ;*/
+static unsigned char * tmp_buf = NULL;
+static DECLARE_MUTEX(tmp_buf_sem);
+static int    aurora_refcount = 0;
+static struct tty_struct * aurora_table[AURORA_TNPORTS] = { NULL, };
+static struct termios * aurora_termios[AURORA_TNPORTS] = { NULL, };
+static struct termios * aurora_termios_locked[AURORA_TNPORTS] = { NULL, };
+
+DECLARE_TASK_QUEUE(tq_aurora);
+
+/* Yes, the board can support 115.2 bit rates, but only on a few ports. The
+ * total badwidth of one chip (ports 0-7 or 8-15) is equal to OSC_FREQ div
+ * 16. In case of my board, each chip can take 6 channels of 115.2 kbaud.
+ * This information is not well-tested.
+ */
+static unsigned long baud_table[] =  {
+        0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+        9600, 19200, 38400, 57600, 115200, 0,
+        };
+
+static inline int aurora_paranoia_check(struct Aurora_port const * port,
+				    kdev_t device, const char *routine)
+{
+#ifdef AURORA_PARANOIA_CHECK
+	static const char *badmagic =
+		KERN_DEBUG "aurora: Warning: bad aurora port magic number for device %s in %s\n";
+	static const char *badinfo =
+		KERN_DEBUG "aurora: Warning: null aurora port for device %s in %s\n";
+
+	if (!port) {
+		printk(badinfo, kdevname(device), routine);
+		return 1;
+	}
+	if (port->magic != AURORA_MAGIC) {
+		printk(badmagic, kdevname(device), routine);
+		return 1;
+	}
+#endif
+	return 0;
+}
+
+/*
+ * 
+ *  Service functions for aurora driver.
+ * 
+ */
+
+/* Get board number from pointer */
+extern inline int board_No (struct Aurora_board const * bp)
+{
+	return bp - aurora_board;
+}
+
+/* Get port number from pointer */
+extern inline int port_No (struct Aurora_port const * port)
+{
+	return AURORA_PORT(port - aurora_port); 
+}
+
+/* Get pointer to board from pointer to port */
+extern inline struct Aurora_board * port_Board(struct Aurora_port const * port)
+{
+	return &aurora_board[AURORA_BOARD(port - aurora_port)];
+}
+
+/* Wait for Channel Command Register ready */
+extern inline void aurora_wait_CCR(struct aurora_reg128 * r)
+{
+	unsigned long delay;
+
+#ifdef AURORA_DEBUG
+printk("aurora_wait_CCR\n");
+#endif
+	/* FIXME: need something more descriptive than 100000 :) */
+	for (delay = 100000; delay; delay--) 
+		if (!r->r[CD180_CCR])
+			return;
+	printk(KERN_DEBUG "aurora: Timeout waiting for CCR.\n");
+}
+
+/*
+ *  aurora probe functions.
+ */
+
+/* Must be called with enabled interrupts */
+extern inline void aurora_long_delay(unsigned long delay)
+{
+	unsigned long i;
+#ifdef AURORA_DEBUG
+printk("aurora_long_delay: start\n");
+#endif
+	for (i = jiffies + delay; i > jiffies; ) ;
+#ifdef AURORA_DEBUG
+printk("aurora_long_delay: end\n");
+#endif
+}
+
+/* Reset and setup CD180 chip */
+static int aurora_init_CD180(struct Aurora_board * bp, int chip)
+{
+	unsigned long flags;
+	int id;
+	
+#ifdef AURORA_DEBUG
+printk("aurora_init_CD180: start %d:%d\n",board_No(bp),chip);
+#endif
+	save_flags(flags); cli();
+	bp->r[chip]->r[CD180_CAR]=0;
+	bp->r[chip]->r[CD180_GSVR]=0;
+	aurora_wait_CCR(bp->r[chip]);			/* Wait for CCR ready        */
+	bp->r[chip]->r[CD180_CCR]=CCR_HARDRESET;      /* Reset CD180 chip          */
+	udelay(1);
+	sti();
+	id=1000;
+	while((--id)&&(bp->r[chip]->r[CD180_GSVR]!=0xff))udelay(100);
+	if(!id) {
+		printk(KERN_ERR "aurora%d: Chip %d failed init.\n",board_No(bp),chip);
+		restore_flags(flags);
+		return(-1);
+		}
+	cli();
+	bp->r[chip]->r[CD180_GSVR]=(board_No(bp)<<5)|((chip+1)<<3); /* Set ID for this chip      */
+	bp->r[chip]->r[CD180_MSMR]=0x80|bp->ACK_MINT;      /* Prio for modem intr       */
+	bp->r[chip]->r[CD180_TSMR]=0x80|bp->ACK_TINT;      /* Prio for transmitter intr */
+	bp->r[chip]->r[CD180_RSMR]=0x80|bp->ACK_RINT;      /* Prio for receiver intr    */
+	/* Setting up prescaler. We need 4 tick per 1 ms */
+	bp->r[chip]->r[CD180_PPRH]=(bp->oscfreq/(1000000/AURORA_TPS)) >> 8;
+	bp->r[chip]->r[CD180_PPRL]=(bp->oscfreq/(1000000/AURORA_TPS)) & 0xff;
+
+	bp->r[chip]->r[CD180_SRCR]=SRCR_AUTOPRI|SRCR_GLOBPRI;
+
+	id=bp->r[chip]->r[CD180_GFRCR];
+	printk(KERN_INFO "aurora%d: Chip %d id %02x: ",board_No(bp),chip,id);
+	if(bp->r[chip]->r[CD180_SRCR]&128)
+		switch(id){
+			case 0x82:printk("CL-CD1864 rev A\n");break;
+			case 0x83:printk("CL-CD1865 rev A\n");break;
+			case 0x84:printk("CL-CD1865 rev B\n");break;
+			case 0x85:printk("CL-CD1865 rev C\n");break;
+			default:printk("Unknown.\n");
+		}else
+		switch(id){
+			case 0x81:printk("CL-CD180 rev B\n");break;
+			case 0x82:printk("CL-CD180 rev C\n");break;
+			default:printk("Unknown.\n");
+		};
+	restore_flags(flags);
+#ifdef AURORA_DEBUG
+printk("aurora_init_CD180: end\n");
+#endif
+	return 0;
+}
+
+static int valid_irq(unsigned char irq)
+{
+int i;
+for(i=0;i<TYPE_1_IRQS;i++)
+	if (type_1_irq[i]==irq) return 1;
+return 0;
+}
+
+static void aurora_interrupt(int irq, void * dev_id, struct pt_regs * regs);
+
+/* Main probing routine, also sets irq. */
+static int aurora_probe(void) {
+	struct linux_sbus *sbus;
+	struct linux_sbus_device *sdev;
+	int grrr;
+	char buf[30];
+	int bn=0;
+	struct Aurora_board *bp;
+
+	for_each_sbus(sbus) {
+		for_each_sbusdev(sdev, sbus) {
+/*			printk("Try: %x %s\n",sdev,sdev->prom_name);*/
+			if (!strcmp(sdev->prom_name, "sio16")) {
+				#ifdef AURORA_DEBUG
+				printk(KERN_INFO "aurora: sio16 at %p\n",sdev);
+				#endif
+				prom_apply_sbus_ranges(sdev->my_bus, sdev->reg_addrs, sdev->num_registers, sdev);
+				if((sdev->reg_addrs[0].reg_size!=1)&&(sdev->reg_addrs[1].reg_size!=128)&&
+				   (sdev->reg_addrs[2].reg_size!=128)&&(sdev->reg_addrs[3].reg_size!=4)){
+				   	printk(KERN_ERR "aurora%d: registers' sizes do not match.\n",bn);
+				   	break;
+				   	}
+				bp=&aurora_board[bn];
+				bp->r0 = (struct aurora_reg1 *) sparc_alloc_io(sdev->reg_addrs[0].phys_addr, 0,
+					sdev->reg_addrs[0].reg_size, "sio16",sdev->reg_addrs[0].which_io, 0x0);
+				if (!bp->r0) {
+					printk(KERN_ERR "aurora%d: can't map reg_addrs[0]\n",bn);
+					break;
+				}
+				#ifdef AURORA_DEBUG
+				printk("Map reg 0: %x\n",bp->r0);
+				#endif
+				bp->r[0] = (struct aurora_reg128 *) sparc_alloc_io(sdev->reg_addrs[1].phys_addr, 0,
+					sdev->reg_addrs[1].reg_size, "sio16", sdev->reg_addrs[1].which_io, 0x0);
+				if (!bp->r[0]) {
+					printk(KERN_ERR "aurora%d: can't map reg_addrs[1]\n",bn);
+					break;
+				}
+				#ifdef AURORA_DEBUG
+				printk("Map reg 1: %x\n",bp->r[0]);
+				#endif
+				bp->r[1] = (struct aurora_reg128 *) sparc_alloc_io(sdev->reg_addrs[2].phys_addr, 0,
+					sdev->reg_addrs[2].reg_size, "sio16", sdev->reg_addrs[2].which_io, 0x0);
+				if (!bp->r[1]) {
+					printk(KERN_ERR "aurora%d: can't map reg_addrs[2]\n",bn);
+					break;
+				}
+				#ifdef AURORA_DEBUG
+				printk("Map reg 2: %x\n",bp->r[1]);
+				#endif
+				bp->r3 = (struct aurora_reg4 *) sparc_alloc_io(sdev->reg_addrs[3].phys_addr, 0,
+					sdev->reg_addrs[3].reg_size, "sio16", sdev->reg_addrs[3].which_io, 0x0);
+				if (!bp->r3) {
+					printk(KERN_ERR "aurora%d: can't map reg_addrs[3]\n",bn);
+					break;
+				}
+				#ifdef AURORA_DEBUG
+				printk("Map reg 3: %x\n",bp->r3);
+				#endif
+				/* Variables setup */
+				bp->flags = 0;
+				#ifdef AURORA_DEBUG
+				grrr=prom_getint(sdev->prom_node,"intr");
+				printk("intr pri %d\n",grrr);
+				#endif
+				if ((bp->irq=irqs[bn]) && valid_irq(bp->irq) &&
+				    !request_irq(bp->irq|0x30, aurora_interrupt, SA_SHIRQ, "sio16", bp)) {
+					free_irq(bp->irq|0x30, bp);
+				} else
+				if ((bp->irq=prom_getint(sdev->prom_node, "bintr")) && valid_irq(bp->irq) &&
+				    !request_irq(bp->irq|0x30, aurora_interrupt, SA_SHIRQ, "sio16", bp)) {
+					free_irq(bp->irq|0x30, bp);
+				} else
+				if ((bp->irq=prom_getint(sdev->prom_node, "intr")) && valid_irq(bp->irq) &&
+				    !request_irq(bp->irq|0x30, aurora_interrupt, SA_SHIRQ, "sio16", bp)) {
+					free_irq(bp->irq|0x30, bp);
+				} else
+				for(grrr=0;grrr<TYPE_1_IRQS;grrr++) {
+					if ((bp->irq=type_1_irq[grrr])&&!request_irq(bp->irq|0x30, aurora_interrupt, SA_SHIRQ, "sio16", bp)) {
+						free_irq(bp->irq|0x30, bp);
+						break;
+					} else {
+					printk(KERN_ERR "aurora%d: Could not get an irq for this board !!!\n",bn);
+					bp->flags=0xff;
+					}
+				}
+				if(bp->flags==0xff)break;
+				printk(KERN_INFO "aurora%d: irq %d\n",bn,bp->irq&0x0f);
+				buf[0]=0;
+				grrr=prom_getproperty(sdev->prom_node,"dtr_rts",buf,sizeof(buf));
+				if(!strcmp(buf,"swapped")){
+					printk(KERN_INFO "aurora%d: Swapped DTR and RTS\n",bn);
+					bp->DTR=MSVR_RTS;
+					bp->RTS=MSVR_DTR;
+					bp->MSVDTR=CD180_MSVRTS;
+					bp->MSVRTS=CD180_MSVDTR;
+					bp->flags|=AURORA_BOARD_DTR_FLOW_OK;
+					}else{
+					#ifdef AURORA_FORCE_DTR_FLOW
+					printk(KERN_INFO "aurora%d: Forcing swapped DTR-RTS\n",bn);
+					bp->DTR=MSVR_RTS;
+					bp->RTS=MSVR_DTR;
+					bp->MSVDTR=CD180_MSVRTS;
+					bp->MSVRTS=CD180_MSVDTR;
+					bp->flags|=AURORA_BOARD_DTR_FLOW_OK;
+					#else
+					printk(KERN_INFO "aurora%d: Normal DTR and RTS\n",bn);
+					bp->DTR=MSVR_DTR;
+					bp->RTS=MSVR_RTS;
+					bp->MSVDTR=CD180_MSVDTR;
+					bp->MSVRTS=CD180_MSVRTS;
+					#endif
+				}
+				bp->oscfreq=prom_getint(sdev->prom_node,"clk")*100;
+				printk(KERN_INFO "aurora%d: Oscillator: %d Hz\n",bn,bp->oscfreq);
+				grrr=prom_getproperty(sdev->prom_node,"chip",buf,sizeof(buf));
+				printk(KERN_INFO "aurora%d: Chips: %s\n",bn,buf);
+				grrr=prom_getproperty(sdev->prom_node,"manu",buf,sizeof(buf));
+				printk(KERN_INFO "aurora%d: Manufacturer: %s\n",bn,buf);
+				grrr=prom_getproperty(sdev->prom_node,"model",buf,sizeof(buf));
+				printk(KERN_INFO "aurora%d: Model: %s\n",bn,buf);
+				grrr=prom_getproperty(sdev->prom_node,"rev",buf,sizeof(buf));
+				printk(KERN_INFO "aurora%d: Revision: %s\n",bn,buf);
+				grrr=prom_getproperty(sdev->prom_node,"mode",buf,sizeof(buf));
+				printk(KERN_INFO "aurora%d: Mode: %s\n",bn,buf);
+				#ifdef MODULE
+				bp->count=0;
+				#endif
+				bp->flags = AURORA_BOARD_PRESENT;
+				/* hardware ack */
+				bp->ACK_MINT=1;
+				bp->ACK_TINT=2;
+				bp->ACK_RINT=3;
+				bn++;
+			}
+		}
+	}
+	return bn;
+}
+
+static void aurora_release_io_range(struct Aurora_board *bp)
+{
+sparc_free_io(bp->r0,1);
+sparc_free_io(bp->r[0],128);
+sparc_free_io(bp->r[1],128);
+sparc_free_io(bp->r3,4);
+}
+
+extern inline void aurora_mark_event(struct Aurora_port * port, int event)
+{
+#ifdef AURORA_DEBUG
+printk("aurora_mark_event: start\n");
+#endif
+	set_bit(event, &port->event);
+	queue_task(&port->tqueue, &tq_aurora);
+	mark_bh(AURORA_BH);
+#ifdef AURORA_DEBUG
+printk("aurora_mark_event: end\n");
+#endif
+}
+
+extern inline struct Aurora_port * aurora_get_port(struct Aurora_board const * bp,
+					int chip, unsigned char const * what)
+{
+	unsigned char channel;
+	struct Aurora_port * port;
+
+	channel = (chip<<3)|((bp->r[chip]->r[CD180_GSCR]&GSCR_CHAN)>>GSCR_CHAN_OFF);
+	port = &aurora_port[board_No(bp) * AURORA_NPORT * AURORA_NCD180 + channel];
+	if (port->flags & ASYNC_INITIALIZED)  {
+		return port;
+	}
+	printk(KERN_DEBUG "aurora%d: %s interrupt from invalid port %d\n",
+	       board_No(bp), what, channel);
+	return NULL;
+}
+
+extern inline void aurora_receive_exc(struct Aurora_board const * bp, int chip)
+{
+	struct Aurora_port *port;
+	struct tty_struct *tty;
+	unsigned char status;
+	unsigned char ch;
+	
+	if (!(port = aurora_get_port(bp, chip, "Receive_x")))
+		return;
+
+	tty = port->tty;
+	if (tty->flip.count >= TTY_FLIPBUF_SIZE)  {
+	#ifdef AURORA_INTNORM
+		printk("aurora%d: port %d: Working around flip buffer overflow.\n",
+		       board_No(bp), port_No(port));
+	#endif
+		return;
+	}
+	
+#ifdef AURORA_REPORT_OVERRUN	
+	status = bp->r[chip]->r[CD180_RCSR];
+	if (status & RCSR_OE)  {
+		port->overrun++;
+#if 1
+		printk("aurora%d: port %d: Overrun. Total %ld overruns.\n",
+		       board_No(bp), port_No(port), port->overrun);
+#endif		
+	}
+	status &= port->mark_mask;
+#else	
+	status = bp->r[chip]->r[CD180_RCSR] & port->mark_mask;
+#endif	
+	ch = bp->r[chip]->r[CD180_RDR];
+	if (!status)  {
+		return;
+	}
+	if (status & RCSR_TOUT)  {
+/*		printk("aurora%d: port %d: Receiver timeout. Hardware problems ?\n",
+		       board_No(bp), port_No(port));*/
+		return;
+		
+	} else if (status & RCSR_BREAK)  {
+		printk(KERN_DEBUG "aurora%d: port %d: Handling break...\n",
+		       board_No(bp), port_No(port));
+		*tty->flip.flag_buf_ptr++ = TTY_BREAK;
+		if (port->flags & ASYNC_SAK)
+			do_SAK(tty);
+		
+	} else if (status & RCSR_PE) 
+		*tty->flip.flag_buf_ptr++ = TTY_PARITY;
+	
+	else if (status & RCSR_FE) 
+		*tty->flip.flag_buf_ptr++ = TTY_FRAME;
+	
+        else if (status & RCSR_OE)
+		*tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
+	
+	else
+		*tty->flip.flag_buf_ptr++ = 0;
+	
+	*tty->flip.char_buf_ptr++ = ch;
+	tty->flip.count++;
+	queue_task(&tty->flip.tqueue, &tq_timer);
+}
+
+extern inline void aurora_receive(struct Aurora_board const * bp, int chip)
+{
+	struct Aurora_port *port;
+	struct tty_struct *tty;
+	unsigned char count,cnt;
+
+	if (!(port = aurora_get_port(bp, chip, "Receive")))
+		return;
+	
+	tty = port->tty;
+	
+	count = bp->r[chip]->r[CD180_RDCR];
+
+#ifdef AURORA_REPORT_FIFO
+	port->hits[count > 8 ? 9 : count]++;
+#endif
+
+	while (count--)  {
+		if (tty->flip.count >= TTY_FLIPBUF_SIZE)  {
+		#ifdef AURORA_INTNORM
+			printk("aurora%d: port %d: Working around flip buffer overflow.\n",
+			       board_No(bp), port_No(port));
+		#endif
+			break;
+		}
+		cnt=bp->r[chip]->r[CD180_RDR];
+		*tty->flip.char_buf_ptr++ = cnt;
+		*tty->flip.flag_buf_ptr++ = 0;
+		tty->flip.count++;
+	}
+	queue_task(&tty->flip.tqueue, &tq_timer);
+}
+
+extern inline void aurora_transmit(struct Aurora_board const * bp, int chip)
+{
+	struct Aurora_port *port;
+	struct tty_struct *tty;
+	unsigned char count;
+	
+	
+	if (!(port = aurora_get_port(bp, chip, "Transmit")))
+		return;
+		
+	tty = port->tty;
+	
+	if (port->SRER & SRER_TXEMPTY)  {
+		/* FIFO drained */
+		bp->r[chip]->r[CD180_CAR]=port_No(port)&7;
+		udelay(1);
+		port->SRER &= ~SRER_TXEMPTY;
+		bp->r[chip]->r[CD180_SRER]=port->SRER;
+		return;
+	}
+	
+	if ((port->xmit_cnt <= 0 && !port->break_length)
+	    || tty->stopped || tty->hw_stopped)  {
+		bp->r[chip]->r[CD180_CAR]=port_No(port)&7;
+		udelay(1);
+		port->SRER &= ~SRER_TXRDY;
+		bp->r[chip]->r[CD180_SRER]=port->SRER;
+		return;
+	}
+	
+	if (port->break_length)  {
+		if (port->break_length > 0)  {
+			if (port->COR2 & COR2_ETC)  {
+				bp->r[chip]->r[CD180_TDR]=CD180_C_ESC;
+				bp->r[chip]->r[CD180_TDR]=CD180_C_SBRK;
+				port->COR2 &= ~COR2_ETC;
+			}
+			count = MIN(port->break_length, 0xff);
+			bp->r[chip]->r[CD180_TDR]=CD180_C_ESC;
+			bp->r[chip]->r[CD180_TDR]=CD180_C_DELAY;
+			bp->r[chip]->r[CD180_TDR]=count;
+			if (!(port->break_length -= count))
+				port->break_length--;
+		} else  {
+			bp->r[chip]->r[CD180_TDR]=CD180_C_ESC;
+			bp->r[chip]->r[CD180_TDR]=CD180_C_EBRK;
+			bp->r[chip]->r[CD180_COR2]=port->COR2;
+			aurora_wait_CCR(bp->r[chip]);
+			bp->r[chip]->r[CD180_CCR]=CCR_CORCHG2;
+			port->break_length = 0;
+		}
+		return;
+	}
+	
+	count = CD180_NFIFO;
+	do {
+		bp->r[chip]->r[CD180_TDR]=port->xmit_buf[port->xmit_tail++];
+		port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE-1);
+		if (--port->xmit_cnt <= 0)
+			break;
+	} while (--count > 0);
+	
+	if (port->xmit_cnt <= 0)  {
+		bp->r[chip]->r[CD180_CAR]=port_No(port)&7;
+		udelay(1);
+		port->SRER &= ~SRER_TXRDY;
+		bp->r[chip]->r[CD180_SRER]=port->SRER;
+	}
+	if (port->xmit_cnt <= port->wakeup_chars)
+		aurora_mark_event(port, RS_EVENT_WRITE_WAKEUP);
+}
+
+extern inline void aurora_check_modem(struct Aurora_board const * bp, int chip)
+{
+	struct Aurora_port *port;
+	struct tty_struct *tty;
+	unsigned char mcr;
+	
+	if (!(port = aurora_get_port(bp, chip, "Modem")))
+		return;
+		
+	tty = port->tty;
+	
+	mcr = bp->r[chip]->r[CD180_MCR];
+	if (mcr & MCR_CDCHG)  {
+		if (bp->r[chip]->r[CD180_MSVR] & MSVR_CD) 
+			wake_up_interruptible(&port->open_wait);
+		else if (!((port->flags & ASYNC_CALLOUT_ACTIVE) &&
+			   (port->flags & ASYNC_CALLOUT_NOHUP)))
+			queue_task(&port->tqueue_hangup,  
+					   &tq_scheduler);      
+	}
+	
+/* We don't have such things yet. My aurora board has DTR and RTS swapped, but that doesn't count in this driver. Let's hope
+ * Aurora didn't made any boards with CTS or DSR broken...
+ */
+/* #ifdef AURORA_BRAIN_DAMAGED_CTS
+	if (mcr & MCR_CTSCHG)  {
+		if (aurora_in(bp, CD180_MSVR) & MSVR_CTS)  {
+			tty->hw_stopped = 0;
+			port->SRER |= SRER_TXRDY;
+			if (port->xmit_cnt <= port->wakeup_chars)
+				aurora_mark_event(port, RS_EVENT_WRITE_WAKEUP);
+		} else  {
+			tty->hw_stopped = 1;
+			port->SRER &= ~SRER_TXRDY;
+		}
+		bp->r[chip]->r[CD180_SRER, port->SRER);
+	}
+	if (mcr & MCR_DSRCHG)  {
+		if (aurora_in(bp, CD180_MSVR) & MSVR_DSR)  {
+			tty->hw_stopped = 0;
+			port->SRER |= SRER_TXRDY;
+			if (port->xmit_cnt <= port->wakeup_chars)
+				aurora_mark_event(port, RS_EVENT_WRITE_WAKEUP);
+		} else  {
+			tty->hw_stopped = 1;
+			port->SRER &= ~SRER_TXRDY;
+		}
+		bp->r[chip]->r[CD180_SRER, port->SRER);
+	}
+#endif AURORA_BRAIN_DAMAGED_CTS */
+	
+	/* Clear change bits */
+	bp->r[chip]->r[CD180_MCR]=0;
+}
+
+/* The main interrupt processing routine */
+static void aurora_interrupt(int irq, void * dev_id, struct pt_regs * regs)
+{
+	unsigned char status;
+	unsigned char ack,chip/*,chip_id*/;
+	struct Aurora_board * bp = (struct Aurora_board *) dev_id;
+	unsigned long loop=0;
+
+	#ifdef AURORA_INT_DEBUG
+	printk("IRQ%d %d\n",irq,++irqhit);
+	#ifdef AURORA_FLOODPRO
+	if (irqhit>=AURORA_FLOODPRO)
+		bp->r0->r=8;
+	#endif
+	#endif
+	
+/* old	bp = IRQ_to_board[irq&0x0f];*/
+	
+	if (!bp || !(bp->flags & AURORA_BOARD_ACTIVE))  {
+		return;
+	}
+
+/*	The while() below takes care of this.
+	status=bp->r[0]->r[CD180_SRSR];
+	#ifdef AURORA_INT_DEBUG
+	printk("mumu: %02x\n",status);
+	#endif
+	if (!(status&SRSR_ANYINT)) return; * Nobody has anything to say, so exit *
+*/
+	while ((loop++ < 48)&&(status=bp->r[0]->r[CD180_SRSR]&SRSR_ANYINT)){
+		#ifdef AURORA_INT_DEBUG
+		printk("SRSR: %02x\n",status);
+		#endif
+		if (status&SRSR_REXT) {
+			ack=bp->r3->r[bp->ACK_RINT];
+			#ifdef AURORA_INT_DEBUG
+			printk("R-ACK %02x\n",ack);
+			#endif
+			if ((ack>>5)==board_No(bp)) {
+				if ((chip=((ack>>3)&3)-1) < AURORA_NCD180) {
+					if ((ack&GSVR_ITMASK)==GSVR_IT_RGD) {
+						aurora_receive(bp,chip);
+						bp->r[chip]->r[CD180_EOSRR]=0;
+					} else
+					if ((ack&GSVR_ITMASK)==GSVR_IT_REXC) {
+						aurora_receive_exc(bp,chip);
+						bp->r[chip]->r[CD180_EOSRR]=0;
+					}
+				}
+			}
+		} else
+		if (status&SRSR_TEXT) {
+			ack=bp->r3->r[bp->ACK_TINT];
+			#ifdef AURORA_INT_DEBUG
+			printk("T-ACK %02x\n",ack);
+			#endif
+			if ((ack>>5)==board_No(bp)) {
+				if ((chip=((ack>>3)&3)-1) < AURORA_NCD180) {
+					if ((ack&GSVR_ITMASK)==GSVR_IT_TX) {
+						aurora_transmit(bp,chip);
+						bp->r[chip]->r[CD180_EOSRR]=0;
+					}
+				}
+			}
+		} else
+		if (status&SRSR_MEXT) {
+			ack=bp->r3->r[bp->ACK_MINT];
+			#ifdef AURORA_INT_DEBUG
+			printk("M-ACK %02x\n",ack);
+			#endif
+			if ((ack>>5)==board_No(bp)) {
+				if ((chip=((ack>>3)&3)-1) < AURORA_NCD180) {
+					if ((ack&GSVR_ITMASK)==GSVR_IT_MDM) {
+						aurora_check_modem(bp,chip);
+						bp->r[chip]->r[CD180_EOSRR]=0;
+					}
+				}
+			}
+		}
+	}
+/* I guess this faster code can be used with CD1865, using AUROPRI and GLOBPRI.
+	while ((loop++ < 48)&&(status=bp->r[0]->r[CD180_SRSR]&SRSR_ANYINT)){
+		#ifdef AURORA_INT_DEBUG
+		printk("SRSR: %02x\n",status);
+		#endif
+		ack=bp->r3->r[0];
+		#ifdef AURORA_INT_DEBUG
+		printk("ACK: %02x\n",ack);
+		#endif
+		if ((ack>>5)==board_No(bp)) {
+			if ((chip=((ack>>3)&3)-1) < AURORA_NCD180) {
+				ack&=GSVR_ITMASK;
+				if (ack==GSVR_IT_RGD) {
+					aurora_receive(bp,chip);
+					bp->r[chip]->r[CD180_EOSRR]=0;
+				} else
+				if (ack==GSVR_IT_REXC) {
+					aurora_receive_exc(bp,chip);
+					bp->r[chip]->r[CD180_EOSRR]=0;
+				} else
+				if (ack==GSVR_IT_TX) {
+					aurora_transmit(bp,chip);
+					bp->r[chip]->r[CD180_EOSRR]=0;
+				} else
+				if (ack==GSVR_IT_MDM) {
+					aurora_check_modem(bp,chip);
+					bp->r[chip]->r[CD180_EOSRR]=0;
+				}
+			}
+		}
+	}
+*/
+/* This is the old handling routine, used in riscom8 for only one CD180. I keep it here for reference.
+for(chip=0;chip<AURORA_NCD180;chip++){
+	chip_id=(board_No(bp)<<5)|((chip+1)<<3);
+	loop=0;
+	while ((loop++ < 1) && ((status = bp->r[chip]->r[CD180_SRSR]) &
+			 (SRSR_TEXT | SRSR_MEXT | SRSR_REXT))) {
+
+		if (status & SRSR_REXT) {
+			ack = bp->r3->r[bp->ACK_RINT];
+			if (ack == (chip_id | GSVR_IT_RGD)){
+				#ifdef AURORA_INTMSG
+				printk("RX ACK\n");
+				#endif
+				aurora_receive(bp,chip);
+				}
+			else if (ack == (chip_id | GSVR_IT_REXC)){
+				#ifdef AURORA_INTMSG
+				printk("RXC ACK\n");
+				#endif
+				aurora_receive_exc(bp,chip);
+				}
+			else
+				#ifdef AURORA_INTNORM
+				printk("aurora%d-%d: Bad receive ack 0x%02x.\n",
+				       board_No(bp), chip, ack)
+				#endif
+				;
+
+		} else if (status & SRSR_TEXT) {
+			ack = bp->r3->r[bp->ACK_TINT];
+			if (ack == (chip_id | GSVR_IT_TX)){
+				#ifdef AURORA_INTMSG
+				printk("TX ACK\n");
+				#endif
+				aurora_transmit(bp,chip);
+				}
+			else{
+				#ifdef AURORA_INTNORM
+				printk("aurora%d-%d: Bad transmit ack 0x%02x.\n",
+				       board_No(bp), chip, ack);
+				#endif
+				       }
+
+		} else  if (status & SRSR_MEXT)  {
+			ack = bp->r3->r[bp->ACK_MINT];
+			if (ack == (chip_id | GSVR_IT_MDM)){
+				#ifdef AURORA_INTMSG
+				printk("MDM ACK\n");
+				#endif
+				aurora_check_modem(bp,chip);
+				}
+			else
+				#ifdef AURORA_INTNORM
+				printk("aurora%d-%d: Bad modem ack 0x%02x.\n",
+				       board_No(bp), chip, ack)
+				#endif
+				;
+		
+		}
+		bp->r[chip]->r[CD180_EOSRR]=0;
+	}
+    }
+*/
+}
+
+
+#ifdef AURORA_INT_DEBUG
+static void aurora_timer (unsigned long ignored);
+
+static struct timer_list
+aurora_poll_timer = { NULL, NULL, 0, 0, aurora_timer };
+
+static void
+aurora_timer (unsigned long ignored)
+{
+	unsigned long flags;
+	int i;
+
+	save_flags(flags); cli();
+
+printk("SRSR: %02x,%02x - ",aurora_board[0].r[0]->r[CD180_SRSR],aurora_board[0].r[1]->r[CD180_SRSR]);
+for(i=0;i<4;i++){
+	udelay(1);
+	printk("%02x ",aurora_board[0].r3->r[i]);
+	}
+printk("\n");
+
+	aurora_poll_timer.expires = jiffies + 300;
+	add_timer (&aurora_poll_timer);
+
+	restore_flags(flags);
+}
+#endif
+
+/*
+ *  Routines for open & close processing.
+ */
+
+/* Called with disabled interrupts */
+extern inline int aurora_setup_board(struct Aurora_board * bp)
+{
+	int error;
+	
+#ifdef AURORA_ALLIRQ
+	int i;
+	for(i=0;i<AURORA_ALLIRQ;i++){
+		error = request_irq(allirq[i]|0x30, aurora_interrupt, SA_SHIRQ, "sio16", bp);
+		if (error){
+			printk(KERN_ERR "IRQ%d request error %d\n",allirq[i],error);
+		}
+	}
+#else
+	error = request_irq(bp->irq|0x30, aurora_interrupt, SA_SHIRQ, "sio16", bp);
+	if (error){
+		printk(KERN_ERR "IRQ request error %d\n",error);
+		return error;
+	}
+#endif
+	/* Board reset */
+	bp->r0->r=0;
+	udelay(1);
+	if(bp->flags&AURORA_BOARD_TYPE_2){
+		/* unknown yet */
+	} else {
+		bp->r0->r=AURORA_CFG_ENABLE_IO|AURORA_CFG_ENABLE_IRQ|(((bp->irq)&0x0f)>>2);
+	}
+	udelay(10000);
+
+	if (aurora_init_CD180(bp,0))error=1;error=0;
+	if (aurora_init_CD180(bp,1))error++;
+	if (error==AURORA_NCD180) {
+		printk(KERN_ERR "Both chips failed initialisation.\n");
+		return -EIO;
+		}
+
+#ifdef AURORA_INT_DEBUG
+	aurora_poll_timer.expires=jiffies+1;
+	add_timer(&aurora_poll_timer);
+#endif
+#ifdef AURORA_DEBUG
+printk("aurora_setup_board: end\n");
+#endif
+	return 0;
+}
+
+/* Called with disabled interrupts */
+extern inline void aurora_shutdown_board(struct Aurora_board *bp)
+{
+	int i;
+
+#ifdef AURORA_DEBUG
+printk("aurora_shutdown_board: start\n");
+#endif
+
+#ifdef AURORA_INT_DEBUG
+	del_timer(&aurora_poll_timer);
+#endif
+
+#ifdef AURORA_ALLIRQ
+for(i=0;i<AURORA_ALLIRQ;i++){
+	free_irq(allirq[i]|0x30, bp);
+/*	IRQ_to_board[allirq[i]&0xf] = NULL;*/
+	}
+#else
+	free_irq(bp->irq|0x30, bp);
+/*	IRQ_to_board[bp->irq&0xf] = NULL;*/
+#endif	
+	/* Drop all DTR's */
+	for(i=0;i<16;i++){
+		bp->r[i>>3]->r[CD180_CAR]=i&7;
+		udelay(1);
+		bp->r[i>>3]->r[CD180_MSVR]=0;
+		udelay(1);
+		}
+	/* Board shutdown */
+	bp->r0->r=0;
+
+#ifdef AURORA_DEBUG
+printk("aurora_shutdown_board: end\n");
+#endif
+}
+
+/*
+ * Setting up port characteristics. 
+ * Must be called with disabled interrupts
+ */
+static void aurora_change_speed(struct Aurora_board *bp, struct Aurora_port *port)
+{
+	struct tty_struct *tty;
+	unsigned long baud;
+	long tmp;
+	unsigned char cor1 = 0, cor3 = 0;
+	unsigned char mcor1 = 0, mcor2 = 0,chip;
+	
+#ifdef AURORA_DEBUG
+printk("aurora_change_speed: start\n");
+#endif
+	if (!(tty = port->tty) || !tty->termios)
+		return;
+		
+	chip=AURORA_CD180(port_No(port));
+
+	port->SRER  = 0;
+	port->COR2 = 0;
+	port->MSVR = MSVR_RTS|MSVR_DTR;
+	
+	baud = C_BAUD(tty);
+	
+	if (baud & CBAUDEX) {
+		baud &= ~CBAUDEX;
+		if (baud < 1 || baud > 2) 
+			port->tty->termios->c_cflag &= ~CBAUDEX;
+		else
+			baud += 15;
+	}
+	if (baud == 15)  {
+		if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+			baud ++;
+		if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+			baud += 2;
+	}
+	
+	/* Select port on the board */
+	bp->r[chip]->r[CD180_CAR]=port_No(port)&7;
+	udelay(1);
+	
+	if (!baud_table[baud])  {
+		/* Drop DTR & exit */
+		port->MSVR &= ~(bp->DTR|bp->RTS);
+		bp->r[chip]->r[CD180_MSVR]=port->MSVR;
+		return;
+	} else  {
+		/* Set DTR on */
+		port->MSVR |= bp->DTR;
+		bp->r[chip]->r[CD180_MSVR]=port->MSVR;
+	}
+	
+	/*
+	 * Now we must calculate some speed depended things 
+	 */
+	
+	/* Set baud rate for port */
+	tmp = (((bp->oscfreq + baud_table[baud]/2) / baud_table[baud] +
+		CD180_TPC/2) / CD180_TPC);
+
+/*	tmp = (bp->oscfreq/7)/baud_table[baud];
+	if((tmp%10)>4)tmp=tmp/10+1;else tmp=tmp/10;*/
+/*	printk("Prescaler period: %d\n",tmp);*/
+
+	bp->r[chip]->r[CD180_RBPRH]=(tmp >> 8) & 0xff;
+	bp->r[chip]->r[CD180_TBPRH]=(tmp >> 8) & 0xff;
+	bp->r[chip]->r[CD180_RBPRL]=tmp & 0xff;
+	bp->r[chip]->r[CD180_TBPRL]=tmp & 0xff;
+	
+	baud = (baud_table[baud] + 5) / 10;   /* Estimated CPS */
+	
+	/* Two timer ticks seems enough to wakeup something like SLIP driver */
+	tmp = ((baud + HZ/2) / HZ) * 2 - CD180_NFIFO;		
+	port->wakeup_chars = (tmp < 0) ? 0 : ((tmp >= SERIAL_XMIT_SIZE) ?
+					      SERIAL_XMIT_SIZE - 1 : tmp);
+	
+	/* Receiver timeout will be transmission time for 1.5 chars */
+	tmp = (AURORA_TPS + AURORA_TPS/2 + baud/2) / baud;
+	tmp = (tmp > 0xff) ? 0xff : tmp;
+	bp->r[chip]->r[CD180_RTPR]=tmp;
+	
+	switch (C_CSIZE(tty))  {
+	 case CS5:
+		cor1 |= COR1_5BITS;
+		break;
+	 case CS6:
+		cor1 |= COR1_6BITS;
+		break;
+	 case CS7:
+		cor1 |= COR1_7BITS;
+		break;
+	 case CS8:
+		cor1 |= COR1_8BITS;
+		break;
+	}
+	
+	if (C_CSTOPB(tty)) 
+		cor1 |= COR1_2SB;
+	
+	cor1 |= COR1_IGNORE;
+	if (C_PARENB(tty))  {
+		cor1 |= COR1_NORMPAR;
+		if (C_PARODD(tty)) 
+			cor1 |= COR1_ODDP;
+		if (I_INPCK(tty)) 
+			cor1 &= ~COR1_IGNORE;
+	}
+	/* Set marking of some errors */
+	port->mark_mask = RCSR_OE | RCSR_TOUT;
+	if (I_INPCK(tty)) 
+		port->mark_mask |= RCSR_FE | RCSR_PE;
+	if (I_BRKINT(tty) || I_PARMRK(tty)) 
+		port->mark_mask |= RCSR_BREAK;
+	if (I_IGNPAR(tty)) 
+		port->mark_mask &= ~(RCSR_FE | RCSR_PE);
+	if (I_IGNBRK(tty))  {
+		port->mark_mask &= ~RCSR_BREAK;
+		if (I_IGNPAR(tty)) 
+			/* Real raw mode. Ignore all */
+			port->mark_mask &= ~RCSR_OE;
+	}
+	/* Enable Hardware Flow Control */
+	if (C_CRTSCTS(tty))  {
+/*#ifdef AURORA_BRAIN_DAMAGED_CTS
+		port->SRER |= SRER_DSR | SRER_CTS;
+		mcor1 |= MCOR1_DSRZD | MCOR1_CTSZD;
+		mcor2 |= MCOR2_DSROD | MCOR2_CTSOD;
+		tty->hw_stopped = !(aurora_in(bp, CD180_MSVR) & (MSVR_CTS|MSVR_DSR));
+#else*/
+		port->COR2 |= COR2_CTSAE;
+/*#endif*/
+		if (bp->flags&AURORA_BOARD_DTR_FLOW_OK) {
+			mcor1 |= AURORA_RXTH;
+		}
+	}
+	/* Enable Software Flow Control. FIXME: I'm not sure about this */
+	/* Some people reported that it works, but I still doubt */
+	if (I_IXON(tty))  {
+		port->COR2 |= COR2_TXIBE;
+		cor3 |= (COR3_FCT | COR3_SCDE);
+		if (I_IXANY(tty))
+			port->COR2 |= COR2_IXM;
+		bp->r[chip]->r[CD180_SCHR1]=START_CHAR(tty);
+		bp->r[chip]->r[CD180_SCHR2]=STOP_CHAR(tty);
+		bp->r[chip]->r[CD180_SCHR3]=START_CHAR(tty);
+		bp->r[chip]->r[CD180_SCHR4]=STOP_CHAR(tty);
+	}
+	if (!C_CLOCAL(tty))  {
+		/* Enable CD check */
+		port->SRER |= SRER_CD;
+		mcor1 |= MCOR1_CDZD;
+		mcor2 |= MCOR2_CDOD;
+	}
+	
+	if (C_CREAD(tty)) 
+		/* Enable receiver */
+		port->SRER |= SRER_RXD;
+	
+	/* Set input FIFO size (1-8 bytes) */
+	cor3 |= AURORA_RXFIFO; 
+	/* Setting up CD180 channel registers */
+	bp->r[chip]->r[CD180_COR1]=cor1;
+	bp->r[chip]->r[CD180_COR2]=port->COR2;
+	bp->r[chip]->r[CD180_COR3]=cor3;
+	/* Make CD180 know about registers change */
+	aurora_wait_CCR(bp->r[chip]);
+	bp->r[chip]->r[CD180_CCR]=CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3;
+	/* Setting up modem option registers */
+	bp->r[chip]->r[CD180_MCOR1]=mcor1;
+	bp->r[chip]->r[CD180_MCOR2]=mcor2;
+	/* Enable CD180 transmitter & receiver */
+	aurora_wait_CCR(bp->r[chip]);
+	bp->r[chip]->r[CD180_CCR]=CCR_TXEN | CCR_RXEN;
+	/* Enable interrupts */
+	bp->r[chip]->r[CD180_SRER]=port->SRER;
+	/* And finally set RTS on */
+	bp->r[chip]->r[CD180_MSVR]=port->MSVR;
+#ifdef AURORA_DEBUG
+printk("aurora_change_speed: end\n");
+#endif
+}
+
+/* Must be called with interrupts enabled */
+static int aurora_setup_port(struct Aurora_board *bp, struct Aurora_port *port)
+{
+	unsigned long flags;
+	
+#ifdef AURORA_DEBUG
+printk("aurora_setup_port: start %d\n",port_No(port));
+#endif
+	if (port->flags & ASYNC_INITIALIZED)
+		return 0;
+		
+	if (!port->xmit_buf) {
+		/* We may sleep in get_free_page() */
+		unsigned long tmp;
+		
+		if (!(tmp = get_free_page(GFP_KERNEL)))
+			return -ENOMEM;
+		    
+		if (port->xmit_buf) {
+			free_page(tmp);
+			return -ERESTARTSYS;
+		}
+		port->xmit_buf = (unsigned char *) tmp;
+	}
+		
+	save_flags(flags); cli();
+		
+	if (port->tty) 
+		clear_bit(TTY_IO_ERROR, &port->tty->flags);
+		
+#ifdef MODULE
+	if (port->count == 1) {
+		MOD_INC_USE_COUNT;
+		if((++bp->count)==1)
+			bp->flags|=AURORA_BOARD_ACTIVE;
+	}
+#endif
+
+	port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
+	aurora_change_speed(bp, port);
+	port->flags |= ASYNC_INITIALIZED;
+		
+	restore_flags(flags);
+#ifdef AURORA_DEBUG
+printk("aurora_setup_port: end\n");
+#endif
+	return 0;
+}
+
+/* Must be called with interrupts disabled */
+static void aurora_shutdown_port(struct Aurora_board *bp, struct Aurora_port *port)
+{
+	struct tty_struct *tty;
+	unsigned char chip;
+
+#ifdef AURORA_DEBUG
+printk("aurora_shutdown_port: start\n");
+#endif
+	if (!(port->flags & ASYNC_INITIALIZED)) 
+		return;
+	
+	chip=AURORA_CD180(port_No(port));
+	
+#ifdef AURORA_REPORT_OVERRUN
+	printk("aurora%d: port %d: Total %ld overruns were detected.\n",
+	       board_No(bp), port_No(port), port->overrun);
+#endif	
+#ifdef AURORA_REPORT_FIFO
+	{
+		int i;
+		
+		printk("aurora%d: port %d: FIFO hits [ ",
+		       board_No(bp), port_No(port));
+		for (i = 0; i < 10; i++)  {
+			printk("%ld ", port->hits[i]);
+		}
+		printk("].\n");
+	}
+#endif	
+	if (port->xmit_buf)  {
+		free_page((unsigned long) port->xmit_buf);
+		port->xmit_buf = NULL;
+	}
+
+	if (!(tty = port->tty) || C_HUPCL(tty))  {
+		/* Drop DTR */
+		port->MSVR &= ~(bp->DTR|bp->RTS);
+		bp->r[chip]->r[CD180_MSVR]=port->MSVR;
+	}
+	
+        /* Select port */
+	bp->r[chip]->r[CD180_CAR]=port_No(port)&7;
+	udelay(1);
+	/* Reset port */
+	aurora_wait_CCR(bp->r[chip]);
+	bp->r[chip]->r[CD180_CCR]=CCR_SOFTRESET;
+	/* Disable all interrupts from this port */
+	port->SRER = 0;
+	bp->r[chip]->r[CD180_SRER]=port->SRER;
+	
+	if (tty)  
+		set_bit(TTY_IO_ERROR, &tty->flags);
+	port->flags &= ~ASYNC_INITIALIZED;
+
+#ifdef MODULE
+	if (--bp->count < 0)  {
+		printk(KERN_DEBUG "aurora%d: aurora_shutdown_port: bad board count: %d\n",
+		       board_No(bp), bp->count);
+		bp->count = 0;
+	}
+	
+	MOD_DEC_USE_COUNT;
+	if (!bp->count)
+		bp->flags&=~AURORA_BOARD_ACTIVE;
+#endif
+
+#ifdef AURORA_DEBUG
+printk("aurora_shutdown_port: end\n");
+#endif
+}
+
+	
+static int block_til_ready(struct tty_struct *tty, struct file * filp,
+			   struct Aurora_port *port)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	struct Aurora_board *bp = port_Board(port);
+	int    retval;
+	int    do_clocal = 0;
+	int    CD;
+	unsigned char chip;
+	
+#ifdef AURORA_DEBUG
+printk("block_til_ready: start\n");
+#endif
+	chip=AURORA_CD180(port_No(port));
+
+	/*
+	 * If the device is in the middle of being closed, then block
+	 * until it's done, and then try again.
+	 */
+	if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) {
+		interruptible_sleep_on(&port->close_wait);
+		if (port->flags & ASYNC_HUP_NOTIFY)
+			return -EAGAIN;
+		else
+			return -ERESTARTSYS;
+	}
+
+	/*
+	 * If non-blocking mode is set, or the port is not enabled,
+	 * then make the check up front and then exit.
+	 */
+	if ((filp->f_flags & O_NONBLOCK) ||
+	    (tty->flags & (1 << TTY_IO_ERROR))) {
+		if (port->flags & ASYNC_CALLOUT_ACTIVE)
+			return -EBUSY;
+		port->flags |= ASYNC_NORMAL_ACTIVE;
+		return 0;
+	}
+
+	if (port->flags & ASYNC_CALLOUT_ACTIVE) {
+		if (port->normal_termios.c_cflag & CLOCAL) 
+			do_clocal = 1;
+	} else {
+		if (C_CLOCAL(tty))  
+			do_clocal = 1;
+	}
+	
+	/*
+	 * Block waiting for the carrier detect and the line to become
+	 * free (i.e., not in use by the callout).  While we are in
+	 * this loop, info->count is dropped by one, so that
+	 * rs_close() knows when to free things.  We restore it upon
+	 * exit, either normal or abnormal.
+	 */
+	retval = 0;
+	add_wait_queue(&port->open_wait, &wait);
+	cli();
+	if (!tty_hung_up_p(filp))
+		port->count--;
+	sti();
+	port->blocked_open++;
+	while (1) {
+		cli();
+		bp->r[chip]->r[CD180_CAR]=port_No(port)&7;
+		udelay(1);
+		CD = bp->r[chip]->r[CD180_MSVR] & MSVR_CD;
+		if (!(port->flags & ASYNC_CALLOUT_ACTIVE))  {
+			port->MSVR=bp->RTS;
+			bp->r[chip]->r[CD180_MSVR]=port->MSVR;/* auto drops DTR */
+		}
+		sti();
+		current->state = TASK_INTERRUPTIBLE;
+		if (tty_hung_up_p(filp) ||
+		    !(port->flags & ASYNC_INITIALIZED)) {
+			if (port->flags & ASYNC_HUP_NOTIFY)
+				retval = -EAGAIN;
+			else
+				retval = -ERESTARTSYS;	
+			break;
+		}
+		if (/*!(port->flags & ASYNC_CALLOUT_ACTIVE) &&*/
+		    !(port->flags & ASYNC_CLOSING) &&
+		    (do_clocal || CD))
+			break;
+		if (signal_pending(current)) {
+			retval = -ERESTARTSYS;
+			break;
+		}
+		schedule();
+	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&port->open_wait, &wait);
+	if (!tty_hung_up_p(filp))
+		port->count++;
+	port->blocked_open--;
+	if (retval)
+		return retval;
+	
+	port->flags |= ASYNC_NORMAL_ACTIVE;
+#ifdef AURORA_DEBUG
+printk("block_til_ready: end\n");
+#endif
+	return 0;
+}	
+
+static int aurora_open(struct tty_struct * tty, struct file * filp)
+{
+	int board;
+	int error;
+	struct Aurora_port * port;
+	struct Aurora_board * bp;
+	unsigned long flags;
+	
+	#ifdef AURORA_DEBUG
+	printk("aurora_open: start\n");
+	#endif
+	
+	board = AURORA_BOARD(MINOR(tty->device));
+	if (board > AURORA_NBOARD || !(aurora_board[board].flags & AURORA_BOARD_PRESENT)){
+		#ifdef AURORA_DEBUG
+		printk("aurora_open: error board %d present %d\n",board,aurora_board[board].flags &AURORA_BOARD_PRESENT);
+		#endif
+		return -ENODEV;
+		}
+	
+	bp = &aurora_board[board];
+	port = aurora_port + board * AURORA_NPORT * AURORA_NCD180 + AURORA_PORT(MINOR(tty->device));
+	if (aurora_paranoia_check(port, tty->device, "aurora_open")){
+		#ifdef AURORA_DEBUG
+		printk("aurora_open: error paranoia check\n");
+		#endif
+		return -ENODEV;
+		}
+	
+	port->count++;
+	tty->driver_data = port;
+	port->tty = tty;
+	
+	if ((error = aurora_setup_port(bp, port))) {
+		#ifdef AURORA_DEBUG
+		printk("aurora_open: error aurora_setup_port ret %d\n",error);
+		#endif
+		return error;
+		}
+
+	if ((error = block_til_ready(tty, filp, port))){
+		#ifdef AURORA_DEBUG
+		printk("aurora_open: error block_til_ready ret %d\n",error);
+		#endif
+		return error;
+		}
+	
+	if ((port->count == 1) && (port->flags & ASYNC_SPLIT_TERMIOS)) {
+		*tty->termios = port->normal_termios;
+		save_flags(flags); cli();
+		aurora_change_speed(bp, port);
+		restore_flags(flags);
+	}
+
+	port->session = current->session;
+	port->pgrp = current->pgrp;
+	#ifdef AURORA_DEBUG
+	printk("aurora_open: end\n");
+	#endif
+	return 0;
+}
+
+static void aurora_close(struct tty_struct * tty, struct file * filp)
+{
+	struct Aurora_port *port = (struct Aurora_port *) tty->driver_data;
+	struct Aurora_board *bp;
+	unsigned long flags;
+	unsigned long timeout;
+	unsigned char chip;
+	
+	#ifdef AURORA_DEBUG
+	printk("aurora_close: start\n");
+	#endif
+	
+	if (!port || aurora_paranoia_check(port, tty->device, "close"))
+		return;
+	
+	chip=AURORA_CD180(port_No(port));
+
+	save_flags(flags); cli();
+	if (tty_hung_up_p(filp))  {
+		restore_flags(flags);
+		return;
+	}
+	
+	bp = port_Board(port);
+	if ((tty->count == 1) && (port->count != 1))  {
+		printk(KERN_DEBUG "aurora%d: aurora_close: bad port count; tty->count is 1, port count is %d\n",
+		       board_No(bp), port->count);
+		port->count = 1;
+	}
+	if (--port->count < 0)  {
+		printk(KERN_DEBUG "aurora%d: aurora_close: bad port count for tty%d: %d\n",
+		       board_No(bp), port_No(port), port->count);
+		port->count = 0;
+	}
+	if (port->count)  {
+		restore_flags(flags);
+		return;
+	}
+	port->flags |= ASYNC_CLOSING;
+	/*
+	 * Save the termios structure, since this port may have
+	 * separate termios for callout and dialin.
+	 */
+	if (port->flags & ASYNC_NORMAL_ACTIVE)
+		port->normal_termios = *tty->termios;
+/*	if (port->flags & ASYNC_CALLOUT_ACTIVE)
+		port->callout_termios = *tty->termios;*/
+	/*
+	 * Now we wait for the transmit buffer to clear; and we notify 
+	 * the line discipline to only process XON/XOFF characters.
+	 */
+	tty->closing = 1;
+	if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE){
+		#ifdef AURORA_DEBUG
+		printk("aurora_close: waiting to flush...\n");
+		#endif
+		tty_wait_until_sent(tty, port->closing_wait);
+		}
+	/*
+	 * At this point we stop accepting input.  To do this, we
+	 * disable the receive line status interrupts, and tell the
+	 * interrupt driver to stop checking the data ready bit in the
+	 * line status register.
+	 */
+	port->SRER &= ~SRER_RXD;
+	if (port->flags & ASYNC_INITIALIZED) {
+		port->SRER &= ~SRER_TXRDY;
+		port->SRER |= SRER_TXEMPTY;
+		bp->r[chip]->r[CD180_CAR]=port_No(port)&7;
+		udelay(1);
+		bp->r[chip]->r[CD180_SRER]=port->SRER;
+		/*
+		 * Before we drop DTR, make sure the UART transmitter
+		 * has completely drained; this is especially
+		 * important if there is a transmit FIFO!
+		 */
+		timeout = jiffies+HZ;
+		while(port->SRER & SRER_TXEMPTY)  {
+			current->state = TASK_INTERRUPTIBLE;
+			schedule_timeout(port->timeout);
+			if (time_after(jiffies, timeout))
+				break;
+		}
+	}
+	#ifdef AURORA_DEBUG
+	printk("aurora_close: shutdown_port\n");
+	#endif
+	aurora_shutdown_port(bp, port);
+	if (tty->driver.flush_buffer)
+		tty->driver.flush_buffer(tty);
+	if (tty->ldisc.flush_buffer)
+		tty->ldisc.flush_buffer(tty);
+	tty->closing = 0;
+	port->event = 0;
+	port->tty = 0;
+	if (port->blocked_open) {
+		if (port->close_delay) {
+			current->state = TASK_INTERRUPTIBLE;
+			schedule_timeout(port->close_delay);
+		}
+		wake_up_interruptible(&port->open_wait);
+	}
+	port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|
+			 ASYNC_CLOSING);
+	wake_up_interruptible(&port->close_wait);
+	restore_flags(flags);
+	#ifdef AURORA_DEBUG
+	printk("aurora_close: end\n");
+	#endif
+}
+
+static int aurora_write(struct tty_struct * tty, int from_user, 
+		    const unsigned char *buf, int count)
+{
+	struct Aurora_port *port = (struct Aurora_port *)tty->driver_data;
+	struct Aurora_board *bp;
+	int c, total = 0;
+	unsigned long flags;
+	unsigned char chip;
+
+#ifdef AURORA_DEBUG
+printk("aurora_write: start %d\n",count);
+#endif
+	if (aurora_paranoia_check(port, tty->device, "aurora_write"))
+		return 0;
+		
+	chip=AURORA_CD180(port_No(port));
+	
+	bp = port_Board(port);
+
+	if (!tty || !port->xmit_buf || !tmp_buf)
+		return 0;
+
+	if (from_user)
+		down(&tmp_buf_sem);
+
+	save_flags(flags);
+	while (1) {
+		cli();		
+		c = MIN(count, MIN(SERIAL_XMIT_SIZE - port->xmit_cnt - 1,
+				   SERIAL_XMIT_SIZE - port->xmit_head));
+		if (c <= 0)
+			break;
+
+		if (from_user) {
+			copy_from_user(tmp_buf, buf, c);
+			c = MIN(c, MIN(SERIAL_XMIT_SIZE - port->xmit_cnt - 1,
+				       SERIAL_XMIT_SIZE - port->xmit_head));
+			memcpy(port->xmit_buf + port->xmit_head, tmp_buf, c);
+		} else
+			memcpy(port->xmit_buf + port->xmit_head, buf, c);
+		port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
+		port->xmit_cnt += c;
+		restore_flags(flags);
+		buf += c;
+		count -= c;
+		total += c;
+	}
+	if (from_user)
+		up(&tmp_buf_sem);
+	if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
+	    !(port->SRER & SRER_TXRDY)) {
+		port->SRER |= SRER_TXRDY;
+		bp->r[chip]->r[CD180_CAR]=port_No(port)&7;
+		udelay(1);
+		bp->r[chip]->r[CD180_SRER]=port->SRER;
+	}
+	restore_flags(flags);
+#ifdef AURORA_DEBUG
+printk("aurora_write: end %d\n",total);
+#endif
+	return total;
+}
+
+static void aurora_put_char(struct tty_struct * tty, unsigned char ch)
+{
+	struct Aurora_port *port = (struct Aurora_port *)tty->driver_data;
+	unsigned long flags;
+
+#ifdef AURORA_DEBUG
+printk("aurora_put_char: start %c\n",ch);
+#endif
+	if (aurora_paranoia_check(port, tty->device, "aurora_put_char"))
+		return;
+
+	if (!tty || !port->xmit_buf)
+		return;
+
+	save_flags(flags); cli();
+	
+	if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) {
+		restore_flags(flags);
+		return;
+	}
+
+	port->xmit_buf[port->xmit_head++] = ch;
+	port->xmit_head &= SERIAL_XMIT_SIZE - 1;
+	port->xmit_cnt++;
+	restore_flags(flags);
+#ifdef AURORA_DEBUG
+printk("aurora_put_char: end\n");
+#endif
+}
+
+static void aurora_flush_chars(struct tty_struct * tty)
+{
+	struct Aurora_port *port = (struct Aurora_port *)tty->driver_data;
+	unsigned long flags;
+	unsigned char chip;
+
+/*#ifdef AURORA_DEBUG
+printk("aurora_flush_chars: start\n");
+#endif*/
+	if (aurora_paranoia_check(port, tty->device, "aurora_flush_chars"))
+		return;
+		
+	chip=AURORA_CD180(port_No(port));
+	
+	if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
+	    !port->xmit_buf)
+		return;
+
+	save_flags(flags); cli();
+	port->SRER |= SRER_TXRDY;
+	port_Board(port)->r[chip]->r[CD180_CAR]=port_No(port)&7;
+	udelay(1);
+	port_Board(port)->r[chip]->r[CD180_SRER]=port->SRER;
+	restore_flags(flags);
+/*#ifdef AURORA_DEBUG
+printk("aurora_flush_chars: end\n");
+#endif*/
+}
+
+static int aurora_write_room(struct tty_struct * tty)
+{
+	struct Aurora_port *port = (struct Aurora_port *)tty->driver_data;
+	int	ret;
+
+#ifdef AURORA_DEBUG
+printk("aurora_write_room: start\n");
+#endif
+	if (aurora_paranoia_check(port, tty->device, "aurora_write_room"))
+		return 0;
+
+	ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1;
+	if (ret < 0)
+		ret = 0;
+#ifdef AURORA_DEBUG
+printk("aurora_write_room: end\n");
+#endif
+	return ret;
+}
+
+static int aurora_chars_in_buffer(struct tty_struct *tty)
+{
+	struct Aurora_port *port = (struct Aurora_port *)tty->driver_data;
+				
+	if (aurora_paranoia_check(port, tty->device, "aurora_chars_in_buffer"))
+		return 0;
+	
+	return port->xmit_cnt;
+}
+
+static void aurora_flush_buffer(struct tty_struct *tty)
+{
+	struct Aurora_port *port = (struct Aurora_port *)tty->driver_data;
+	unsigned long flags;
+
+#ifdef AURORA_DEBUG
+printk("aurora_flush_buffer: start\n");
+#endif
+	if (aurora_paranoia_check(port, tty->device, "aurora_flush_buffer"))
+		return;
+
+	save_flags(flags); cli();
+	port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
+	restore_flags(flags);
+	
+	wake_up_interruptible(&tty->write_wait);
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+	    tty->ldisc.write_wakeup)
+		(tty->ldisc.write_wakeup)(tty);
+#ifdef AURORA_DEBUG
+printk("aurora_flush_buffer: end\n");
+#endif
+}
+
+static int aurora_get_modem_info(struct Aurora_port * port, unsigned int *value)
+{
+	struct Aurora_board * bp;
+	unsigned char status,chip;
+	unsigned int result;
+	unsigned long flags;
+
+#ifdef AURORA_DEBUG
+printk("aurora_get_modem_info: start\n");
+#endif
+	chip=AURORA_CD180(port_No(port));
+
+	bp = port_Board(port);
+	save_flags(flags); cli();
+	bp->r[chip]->r[CD180_CAR]=port_No(port)&7;
+	udelay(1);
+	status = bp->r[chip]->r[CD180_MSVR];
+	result = 0/*bp->r[chip]->r[AURORA_RI] & (1u << port_No(port)) ? 0 : TIOCM_RNG*/;
+	restore_flags(flags);
+	result |= ((status & bp->RTS) ? TIOCM_RTS : 0)
+		| ((status & bp->DTR) ? TIOCM_DTR : 0)
+		| ((status & MSVR_CD)  ? TIOCM_CAR : 0)
+		| ((status & MSVR_DSR) ? TIOCM_DSR : 0)
+		| ((status & MSVR_CTS) ? TIOCM_CTS : 0);
+	put_user(result,(unsigned long *) value);
+#ifdef AURORA_DEBUG
+printk("aurora_get_modem_info: end\n");
+#endif
+	return 0;
+}
+
+static int aurora_set_modem_info(struct Aurora_port * port, unsigned int cmd,
+			     unsigned int *value)
+{
+	int error;
+	unsigned int arg;
+	unsigned long flags;
+	struct Aurora_board *bp = port_Board(port);
+	unsigned char chip;
+
+#ifdef AURORA_DEBUG
+printk("aurora_set_modem_info: start\n");
+#endif
+	error = get_user(arg, value);
+	if (error) 
+		return error;
+	chip=AURORA_CD180(port_No(port));
+	switch (cmd) {
+	 case TIOCMBIS: 
+		if (arg & TIOCM_RTS) 
+			port->MSVR |= bp->RTS;
+		if (arg & TIOCM_DTR)
+			port->MSVR |= bp->DTR;
+		break;
+	case TIOCMBIC:
+		if (arg & TIOCM_RTS)
+			port->MSVR &= ~bp->RTS;
+		if (arg & TIOCM_DTR)
+			port->MSVR &= ~bp->DTR;
+		break;
+	case TIOCMSET:
+		port->MSVR = (arg & TIOCM_RTS) ? (port->MSVR | bp->RTS) : 
+					         (port->MSVR & ~bp->RTS);
+		port->MSVR = (arg & TIOCM_DTR) ? (port->MSVR | bp->RTS) :
+						 (port->MSVR & ~bp->RTS);
+		break;
+	 default:
+		return -EINVAL;
+	}
+	save_flags(flags); cli();
+	bp->r[chip]->r[CD180_CAR]=port_No(port)&7;
+	udelay(1);
+	bp->r[chip]->r[CD180_MSVR]=port->MSVR;
+	restore_flags(flags);
+#ifdef AURORA_DEBUG
+printk("aurora_set_modem_info: end\n");
+#endif
+	return 0;
+}
+
+extern inline void aurora_send_break(struct Aurora_port * port, unsigned long length)
+{
+	struct Aurora_board *bp = port_Board(port);
+	unsigned long flags;
+	unsigned char chip;
+	
+#ifdef AURORA_DEBUG
+printk("aurora_send_break: start\n");
+#endif
+	chip=AURORA_CD180(port_No(port));
+	
+	save_flags(flags); cli();
+	port->break_length = AURORA_TPS / HZ * length;
+	port->COR2 |= COR2_ETC;
+	port->SRER  |= SRER_TXRDY;
+	bp->r[chip]->r[CD180_CAR]=port_No(port)&7;
+	udelay(1);
+	bp->r[chip]->r[CD180_COR2]=port->COR2;
+	bp->r[chip]->r[CD180_SRER]=port->SRER;
+	aurora_wait_CCR(bp->r[chip]);
+	bp->r[chip]->r[CD180_CCR]=CCR_CORCHG2;
+	aurora_wait_CCR(bp->r[chip]);
+	restore_flags(flags);
+#ifdef AURORA_DEBUG
+printk("aurora_send_break: end\n");
+#endif
+}
+
+extern inline int aurora_set_serial_info(struct Aurora_port * port,
+				     struct serial_struct * newinfo)
+{
+	struct serial_struct tmp;
+	struct Aurora_board *bp = port_Board(port);
+	int change_speed;
+	unsigned long flags;
+	int error;
+
+#ifdef AURORA_DEBUG
+printk("aurora_set_serial_info: start\n");
+#endif
+	error = verify_area(VERIFY_READ, (void *) newinfo, sizeof(tmp));
+	if (error)
+		return error;
+	copy_from_user(&tmp, newinfo, sizeof(tmp));
+	
+#if 0	
+	if ((tmp.irq != bp->irq) ||
+	    (tmp.port != bp->base) ||
+	    (tmp.type != PORT_CIRRUS) ||
+	    (tmp.baud_base != (bp->oscfreq + CD180_TPC/2) / CD180_TPC) ||
+	    (tmp.custom_divisor != 0) ||
+	    (tmp.xmit_fifo_size != CD180_NFIFO) ||
+	    (tmp.flags & ~AURORA_LEGAL_FLAGS))
+		return -EINVAL;
+#endif	
+	
+	change_speed = ((port->flags & ASYNC_SPD_MASK) !=
+			(tmp.flags & ASYNC_SPD_MASK));
+	
+	if (!suser()) {
+		if ((tmp.close_delay != port->close_delay) ||
+		    (tmp.closing_wait != port->closing_wait) ||
+		    ((tmp.flags & ~ASYNC_USR_MASK) !=
+		     (port->flags & ~ASYNC_USR_MASK)))  
+			return -EPERM;
+		port->flags = ((port->flags & ~ASYNC_USR_MASK) |
+			       (tmp.flags & ASYNC_USR_MASK));
+	} else  {
+		port->flags = ((port->flags & ~ASYNC_FLAGS) |
+			       (tmp.flags & ASYNC_FLAGS));
+		port->close_delay = tmp.close_delay;
+		port->closing_wait = tmp.closing_wait;
+	}
+	if (change_speed)  {
+		save_flags(flags); cli();
+		aurora_change_speed(bp, port);
+		restore_flags(flags);
+	}
+#ifdef AURORA_DEBUG
+printk("aurora_set_serial_info: end\n");
+#endif
+	return 0;
+}
+
+extern inline int aurora_get_serial_info(struct Aurora_port * port,
+				     struct serial_struct * retinfo)
+{
+	struct serial_struct tmp;
+	struct Aurora_board *bp = port_Board(port);
+	int error;
+	
+#ifdef AURORA_DEBUG
+printk("aurora_get_serial_info: start\n");
+#endif
+	error = verify_area(VERIFY_WRITE, (void *) retinfo, sizeof(tmp));
+	if (error)
+		return error;
+	
+	memset(&tmp, 0, sizeof(tmp));
+	tmp.type = PORT_CIRRUS;
+	tmp.line = port - aurora_port;
+	tmp.port = 0;
+	tmp.irq  = bp->irq;
+	tmp.flags = port->flags;
+	tmp.baud_base = (bp->oscfreq + CD180_TPC/2) / CD180_TPC;
+	tmp.close_delay = port->close_delay * HZ/100;
+	tmp.closing_wait = port->closing_wait * HZ/100;
+	tmp.xmit_fifo_size = CD180_NFIFO;
+	copy_to_user(retinfo, &tmp, sizeof(tmp));
+#ifdef AURORA_DEBUG
+printk("aurora_get_serial_info: end\n");
+#endif
+	return 0;
+}
+
+static int aurora_ioctl(struct tty_struct * tty, struct file * filp, 
+		    unsigned int cmd, unsigned long arg)
+		    
+{
+	struct Aurora_port *port = (struct Aurora_port *)tty->driver_data;
+	int error;
+	int retval;
+
+#ifdef AURORA_DEBUG
+printk("aurora_ioctl: start\n");
+#endif
+	if (aurora_paranoia_check(port, tty->device, "aurora_ioctl"))
+		return -ENODEV;
+	
+	switch (cmd) {
+	 case TCSBRK:	/* SVID version: non-zero arg --> no break */
+		retval = tty_check_change(tty);
+		if (retval)
+			return retval;
+		tty_wait_until_sent(tty, 0);
+		if (!arg)
+			aurora_send_break(port, HZ/4);	/* 1/4 second */
+		return 0;
+	 case TCSBRKP:	/* support for POSIX tcsendbreak() */
+		retval = tty_check_change(tty);
+		if (retval)
+			return retval;
+		tty_wait_until_sent(tty, 0);
+		aurora_send_break(port, arg ? arg*(HZ/10) : HZ/4);
+		return 0;
+	 case TIOCGSOFTCAR:
+		error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long));
+		if (error)
+			return error;
+		put_user(C_CLOCAL(tty) ? 1 : 0,
+			    (unsigned long *) arg);
+		return 0;
+	 case TIOCSSOFTCAR:
+		retval = get_user(arg,(unsigned long *) arg);
+		if (retval)
+			return retval;
+		tty->termios->c_cflag =
+			((tty->termios->c_cflag & ~CLOCAL) |
+			(arg ? CLOCAL : 0));
+		return 0;
+	 case TIOCMGET:
+		error = verify_area(VERIFY_WRITE, (void *) arg,
+				    sizeof(unsigned int));
+		if (error)
+			return error;
+		return aurora_get_modem_info(port, (unsigned int *) arg);
+	 case TIOCMBIS:
+	 case TIOCMBIC:
+	 case TIOCMSET:
+		return aurora_set_modem_info(port, cmd, (unsigned int *) arg);
+	 case TIOCGSERIAL:	
+		return aurora_get_serial_info(port, (struct serial_struct *) arg);
+	 case TIOCSSERIAL:	
+		return aurora_set_serial_info(port, (struct serial_struct *) arg);
+	 default:
+		return -ENOIOCTLCMD;
+	}
+#ifdef AURORA_DEBUG
+printk("aurora_ioctl: end\n");
+#endif
+	return 0;
+}
+
+static void aurora_throttle(struct tty_struct * tty)
+{
+	struct Aurora_port *port = (struct Aurora_port *)tty->driver_data;
+	struct Aurora_board *bp;
+	unsigned long flags;
+	unsigned char chip;
+
+#ifdef AURORA_DEBUG
+printk("aurora_throttle: start\n");
+#endif
+	if (aurora_paranoia_check(port, tty->device, "aurora_throttle"))
+		return;
+	
+	bp = port_Board(port);
+	chip=AURORA_CD180(port_No(port));
+	
+	save_flags(flags); cli();
+	port->MSVR &= ~bp->RTS;
+	bp->r[chip]->r[CD180_CAR]=port_No(port)&7;
+	udelay(1);
+	if (I_IXOFF(tty))  {
+		aurora_wait_CCR(bp->r[chip]);
+		bp->r[chip]->r[CD180_CCR]=CCR_SSCH2;
+		aurora_wait_CCR(bp->r[chip]);
+	}
+	bp->r[chip]->r[CD180_MSVR]=port->MSVR;
+	restore_flags(flags);
+#ifdef AURORA_DEBUG
+printk("aurora_throttle: end\n");
+#endif
+}
+
+static void aurora_unthrottle(struct tty_struct * tty)
+{
+	struct Aurora_port *port = (struct Aurora_port *)tty->driver_data;
+	struct Aurora_board *bp;
+	unsigned long flags;
+	unsigned char chip;
+
+#ifdef AURORA_DEBUG
+printk("aurora_unthrottle: start\n");
+#endif
+	if (aurora_paranoia_check(port, tty->device, "aurora_unthrottle"))
+		return;
+	
+	bp = port_Board(port);
+	
+	chip=AURORA_CD180(port_No(port));
+	
+	save_flags(flags); cli();
+	port->MSVR |= bp->RTS;
+	bp->r[chip]->r[CD180_CAR]=port_No(port)&7;
+	udelay(1);
+	if (I_IXOFF(tty))  {
+		aurora_wait_CCR(bp->r[chip]);
+		bp->r[chip]->r[CD180_CCR]=CCR_SSCH1;
+		aurora_wait_CCR(bp->r[chip]);
+	}
+	bp->r[chip]->r[CD180_MSVR]=port->MSVR;
+	restore_flags(flags);
+#ifdef AURORA_DEBUG
+printk("aurora_unthrottle: end\n");
+#endif
+}
+
+static void aurora_stop(struct tty_struct * tty)
+{
+	struct Aurora_port *port = (struct Aurora_port *)tty->driver_data;
+	struct Aurora_board *bp;
+	unsigned long flags;
+	unsigned char chip;
+
+#ifdef AURORA_DEBUG
+printk("aurora_stop: start\n");
+#endif
+	if (aurora_paranoia_check(port, tty->device, "aurora_stop"))
+		return;
+	
+	bp = port_Board(port);
+	
+	chip=AURORA_CD180(port_No(port));
+	
+	save_flags(flags); cli();
+	port->SRER &= ~SRER_TXRDY;
+	bp->r[chip]->r[CD180_CAR]=port_No(port)&7;
+	udelay(1);
+	bp->r[chip]->r[CD180_SRER]=port->SRER;
+	restore_flags(flags);
+#ifdef AURORA_DEBUG
+printk("aurora_stop: end\n");
+#endif
+}
+
+static void aurora_start(struct tty_struct * tty)
+{
+	struct Aurora_port *port = (struct Aurora_port *)tty->driver_data;
+	struct Aurora_board *bp;
+	unsigned long flags;
+	unsigned char chip;
+
+#ifdef AURORA_DEBUG
+printk("aurora_start: start\n");
+#endif
+	if (aurora_paranoia_check(port, tty->device, "aurora_start"))
+		return;
+	
+	bp = port_Board(port);
+	
+	chip=AURORA_CD180(port_No(port));
+	
+	save_flags(flags); cli();
+	if (port->xmit_cnt && port->xmit_buf && !(port->SRER & SRER_TXRDY))  {
+		port->SRER |= SRER_TXRDY;
+		bp->r[chip]->r[CD180_CAR]=port_No(port)&7;
+		udelay(1);
+		bp->r[chip]->r[CD180_SRER]=port->SRER;
+	}
+	restore_flags(flags);
+#ifdef AURORA_DEBUG
+printk("aurora_start: end\n");
+#endif
+}
+
+/*
+ * This routine is called from the scheduler tqueue when the interrupt
+ * routine has signalled that a hangup has occurred.  The path of
+ * hangup processing is:
+ *
+ * 	serial interrupt routine -> (scheduler tqueue) ->
+ * 	do_aurora_hangup() -> tty->hangup() -> aurora_hangup()
+ * 
+ */
+static void do_aurora_hangup(void *private_)
+{
+	struct Aurora_port	*port = (struct Aurora_port *) private_;
+	struct tty_struct	*tty;
+
+#ifdef AURORA_DEBUG
+printk("do_aurora_hangup: start\n");
+#endif
+	tty = port->tty;
+	if (!tty)
+		return;
+
+	tty_hangup(tty);
+#ifdef AURORA_DEBUG
+printk("do_aurora_hangup: end\n");
+#endif
+}
+
+static void aurora_hangup(struct tty_struct * tty)
+{
+	struct Aurora_port *port = (struct Aurora_port *)tty->driver_data;
+	struct Aurora_board *bp;
+				
+#ifdef AURORA_DEBUG
+printk("aurora_hangup: start\n");
+#endif
+	if (aurora_paranoia_check(port, tty->device, "aurora_hangup"))
+		return;
+	
+	bp = port_Board(port);
+	
+	aurora_shutdown_port(bp, port);
+	port->event = 0;
+	port->count = 0;
+	port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
+	port->tty = 0;
+	wake_up_interruptible(&port->open_wait);
+#ifdef AURORA_DEBUG
+printk("aurora_hangup: end\n");
+#endif
+}
+
+static void aurora_set_termios(struct tty_struct * tty, struct termios * old_termios)
+{
+	struct Aurora_port *port = (struct Aurora_port *)tty->driver_data;
+	unsigned long flags;
+
+#ifdef AURORA_DEBUG
+printk("aurora_set_termios: start\n");
+#endif
+	if (aurora_paranoia_check(port, tty->device, "aurora_set_termios"))
+		return;
+	
+	if (tty->termios->c_cflag == old_termios->c_cflag &&
+	    tty->termios->c_iflag == old_termios->c_iflag)
+		return;
+
+	save_flags(flags); cli();
+	aurora_change_speed(port_Board(port), port);
+	restore_flags(flags);
+
+	if ((old_termios->c_cflag & CRTSCTS) &&
+	    !(tty->termios->c_cflag & CRTSCTS)) {
+		tty->hw_stopped = 0;
+		aurora_start(tty);
+	}
+#ifdef AURORA_DEBUG
+printk("aurora_set_termios: end\n");
+#endif
+}
+
+static void do_aurora_bh(void)
+{
+	 run_task_queue(&tq_aurora);
+}
+
+static void do_softint(void *private_)
+{
+	struct Aurora_port	*port = (struct Aurora_port *) private_;
+	struct tty_struct	*tty;
+
+#ifdef AURORA_DEBUG
+printk("do_softint: start\n");
+#endif
+	if(!(tty = port->tty)) 
+		return;
+
+	if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) {
+		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+		    tty->ldisc.write_wakeup)
+			(tty->ldisc.write_wakeup)(tty);
+		wake_up_interruptible(&tty->write_wait);
+	}
+#ifdef AURORA_DEBUG
+printk("do_softint: end\n");
+#endif
+}
+
+static int aurora_init_drivers(void)
+{
+	int error;
+	int i;
+
+#ifdef AURORA_DEBUG
+printk("aurora_init_drivers: start\n");
+#endif
+	if (!(tmp_buf = (unsigned char *) get_free_page(GFP_KERNEL))) {
+		printk(KERN_ERR "aurora: Couldn't get free page.\n");
+		return 1;
+	}
+	init_bh(AURORA_BH, do_aurora_bh);
+/*	memset(IRQ_to_board, 0, sizeof(IRQ_to_board));*/
+	memset(&aurora_driver, 0, sizeof(aurora_driver));
+	aurora_driver.magic = TTY_DRIVER_MAGIC;
+	aurora_driver.name = "ttyA";
+	aurora_driver.major = AURORA_MAJOR;
+	aurora_driver.num = AURORA_TNPORTS;
+	aurora_driver.type = TTY_DRIVER_TYPE_SERIAL;
+	aurora_driver.subtype = AURORA_TYPE_NORMAL;
+	aurora_driver.init_termios = tty_std_termios;
+	aurora_driver.init_termios.c_cflag =
+		B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+	aurora_driver.flags = TTY_DRIVER_REAL_RAW;
+	aurora_driver.refcount = &aurora_refcount;
+	aurora_driver.table = aurora_table;
+	aurora_driver.termios = aurora_termios;
+	aurora_driver.termios_locked = aurora_termios_locked;
+
+	aurora_driver.open  = aurora_open;
+	aurora_driver.close = aurora_close;
+	aurora_driver.write = aurora_write;
+	aurora_driver.put_char = aurora_put_char;
+	aurora_driver.flush_chars = aurora_flush_chars;
+	aurora_driver.write_room = aurora_write_room;
+	aurora_driver.chars_in_buffer = aurora_chars_in_buffer;
+	aurora_driver.flush_buffer = aurora_flush_buffer;
+	aurora_driver.ioctl = aurora_ioctl;
+	aurora_driver.throttle = aurora_throttle;
+	aurora_driver.unthrottle = aurora_unthrottle;
+	aurora_driver.set_termios = aurora_set_termios;
+	aurora_driver.stop = aurora_stop;
+	aurora_driver.start = aurora_start;
+	aurora_driver.hangup = aurora_hangup;
+
+	if ((error = tty_register_driver(&aurora_driver)))  {
+		free_page((unsigned long)tmp_buf);
+		printk(KERN_ERR "aurora: Couldn't register aurora driver, error = %d\n",
+		       error);
+		return 1;
+	}
+	
+	memset(aurora_port, 0, sizeof(aurora_port));
+	for (i = 0; i < AURORA_TNPORTS; i++)  {
+		aurora_port[i].normal_termios  = aurora_driver.init_termios;
+		aurora_port[i].magic = AURORA_MAGIC;
+		aurora_port[i].tqueue.routine = do_softint;
+		aurora_port[i].tqueue.data = &aurora_port[i];
+		aurora_port[i].tqueue_hangup.routine = do_aurora_hangup;
+		aurora_port[i].tqueue_hangup.data = &aurora_port[i];
+		aurora_port[i].close_delay = 50 * HZ/100;
+		aurora_port[i].closing_wait = 3000 * HZ/100;
+		init_waitqueue_head(&aurora_port[i].open_wait);
+		init_waitqueue_head(&aurora_port[i].close_wait);
+	}
+#ifdef AURORA_DEBUG
+printk("aurora_init_drivers: end\n");
+#endif
+	return 0;
+}
+
+static void aurora_release_drivers(void)
+{
+#ifdef AURORA_DEBUG
+printk("aurora_release_drivers: start\n");
+#endif
+	free_page((unsigned long)tmp_buf);
+	tty_unregister_driver(&aurora_driver);
+#ifdef AURORA_DEBUG
+printk("aurora_release_drivers: end\n");
+#endif
+}
+
+#ifndef MODULE
+/*
+ * Called at boot time.
+ *
+ * You can specify IO base for up to RC_NBOARD cards,
+ * using line "riscom8=0xiobase1,0xiobase2,.." at LILO prompt.
+ * Note that there will be no probing at default
+ * addresses in this case.
+ *
+ */
+__init_func(void aurora_setup(char *str, int *ints))
+{
+	int i;
+
+	for(i=0;(i<ints[0])&&(i<4);i++) {
+		if (ints[i+1]) irqs[i]=ints[i+1];
+		}
+}
+
+__init_func(int aurora_init(void))
+#else
+int aurora_init(void)
+#endif
+{
+int found;
+int grrr,i;
+
+printk(KERN_INFO "aurora: Driver starting.\n");
+if(aurora_init_drivers())return -EIO;
+found=aurora_probe();
+if(!found){
+  aurora_release_drivers();
+  printk(KERN_INFO "aurora: No Aurora Multiport boards detected.\n");
+  return -EIO;
+  } else {
+  printk(KERN_INFO "aurora: %d boards found.\n",found);
+  }
+for(i=0;i<found;i++){
+	if ((grrr = aurora_setup_board(&aurora_board[i]))) {
+		#ifdef AURORA_DEBUG
+		printk(KERN_ERR "aurora_init: error aurora_setup_board ret %d\n",grrr);
+		#endif
+		return grrr;
+	}
+}
+return 0;
+}
+
+#ifdef MODULE
+int irq  = 0;
+int irq1 = 0;
+int irq2 = 0;
+int irq3 = 0;
+MODULE_PARM(irq , "i");
+MODULE_PARM(irq1, "i");
+MODULE_PARM(irq2, "i");
+MODULE_PARM(irq3, "i");
+
+int init_module(void) 
+{
+	if (irq ) irqs[0]=irq ;
+	if (irq1) irqs[1]=irq1;
+	if (irq2) irqs[2]=irq2;
+	if (irq3) irqs[3]=irq3;
+	return aurora_init();
+}
+	
+void cleanup_module(void)
+{
+	int i;
+	
+#ifdef AURORA_DEBUG
+printk("cleanup_module: aurora_release_drivers\n");
+#endif;
+
+	aurora_release_drivers();
+	for (i = 0; i < AURORA_NBOARD; i++)
+		if (aurora_board[i].flags & AURORA_BOARD_PRESENT) {
+			aurora_shutdown_board(&aurora_board[i]);
+			aurora_release_io_range(&aurora_board[i]);
+		}
+}
+#endif /* MODULE */
diff -u --recursive --new-file v2.3.3/linux/drivers/sbus/char/aurora.h linux/drivers/sbus/char/aurora.h
--- v2.3.3/linux/drivers/sbus/char/aurora.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sbus/char/aurora.h	Mon May 31 22:08:10 1999
@@ -0,0 +1,278 @@
+/*
+ *	linux/drivers/sbus/char/aurora.h -- Aurora multiport driver
+ *
+ *	Copyright (c) 1999 by Oliver Aldulea (oli@bv.ro)
+ *
+ *	This code is based on the RISCom/8 multiport serial driver written
+ *	by Dmitry Gorodchanin (pgmdsg@ibi.com), based on the Linux serial
+ *	driver, written by Linus Torvalds, Theodore T'so and others.
+ *	The Aurora multiport programming info was obtained mainly from the
+ *	Cirrus Logic CD180 documentation (available on the web), and by
+ *	doing heavy tests on the board. Many thanks to Eddie C. Dost for the
+ *	help on the sbus interface.
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *	Revision 1.0
+ *
+ *	This is the first public release.
+ *
+ *	This version needs a lot of feedback. This is the version that works
+ *	with _my_ board. My board is model 1600se, revision '@(#)1600se.fth
+ *	1.2 3/28/95 1'. The driver might work with your board, but I do not
+ *	guarantee it. If you have _any_ type of board, I need to know if the
+ *	driver works or not, I need to know exactly your board parameters
+ *	(get them with 'cd /proc/openprom/iommu/sbus/sio16/; ls *; cat *')
+ *	Also, I need your board revision code, which is written on the board.
+ *	Send me the output of my driver too (it outputs through klogd).
+ *
+ *	If the driver does not work, you can try enabling the debug options
+ *	to see what's wrong or what should be done.
+ *
+ *	I'm sorry about the alignment of the code. It was written in a
+ *	128x48 environment.
+ *
+ *	I must say that I do not like Aurora Technologies' policy. I asked
+ *	them to help me do this driver faster, but they ended by something
+ *	like "don't call us, we'll call you", and I never heard anything
+ *	from them. They told me "knowing the way the board works, I don't
+ *	doubt you and others on the net will make the driver."
+ *	The truth about this board is that it has nothing intelligent on it.
+ *	If you want to say to somebody what kind of board you have, say that
+ *	it uses Cirrus Logic processors (CD180). The power of the board is
+ *	in those two chips. The rest of the board is the interface to the
+ *	sbus and to the peripherals. Still, they did something smart: they
+ *	reversed DTR and RTS to make on-board automatic hardware flow
+ *	control usable.
+ *	Thanks to Aurora Technologies for wasting my time, nerves and money.
+ */
+
+#ifndef __LINUX_AURORA_H
+#define __LINUX_AURORA_H
+
+#include <linux/serial.h>
+
+#ifdef __KERNEL__
+
+/* This is the number of boards to support. I've only tested this driver with
+ * one board, so it might not work.
+ */
+#define AURORA_NBOARD 1
+
+/* Useful ? Yes. But you can safely comment the warnings if they annoy you
+ * (let me say that again: the warnings in the code, not this define). 
+ */
+#define AURORA_PARANOIA_CHECK
+
+/* Well, after many lost nights, I found that the IRQ for this board is
+ * selected from four built-in values by writing some bits in the
+ * configuration register. This causes a little problem to occur: which
+ * IRQ to select ? Which one is the best for the user ? Well, I finally
+ * decided for the following algorithm: if the "bintr" value is not acceptable
+ * (not within type_1_irq[], then test the "intr" value, if that fails too,
+ * try each value from type_1_irq until succeded. Hope it's ok.
+ * You can safely reorder the irq's.
+ */
+#define TYPE_1_IRQS 4
+unsigned char type_1_irq[TYPE_1_IRQS] = {
+	3, 5, 9, 13
+	};
+/* I know something about another method of interrupt setting, but not enough.
+ * Also, this is for another type of board, so I first have to learn how to
+ * detect it.
+#define TYPE_2_IRQS 3
+unsigned char type_2_irq[TYPE_2_IRQS] = {
+	0, 0, 0 ** could anyone find these for me ? (see AURORA_ALLIRQ below) **
+	};
+unsigned char type_2_mask[TYPE_2_IRQS] = {
+	32, 64, 128
+	};
+*/
+
+/* The following section should only be modified by those who know what
+ * they're doing (or don't, but want to help with some feedback). Modifying
+ * anything raises a _big_ probability for your system to hang, but the
+ * sacrifice worths. (I sacrificed my ext2fs many, many times...)
+ */
+
+/* This one tries to dump to console the name of almost every function called,
+ * and many other debugging info.
+ */
+#undef AURORA_DEBUG
+
+/* These are the most dangerous and useful defines. They do printk() during
+ * the interrupt processing routine(s), so if you manage to get "flooded" by
+ * irq's, start thinking about the "Power off/on" button...
+ */
+#undef AURORA_INTNORM	/* This one enables the "normal" messages, but some
+			 * of them cause flood, so I preffered putting
+			 * them under a define */
+#undef AURORA_INT_DEBUG /* This one is really bad. */
+
+/* Here's something helpful: after n irq's, the board will be disabled. This
+ * prevents irq flooding during debug (no need to think about power
+ * off/on anymore...)
+ */
+#define AURORA_FLOODPRO	10
+
+/* This one helps finding which irq the board calls, in case of a strange/
+ * unsupported board. AURORA_INT_DEBUG should be enabled, because I don't
+ * think /proc/interrupts or any command will be available in case of an irq
+ * flood... "allirq" is the list of all free irq's.
+ */
+/*
+#define AURORA_ALLIRQ 6
+int allirq[AURORA_ALLIRQ]={
+	2,3,5,7,9,13
+	};
+*/
+
+/* These must not be modified. These values are assumed during the code for
+ * performance optimisations.
+ */
+#define AURORA_NCD180 2 /* two chips per board */
+#define AURORA_NPORT 8  /* 8 ports per chip */
+
+/* several utilities */
+#define AURORA_BOARD(line)	(((line) >> 4) & 0x01)
+#define AURORA_CD180(line)	(((line) >> 3) & 0x01)
+#define AURORA_PORT(line)	((line) & 15)
+
+#define AURORA_TNPORTS (AURORA_NBOARD*AURORA_NCD180*AURORA_NPORT)
+
+/* Ticks per sec. Used for setting receiver timeout and break length */
+#define AURORA_TPS		4000
+
+#define AURORA_MAGIC	0x0A18
+
+/* Yeah, after heavy testing I decided it must be 6.
+ * Sure, You can change it if needed.
+ */
+#define AURORA_RXFIFO		6	/* Max. receiver FIFO size (1-8) */
+
+#define AURORA_RXTH		7
+
+struct aurora_reg1 {
+	__volatile__ unsigned char r;
+	};
+
+struct aurora_reg128 {
+	__volatile__ unsigned char r[128];
+	};
+	
+struct aurora_reg4 {
+	__volatile__ unsigned char r[4];
+	};
+
+struct Aurora_board {
+	unsigned long		flags;
+	struct aurora_reg1	* r0;	/* This is the board configuration
+					 * register (write-only). */
+	struct aurora_reg128	* r[2];	/* These are the registers for the
+					 * two chips. */
+	struct aurora_reg4	* r3;	/* These are used for hardware-based
+					 * acknowledge. Software-based ack is
+					 * not supported by CD180. */
+	unsigned int		oscfreq; /* The on-board oscillator
+					  * frequency, in Hz. */
+	unsigned char		irq;
+#ifdef MODULE
+	signed char		count;	/* counts the use of the board */
+#endif
+	/* Values for the dtr_rts swapped mode. */
+	unsigned char		DTR;
+	unsigned char		RTS;
+	unsigned char		MSVDTR;
+	unsigned char		MSVRTS;
+	/* Values for hardware acknowledge. */
+	unsigned char		ACK_MINT,ACK_TINT,ACK_RINT;
+};
+
+/* Board configuration register */
+#define AURORA_CFG_ENABLE_IO	8
+#define AURORA_CFG_ENABLE_IRQ	4
+
+/* Board flags */
+#define AURORA_BOARD_PRESENT		0x00000001
+#define AURORA_BOARD_ACTIVE		0x00000002
+#define AURORA_BOARD_TYPE_2		0x00000004	/* don't know how to
+							 * detect this yet */
+#define AURORA_BOARD_DTR_FLOW_OK	0x00000008
+
+/* The story goes like this: Cirrus programmed the CD-180 chip to do automatic
+ * hardware flow control, and do it using CTS and DTR. CTS is ok, but, if you
+ * have a modem and the chip drops DTR, then the modem will drop the carrier
+ * (ain't that cute...). Luckily, the guys at Aurora decided to swap DTR and
+ * RTS, which makes the flow control usable. I hope that all the boards made
+ * by Aurora have these two signals swapped. If your's doesn't but you have a
+ * breakout box, you can try to reverse them yourself, then set the following
+ * flag.
+ */
+#undef AURORA_FORCE_DTR_FLOW
+
+/* In fact, a few more words have to be said about hardware flow control.
+ * This driver handles "output" flow control through the on-board facility
+ * CTS Auto Enable. For the "input" flow control there are two cases when
+ * the flow should be controlled. The first case is when the kernel is so
+ * busy that it cannot process IRQ's in time; this flow control can only be
+ * activated by the on-board chip, and if the board has RTS and DTR swapped,
+ * this facility is usable. The second case is when the application is so
+ * busy that it cannot receive bytes from the kernel, and this flow must be
+ * activated by software. This second case is not yet implemented in this
+ * driver. Unfortunately, I estimate that the second case is the one that
+ * occurs the most.
+ */
+
+
+struct Aurora_port {
+	int			magic;
+	int			baud_base;
+	int			flags;
+	struct tty_struct 	* tty;
+	int			count;
+	int			blocked_open;
+	int			event;
+	int			timeout;
+	int			close_delay;
+	long			session;
+	long			pgrp;
+	unsigned char 		* xmit_buf;
+	int			custom_divisor;
+	int			xmit_head;
+	int			xmit_tail;
+	int			xmit_cnt;
+	struct termios          normal_termios;
+	wait_queue_head_t	open_wait;
+	wait_queue_head_t	close_wait;
+	struct tq_struct	tqueue;
+	struct tq_struct	tqueue_hangup;
+	short			wakeup_chars;
+	short			break_length;
+	unsigned short		closing_wait;
+	unsigned char		mark_mask;
+	unsigned char		SRER;
+	unsigned char		MSVR;
+	unsigned char		COR2;
+#ifdef AURORA_REPORT_OVERRUN
+	unsigned long		overrun;
+#endif	
+#ifdef AURORA_REPORT_FIFO
+	unsigned long		hits[10];
+#endif
+};
+
+#endif
+#endif /*__LINUX_AURORA_H*/
+
diff -u --recursive --new-file v2.3.3/linux/drivers/sbus/char/cd180.h linux/drivers/sbus/char/cd180.h
--- v2.3.3/linux/drivers/sbus/char/cd180.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sbus/char/cd180.h	Mon May 31 22:08:10 1999
@@ -0,0 +1,240 @@
+
+/* Definitions for Cirrus Logic CL-CD180 8-port async mux chip */
+#define CD180_NCH       8       /* Total number of channels                */
+#define CD180_TPC       16      /* Ticks per character                     */
+#define CD180_NFIFO	8	/* TX FIFO size                            */
+
+/* Global registers */
+#define CD180_GFRCR	0x6b	/* Global Firmware Revision Code Register  */
+#define CD180_SRCR	0x66	/* Service Request Configuration Register  */
+#define CD180_PPRH	0x70	/* Prescaler Period Register High	   */
+#define CD180_PPRL	0x71	/* Prescaler Period Register Low	   */
+#define CD180_MSMR	0x61	/* Modem Service Match Register		   */
+#define CD180_TSMR	0x62	/* Transmit Service Match Register	   */
+#define CD180_RSMR	0x63	/* Receive Service Match Register	   */
+#define CD180_GSVR	0x40	/* Global Service Vector Register	   */
+#define CD180_SRSR	0x65	/* Service Request Status Register	   */
+#define CD180_GSCR	0x41	/* Global Service Channel Register	   */
+#define CD180_CAR	0x64	/* Channel Access Register		   */
+
+/* Indexed registers */
+#define CD180_RDCR	0x07	/* Receive Data Count Register		   */
+#define CD180_RDR	0x78	/* Receiver Data Register		   */
+#define CD180_RCSR	0x7a	/* Receiver Character Status Register	   */
+#define CD180_TDR	0x7b	/* Transmit Data Register		   */
+#define CD180_EOSRR	0x7f	/* End of Service Request Register	   */
+
+/* Channel Registers */
+#define CD180_SRER      0x02    /* Service Request Enable Register         */
+#define CD180_CCR       0x01    /* Channel Command Register                */
+#define CD180_COR1      0x03    /* Channel Option Register 1               */
+#define CD180_COR2      0x04    /* Channel Option Register 2               */
+#define CD180_COR3      0x05    /* Channel Option Register 3               */
+#define CD180_CCSR      0x06    /* Channel Control Status Register         */
+#define CD180_RTPR      0x18    /* Receive Timeout Period Register         */
+#define CD180_RBPRH     0x31    /* Receive Bit Rate Period Register High  */
+#define CD180_RBPRL     0x32    /* Receive Bit Rate Period Register Low   */
+#define CD180_TBPRH     0x39    /* Transmit Bit Rate Period Register High */
+#define CD180_TBPRL     0x3a    /* Transmit Bit Rate Period Register Low  */
+#define CD180_SCHR1     0x09    /* Special Character Register 1            */
+#define CD180_SCHR2     0x0a    /* Special Character Register 2            */
+#define CD180_SCHR3     0x0b    /* Special Character Register 3            */
+#define CD180_SCHR4     0x0c    /* Special Character Register 4            */
+#define CD180_MCR       0x12    /* Modem Change Register                   */
+#define CD180_MCOR1     0x10    /* Modem Change Option 1 Register          */
+#define CD180_MCOR2     0x11    /* Modem Change Option 2 Register          */
+#define CD180_MSVR      0x28    /* Modem Signal Value Register             */
+#define CD180_MSVRTS    0x29    /* Modem Signal Value RTS                  */
+#define CD180_MSVDTR    0x2a    /* Modem Signal Value DTR                  */
+
+/* Global Interrupt Vector Register (R/W) */
+
+#define GSVR_ITMASK     0x07     /* Interrupt type mask                     */
+#define  GSVR_IT_MDM     0x01    /* Modem Signal Change Interrupt           */
+#define  GSVR_IT_TX      0x02    /* Transmit Data Interrupt                 */
+#define  GSVR_IT_RGD     0x03    /* Receive Good Data Interrupt             */
+#define  GSVR_IT_REXC    0x07    /* Receive Exception Interrupt             */
+
+
+/* Global Interrupt Channel Register (R/W) */
+ 
+#define GSCR_CHAN       0x1c    /* Channel Number Mask                     */
+#define GSCR_CHAN_OFF   2       /* Channel Number Offset                   */
+
+
+/* Channel Address Register (R/W) */
+
+#define CAR_CHAN        0x07    /* Channel Number Mask                     */
+
+
+/* Receive Character Status Register (R/O) */
+
+#define RCSR_TOUT       0x80    /* Rx Timeout                              */
+#define RCSR_SCDET      0x70    /* Special Character Detected Mask         */
+#define  RCSR_NO_SC      0x00   /* No Special Characters Detected          */
+#define  RCSR_SC_1       0x10   /* Special Char 1 (or 1 & 3) Detected      */
+#define  RCSR_SC_2       0x20   /* Special Char 2 (or 2 & 4) Detected      */
+#define  RCSR_SC_3       0x30   /* Special Char 3 Detected                 */
+#define  RCSR_SC_4       0x40   /* Special Char 4 Detected                 */
+#define RCSR_BREAK      0x08    /* Break has been detected                 */
+#define RCSR_PE         0x04    /* Parity Error                            */
+#define RCSR_FE         0x02    /* Frame Error                             */
+#define RCSR_OE         0x01    /* Overrun Error                           */
+
+
+/* Channel Command Register (R/W) (commands in groups can be OR-ed) */
+
+#define CCR_HARDRESET   0x81    /* Reset the chip                          */
+
+#define CCR_SOFTRESET   0x80    /* Soft Channel Reset                      */
+
+#define CCR_CORCHG1     0x42    /* Channel Option Register 1 Changed       */
+#define CCR_CORCHG2     0x44    /* Channel Option Register 2 Changed       */
+#define CCR_CORCHG3     0x48    /* Channel Option Register 3 Changed       */
+
+#define CCR_SSCH1       0x21    /* Send Special Character 1                */
+
+#define CCR_SSCH2       0x22    /* Send Special Character 2                */
+
+#define CCR_SSCH3       0x23    /* Send Special Character 3                */
+
+#define CCR_SSCH4       0x24    /* Send Special Character 4                */
+
+#define CCR_TXEN        0x18    /* Enable Transmitter                      */
+#define CCR_RXEN        0x12    /* Enable Receiver                         */
+
+#define CCR_TXDIS       0x14    /* Disable Transmitter                     */
+#define CCR_RXDIS       0x11    /* Disable Receiver                        */
+
+
+/* Service Request Enable Register (R/W) */
+
+#define SRER_DSR         0x80    /* Enable interrupt on DSR change          */
+#define SRER_CD          0x40    /* Enable interrupt on CD change           */
+#define SRER_CTS         0x20    /* Enable interrupt on CTS change          */
+#define SRER_RXD         0x10    /* Enable interrupt on Receive Data        */
+#define SRER_RXSC        0x08    /* Enable interrupt on Receive Spec. Char  */
+#define SRER_TXRDY       0x04    /* Enable interrupt on TX FIFO empty       */
+#define SRER_TXEMPTY     0x02    /* Enable interrupt on TX completely empty */
+#define SRER_RET         0x01    /* Enable interrupt on RX Exc. Timeout     */
+
+
+/* Channel Option Register 1 (R/W) */
+
+#define COR1_ODDP       0x80    /* Odd Parity                              */
+#define COR1_PARMODE    0x60    /* Parity Mode mask                        */
+#define  COR1_NOPAR      0x00   /* No Parity                               */
+#define  COR1_FORCEPAR   0x20   /* Force Parity                            */
+#define  COR1_NORMPAR    0x40   /* Normal Parity                           */
+#define COR1_IGNORE     0x10    /* Ignore Parity on RX                     */
+#define COR1_STOPBITS   0x0c    /* Number of Stop Bits                     */
+#define  COR1_1SB        0x00   /* 1 Stop Bit                              */
+#define  COR1_15SB       0x04   /* 1.5 Stop Bits                           */
+#define  COR1_2SB        0x08   /* 2 Stop Bits                             */
+#define COR1_CHARLEN    0x03    /* Character Length                        */
+#define  COR1_5BITS      0x00   /* 5 bits                                  */
+#define  COR1_6BITS      0x01   /* 6 bits                                  */
+#define  COR1_7BITS      0x02   /* 7 bits                                  */
+#define  COR1_8BITS      0x03   /* 8 bits                                  */
+
+
+/* Channel Option Register 2 (R/W) */
+
+#define COR2_IXM        0x80    /* Implied XON mode                        */
+#define COR2_TXIBE      0x40    /* Enable In-Band (XON/XOFF) Flow Control  */
+#define COR2_ETC        0x20    /* Embedded Tx Commands Enable             */
+#define COR2_LLM        0x10    /* Local Loopback Mode                     */
+#define COR2_RLM        0x08    /* Remote Loopback Mode                    */
+#define COR2_RTSAO      0x04    /* RTS Automatic Output Enable             */
+#define COR2_CTSAE      0x02    /* CTS Automatic Enable                    */
+#define COR2_DSRAE      0x01    /* DSR Automatic Enable                    */
+
+
+/* Channel Option Register 3 (R/W) */
+
+#define COR3_XONCH      0x80    /* XON is a pair of characters (1 & 3)     */
+#define COR3_XOFFCH     0x40    /* XOFF is a pair of characters (2 & 4)    */
+#define COR3_FCT        0x20    /* Flow-Control Transparency Mode          */
+#define COR3_SCDE       0x10    /* Special Character Detection Enable      */
+#define COR3_RXTH       0x0f    /* RX FIFO Threshold value (1-8)           */
+
+
+/* Channel Control Status Register (R/O) */
+
+#define CCSR_RXEN       0x80    /* Receiver Enabled                        */
+#define CCSR_RXFLOFF    0x40    /* Receive Flow Off (XOFF was sent)        */
+#define CCSR_RXFLON     0x20    /* Receive Flow On (XON was sent)          */
+#define CCSR_TXEN       0x08    /* Transmitter Enabled                     */
+#define CCSR_TXFLOFF    0x04    /* Transmit Flow Off (got XOFF)            */
+#define CCSR_TXFLON     0x02    /* Transmit Flow On (got XON)              */
+
+
+/* Modem Change Option Register 1 (R/W) */
+
+#define MCOR1_DSRZD     0x80    /* Detect 0->1 transition of DSR           */
+#define MCOR1_CDZD      0x40    /* Detect 0->1 transition of CD            */
+#define MCOR1_CTSZD     0x20    /* Detect 0->1 transition of CTS           */
+#define MCOR1_DTRTH     0x0f    /* Auto DTR flow control Threshold (1-8)   */
+#define  MCOR1_NODTRFC   0x0     /* Automatic DTR flow control disabled     */
+
+
+/* Modem Change Option Register 2 (R/W) */
+
+#define MCOR2_DSROD     0x80    /* Detect 1->0 transition of DSR           */
+#define MCOR2_CDOD      0x40    /* Detect 1->0 transition of CD            */
+#define MCOR2_CTSOD     0x20    /* Detect 1->0 transition of CTS           */
+
+
+/* Modem Change Register (R/W) */
+
+#define MCR_DSRCHG      0x80    /* DSR Changed                             */
+#define MCR_CDCHG       0x40    /* CD Changed                              */
+#define MCR_CTSCHG      0x20    /* CTS Changed                             */
+
+
+/* Modem Signal Value Register (R/W) */
+
+#define MSVR_DSR        0x80    /* Current state of DSR input              */
+#define MSVR_CD         0x40    /* Current state of CD input               */
+#define MSVR_CTS        0x20    /* Current state of CTS input              */
+#define MSVR_DTR        0x02    /* Current state of DTR output             */
+#define MSVR_RTS        0x01    /* Current state of RTS output             */
+
+
+/* Service Request Status Register */
+
+#define SRSR_CMASK	0xC0	/* Current Service Context Mask            */
+#define  SRSR_CNONE	0x00	/* Not in a service context		   */
+#define  SRSR_CRX	0x40	/* Rx Context				   */
+#define  SRSR_CTX	0x80	/* Tx Context				   */
+#define  SRSR_CMDM	0xC0	/* Modem Context			   */
+#define SRSR_ANYINT	0x6F	/* Any interrupt flag			   */
+#define SRSR_RINT	0x10	/* Receive Interrupt			   */
+#define SRSR_TINT	0x04	/* Transmit Interrupt			   */
+#define SRSR_MINT	0x01	/* Modem Interrupt			   */
+#define SRSR_REXT	0x20	/* Receive External Interrupt		   */
+#define SRSR_TEXT	0x08	/* Transmit External Interrupt		   */
+#define SRSR_MEXT	0x02	/* Modem External Interrupt		   */
+
+
+/* Service Request Configuration Register */
+
+#define SRCR_PKGTYPE    0x80
+#define SRCR_REGACKEN   0x40
+#define SRCR_DAISYEN    0x20
+#define SRCR_GLOBPRI    0x10
+#define SRCR_UNFAIR     0x08
+#define SRCR_AUTOPRI    0x02
+#define SRCR_PRISEL     0x01
+
+/* Values for register-based Interrupt ACKs */
+#define CD180_ACK_MINT	0x75	/* goes to MSMR				   */
+#define CD180_ACK_TINT	0x76	/* goes to TSMR				   */
+#define CD180_ACK_RINT	0x77	/* goes to RSMR				   */
+
+/* Escape characters */
+
+#define CD180_C_ESC     0x00    /* Escape character                        */
+#define CD180_C_SBRK    0x81    /* Start sending BREAK                     */
+#define CD180_C_DELAY   0x82    /* Delay output                            */
+#define CD180_C_EBRK    0x83    /* Stop sending BREAK                      */
diff -u --recursive --new-file v2.3.3/linux/drivers/sbus/sbus.c linux/drivers/sbus/sbus.c
--- v2.3.3/linux/drivers/sbus/sbus.c	Mon Mar 15 16:11:30 1999
+++ linux/drivers/sbus/sbus.c	Mon May 31 22:08:10 1999
@@ -1,4 +1,4 @@
-/* $Id: sbus.c,v 1.76 1998/12/17 11:11:26 davem Exp $
+/* $Id: sbus.c,v 1.77 1999/05/29 06:25:57 davem Exp $
  * sbus.c:  SBus support routines.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -207,6 +207,9 @@
 #ifdef CONFIG_OBP_FLASH
 extern int flash_init(void);
 #endif
+#ifdef CONFIG_SUN_AURORA
+extern int aurora_init(void);
+#endif
 
 __initfunc(static void
 sbus_do_child_siblings(int start_node, struct linux_sbus_device *child,
@@ -440,6 +443,9 @@
 #endif
 #ifdef CONFIG_OBP_FLASH
 	flash_init();
+#endif
+#ifdef CONFIG_SUN_AURORA
+	aurora_init();
 #endif
 #ifdef __sparc_v9__
 	if (sparc_cpu_model == sun4u) {
diff -u --recursive --new-file v2.3.3/linux/drivers/scsi/README.st linux/drivers/scsi/README.st
--- v2.3.3/linux/drivers/scsi/README.st	Sun Mar  7 15:20:26 1999
+++ linux/drivers/scsi/README.st	Sat May 22 14:50:07 1999
@@ -2,7 +2,7 @@
 The driver is currently maintained by Kai M{kisara (email
 Kai.Makisara@metla.fi)
 
-Last modified: Sun Jan 17 10:57:41 1999 by makisara@home
+Last modified: Sun Apr 18 13:24:43 1999 by makisara@home
 
 
 BASICS
@@ -348,15 +348,6 @@
 	The GMT_xxx status bits reflect the drive status. GMT_DR_OPEN
 	is set if there is no tape in the drive. GMT_EOD means either
 	end of recorded data or end of tape. GMT_EOT means end of tape.
-
-The following ioctls use the structure mtlocation that contains both
-the block number and the partition number. These ioctls are available
-only for SCSI-2 tape drives and the block number is the
-device-independent logical block number defined by the standard.
-
-MTGETLOC Returns the current block and partition number.
-MTSETLOC Sets the tape to the block and partition specified by the
-	arguments.
 
 
 MISCELLANEOUS COMPILE OPTIONS
diff -u --recursive --new-file v2.3.3/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c
--- v2.3.3/linux/drivers/scsi/scsi.c	Fri May 14 18:55:22 1999
+++ linux/drivers/scsi/scsi.c	Wed May 26 09:33:40 1999
@@ -109,6 +109,7 @@
 #define BLIST_SINGLELUN 0x10
 #define BLIST_NOTQ	0x20
 #define BLIST_SPARSELUN 0x40
+#define BLIST_MAX5LUN	0x80
 
 /*
  * Data declarations.
@@ -273,6 +274,7 @@
 {"INSITE","I325VM","*", BLIST_KEY},
 {"NRC","MBR-7","*", BLIST_FORCELUN | BLIST_SINGLELUN},
 {"NRC","MBR-7.4","*", BLIST_FORCELUN | BLIST_SINGLELUN},
+{"REGAL","CDC-4X","*", BLIST_MAX5LUN | BLIST_SINGLELUN},
 {"NAKAMICH","MJ-4.8S","*", BLIST_FORCELUN | BLIST_SINGLELUN},
 {"NAKAMICH","MJ-5.16S","*", BLIST_FORCELUN | BLIST_SINGLELUN},
 {"PIONEER","CD-ROM DRM-600","*", BLIST_FORCELUN | BLIST_SINGLELUN},
@@ -936,6 +938,15 @@
     *max_dev_lun = 8;
     return 1;
   }
+
+  /*
+   * REGAL CDC-4X: avoid hang after LUN 4
+   */
+  if (bflags & BLIST_MAX5LUN) {
+    *max_dev_lun = 5;
+    return 1;
+  }
+
   /*
    * We assume the device can't handle lun!=0 if: - it reports scsi-0 (ANSI
    * SCSI Revision 0) (old drives like MAXTOR XT-3280) or - it reports scsi-1
diff -u --recursive --new-file v2.3.3/linux/drivers/scsi/st.c linux/drivers/scsi/st.c
--- v2.3.3/linux/drivers/scsi/st.c	Fri May 14 18:55:22 1999
+++ linux/drivers/scsi/st.c	Sat May 22 14:50:22 1999
@@ -11,7 +11,7 @@
   Copyright 1992 - 1999 Kai Makisara
 		 email Kai.Makisara@metla.fi
 
-  Last modified: Sun Mar  7 09:03:17 1999 by makisara@home
+  Last modified: Tue May 18 08:32:34 1999 by makisara@home
   Some small formal changes - aeb, 950809
 */
 
@@ -164,8 +164,6 @@
 	   SCpnt->request_bufflen);
     if (driver_byte(result) & DRIVER_SENSE)
       print_sense("st", SCpnt);
-    else
-      printk("\n");
   }
   else
 #endif
@@ -289,6 +287,7 @@
   }
   else
       bp = (STp->buffer)->b_data;
+  SCpnt->cmd_len = 0;
   SCpnt->request.sem = &(STp->sem);
   SCpnt->request.rq_status = RQ_SCSI_BUSY;
   SCpnt->request.rq_dev = STp->devt;
@@ -3380,7 +3379,6 @@
 
    tpnt->devt = MKDEV(SCSI_TAPE_MAJOR, i);
    tpnt->dirty = 0;
-   tpnt->waiting = NULL;
    tpnt->in_use = 0;
    tpnt->drv_buffer = 1;  /* Try buffering if no mode sense */
    tpnt->restr_dma = (SDp->host)->unchecked_isa_dma;
diff -u --recursive --new-file v2.3.3/linux/drivers/scsi/st.h linux/drivers/scsi/st.h
--- v2.3.3/linux/drivers/scsi/st.h	Mon May 17 09:55:22 1999
+++ linux/drivers/scsi/st.h	Sat May 22 14:50:07 1999
@@ -65,7 +65,6 @@
 typedef struct {
   kdev_t devt;
   unsigned capacity;
-  wait_queue_head_t waiting;
   Scsi_Device* device;
   struct semaphore sem;
   ST_buffer * buffer;
diff -u --recursive --new-file v2.3.3/linux/drivers/sound/Config.in linux/drivers/sound/Config.in
--- v2.3.3/linux/drivers/sound/Config.in	Sun Mar  7 15:22:06 1999
+++ linux/drivers/sound/Config.in	Wed May 26 09:35:00 1999
@@ -9,6 +9,7 @@
 
 # Prompt user for primary drivers.
 
+dep_tristate 'C-Media PCI (CMI8338/8378)' CONFIG_SOUND_CMPCI $CONFIG_SOUND
 dep_tristate 'Ensoniq AudioPCI (ES1370)' CONFIG_SOUND_ES1370 $CONFIG_SOUND
 if [ "$CONFIG_SOUND_ES1370" = "y" ]; then
    bool 'Joystick support at boot time' CONFIG_SOUND_ES1370_JOYPORT_BOOT
diff -u --recursive --new-file v2.3.3/linux/drivers/sound/Makefile linux/drivers/sound/Makefile
--- v2.3.3/linux/drivers/sound/Makefile	Thu Jan 14 22:59:47 1999
+++ linux/drivers/sound/Makefile	Wed May 26 09:35:00 1999
@@ -76,6 +76,7 @@
 obj-$(CONFIG_SOUND_WAVEFRONT)   += wavefront.o
 
 #jnx
+obj-$(CONFIG_SOUND_CMPCI)	+= cmpci.o
 obj-$(CONFIG_SOUND_ES1370)	+= es1370.o
 obj-$(CONFIG_SOUND_ES1371)	+= es1371.o
 obj-$(CONFIG_SOUND_SONICVIBES)	+= sonicvibes.o
diff -u --recursive --new-file v2.3.3/linux/drivers/sound/cmpci.c linux/drivers/sound/cmpci.c
--- v2.3.3/linux/drivers/sound/cmpci.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sound/cmpci.c	Wed May 26 09:35:00 1999
@@ -0,0 +1,2391 @@
+/*****************************************************************************/
+
+/*
+ *      cmpci.c  --  C-Media PCI audio driver.
+ *
+ *      Copyright (C) 1999  ChenLi Tien (cltien@home.com)
+ *
+ *	Based on the PCI drivers by Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Special thanks to David C. Niemi
+ *
+ *
+ * Module command line parameters:
+ *   none so far
+ *
+ *
+ *  Supported devices:
+ *  /dev/dsp    standard /dev/dsp device, (mostly) OSS compatible
+ *  /dev/mixer  standard /dev/mixer device, (mostly) OSS compatible
+ *  /dev/midi   simple MIDI UART interface, no ioctl
+ *
+ *  The card has both an FM and a Wavetable synth, but I have to figure
+ *  out first how to drive them...
+ *
+ *  Revision history
+ *    06.05.98   0.1   Initial release
+ *    10.05.98   0.2   Fixed many bugs, esp. ADC rate calculation
+ *                     First stab at a simple midi interface (no bells&whistles)
+ *    13.05.98   0.3   Fix stupid cut&paste error: set_adc_rate was called instead of
+ *                     set_dac_rate in the FMODE_WRITE case in cm_open
+ *                     Fix hwptr out of bounds (now mpg123 works)
+ *    14.05.98   0.4   Don't allow excessive interrupt rates
+ *    08.06.98   0.5   First release using Alan Cox' soundcore instead of miscdevice
+ *    03.08.98   0.6   Do not include modversions.h
+ *                     Now mixer behaviour can basically be selected between
+ *                     "OSS documented" and "OSS actual" behaviour
+ *    31.08.98   0.7   Fix realplayer problems - dac.count issues
+ *    10.12.98   0.8   Fix drain_dac trying to wait on not yet initialized DMA
+ *    16.12.98   0.9   Fix a few f_file & FMODE_ bugs
+ *    06.01.99   0.10  remove the silly SA_INTERRUPT flag.
+ *                     hopefully killed the egcs section type conflict
+ *    12.03.99   0.11  cinfo.blocks should be reset after GETxPTR ioctl.
+ *                     reported by Johan Maes <joma@telindus.be>
+ *    22.03.99   0.12  return EAGAIN instead of EBUSY when O_NONBLOCK
+ *                     read/write cannot be executed
+ *
+ */
+
+/*****************************************************************************/
+      
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/sound.h>
+#include <linux/malloc.h>
+#include <linux/soundcard.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <asm/spinlock.h>
+#include <asm/uaccess.h>
+#include <asm/hardirq.h>
+
+#include "dm.h"
+
+/* --------------------------------------------------------------------- */
+
+#undef OSS_DOCUMENTED_MIXER_SEMANTICS
+
+/* --------------------------------------------------------------------- */
+
+#ifndef PCI_VENDOR_ID_CMEDIA
+#define PCI_VENDOR_ID_CMEDIA         0x13F6
+#endif
+#ifndef PCI_DEVICE_ID_CMEDIA_CM8338A
+#define PCI_DEVICE_ID_CMEDIA_CM8338A 0x0100
+#endif
+#ifndef PCI_DEVICE_ID_CMEDIA_CM8338B
+#define PCI_DEVICE_ID_CMEDIA_CM8338B 0x0101
+#endif
+#ifndef PCI_DEVICE_ID_CMEDIA_CM8738
+#define PCI_DEVICE_ID_CMEDIA_CM8738  0x0111
+#endif
+
+#define CM_MAGIC  ((PCI_VENDOR_ID_CMEDIA<<16)|PCI_DEVICE_ID_CMEDIA_CM8338A)
+
+/*
+ * CM8338 registers definition
+ */
+
+#define CODEC_CMI_FUNCTRL0      (0x00)
+#define CODEC_CMI_FUNCTRL1      (0x04)
+#define CODEC_CMI_CHFORMAT      (0x08)
+#define CODEC_CMI_INT_HLDCLR    (0x0C)
+#define CODEC_CMI_INT_STATUS    (0x10)
+#define CODEC_CMI_LEGACY_CTRL   (0x14)
+#define CODEC_CMI_MISC_CTRL     (0x18)
+#define CODEC_CMI_TDMA_POS      (0x1C)
+#define CODEC_CMI_MIXER         (0x20)
+#define CODEC_SB16_DATA         (0x22)
+#define CODEC_SB16_ADDR         (0x23)
+#define CODEC_CMI_MIXER1        (0x24)
+#define CODEC_CMI_MIXER2        (0x25)
+#define CODEC_CMI_AUX_VOL       (0x26)
+#define CODEC_CMI_MISC          (0x27)
+#define CODEC_CMI_AC97          (0x28)
+
+#define CODEC_CMI_CH0_FRAME1    (0x80)
+#define CODEC_CMI_CH0_FRAME2    (0x84)
+#define CODEC_CMI_CH1_FRAME1    (0x88)
+#define CODEC_CMI_CH1_FRAME2    (0x8C)
+
+#define CODEC_CMI_EXT_REG       (0xF0)
+#define UCHAR	unsigned char
+/*
+**  Mixer registers for SB16
+*/
+
+#define DSP_MIX_DATARESETIDX    ((UCHAR)(0x00))
+
+#define DSP_MIX_MASTERVOLIDX_L  ((UCHAR)(0x30))
+#define DSP_MIX_MASTERVOLIDX_R  ((UCHAR)(0x31))
+#define DSP_MIX_VOICEVOLIDX_L   ((UCHAR)(0x32))
+#define DSP_MIX_VOICEVOLIDX_R   ((UCHAR)(0x33))
+#define DSP_MIX_FMVOLIDX_L      ((UCHAR)(0x34))
+#define DSP_MIX_FMVOLIDX_R      ((UCHAR)(0x35))
+#define DSP_MIX_CDVOLIDX_L      ((UCHAR)(0x36))
+#define DSP_MIX_CDVOLIDX_R      ((UCHAR)(0x37))
+#define DSP_MIX_LINEVOLIDX_L    ((UCHAR)(0x38))
+#define DSP_MIX_LINEVOLIDX_R    ((UCHAR)(0x39))
+
+#define DSP_MIX_MICVOLIDX       ((UCHAR)(0x3A))
+#define DSP_MIX_SPKRVOLIDX      ((UCHAR)(0x3B))
+
+#define DSP_MIX_OUTMIXIDX       ((UCHAR)(0x3C))
+
+#define DSP_MIX_ADCMIXIDX_L     ((UCHAR)(0x3D))
+#define DSP_MIX_ADCMIXIDX_R     ((UCHAR)(0x3E))
+
+#define DSP_MIX_INGAINIDX_L     ((UCHAR)(0x3F))
+#define DSP_MIX_INGAINIDX_R     ((UCHAR)(0x40))
+#define DSP_MIX_OUTGAINIDX_L    ((UCHAR)(0x41))
+#define DSP_MIX_OUTGAINIDX_R    ((UCHAR)(0x42))
+
+#define DSP_MIX_AGCIDX          ((UCHAR)(0x43))
+
+#define DSP_MIX_TREBLEIDX_L     ((UCHAR)(0x44))
+#define DSP_MIX_TREBLEIDX_R     ((UCHAR)(0x45))
+#define DSP_MIX_BASSIDX_L       ((UCHAR)(0x46))
+#define DSP_MIX_BASSIDX_R       ((UCHAR)(0x47))
+#define CM_CH0_RESET	  0x04
+#define CM_CH1_RESET	  0x08
+#define CM_EXTENT_CODEC	  0x100
+#define CM_EXTENT_MIDI	  0x2
+#define CM_EXTENT_SYNTH	  0x4
+#define CM_INT_CH0	  1
+#define CM_INT_CH1	  2
+
+#define CM_CFMT_STEREO     0x01
+#define CM_CFMT_16BIT      0x02
+#define CM_CFMT_MASK       0x03
+#define CM_CFMT_DACSHIFT   0   
+#define CM_CFMT_ADCSHIFT   2
+
+static const unsigned sample_size[] = { 1, 2, 2, 4 };
+static const unsigned sample_shift[] = { 0, 1, 1, 2 };
+
+#define CM_CENABLE_RE      0x2
+#define CM_CENABLE_PE      0x1
+
+
+/* MIDI buffer sizes */
+
+#define MIDIINBUF  256
+#define MIDIOUTBUF 256
+
+#define FMODE_MIDI_SHIFT 2
+#define FMODE_MIDI_READ  (FMODE_READ << FMODE_MIDI_SHIFT)
+#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT)
+
+#define FMODE_DMFM 0x10
+
+#define SND_DEV_DSP16   5 
+
+/* --------------------------------------------------------------------- */
+
+struct cm_state {
+	/* magic */
+	unsigned int magic;
+
+	/* we keep cm cards in a linked list */
+	struct cm_state *next;
+
+	/* soundcore stuff */
+	int dev_audio;
+	int dev_mixer;
+	int dev_midi;
+	int dev_dmfm;
+
+	/* hardware resources */
+	unsigned int iosb, iobase, iosynth, iomidi, iogame, irq;
+
+        /* mixer stuff */
+        struct {
+                unsigned int modcnt;
+#ifndef OSS_DOCUMENTED_MIXER_SEMANTICS
+		unsigned short vol[13];
+#endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */
+        } mix;
+
+	/* wave stuff */
+	unsigned int rateadc, ratedac;
+	unsigned char fmt, enable;
+
+	spinlock_t lock;
+	struct semaphore open_sem;
+	mode_t open_mode;
+	wait_queue_head_t open_wait;
+
+	struct dmabuf {
+		void *rawbuf;
+		unsigned buforder;
+		unsigned numfrag;
+		unsigned fragshift;
+		unsigned hwptr, swptr;
+		unsigned total_bytes;
+		int count;
+		unsigned error; /* over/underrun */
+		wait_queue_head_t wait;
+		/* redundant, but makes calculations easier */
+		unsigned fragsize;
+		unsigned dmasize;
+		unsigned fragsamples;
+		/* OSS stuff */
+		unsigned mapped:1;
+		unsigned ready:1;
+		unsigned endcleared:1;
+		unsigned ossfragshift;
+		int ossmaxfrags;
+		unsigned subdivision;
+	} dma_dac, dma_adc;
+
+	/* midi stuff */
+	struct {
+		unsigned ird, iwr, icnt;
+		unsigned ord, owr, ocnt;
+		wait_queue_head_t iwait;
+		wait_queue_head_t owait;
+		struct timer_list timer;
+		unsigned char ibuf[MIDIINBUF];
+		unsigned char obuf[MIDIOUTBUF];
+	} midi;
+};
+
+/* --------------------------------------------------------------------- */
+
+static struct cm_state *devs = NULL;
+static unsigned long wavetable_mem = 0;
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ unsigned ld2(unsigned int x)
+{
+	unsigned r = 0;
+	
+	if (x >= 0x10000) {
+		x >>= 16;
+		r += 16;
+	}
+	if (x >= 0x100) {
+		x >>= 8;
+		r += 8;
+	}
+	if (x >= 0x10) {
+		x >>= 4;
+		r += 4;
+	}
+	if (x >= 4) {
+		x >>= 2;
+		r += 2;
+	}
+	if (x >= 2)
+		r++;
+	return r;
+}
+
+/*
+ * hweightN: returns the hamming weight (i.e. the number
+ * of bits set) of a N-bit word
+ */
+
+#ifdef hweight32
+#undef hweight32
+#endif
+
+extern __inline__ unsigned int hweight32(unsigned int w)
+{
+        unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555);
+        res = (res & 0x33333333) + ((res >> 2) & 0x33333333);
+        res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F);
+        res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF);
+        return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void set_dmadac(struct cm_state *s, unsigned int addr, unsigned int count)
+{
+	count--;
+	outl(addr, s->iobase + CODEC_CMI_CH0_FRAME1);
+	outw(count, s->iobase + CODEC_CMI_CH0_FRAME2);
+	outb(inb(s->iobase + CODEC_CMI_FUNCTRL0) & ~1, s->iobase + CODEC_CMI_FUNCTRL0);
+	outb(inb(s->iobase + CODEC_CMI_FUNCTRL0 + 2) | 1, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
+}
+
+static void set_dmaadc(struct cm_state *s, unsigned int addr, unsigned int count)
+{
+	count--;
+	outl(addr, s->iobase + CODEC_CMI_CH1_FRAME1);
+	outw(count, s->iobase + CODEC_CMI_CH1_FRAME2);
+	outb(inb(s->iobase + CODEC_CMI_FUNCTRL0) | 2, s->iobase + CODEC_CMI_FUNCTRL0);
+	outb(inb(s->iobase + CODEC_CMI_FUNCTRL0 + 2) | 2, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
+}
+
+extern __inline__ unsigned get_dmadac(struct cm_state *s)
+{
+	unsigned int curr_addr;
+
+	curr_addr = inl(s->iobase + CODEC_CMI_CH0_FRAME1);
+	curr_addr -= virt_to_bus(s->dma_dac.rawbuf);
+	curr_addr = s->dma_dac.dmasize - curr_addr;
+	curr_addr &= ~(sample_size[(s->fmt >> CM_CFMT_DACSHIFT) & CM_CFMT_MASK]-1);
+	return curr_addr;
+}
+
+extern __inline__ unsigned get_dmaadc(struct cm_state *s)
+{
+	unsigned int curr_addr;
+
+	curr_addr = inl(s->iobase + CODEC_CMI_CH1_FRAME1);
+	curr_addr -= virt_to_bus(s->dma_adc.rawbuf);
+	curr_addr = s->dma_adc.dmasize - curr_addr;
+	curr_addr &= ~(sample_size[(s->fmt >> CM_CFMT_ADCSHIFT) & CM_CFMT_MASK]-1);
+	return curr_addr;
+}
+
+static void wrmixer(struct cm_state *s, unsigned char idx, unsigned char data)
+{
+	outb(idx, s->iobase + CODEC_SB16_ADDR);
+	udelay(10);
+	outb(data, s->iobase + CODEC_SB16_DATA);
+	udelay(10);
+}
+
+static unsigned char rdmixer(struct cm_state *s, unsigned char idx)
+{
+	unsigned char v;
+
+	outb(idx, s->iobase + CODEC_SB16_ADDR);
+	udelay(10);
+	v = inb(s->iobase + CODEC_SB16_DATA);
+	udelay(10);
+	return v;
+}
+
+static void set_fmt(struct cm_state *s, unsigned char mask, unsigned char data)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	if (mask) {
+		s->fmt = inb(s->iobase + CODEC_CMI_CHFORMAT);
+		udelay(10);
+	}
+	s->fmt = (s->fmt & mask) | data;
+	outb(s->fmt, s->iobase + CODEC_CMI_CHFORMAT);
+	spin_unlock_irqrestore(&s->lock, flags);
+	udelay(10);
+}
+
+static void frobindir(struct cm_state *s, unsigned char idx, unsigned char mask, unsigned char data)
+{
+	outb(idx, s->iobase + CODEC_SB16_ADDR);
+	udelay(10);
+	outb((inb(s->iobase + CODEC_SB16_DATA) & mask) | data, s->iobase + CODEC_SB16_DATA);
+	udelay(10);
+}
+
+static struct {
+	unsigned	rate;
+	unsigned	lower;
+	unsigned	upper;
+	unsigned char	freq;
+} rate_lookup[] =
+{
+	{ 5512,		(0 + 5512) / 2,		(5512 + 8000) / 2,	0 },
+	{ 8000,		(5512 + 8000) / 2,	(8000 + 11025) / 2,	4 },
+	{ 11025,	(8000 + 11025) / 2,	(11025 + 16000) / 2,	1 },
+	{ 16000,	(11025 + 16000) / 2,	(16000 + 22050) / 2,	5 },
+	{ 22050,	(16000 + 22050) / 2,	(22050 + 32000) / 2,	2 },
+	{ 32000,	(22050 + 32000) / 2,	(32000 + 44100) / 2,	6 },
+	{ 44100,	(32000 + 44100) / 2,	(44100 + 48000) / 2,	3 },
+	{ 48000,	48000,			48000,			7 }
+};
+
+static void set_dac_rate(struct cm_state *s, unsigned rate)
+{
+	unsigned long flags;
+	unsigned char freq = 4, val;
+	int	i;
+
+	if (rate > 48000)
+		rate = 48000;
+	if (rate < 5512)
+		rate = 5512;
+	for (i = 0; i < sizeof(rate_lookup) / sizeof(rate_lookup[0]); i++)
+	{
+		if (rate > rate_lookup[i].lower && rate <= rate_lookup[i].upper)
+		{
+			rate = rate_lookup[i].rate;
+			freq = rate_lookup[i].freq;
+			break;
+	    	}
+	}
+	s->ratedac = rate;
+	freq <<= 2;
+	spin_lock_irqsave(&s->lock, flags);
+	val = inb(s->iobase + CODEC_CMI_FUNCTRL1 + 1) & ~0x1c; 
+	outb(val | freq, s->iobase + CODEC_CMI_FUNCTRL1 + 1);
+	spin_unlock_irqrestore(&s->lock, flags);
+}
+
+static void set_adc_rate(struct cm_state *s, unsigned rate)
+{
+	unsigned long flags;
+	unsigned char freq = 4, val;
+	int	i;
+
+	if (rate > 48000)
+		rate = 48000;
+	if (rate < 5512)
+		rate = 5512;
+	for (i = 0; i < sizeof(rate_lookup) / sizeof(rate_lookup[0]); i++)
+	{
+		if (rate > rate_lookup[i].lower && rate <= rate_lookup[i].upper)
+		{
+			rate = rate_lookup[i].rate;
+			freq = rate_lookup[i].freq;
+			break;
+	    	}
+	}
+	s->rateadc = rate;
+	freq <<= 5;
+	spin_lock_irqsave(&s->lock, flags);
+	val = inb(s->iobase + CODEC_CMI_FUNCTRL1 + 1) & ~0xe0; 
+	outb(val | freq, s->iobase + CODEC_CMI_FUNCTRL1 + 1);
+	spin_unlock_irqrestore(&s->lock, flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+extern inline void stop_adc(struct cm_state *s)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	s->enable &= ~CM_CENABLE_RE;
+	/* disable interrupt */
+	outb(inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2) & ~2, s->iobase + CODEC_CMI_INT_HLDCLR + 2);
+	/* disable channel and reset */
+	outb(s->enable | CM_CH1_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
+	udelay(10);
+	outb(s->enable & ~CM_CH1_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
+	spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+extern inline void stop_dac(struct cm_state *s)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	s->enable &= ~CM_CENABLE_PE;
+	/* disable interrupt */
+	outb(inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2) & ~1, s->iobase + CODEC_CMI_INT_HLDCLR + 2);
+	/* disable channel and reset */
+	outb(s->enable | CM_CH0_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
+	udelay(10);
+	outb(s->enable & ~CM_CH0_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
+	spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+static void start_dac(struct cm_state *s)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	if ((s->dma_dac.mapped || s->dma_dac.count > 0) && s->dma_dac.ready) {
+		s->enable |= CM_CENABLE_PE;
+		outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
+	}
+	outb(inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2) | 1, s->iobase + CODEC_CMI_INT_HLDCLR + 2);
+	spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+static void start_adc(struct cm_state *s)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	if ((s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) 
+	    && s->dma_adc.ready) {
+		s->enable |= CM_CENABLE_RE;
+		outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
+	}
+	outb(inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2) | 2, s->iobase + CODEC_CMI_INT_HLDCLR + 2);
+	spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+/* --------------------------------------------------------------------- */
+
+#define DMABUF_DEFAULTORDER (17-PAGE_SHIFT)
+#define DMABUF_MINORDER 1
+
+static void dealloc_dmabuf(struct dmabuf *db)
+{
+	unsigned long map, mapend;
+
+	if (db->rawbuf) {
+		/* undo marking the pages as reserved */
+		mapend = MAP_NR(db->rawbuf + (PAGE_SIZE << db->buforder) - 1);
+		for (map = MAP_NR(db->rawbuf); map <= mapend; map++)
+			clear_bit(PG_reserved, &mem_map[map].flags);	
+		free_pages((unsigned long)db->rawbuf, db->buforder);
+	}
+	db->rawbuf = NULL;
+	db->mapped = db->ready = 0;
+}
+
+
+/* Ch0 is used for playback, Ch1 is used for recording */
+
+static int prog_dmabuf(struct cm_state *s, unsigned rec)
+{
+	struct dmabuf *db = rec ? &s->dma_adc : &s->dma_dac;
+	unsigned rate = rec ? s->rateadc : s->ratedac;
+	int order;
+	unsigned bytepersec;
+	unsigned bufs;
+	unsigned long map, mapend;
+	unsigned char fmt;
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	fmt = s->fmt;
+	if (rec) {
+		s->enable &= ~CM_CENABLE_RE;
+		fmt >>= CM_CFMT_ADCSHIFT;
+	} else {
+		s->enable &= ~CM_CENABLE_PE;
+		fmt >>= CM_CFMT_DACSHIFT;
+	}
+	outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
+	spin_unlock_irqrestore(&s->lock, flags);
+	fmt &= CM_CFMT_MASK;
+	db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0;
+	if (!db->rawbuf) {
+		db->ready = db->mapped = 0;
+		for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER && !db->rawbuf; order--)
+			db->rawbuf = (void *)__get_free_pages(GFP_KERNEL | GFP_DMA, order);
+		if (!db->rawbuf)
+			return -ENOMEM;
+		db->buforder = order;
+		if ((virt_to_bus(db->rawbuf) ^ (virt_to_bus(db->rawbuf) + (PAGE_SIZE << db->buforder) - 1)) & ~0xffff)
+			printk(KERN_DEBUG "cm: DMA buffer crosses 64k boundary: busaddr 0x%lx  size %ld\n", 
+			       virt_to_bus(db->rawbuf), PAGE_SIZE << db->buforder);
+		if ((virt_to_bus(db->rawbuf) + (PAGE_SIZE << db->buforder) - 1) & ~0xffffff)
+			printk(KERN_DEBUG "cm: DMA buffer beyond 16MB: busaddr 0x%lx  size %ld\n", 
+			       virt_to_bus(db->rawbuf), PAGE_SIZE << db->buforder);
+		/* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */
+		mapend = MAP_NR(db->rawbuf + (PAGE_SIZE << db->buforder) - 1);
+		for (map = MAP_NR(db->rawbuf); map <= mapend; map++)
+			set_bit(PG_reserved, &mem_map[map].flags);
+	}
+	bytepersec = rate << sample_shift[fmt];
+	bufs = PAGE_SIZE << db->buforder;
+	if (db->ossfragshift) {
+		if ((1000 << db->ossfragshift) < bytepersec)
+			db->fragshift = ld2(bytepersec/1000);
+		else
+			db->fragshift = db->ossfragshift;
+	} else {
+		db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1));
+		if (db->fragshift < 3)
+			db->fragshift = 3;
+	}
+	db->numfrag = bufs >> db->fragshift;
+	while (db->numfrag < 4 && db->fragshift > 3) {
+		db->fragshift--;
+		db->numfrag = bufs >> db->fragshift;
+	}
+	db->fragsize = 1 << db->fragshift;
+	if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag)
+		db->numfrag = db->ossmaxfrags;
+#if 1
+	/* to make fragsize >= 4096 */
+	while (db->fragsize < 4096 && db->numfrag >= 4)
+	{
+		db->fragsize *= 2;
+		db->fragshift++;
+		db->numfrag /= 2;
+	}
+#endif
+	db->fragsamples = db->fragsize >> sample_shift[fmt];
+	db->dmasize = db->numfrag << db->fragshift;
+	memset(db->rawbuf, (fmt & CM_CFMT_16BIT) ? 0 : 0x80, db->dmasize);
+	spin_lock_irqsave(&s->lock, flags);
+	if (rec) {
+		set_dmaadc(s, virt_to_bus(db->rawbuf), db->dmasize >> sample_shift[fmt]);
+		/* program sample counts */
+		outw(db->fragsamples-1, s->iobase + CODEC_CMI_CH1_FRAME2 + 2);
+	} else {
+		set_dmadac(s, virt_to_bus(db->rawbuf), db->dmasize >> sample_shift[fmt]);
+		/* program sample counts */
+		outw(db->fragsamples-1, s->iobase + CODEC_CMI_CH0_FRAME2 + 2);
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	db->ready = 1;
+	return 0;
+}
+
+extern __inline__ void clear_advance(struct cm_state *s)
+{
+	unsigned char c = (s->fmt & (CM_CFMT_16BIT << CM_CFMT_DACSHIFT)) ? 0 : 0x80;
+	unsigned char *buf = s->dma_dac.rawbuf;
+	unsigned bsize = s->dma_dac.dmasize;
+	unsigned bptr = s->dma_dac.swptr;
+	unsigned len = s->dma_dac.fragsize;
+
+	if (bptr + len > bsize) {
+		unsigned x = bsize - bptr;
+		memset(buf + bptr, c, x);
+		bptr = 0;
+		len -= x;
+	}
+	memset(buf + bptr, c, len);
+}
+
+/* call with spinlock held! */
+static void cm_update_ptr(struct cm_state *s)
+{
+	unsigned hwptr;
+	int diff;
+
+	/* update ADC pointer */
+	if (s->dma_adc.ready) {
+		hwptr = (s->dma_adc.dmasize - get_dmaadc(s)) % s->dma_adc.dmasize;
+		diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize;
+		s->dma_adc.hwptr = hwptr;
+		s->dma_adc.total_bytes += diff;
+		s->dma_adc.count += diff;
+		if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) 
+			wake_up(&s->dma_adc.wait);
+		if (!s->dma_adc.mapped) {
+			if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) {
+				s->enable &= ~CM_CENABLE_RE;
+				outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
+				s->dma_adc.error++;
+			}
+		}
+	}
+	/* update DAC pointer */
+	if (s->dma_dac.ready) {
+		hwptr = (s->dma_dac.dmasize - get_dmadac(s)) % s->dma_dac.dmasize;
+		diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize;
+		s->dma_dac.hwptr = hwptr;
+		s->dma_dac.total_bytes += diff;
+		if (s->dma_dac.mapped) {
+			s->dma_dac.count += diff;
+			if (s->dma_dac.count >= (signed)s->dma_dac.fragsize)
+				wake_up(&s->dma_dac.wait);
+		} else {
+			s->dma_dac.count -= diff;
+			if (s->dma_dac.count <= 0) {
+				s->enable &= ~CM_CENABLE_PE;
+				outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
+				s->dma_dac.error++;
+			} else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) {
+				clear_advance(s);
+				s->dma_dac.endcleared = 1;
+			}
+			if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize)
+				wake_up(&s->dma_dac.wait);
+		}
+	}
+}
+
+/* hold spinlock for the following! */
+static void cm_handle_midi(struct cm_state *s)
+{
+	unsigned char ch;
+	int wake;
+
+	wake = 0;
+	while (!(inb(s->iomidi+1) & 0x80)) {
+		ch = inb(s->iomidi);
+		if (s->midi.icnt < MIDIINBUF) {
+			s->midi.ibuf[s->midi.iwr] = ch;
+			s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF;
+			s->midi.icnt++;
+		}
+		wake = 1;
+	}
+	if (wake)
+		wake_up(&s->midi.iwait);
+	wake = 0;
+	while (!(inb(s->iomidi+1) & 0x40) && s->midi.ocnt > 0) {
+		outb(s->midi.obuf[s->midi.ord], s->iomidi);
+		s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF;
+		s->midi.ocnt--;
+		if (s->midi.ocnt < MIDIOUTBUF-16)
+			wake = 1;
+	}
+	if (wake)
+		wake_up(&s->midi.owait);
+}
+
+static void cm_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+        struct cm_state *s = (struct cm_state *)dev_id;
+	unsigned int intsrc, intstat;
+	
+	/* fastpath out, to ease interrupt sharing */
+	intsrc = inb(s->iobase + CODEC_CMI_INT_STATUS);
+	if (!(intsrc & (CM_INT_CH0 | CM_INT_CH1)))
+		return;
+	spin_lock(&s->lock);
+	intstat = inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2);
+	/* disable interrupt */
+	if (intsrc & CM_INT_CH0)
+		outb(intstat & ~1, s->iobase + CODEC_CMI_INT_HLDCLR + 2);
+	if (intsrc & CM_INT_CH1)
+		outb(intstat & ~2, s->iobase + CODEC_CMI_INT_HLDCLR + 2);
+	cm_update_ptr(s);
+#ifdef SOUND_CONFIG_CMPCI_MIDI
+	cm_handle_midi(s);
+#endif
+	/* enable interrupt */
+	if (intsrc & CM_INT_CH0)
+		outb(intstat | 1, s->iobase + CODEC_CMI_INT_HLDCLR + 2);
+	if (intsrc & CM_INT_CH1)
+		outb(intstat | 2, s->iobase + CODEC_CMI_INT_HLDCLR + 2);
+	spin_unlock(&s->lock);
+}
+
+static void cm_midi_timer(unsigned long data)
+{
+	struct cm_state *s = (struct cm_state *)data;
+	unsigned long flags;
+	
+	spin_lock_irqsave(&s->lock, flags);
+	cm_handle_midi(s);
+	spin_unlock_irqrestore(&s->lock, flags);
+	s->midi.timer.expires = jiffies+1;
+	add_timer(&s->midi.timer);
+}
+
+/* --------------------------------------------------------------------- */
+
+static const char invalid_magic[] = KERN_CRIT "cm: invalid magic value\n";
+
+#ifdef CONFIG_SOUND_CMPCI	/* support multiple chips */
+#define VALIDATE_STATE(s)
+#else
+#define VALIDATE_STATE(s)                         \
+({                                                \
+	if (!(s) || (s)->magic != CM_MAGIC) { \
+		printk(invalid_magic);            \
+		return -ENXIO;                    \
+	}                                         \
+})
+#endif
+
+/* --------------------------------------------------------------------- */
+
+#define MT_4          1
+#define MT_5MUTE      2
+#define MT_4MUTEMONO  3
+#define MT_6MUTE      4
+
+static const struct {
+	unsigned left;
+	unsigned right;
+	unsigned type;
+	unsigned rec;
+	unsigned play;
+} mixtable[SOUND_MIXER_NRDEVICES] = {
+	[SOUND_MIXER_CD]     = { DSP_MIX_CDVOLIDX_L,     DSP_MIX_CDVOLIDX_R,     MT_5MUTE,     0x04, 0x02 },
+	[SOUND_MIXER_LINE]   = { DSP_MIX_LINEVOLIDX_L,   DSP_MIX_LINEVOLIDX_R,   MT_5MUTE,     0x10, 0x08 },
+	[SOUND_MIXER_MIC]    = { DSP_MIX_MICVOLIDX,      CODEC_CMI_MIXER2,       MT_4MUTEMONO, 0x01, 0x01 },
+	[SOUND_MIXER_SYNTH]  = { DSP_MIX_FMVOLIDX_L,  	 DSP_MIX_FMVOLIDX_R,     MT_5MUTE,     0x40, 0x00 },
+	[SOUND_MIXER_VOLUME] = { DSP_MIX_MASTERVOLIDX_L, DSP_MIX_MASTERVOLIDX_R, MT_5MUTE,     0x00, 0x00 },
+	[SOUND_MIXER_PCM]    = { DSP_MIX_VOICEVOLIDX_L,  DSP_MIX_VOICEVOLIDX_R,  MT_5MUTE,     0x00, 0x00 }
+};
+
+#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS
+
+static int return_mixval(struct cm_state *s, unsigned i, int *arg)
+{
+	unsigned long flags;
+	unsigned char l, r, rl, rr;
+
+	spin_lock_irqsave(&s->lock, flags);
+	l = rdmixer(s, mixtable[i].left);
+	r = rdmixer(s, mixtable[i].right);
+	spin_unlock_irqrestore(&s->lock, flags);
+	switch (mixtable[i].type) {
+	case MT_4:
+		r &= 0xf;
+		l &= 0xf;
+		rl = 10 + 6 * (l & 15);
+		rr = 10 + 6 * (r & 15);
+		break;
+
+	case MT_4MUTEMONO:
+		rl = 55 - 3 * (l & 15);
+		if (r & 0x10)
+			rl += 45;
+		rr = rl;
+		r = l;
+		break;
+
+	case MT_5MUTE:
+	default:
+		rl = 100 - 3 * (l & 31);
+		rr = 100 - 3 * (r & 31);
+		break;
+				
+	case MT_6MUTE:
+		rl = 100 - 3 * (l & 63) / 2;
+		rr = 100 - 3 * (r & 63) / 2;
+		break;
+	}
+	if (l & 0x80)
+		rl = 0;
+	if (r & 0x80)
+		rr = 0;
+	return put_user((rr << 8) | rl, arg);
+}
+
+#else /* OSS_DOCUMENTED_MIXER_SEMANTICS */
+
+static const unsigned char volidx[SOUND_MIXER_NRDEVICES] = 
+{
+	[SOUND_MIXER_CD]     = 1,
+	[SOUND_MIXER_LINE]   = 2,
+	[SOUND_MIXER_MIC]    = 3,
+	[SOUND_MIXER_SYNTH]  = 4,
+	[SOUND_MIXER_VOLUME] = 5,
+	[SOUND_MIXER_PCM]    = 6
+};
+
+#endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */
+
+static unsigned mixer_recmask(struct cm_state *s)
+{
+	unsigned long flags;
+	int i, j, k;
+
+	spin_lock_irqsave(&s->lock, flags);
+	j = rdmixer(s, DSP_MIX_ADCMIXIDX_L);
+	spin_unlock_irqrestore(&s->lock, flags);
+	j &= 0x7f;
+	for (k = i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+		if (j & mixtable[i].rec)
+			k |= 1 << i;
+	return k;
+}
+
+static int mixer_ioctl(struct cm_state *s, unsigned int cmd, unsigned long arg)
+{
+	unsigned long flags;
+	int i, val, j;
+	unsigned char l, r, rl, rr;
+
+	VALIDATE_STATE(s);
+        if (cmd == SOUND_MIXER_INFO) {
+		mixer_info info;
+		strncpy(info.id, "cmpci", sizeof(info.id));
+		strncpy(info.name, "C-Media PCI", sizeof(info.name));
+		info.modify_counter = s->mix.modcnt;
+		if (copy_to_user((void *)arg, &info, sizeof(info)))
+			return -EFAULT;
+		return 0;
+	}
+	if (cmd == SOUND_OLD_MIXER_INFO) {
+		_old_mixer_info info;
+		strncpy(info.id, "cmpci", sizeof(info.id));
+		strncpy(info.name, "C-Media cmpci", sizeof(info.name));
+		if (copy_to_user((void *)arg, &info, sizeof(info)))
+			return -EFAULT;
+		return 0;
+	}
+	if (cmd == OSS_GETVERSION)
+		return put_user(SOUND_VERSION, (int *)arg);
+	if (_IOC_TYPE(cmd) != 'M' || _IOC_SIZE(cmd) != sizeof(int))
+                return -EINVAL;
+        if (_IOC_DIR(cmd) == _IOC_READ) {
+                switch (_IOC_NR(cmd)) {
+                case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */
+			return put_user(mixer_recmask(s), (int *)arg);
+			
+                case SOUND_MIXER_OUTSRC: /* Arg contains a bit for each recording source */
+			return put_user(mixer_recmask(s), (int *)arg);//need fix
+			
+                case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */
+			for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+				if (mixtable[i].type)
+					val |= 1 << i;
+			return put_user(val, (int *)arg);
+
+                case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */
+			for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+				if (mixtable[i].rec)
+					val |= 1 << i;
+			return put_user(val, (int *)arg);
+			
+                case SOUND_MIXER_OUTMASK: /* Arg contains a bit for each supported recording source */
+			for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+				if (mixtable[i].play)
+					val |= 1 << i;
+			return put_user(val, (int *)arg);
+			
+                 case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */
+			for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+				if (mixtable[i].type && mixtable[i].type != MT_4MUTEMONO)
+					val |= 1 << i;
+			return put_user(val, (int *)arg);
+			
+                case SOUND_MIXER_CAPS:
+			return put_user(0, (int *)arg);
+
+		default:
+			i = _IOC_NR(cmd);
+                        if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].type)
+                                return -EINVAL;
+#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS
+			return return_mixval(s, i, (int *)arg);
+#else /* OSS_DOCUMENTED_MIXER_SEMANTICS */
+			if (!volidx[i])
+				return -EINVAL;
+			return put_user(s->mix.vol[volidx[i]-1], (int *)arg);
+#endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */
+		}
+	}
+        if (_IOC_DIR(cmd) != (_IOC_READ|_IOC_WRITE)) 
+		return -EINVAL;
+	s->mix.modcnt++;
+	switch (_IOC_NR(cmd)) {
+	case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */
+		get_user_ret(val, (int *)arg, -EFAULT);
+		i = hweight32(val);
+		for (j = i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
+			if (!(val & (1 << i)))
+				continue;
+			if (!mixtable[i].rec) {
+				val &= ~(1 << i);
+				continue;
+			}
+			j |= mixtable[i].rec;
+		}
+		spin_lock_irqsave(&s->lock, flags);
+		wrmixer(s, DSP_MIX_ADCMIXIDX_L, j);
+		wrmixer(s, DSP_MIX_ADCMIXIDX_R, (j & 1) | j>>1);
+		spin_unlock_irqrestore(&s->lock, flags);
+		return 0;
+
+	case SOUND_MIXER_OUTSRC: /* Arg contains a bit for each recording source */
+		get_user_ret(val, (int *)arg, -EFAULT);
+		for (j = i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
+			if (!(val & (1 << i)))
+				continue;
+			if (!mixtable[i].play) {
+				val &= ~(1 << i);
+				continue;
+			}
+			j |= mixtable[i].play;
+		}
+		spin_lock_irqsave(&s->lock, flags);
+		frobindir(s, DSP_MIX_OUTMIXIDX, 0x1f, j);
+		spin_unlock_irqrestore(&s->lock, flags);
+		return 0;
+
+		default:
+		i = _IOC_NR(cmd);
+		if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].type)
+			return -EINVAL;
+		get_user_ret(val, (int *)arg, -EFAULT);
+		l = val & 0xff;
+		r = (val >> 8) & 0xff;
+		if (l > 100)
+			l = 100;
+		if (r > 100)
+			r = 100;
+		spin_lock_irqsave(&s->lock, flags);
+		switch (mixtable[i].type) {
+		case MT_4:
+			if (l >= 10)
+				l -= 10;
+			if (r >= 10)
+				r -= 10;
+			frobindir(s, mixtable[i].left, 0xf0, l / 6);
+			frobindir(s, mixtable[i].right, 0xf0, l / 6);
+			break;
+
+		case MT_4MUTEMONO:
+			rl = (l < 4 ? 0 : (l - 5) / 3) & 31;
+			rr = (rl >> 2) & 7;
+			wrmixer(s, mixtable[i].left, rl<<3);
+			outb((inb(s->iobase + CODEC_CMI_MIXER2) & ~0x0e) | rr<<1, s->iobase + CODEC_CMI_MIXER2);
+			break;
+			
+		case MT_5MUTE:
+			rl = l < 4 ? 0 : (l - 5) / 3;
+			rr = r < 4 ? 0 : (r - 5) / 3;
+ 			wrmixer(s, mixtable[i].left, rl<<3);
+			wrmixer(s, mixtable[i].right, rr<<3);
+			break;
+				
+		case MT_6MUTE:
+			if (l < 6)
+				rl = 0x00;
+			else
+				rl = l * 2 / 3;
+			if (r < 6)
+				rr = 0x00;
+			else
+				rr = r * 2 / 3;
+			wrmixer(s, mixtable[i].left, rl);
+			wrmixer(s, mixtable[i].right, rr);
+			break;
+		}
+		spin_unlock_irqrestore(&s->lock, flags);
+#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS
+                return return_mixval(s, i, (int *)arg);
+#else /* OSS_DOCUMENTED_MIXER_SEMANTICS */
+		if (!volidx[i])
+			return -EINVAL;
+		s->mix.vol[volidx[i]-1] = val;
+		return put_user(s->mix.vol[volidx[i]-1], (int *)arg);
+#endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static loff_t cm_llseek(struct file *file, loff_t offset, int origin)
+{
+	return -ESPIPE;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int cm_open_mixdev(struct inode *inode, struct file *file)
+{
+	int minor = MINOR(inode->i_rdev);
+	struct cm_state *s = devs;
+
+	while (s && s->dev_mixer != minor)
+		s = s->next;
+	if (!s)
+		return -ENODEV;
+       	VALIDATE_STATE(s);
+	file->private_data = s;
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int cm_release_mixdev(struct inode *inode, struct file *file)
+{
+	struct cm_state *s = (struct cm_state *)file->private_data;
+	
+	VALIDATE_STATE(s);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static int cm_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	return mixer_ioctl((struct cm_state *)file->private_data, cmd, arg);
+}
+
+static /*const*/ struct file_operations cm_mixer_fops = {
+	&cm_llseek,
+	NULL,  /* read */
+	NULL,  /* write */
+	NULL,  /* readdir */
+	NULL,  /* poll */
+	&cm_ioctl_mixdev,
+	NULL,  /* mmap */
+	&cm_open_mixdev,
+	NULL,	/* flush */
+	&cm_release_mixdev,
+	NULL,  /* fsync */
+	NULL,  /* fasync */
+	NULL,  /* check_media_change */
+	NULL,  /* revalidate */
+	NULL,  /* lock */
+};
+
+/* --------------------------------------------------------------------- */
+
+static int drain_dac(struct cm_state *s, int nonblock)
+{
+        DECLARE_WAITQUEUE(wait, current);
+	unsigned long flags;
+	int count, tmo;
+
+	if (s->dma_dac.mapped || !s->dma_dac.ready)
+		return 0;
+        current->state = TASK_INTERRUPTIBLE;
+        add_wait_queue(&s->dma_dac.wait, &wait);
+        for (;;) {
+                spin_lock_irqsave(&s->lock, flags);
+		count = s->dma_dac.count;
+                spin_unlock_irqrestore(&s->lock, flags);
+		if (count <= 0)
+			break;
+		if (signal_pending(current))
+                        break;
+                if (nonblock) {
+                        remove_wait_queue(&s->dma_dac.wait, &wait);
+                        current->state = TASK_RUNNING;
+                        return -EBUSY;
+                }
+		tmo = (count * HZ) / s->ratedac;
+		tmo >>= sample_shift[(s->fmt >> CM_CFMT_DACSHIFT) & CM_CFMT_MASK];
+		if (!schedule_timeout(tmo ? : 1) && tmo)
+			printk(KERN_DEBUG "cm: dma timed out??\n");
+        }
+        remove_wait_queue(&s->dma_dac.wait, &wait);
+        current->state = TASK_RUNNING;
+        if (signal_pending(current))
+                return -ERESTARTSYS;
+        return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static ssize_t cm_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+	struct cm_state *s = (struct cm_state *)file->private_data;
+	ssize_t ret;
+	unsigned long flags;
+	unsigned swptr;
+	int cnt;
+
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (s->dma_adc.mapped)
+		return -ENXIO;
+	if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1)))
+		return ret;
+	if (!access_ok(VERIFY_WRITE, buffer, count))
+		return -EFAULT;
+	ret = 0;
+#if 0
+   spin_lock_irqsave(&s->lock, flags);
+   cm_update_ptr(s);
+   spin_unlock_irqrestore(&s->lock, flags);
+#endif
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		swptr = s->dma_adc.swptr;
+		cnt = s->dma_adc.dmasize-swptr;
+		if (s->dma_adc.count < cnt)
+			cnt = s->dma_adc.count;
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			start_adc(s);
+			if (file->f_flags & O_NONBLOCK)
+				return ret ? ret : -EAGAIN;
+			interruptible_sleep_on(&s->dma_adc.wait);
+			if (signal_pending(current))
+				return ret ? ret : -ERESTARTSYS;
+			continue;
+		}
+		if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt))
+			return ret ? ret : -EFAULT;
+		swptr = (swptr + cnt) % s->dma_adc.dmasize;
+		spin_lock_irqsave(&s->lock, flags);
+		s->dma_adc.swptr = swptr;
+		s->dma_adc.count -= cnt;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		start_adc(s);
+	}
+	return ret;
+}
+
+static ssize_t cm_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+	struct cm_state *s = (struct cm_state *)file->private_data;
+	ssize_t ret;
+	unsigned long flags;
+	unsigned swptr;
+	int cnt;
+
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (s->dma_dac.mapped)
+		return -ENXIO;
+	if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0)))
+		return ret;
+	if (!access_ok(VERIFY_READ, buffer, count))
+		return -EFAULT;
+	ret = 0;
+#if 0
+   spin_lock_irqsave(&s->lock, flags);
+   cm_update_ptr(s);
+   spin_unlock_irqrestore(&s->lock, flags);
+#endif
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		if (s->dma_dac.count < 0) {
+			s->dma_dac.count = 0;
+			s->dma_dac.swptr = s->dma_dac.hwptr;
+		}
+		swptr = s->dma_dac.swptr;
+		cnt = s->dma_dac.dmasize-swptr;
+		if (s->dma_dac.count + cnt > s->dma_dac.dmasize)
+			cnt = s->dma_dac.dmasize - s->dma_dac.count;
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			start_dac(s);
+			if (file->f_flags & O_NONBLOCK)
+				return ret ? ret : -EAGAIN;
+			interruptible_sleep_on(&s->dma_dac.wait);
+			if (signal_pending(current))
+				return ret ? ret : -ERESTARTSYS;
+			continue;
+		}
+		if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt))
+			return ret ? ret : -EFAULT;
+		swptr = (swptr + cnt) % s->dma_dac.dmasize;
+		spin_lock_irqsave(&s->lock, flags);
+		s->dma_dac.swptr = swptr;
+		s->dma_dac.count += cnt;
+		s->dma_dac.endcleared = 0;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		start_dac(s);
+	}
+	return ret;
+}
+
+static unsigned int cm_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct cm_state *s = (struct cm_state *)file->private_data;
+	unsigned long flags;
+	unsigned int mask = 0;
+
+	VALIDATE_STATE(s);
+	if (file->f_mode & FMODE_WRITE)
+		poll_wait(file, &s->dma_dac.wait, wait);
+	if (file->f_mode & FMODE_READ)
+		poll_wait(file, &s->dma_adc.wait, wait);
+	spin_lock_irqsave(&s->lock, flags);
+	cm_update_ptr(s);
+	if (file->f_mode & FMODE_READ) {
+		if (s->dma_adc.count >= (signed)s->dma_adc.fragsize)
+			mask |= POLLIN | POLLRDNORM;
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		if (s->dma_dac.mapped) {
+			if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) 
+				mask |= POLLOUT | POLLWRNORM;
+		} else {
+			if ((signed)s->dma_dac.dmasize >= s->dma_dac.count + (signed)s->dma_dac.fragsize)
+				mask |= POLLOUT | POLLWRNORM;
+		}
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	return mask;
+}
+
+static int cm_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct cm_state *s = (struct cm_state *)file->private_data;
+	struct dmabuf *db;
+	int ret;
+	unsigned long size;
+
+	VALIDATE_STATE(s);
+	if (vma->vm_flags & VM_WRITE) {
+		if ((ret = prog_dmabuf(s, 1)) != 0)
+			return ret;
+		db = &s->dma_dac;
+	} else if (vma->vm_flags & VM_READ) {
+		if ((ret = prog_dmabuf(s, 0)) != 0)
+			return ret;
+		db = &s->dma_adc;
+	} else 
+		return -EINVAL;
+	if (vma->vm_offset != 0)
+		return -EINVAL;
+	size = vma->vm_end - vma->vm_start;
+	if (size > (PAGE_SIZE << db->buforder))
+		return -EINVAL;
+	if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot))
+		return -EAGAIN;
+	db->mapped = 1;
+	return 0;
+}
+
+static int cm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct cm_state *s = (struct cm_state *)file->private_data;
+	unsigned long flags;
+        audio_buf_info abinfo;
+        count_info cinfo;
+	int val, mapped, ret;
+	unsigned char fmtm, fmtd;
+
+	VALIDATE_STATE(s);
+        mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) ||
+		((file->f_mode & FMODE_READ) && s->dma_adc.mapped);
+	switch (cmd) {
+	case OSS_GETVERSION:
+		return put_user(SOUND_VERSION, (int *)arg);
+
+	case SNDCTL_DSP_SYNC:
+		if (file->f_mode & FMODE_WRITE)
+			return drain_dac(s, 0/*file->f_flags & O_NONBLOCK*/);
+		return 0;
+		
+	case SNDCTL_DSP_SETDUPLEX:
+		return 0;
+
+	case SNDCTL_DSP_GETCAPS:
+		return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg);
+		
+        case SNDCTL_DSP_RESET:
+		if (file->f_mode & FMODE_WRITE) {
+			stop_dac(s);
+			synchronize_irq();
+			s->dma_dac.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0;
+		}
+		if (file->f_mode & FMODE_READ) {
+			stop_adc(s);
+			synchronize_irq();
+			s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0;
+		}
+		return 0;
+
+        case SNDCTL_DSP_SPEED:
+                get_user_ret(val, (int *)arg, -EFAULT);
+		if (val >= 0) {
+			if (file->f_mode & FMODE_READ) {
+				stop_adc(s);
+				s->dma_adc.ready = 0;
+				set_adc_rate(s, val);
+			}
+			if (file->f_mode & FMODE_WRITE) {
+				stop_dac(s);
+				s->dma_dac.ready = 0;
+				set_dac_rate(s, val);
+			}
+		}
+		return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg);
+		
+        case SNDCTL_DSP_STEREO:
+                get_user_ret(val, (int *)arg, -EFAULT);
+		fmtd = 0;
+		fmtm = ~0;
+		if (file->f_mode & FMODE_READ) {
+			stop_adc(s);
+			s->dma_adc.ready = 0;
+			if (val)
+				fmtd |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT;
+			else
+				fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_ADCSHIFT);
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			stop_dac(s);
+			s->dma_dac.ready = 0;
+			if (val)
+				fmtd |= CM_CFMT_STEREO << CM_CFMT_DACSHIFT;
+			else
+				fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_DACSHIFT);
+		}
+		set_fmt(s, fmtm, fmtd);
+		return 0;
+
+        case SNDCTL_DSP_CHANNELS:
+                get_user_ret(val, (int *)arg, -EFAULT);
+		if (val != 0) {
+			fmtd = 0;
+			fmtm = ~0;
+			if (file->f_mode & FMODE_READ) {
+				stop_adc(s);
+				s->dma_adc.ready = 0;
+				if (val >= 2)
+					fmtd |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT;
+				else
+					fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_ADCSHIFT);
+			}
+			if (file->f_mode & FMODE_WRITE) {
+				stop_dac(s);
+				s->dma_dac.ready = 0;
+				if (val >= 2)
+					fmtd |= CM_CFMT_STEREO << CM_CFMT_DACSHIFT;
+				else
+					fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_DACSHIFT);
+			}
+			set_fmt(s, fmtm, fmtd);
+		}
+		return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (CM_CFMT_STEREO << CM_CFMT_ADCSHIFT) 
+					   : (CM_CFMT_STEREO << CM_CFMT_DACSHIFT))) ? 2 : 1, (int *)arg);
+		
+	case SNDCTL_DSP_GETFMTS: /* Returns a mask */
+                return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg);
+		
+	case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/
+		get_user_ret(val, (int *)arg, -EFAULT);
+		if (val != AFMT_QUERY) {
+			fmtd = 0;
+			fmtm = ~0;
+			if (file->f_mode & FMODE_READ) {
+				stop_adc(s);
+				s->dma_adc.ready = 0;
+				if (val == AFMT_S16_LE)
+					fmtd |= CM_CFMT_16BIT << CM_CFMT_ADCSHIFT;
+				else
+					fmtm &= ~(CM_CFMT_16BIT << CM_CFMT_ADCSHIFT);
+			}
+			if (file->f_mode & FMODE_WRITE) {
+				stop_dac(s);
+				s->dma_dac.ready = 0;
+				if (val == AFMT_S16_LE)
+					fmtd |= CM_CFMT_16BIT << CM_CFMT_DACSHIFT;
+				else
+					fmtm &= ~(CM_CFMT_16BIT << CM_CFMT_DACSHIFT);
+			}
+			set_fmt(s, fmtm, fmtd);
+		}
+		return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (CM_CFMT_16BIT << CM_CFMT_ADCSHIFT) 
+					   : (CM_CFMT_16BIT << CM_CFMT_DACSHIFT))) ? AFMT_S16_LE : AFMT_U8, (int *)arg);
+		
+	case SNDCTL_DSP_POST:
+                return 0;
+
+        case SNDCTL_DSP_GETTRIGGER:
+		val = 0;
+		if (file->f_mode & FMODE_READ && s->enable & CM_CENABLE_RE) 
+			val |= PCM_ENABLE_INPUT;
+		if (file->f_mode & FMODE_WRITE && s->enable & CM_CENABLE_PE) 
+			val |= PCM_ENABLE_OUTPUT;
+		return put_user(val, (int *)arg);
+		
+	case SNDCTL_DSP_SETTRIGGER:
+		get_user_ret(val, (int *)arg, -EFAULT);
+		if (file->f_mode & FMODE_READ) {
+			if (val & PCM_ENABLE_INPUT) {
+				if (!s->dma_adc.ready && (ret =  prog_dmabuf(s, 1)))
+					return ret;
+				start_adc(s);
+			} else
+				stop_adc(s);
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			if (val & PCM_ENABLE_OUTPUT) {
+				if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0)))
+					return ret;
+				start_dac(s);
+			} else
+				stop_dac(s);
+		}
+		return 0;
+
+	case SNDCTL_DSP_GETOSPACE:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		if (!(s->enable & CM_CENABLE_PE) && (val = prog_dmabuf(s, 0)) != 0)
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		cm_update_ptr(s);
+		abinfo.fragsize = s->dma_dac.fragsize;
+                abinfo.bytes = s->dma_dac.dmasize - s->dma_dac.count;
+                abinfo.fragstotal = s->dma_dac.numfrag;
+                abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift;      
+		spin_unlock_irqrestore(&s->lock, flags);
+		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+
+	case SNDCTL_DSP_GETISPACE:
+		if (!(file->f_mode & FMODE_READ))
+			return -EINVAL;
+		if (!(s->enable & CM_CENABLE_RE) && (val = prog_dmabuf(s, 1)) != 0)
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		cm_update_ptr(s);
+		abinfo.fragsize = s->dma_adc.fragsize;
+                abinfo.bytes = s->dma_adc.count;
+                abinfo.fragstotal = s->dma_adc.numfrag;
+                abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift;      
+		spin_unlock_irqrestore(&s->lock, flags);
+		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+		
+        case SNDCTL_DSP_NONBLOCK:
+                file->f_flags |= O_NONBLOCK;
+                return 0;
+
+        case SNDCTL_DSP_GETODELAY:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		spin_lock_irqsave(&s->lock, flags);
+		cm_update_ptr(s);
+                val = s->dma_dac.count;
+		spin_unlock_irqrestore(&s->lock, flags);
+		return put_user(val, (int *)arg);
+
+        case SNDCTL_DSP_GETIPTR:
+		if (!(file->f_mode & FMODE_READ))
+			return -EINVAL;
+		spin_lock_irqsave(&s->lock, flags);
+		cm_update_ptr(s);
+                cinfo.bytes = s->dma_adc.total_bytes;
+                cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift;
+                cinfo.ptr = s->dma_adc.hwptr;
+		if (s->dma_adc.mapped)
+			s->dma_adc.count &= s->dma_adc.fragsize-1;
+		spin_unlock_irqrestore(&s->lock, flags);
+                return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+
+        case SNDCTL_DSP_GETOPTR:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		spin_lock_irqsave(&s->lock, flags);
+		cm_update_ptr(s);
+                cinfo.bytes = s->dma_dac.total_bytes;
+                cinfo.blocks = s->dma_dac.count >> s->dma_dac.fragshift;
+                cinfo.ptr = s->dma_dac.hwptr;
+		if (s->dma_dac.mapped)
+			s->dma_dac.count &= s->dma_dac.fragsize-1;
+		spin_unlock_irqrestore(&s->lock, flags);
+                return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+
+        case SNDCTL_DSP_GETBLKSIZE:
+		if (file->f_mode & FMODE_WRITE) {
+			if ((val = prog_dmabuf(s, 0)))
+				return val;
+			return put_user(s->dma_dac.fragsize, (int *)arg);
+		}
+		if ((val = prog_dmabuf(s, 1)))
+			return val;
+		return put_user(s->dma_adc.fragsize, (int *)arg);
+
+        case SNDCTL_DSP_SETFRAGMENT:
+                get_user_ret(val, (int *)arg, -EFAULT);
+		if (file->f_mode & FMODE_READ) {
+			s->dma_adc.ossfragshift = val & 0xffff;
+			s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff;
+			if (s->dma_adc.ossfragshift < 4)
+				s->dma_adc.ossfragshift = 4;
+			if (s->dma_adc.ossfragshift > 15)
+				s->dma_adc.ossfragshift = 15;
+			if (s->dma_adc.ossmaxfrags < 4)
+				s->dma_adc.ossmaxfrags = 4;
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			s->dma_dac.ossfragshift = val & 0xffff;
+			s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff;
+			if (s->dma_dac.ossfragshift < 4)
+				s->dma_dac.ossfragshift = 4;
+			if (s->dma_dac.ossfragshift > 15)
+				s->dma_dac.ossfragshift = 15;
+			if (s->dma_dac.ossmaxfrags < 4)
+				s->dma_dac.ossmaxfrags = 4;
+		}
+		return 0;
+
+        case SNDCTL_DSP_SUBDIVIDE:
+		if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) ||
+		    (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision))
+			return -EINVAL;
+                get_user_ret(val, (int *)arg, -EFAULT);
+		if (val != 1 && val != 2 && val != 4)
+			return -EINVAL;
+		if (file->f_mode & FMODE_READ)
+			s->dma_adc.subdivision = val;
+		if (file->f_mode & FMODE_WRITE)
+			s->dma_dac.subdivision = val;
+		return 0;
+
+        case SOUND_PCM_READ_RATE:
+		return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg);
+
+        case SOUND_PCM_READ_CHANNELS:
+		return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (CM_CFMT_STEREO << CM_CFMT_ADCSHIFT) : (CM_CFMT_STEREO << CM_CFMT_DACSHIFT))) ? 2 : 1, (int *)arg);
+
+        case SOUND_PCM_READ_BITS:
+		return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (CM_CFMT_16BIT << CM_CFMT_ADCSHIFT) : (CM_CFMT_16BIT << CM_CFMT_DACSHIFT))) ? 16 : 8, (int *)arg);
+
+        case SOUND_PCM_WRITE_FILTER:
+        case SNDCTL_DSP_SETSYNCRO:
+        case SOUND_PCM_READ_FILTER:
+                return -EINVAL;
+		
+	}
+	return mixer_ioctl(s, cmd, arg);
+}
+
+static int cm_open(struct inode *inode, struct file *file)
+{
+	int minor = MINOR(inode->i_rdev);
+	struct cm_state *s = devs;
+	unsigned char fmtm = ~0, fmts = 0;
+
+	while (s && ((s->dev_audio ^ minor) & ~0xf))
+		s = s->next;
+	if (!s)
+		return -ENODEV;
+       	VALIDATE_STATE(s);
+	file->private_data = s;
+	/* wait for device to become free */
+	down(&s->open_sem);
+	while (s->open_mode & file->f_mode) {
+		if (file->f_flags & O_NONBLOCK) {
+			up(&s->open_sem);
+			return -EBUSY;
+		}
+		up(&s->open_sem);
+		interruptible_sleep_on(&s->open_wait);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+		down(&s->open_sem);
+	}
+	if (file->f_mode & FMODE_READ) {
+		fmtm &= ~((CM_CFMT_STEREO | CM_CFMT_16BIT) << CM_CFMT_ADCSHIFT);
+		if ((minor & 0xf) == SND_DEV_DSP16)
+			fmts |= CM_CFMT_16BIT << CM_CFMT_ADCSHIFT;
+		s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0;
+		set_adc_rate(s, 8000);
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		fmtm &= ~((CM_CFMT_STEREO | CM_CFMT_16BIT) << CM_CFMT_DACSHIFT);
+		if ((minor & 0xf) == SND_DEV_DSP16)
+			fmts |= CM_CFMT_16BIT << CM_CFMT_DACSHIFT;
+		s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0;
+		set_dac_rate(s, 8000);
+	}
+	set_fmt(s, fmtm, fmts);
+	s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
+	up(&s->open_sem);
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int cm_release(struct inode *inode, struct file *file)
+{
+	struct cm_state *s = (struct cm_state *)file->private_data;
+
+	VALIDATE_STATE(s);
+	if (file->f_mode & FMODE_WRITE)
+		drain_dac(s, file->f_flags & O_NONBLOCK);
+	down(&s->open_sem);
+	if (file->f_mode & FMODE_WRITE) {
+		stop_dac(s);
+		dealloc_dmabuf(&s->dma_dac);
+	}
+	if (file->f_mode & FMODE_READ) {
+		stop_adc(s);
+		dealloc_dmabuf(&s->dma_adc);
+	}
+	s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE);
+	up(&s->open_sem);
+	wake_up(&s->open_wait);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static /*const*/ struct file_operations cm_audio_fops = {
+	&cm_llseek,
+	&cm_read,
+	&cm_write,
+	NULL,  /* readdir */
+	&cm_poll,
+	&cm_ioctl,
+	&cm_mmap,
+	&cm_open,
+	NULL,	/* flush */
+	&cm_release,
+	NULL,  /* fsync */
+	NULL,  /* fasync */
+	NULL,  /* check_media_change */
+	NULL,  /* revalidate */
+	NULL,  /* lock */
+};
+
+#ifdef CONFIG_SOUND_CMPCI_MIDI
+/* --------------------------------------------------------------------- */
+
+static ssize_t cm_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+	struct cm_state *s = (struct cm_state *)file->private_data;
+	ssize_t ret;
+	unsigned long flags;
+	unsigned ptr;
+	int cnt;
+
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (!access_ok(VERIFY_WRITE, buffer, count))
+		return -EFAULT;
+	ret = 0;
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		ptr = s->midi.ird;
+		cnt = MIDIINBUF - ptr;
+		if (s->midi.icnt < cnt)
+			cnt = s->midi.icnt;
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			if (file->f_flags & O_NONBLOCK)
+				return ret ? ret : -EAGAIN;
+			interruptible_sleep_on(&s->midi.iwait);
+			if (signal_pending(current))
+				return ret ? ret : -ERESTARTSYS;
+			continue;
+		}
+		if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt))
+			return ret ? ret : -EFAULT;
+		ptr = (ptr + cnt) % MIDIINBUF;
+		spin_lock_irqsave(&s->lock, flags);
+		s->midi.ird = ptr;
+		s->midi.icnt -= cnt;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+	}
+	return ret;
+}
+
+static ssize_t cm_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+	struct cm_state *s = (struct cm_state *)file->private_data;
+	ssize_t ret;
+	unsigned long flags;
+	unsigned ptr;
+	int cnt;
+
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (!access_ok(VERIFY_READ, buffer, count))
+		return -EFAULT;
+	ret = 0;
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		ptr = s->midi.owr;
+		cnt = MIDIOUTBUF - ptr;
+		if (s->midi.ocnt + cnt > MIDIOUTBUF)
+			cnt = MIDIOUTBUF - s->midi.ocnt;
+		if (cnt <= 0)
+			cm_handle_midi(s);
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			if (file->f_flags & O_NONBLOCK)
+				return ret ? ret : -EAGAIN;
+			interruptible_sleep_on(&s->midi.owait);
+			if (signal_pending(current))
+				return ret ? ret : -ERESTARTSYS;
+			continue;
+		}
+		if (copy_from_user(s->midi.obuf + ptr, buffer, cnt))
+			return ret ? ret : -EFAULT;
+		ptr = (ptr + cnt) % MIDIOUTBUF;
+		spin_lock_irqsave(&s->lock, flags);
+		s->midi.owr = ptr;
+		s->midi.ocnt += cnt;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		spin_lock_irqsave(&s->lock, flags);
+		cm_handle_midi(s);
+		spin_unlock_irqrestore(&s->lock, flags);
+	}
+	return ret;
+}
+
+static unsigned int cm_midi_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct cm_state *s = (struct cm_state *)file->private_data;
+	unsigned long flags;
+	unsigned int mask = 0;
+
+	VALIDATE_STATE(s);
+	if (file->f_mode & FMODE_WRITE)
+		poll_wait(file, &s->midi.owait, wait);
+	if (file->f_mode & FMODE_READ)
+		poll_wait(file, &s->midi.iwait, wait);
+	spin_lock_irqsave(&s->lock, flags);
+	if (file->f_mode & FMODE_READ) {
+		if (s->midi.icnt > 0)
+			mask |= POLLIN | POLLRDNORM;
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		if (s->midi.ocnt < MIDIOUTBUF)
+			mask |= POLLOUT | POLLWRNORM;
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	return mask;
+}
+
+static int cm_midi_open(struct inode *inode, struct file *file)
+{
+	int minor = MINOR(inode->i_rdev);
+	struct cm_state *s = devs;
+	unsigned long flags;
+
+	while (s && s->dev_midi != minor)
+		s = s->next;
+	if (!s)
+		return -ENODEV;
+       	VALIDATE_STATE(s);
+	file->private_data = s;
+	/* wait for device to become free */
+	down(&s->open_sem);
+	while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) {
+		if (file->f_flags & O_NONBLOCK) {
+			up(&s->open_sem);
+			return -EBUSY;
+		}
+		up(&s->open_sem);
+		interruptible_sleep_on(&s->open_wait);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+		down(&s->open_sem);
+	}
+	spin_lock_irqsave(&s->lock, flags);
+	if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) {
+		s->midi.ird = s->midi.iwr = s->midi.icnt = 0;
+		s->midi.ord = s->midi.owr = s->midi.ocnt = 0;
+		/* enable MPU-401 */
+		outb(inb(s->iobase + CODEC_CMI_FUNCTRL1) | 4, s->iobase + CODEC_CMI_FUNCTRL1);
+		outb(0xff, s->iomidi+1); /* reset command */
+		if (!(inb(s->iomidi+1) & 0x80))
+			inb(s->iomidi);
+		outb(0x3f, s->iomidi+1); /* uart command */
+		if (!(inb(s->iomidi+1) & 0x80))
+			inb(s->iomidi);
+		s->midi.ird = s->midi.iwr = s->midi.icnt = 0;
+		init_timer(&s->midi.timer);
+		s->midi.timer.expires = jiffies+1;
+		s->midi.timer.data = (unsigned long)s;
+		s->midi.timer.function = cm_midi_timer;
+		add_timer(&s->midi.timer);
+	}
+	if (file->f_mode & FMODE_READ) {
+		s->midi.ird = s->midi.iwr = s->midi.icnt = 0;
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		s->midi.ord = s->midi.owr = s->midi.ocnt = 0;
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE);
+	up(&s->open_sem);
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int cm_midi_release(struct inode *inode, struct file *file)
+{
+	struct cm_state *s = (struct cm_state *)file->private_data;
+        DECLARE_WAITQUEUE(wait, current);
+	unsigned long flags;
+	unsigned count, tmo;
+
+	VALIDATE_STATE(s);
+
+	if (file->f_mode & FMODE_WRITE) {
+		current->state = TASK_INTERRUPTIBLE;
+		add_wait_queue(&s->midi.owait, &wait);
+		for (;;) {
+			spin_lock_irqsave(&s->lock, flags);
+			count = s->midi.ocnt;
+			spin_unlock_irqrestore(&s->lock, flags);
+			if (count <= 0)
+				break;
+			if (signal_pending(current))
+				break;
+			if (file->f_flags & O_NONBLOCK) {
+				remove_wait_queue(&s->midi.owait, &wait);
+				current->state = TASK_RUNNING;
+				return -EBUSY;
+			}
+			tmo = (count * HZ) / 3100;
+			if (!schedule_timeout(tmo ? : 1) && tmo)
+				printk(KERN_DEBUG "cm: midi timed out??\n");
+		}
+		remove_wait_queue(&s->midi.owait, &wait);
+		current->state = TASK_RUNNING;
+	}
+	down(&s->open_sem);
+	s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE);
+	spin_lock_irqsave(&s->lock, flags);
+	if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) {
+		del_timer(&s->midi.timer);		
+		outb(0xff, s->iomidi+1); /* reset command */
+		if (!(inb(s->iomidi+1) & 0x80))
+			inb(s->iomidi);
+		/* disable MPU-401 */
+		outb(inb(s->iobase + CODEC_CMI_FUNCTRL1) & ~4, s->iobase + CODEC_CMI_FUNCTRL1);
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	up(&s->open_sem);
+	wake_up(&s->open_wait);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static /*const*/ struct file_operations cm_midi_fops = {
+	&cm_llseek,
+	&cm_midi_read,
+	&cm_midi_write,
+	NULL,  /* readdir */
+	&cm_midi_poll,
+	NULL,  /* ioctl */
+	NULL,  /* mmap */
+	&cm_midi_open,
+	NULL,	/* flush */
+	&cm_midi_release,
+	NULL,  /* fsync */
+	NULL,  /* fasync */
+	NULL,  /* check_media_change */
+	NULL,  /* revalidate */
+	NULL,  /* lock */
+};
+#endif
+
+/* --------------------------------------------------------------------- */
+
+#ifdef CONFIG_SOUND_CMPCI_FM
+static int cm_dmfm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	static const unsigned char op_offset[18] = {
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
+		0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
+		0x10, 0x11, 0x12, 0x13, 0x14, 0x15
+	};
+	struct cm_state *s = (struct cm_state *)file->private_data;
+	struct dm_fm_voice v;
+	struct dm_fm_note n;
+	struct dm_fm_params p;
+	unsigned int io;
+	unsigned int regb;
+
+	switch (cmd) {		
+	case FM_IOCTL_RESET:
+		for (regb = 0xb0; regb < 0xb9; regb++) {
+			outb(regb, s->iosynth);
+			outb(0, s->iosynth+1);
+			outb(regb, s->iosynth+2);
+			outb(0, s->iosynth+3);
+		}
+		return 0;
+
+	case FM_IOCTL_PLAY_NOTE:
+		if (copy_from_user(&n, (void *)arg, sizeof(n)))
+			return -EFAULT;
+		if (n.voice >= 18)
+			return -EINVAL;
+		if (n.voice >= 9) {
+			regb = n.voice - 9;
+			io = s->iosynth+2;
+		} else {
+			regb = n.voice;
+			io = s->iosynth;
+		}
+		outb(0xa0 + regb, io);
+		outb(n.fnum & 0xff, io+1);
+		outb(0xb0 + regb, io);
+		outb(((n.fnum >> 8) & 3) | ((n.octave & 7) << 2) | ((n.key_on & 1) << 5), io+1);
+		return 0;
+
+	case FM_IOCTL_SET_VOICE:
+		if (copy_from_user(&v, (void *)arg, sizeof(v)))
+			return -EFAULT;
+		if (v.voice >= 18)
+			return -EINVAL;
+		regb = op_offset[v.voice];
+		io = s->iosynth + ((v.op & 1) << 1);
+		outb(0x20 + regb, io);
+		outb(((v.am & 1) << 7) | ((v.vibrato & 1) << 6) | ((v.do_sustain & 1) << 5) | 
+		     ((v.kbd_scale & 1) << 4) | (v.harmonic & 0xf), io+1);
+		outb(0x40 + regb, io);
+		outb(((v.scale_level & 0x3) << 6) | (v.volume & 0x3f), io+1);
+		outb(0x60 + regb, io);
+		outb(((v.attack & 0xf) << 4) | (v.decay & 0xf), io+1);
+		outb(0x80 + regb, io);
+		outb(((v.sustain & 0xf) << 4) | (v.release & 0xf), io+1);
+		outb(0xe0 + regb, io);
+		outb(v.waveform & 0x7, io+1);
+		if (n.voice >= 9) {
+			regb = n.voice - 9;
+			io = s->iosynth+2;
+		} else {
+			regb = n.voice;
+			io = s->iosynth;
+		}
+		outb(0xc0 + regb, io);
+		outb(((v.right & 1) << 5) | ((v.left & 1) << 4) | ((v.feedback & 7) << 1) |
+		     (v.connection & 1), io+1);
+		return 0;
+		
+	case FM_IOCTL_SET_PARAMS:
+		if (copy_from_user(&p, (void *)arg, sizeof(p)))
+			return -EFAULT;
+		outb(0x08, s->iosynth);
+		outb((p.kbd_split & 1) << 6, s->iosynth+1);
+		outb(0xbd, s->iosynth);
+		outb(((p.am_depth & 1) << 7) | ((p.vib_depth & 1) << 6) | ((p.rhythm & 1) << 5) | ((p.bass & 1) << 4) |
+		     ((p.snare & 1) << 3) | ((p.tomtom & 1) << 2) | ((p.cymbal & 1) << 1) | (p.hihat & 1), s->iosynth+1);
+		return 0;
+
+	case FM_IOCTL_SET_OPL:
+		outb(4, s->iosynth+2);
+		outb(arg, s->iosynth+3);
+		return 0;
+
+	case FM_IOCTL_SET_MODE:
+		outb(5, s->iosynth+2);
+		outb(arg & 1, s->iosynth+3);
+		return 0;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static int cm_dmfm_open(struct inode *inode, struct file *file)
+{
+	int minor = MINOR(inode->i_rdev);
+	struct cm_state *s = devs;
+
+	while (s && s->dev_dmfm != minor)
+		s = s->next;
+	if (!s)
+		return -ENODEV;
+       	VALIDATE_STATE(s);
+	file->private_data = s;
+	/* wait for device to become free */
+	down(&s->open_sem);
+	while (s->open_mode & FMODE_DMFM) {
+		if (file->f_flags & O_NONBLOCK) {
+			up(&s->open_sem);
+			return -EBUSY;
+		}
+		up(&s->open_sem);
+		interruptible_sleep_on(&s->open_wait);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+		down(&s->open_sem);
+	}
+	/* init the stuff */
+	outb(1, s->iosynth);
+	outb(0x20, s->iosynth+1); /* enable waveforms */
+	outb(4, s->iosynth+2);
+	outb(0, s->iosynth+3);  /* no 4op enabled */
+	outb(5, s->iosynth+2);
+	outb(1, s->iosynth+3);  /* enable OPL3 */
+	s->open_mode |= FMODE_DMFM;
+	up(&s->open_sem);
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int cm_dmfm_release(struct inode *inode, struct file *file)
+{
+	struct cm_state *s = (struct cm_state *)file->private_data;
+	unsigned int regb;
+
+	VALIDATE_STATE(s);
+	down(&s->open_sem);
+	s->open_mode &= ~FMODE_DMFM;
+	for (regb = 0xb0; regb < 0xb9; regb++) {
+		outb(regb, s->iosynth);
+		outb(0, s->iosynth+1);
+		outb(regb, s->iosynth+2);
+		outb(0, s->iosynth+3);
+	}
+	up(&s->open_sem);
+	wake_up(&s->open_wait);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static /*const*/ struct file_operations cm_dmfm_fops = {
+	&cm_llseek,
+	NULL,  /* read */
+	NULL,  /* write */
+	NULL,  /* readdir */
+	NULL,  /* poll */
+	&cm_dmfm_ioctl,
+	NULL,  /* mmap */
+	&cm_dmfm_open,
+	NULL,	/* flush */
+	&cm_dmfm_release,
+	NULL,  /* fsync */
+	NULL,  /* fasync */
+	NULL,  /* check_media_change */
+	NULL,  /* revalidate */
+	NULL,  /* lock */
+};
+#endif /* CONFIG_SOUND_CMPCI_FM */
+
+/* --------------------------------------------------------------------- */
+
+/* maximum number of devices */
+#define NR_DEVICE 5
+
+#if 0
+static int reverb[NR_DEVICE] = { 0, };
+
+static int wavetable[NR_DEVICE] = { 0, };
+#endif
+
+/* --------------------------------------------------------------------- */
+
+static struct initvol {
+	int mixch;
+	int vol;
+} initvol[] __initdata = {
+	{ SOUND_MIXER_WRITE_CD, 0x4040 },
+	{ SOUND_MIXER_WRITE_LINE, 0x4040 },
+	{ SOUND_MIXER_WRITE_MIC, 0x4040 },
+	{ SOUND_MIXER_WRITE_SYNTH, 0x4040 },
+	{ SOUND_MIXER_WRITE_VOLUME, 0x4040 },
+	{ SOUND_MIXER_WRITE_PCM, 0x4040 }
+};
+
+#ifdef MODULE
+__initfunc(int init_module(void))
+#else
+__initfunc(int init_cmpci(void))
+#endif
+{
+	struct cm_state *s;
+	struct pci_dev *pcidev = NULL;
+	mm_segment_t fs;
+	int i, val, index = 0;
+	struct {
+		unsigned short	deviceid;
+		char		*devicename;
+	} devicetable[] =
+	{
+		{ PCI_DEVICE_ID_CMEDIA_CM8338A, "CM8338A" },
+		{ PCI_DEVICE_ID_CMEDIA_CM8338B, "CM8338B" },
+		{ PCI_DEVICE_ID_CMEDIA_CM8738,  "CM8738" },
+	};
+	char	*devicename = "unknown";
+
+#ifdef CONFIG_PCI
+	if (!pci_present())   /* No PCI bus in this machine! */
+#endif
+		return -ENODEV;
+	printk(KERN_INFO "cm: version v1.1 time " __TIME__ " " __DATE__ "\n");
+#if 0
+	if (!(wavetable_mem = __get_free_pages(GFP_KERNEL, 20-PAGE_SHIFT)))
+		printk(KERN_INFO "cm: cannot allocate 1MB of contiguous nonpageable memory for wavetable data\n");
+#endif
+	while (index < NR_DEVICE && pcidev == NULL && (
+ 	       (pcidev = pci_find_device(PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338A, pcidev)) ||
+	       (pcidev = pci_find_device(PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338B, pcidev)) ||
+	       (pcidev = pci_find_device(PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8738, pcidev)))) {
+		if (pcidev->irq == 0)
+			continue;
+		if (!(s = kmalloc(sizeof(struct cm_state), GFP_KERNEL))) {
+			printk(KERN_WARNING "cm: out of memory\n");
+			continue;
+		}
+		/* search device name */
+		for (i = 0; i < sizeof(devicetable) / sizeof(devicetable[0]); i++)
+		{
+			if (devicetable[i].deviceid == pcidev->device)
+			{
+				devicename = devicetable[i].devicename;
+				break;
+			}
+		}
+		memset(s, 0, sizeof(struct cm_state));
+		init_waitqueue_head(&s->dma_adc.wait);
+		init_waitqueue_head(&s->dma_dac.wait);
+		init_waitqueue_head(&s->open_wait);
+		init_waitqueue_head(&s->midi.iwait);
+		init_waitqueue_head(&s->midi.owait);
+		init_MUTEX(&s->open_sem);
+		s->magic = CM_MAGIC;
+		s->iobase = pcidev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK;
+#ifdef CONFIG_SOUND_CMPCI_FM
+		s->iosynth = 0x388;
+#endif
+#ifdef CONFIG_SOUND_CMPCI_MIDI
+		s->iomidi = 0x330;
+#endif
+		if (s->iobase == 0)
+			continue;
+		s->irq = pcidev->irq;
+
+		if (check_region(s->iobase, CM_EXTENT_CODEC)) {
+			printk(KERN_ERR "cm: io ports %#x-%#x in use\n", s->iobase, s->iobase+CM_EXTENT_CODEC-1);
+			goto err_region5;
+		}
+		request_region(s->iobase, CM_EXTENT_CODEC, "cmpci");
+#ifdef CONFIG_SOUND_CMPCI_MIDI
+		if (check_region(s->iomidi, CM_EXTENT_MIDI)) {
+			printk(KERN_ERR "cm: io ports %#x-%#x in use\n", s->iomidi, s->iomidi+CM_EXTENT_MIDI-1);
+			goto err_region4;
+		}
+		request_region(s->iomidi, CM_EXTENT_MIDI, "cmpci Midi");
+		/* set IO based at 0x330 */
+		outb(inb(s->iobase + CODEC_CMI_LEGACY_CTRL + 3) & ~0x60, s->iobase + CODEC_CMI_LEGACY_CTRL + 3);
+#endif
+#ifdef CONFIG_SOUND_CMPCI_FM
+		if (check_region(s->iosynth, CM_EXTENT_SYNTH)) {
+			printk(KERN_ERR "cm: io ports %#x-%#x in use\n", s->iosynth, s->iosynth+CM_EXTENT_SYNTH-1);
+			goto err_region1;
+		}
+		request_region(s->iosynth, CM_EXTENT_SYNTH, "cmpci FM");
+		/* enable FM */
+		outb(inb(s->iobase + CODEC_CMI_MISC_CTRL + 2) | 8, s->iobase + CODEC_CMI_MISC_CTRL);
+#endif
+		/* initialize codec registers */
+		outb(0, s->iobase + CODEC_CMI_INT_HLDCLR + 2);  /* disable ints */
+		outb(0, s->iobase + CODEC_CMI_FUNCTRL0 + 2); /* reset channels */
+		/* reset mixer */
+		wrmixer(s, DSP_MIX_DATARESETIDX, 0);
+
+		/* request irq */
+		if (request_irq(s->irq, cm_interrupt, SA_SHIRQ, "cmpci", s)) {
+			printk(KERN_ERR "cm: irq %u in use\n", s->irq);
+			goto err_irq;
+		}
+		printk(KERN_INFO "cm: found %s adapter at io %#06x irq %u\n",
+		       devicename, s->iobase, s->irq);
+		/* register devices */
+		if ((s->dev_audio = register_sound_dsp(&cm_audio_fops, -1)) < 0)
+			goto err_dev1;
+		if ((s->dev_mixer = register_sound_mixer(&cm_mixer_fops, -1)) < 0)
+			goto err_dev2;
+#ifdef CONFIG_SOUND_CMPCI_MIDI
+		if ((s->dev_midi = register_sound_midi(&cm_midi_fops, -1)) < 0)
+			goto err_dev3;
+#endif
+#ifdef CONFIG_SOUND_CMPCI_FM
+		if ((s->dev_dmfm = register_sound_special(&cm_dmfm_fops, 15 /* ?? */)) < 0)
+			goto err_dev4;
+#endif
+		/* initialize the chips */
+		fs = get_fs();
+		set_fs(KERNEL_DS);
+		/* set mixer output */
+		frobindir(s, DSP_MIX_OUTMIXIDX, 0x1f, 0x1f);
+		/* set mixer input */
+		val = SOUND_MASK_LINE|SOUND_MASK_SYNTH|SOUND_MASK_CD|SOUND_MASK_MIC;
+		mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val);
+		for (i = 0; i < sizeof(initvol)/sizeof(initvol[0]); i++) {
+			val = initvol[i].vol;
+			mixer_ioctl(s, initvol[i].mixch, (unsigned long)&val);
+		}
+		set_fs(fs);
+		/* queue it for later freeing */
+		s->next = devs;
+		devs = s;
+		index++;
+		continue;
+
+	err_dev4:
+		unregister_sound_midi(s->dev_midi);
+	err_dev3:
+		unregister_sound_mixer(s->dev_mixer);
+	err_dev2:
+		unregister_sound_dsp(s->dev_audio);
+	err_dev1:
+		printk(KERN_ERR "cm: cannot register misc device\n");
+		free_irq(s->irq, s);
+	err_irq:
+#ifdef CONFIG_SOUND_CMPCI_FM
+		release_region(s->iosynth, CM_EXTENT_SYNTH);
+	err_region1:
+#endif
+#ifdef CONFIG_SOUND_CMPCI_MIDI
+		release_region(s->iomidi, CM_EXTENT_MIDI);
+#endif
+	err_region4:
+		release_region(s->iobase, CM_EXTENT_CODEC);
+	err_region5:
+		kfree_s(s, sizeof(struct cm_state));
+	}
+	if (!devs) {
+		if (wavetable_mem)
+			free_pages(wavetable_mem, 20-PAGE_SHIFT);
+		return -ENODEV;
+	}
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+
+#if 0
+MODULE_PARM(wavetable, "1-" __MODULE_STRING(NR_DEVICE) "i");
+MODULE_PARM_DESC(wavetable, "if 1 the wavetable synth is enabled");
+#endif
+
+MODULE_AUTHOR("ChenLi Tien, cltien@home.com");
+MODULE_DESCRIPTION("CMPCI Audio Driver");
+
+void cleanup_module(void)
+{
+	struct cm_state *s;
+
+	while ((s = devs)) {
+		devs = devs->next;
+		outb(0, s->iobase + CODEC_CMI_INT_HLDCLR + 2);  /* disable ints */
+		synchronize_irq();
+		outb(0, s->iobase + CODEC_CMI_FUNCTRL0 + 2); /* reset channels */
+		free_irq(s->irq, s);
+
+		/* reset mixer */
+		wrmixer(s, DSP_MIX_DATARESETIDX, 0);
+
+		release_region(s->iobase, CM_EXTENT_CODEC);
+#ifdef CONFIG_SOUND_CMPCI_MIDI
+		release_region(s->iomidi, CM_EXTENT_MIDI);
+#endif
+#ifdef CONFIG_SOUND_CMPCI_FM
+		release_region(s->iosynth, CM_EXTENT_SYNTH);
+#endif
+		unregister_sound_dsp(s->dev_audio);
+		unregister_sound_mixer(s->dev_mixer);
+#ifdef CONFIG_SOUND_CMPCI_MIDI
+		unregister_sound_midi(s->dev_midi);
+#endif
+#ifdef CONFIG_SOUND_CMPCI_FM
+		unregister_sound_special(s->dev_dmfm);
+#endif
+		kfree_s(s, sizeof(struct cm_state));
+	}
+	if (wavetable_mem)
+		free_pages(wavetable_mem, 20-PAGE_SHIFT);
+	printk(KERN_INFO "cm: unloading\n");
+}
+
+#endif /* MODULE */
diff -u --recursive --new-file v2.3.3/linux/drivers/sound/es1370.c linux/drivers/sound/es1370.c
--- v2.3.3/linux/drivers/sound/es1370.c	Fri May 14 18:55:22 1999
+++ linux/drivers/sound/es1370.c	Sat May 22 13:05:35 1999
@@ -33,8 +33,8 @@
  *            to make the card a four channel one: use dsp to output two
  *            channels to LINE and dac to output the other two channels to
  *            SPKR. Set the mixer to only output synth to SPKR.
- *   micz     it looks like this changes the MIC input impedance. I don't know
- *            any detail though.
+ *   micbias  sets the +5V bias to the mic if using an electretmic.
+ *            
  *
  *  Note: sync mode is not yet supported (i.e. running dsp and dac from the same
  *  clock source)
@@ -92,6 +92,12 @@
  *                     Alpha fixes reported by Peter Jones <pjones@redhat.com>
  *                     Note: joystick address handling might still be wrong on archs
  *                     other than i386
+ *    10.05.99   0.21  Added support for an electret mic for SB PCI64
+ *                     to the Linux kernel sound driver. This mod also straighten
+ *                     out the question marks around the mic impedance setting
+ *                     (micz). From Kim.Berts@fisub.mail.abb.com
+ *    11.05.99   0.22  Implemented the IMIX call to mute recording monitor.
+ *                     Guenter Geiger <geiger@epy.co.at>
  *
  * some important things missing in Ensoniq documentation:
  *
@@ -107,8 +113,8 @@
  * The card uses a 22.5792 MHz crystal.
  * The LINEIN jack may be converted to an AOUT jack by
  * setting pin 47 (XCTL0) of the ES1370 to high.
- * Pin 48 (XCTL1) of the ES1370 presumably changes the input impedance of the
- * MIC jack.
+ * Pin 48 (XCTL1) of the ES1370 sets the +5V bias for an electretmic
+ * 
  *
  */
 
@@ -190,7 +196,7 @@
 #define DAC2_DIVTOSR(x) (1411200/((x)+2))
 
 #define CTRL_ADC_STOP   0x80000000  /* 1 = ADC stopped */
-#define CTRL_XCTL1      0x40000000  /* ? mic impedance */
+#define CTRL_XCTL1      0x40000000  /* electret mic bias */
 #define CTRL_OPEN       0x20000000  /* no function, can be read and written */
 #define CTRL_PCLKDIV    0x1fff0000  /* ADC/DAC2 clock divider */
 #define CTRL_SH_PCLKDIV 16
@@ -301,6 +307,7 @@
 		unsigned int recsrc;
 		unsigned int modcnt;
 		unsigned short micpreamp;
+	        unsigned int imix;
 	} mix;
 
 	/* wave stuff */
@@ -839,7 +846,8 @@
 			return put_user(s->mix.recsrc, (int *)arg);
 			
                 case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */
-			for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+			val = SOUND_MASK_IMIX;
+			for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
 				if (mixtable[i].avail)
 					val |= 1 << i;
 			return put_user(val, (int *)arg);
@@ -858,6 +866,9 @@
 			
                 case SOUND_MIXER_CAPS:
 			return put_user(0, (int *)arg);
+		
+		case SOUND_MIXER_IMIX:
+			return put_user(s->mix.imix, (int *)arg);
 
 		default:
 			i = _IOC_NR(cmd);
@@ -870,6 +881,14 @@
 		return -EINVAL;
 	s->mix.modcnt++;
 	switch (_IOC_NR(cmd)) {
+
+	case SOUND_MIXER_IMIX:
+		if (arg == 0) 
+			return -EFAULT;
+		get_user_ret(s->mix.imix,(int *)arg, -EFAULT);
+		val = s->mix.recsrc;
+		/* fall through */
+
 	case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */
 		get_user_ret(val, (int *)arg, -EFAULT);
 		for (j = i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
@@ -886,7 +905,10 @@
 		wrcodec(s, 0x13, j & 0xaa);
 		wrcodec(s, 0x14, (j >> 8) & 0x17);
 		wrcodec(s, 0x15, (j >> 8) & 0x0f);
-		i = (j & 0x37f) | ((j << 1) & 0x3000) | 0xc30;
+		i = (j & 0x37f) | ((j << 1) & 0x3000) | 0xc60;
+		if (!s->mix.imix) {
+		     i &= 0xff60;  /* mute record and line monitor */
+		}
 		wrcodec(s, 0x10, i);
 		wrcodec(s, 0x11, i >> 8);
 		return 0;
@@ -2262,7 +2284,7 @@
 static int joystick[NR_DEVICE] = { 0, };
 #endif
 static int lineout[NR_DEVICE] = { 0, };
-static int micz[NR_DEVICE] = { 0, };
+static int micbias[NR_DEVICE] = { 0, };
 
 /* --------------------------------------------------------------------- */
 
@@ -2295,7 +2317,7 @@
 
 	if (!pci_present())   /* No PCI bus in this machine! */
 		return -ENODEV;
-	printk(KERN_INFO "es1370: version v0.20 time " __TIME__ " " __DATE__ "\n");
+	printk(KERN_INFO "es1370: version v0.22 time " __TIME__ " " __DATE__ "\n");
 	while (index < NR_DEVICE && 
 	       (pcidev = pci_find_device(PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1370, pcidev))) {
 		if (pcidev->base_address[0] == 0 || 
@@ -2328,8 +2350,10 @@
 			goto err_irq;
 		}
 		/* initialize codec registers */
-		s->ctrl = CTRL_CDC_EN | CTRL_SERR_DIS | (DAC2_SRTODIV(8000) << CTRL_SH_PCLKDIV) | (1 << CTRL_SH_WTSRSEL);
-		if (joystick[index]) {
+		/* note: setting CTRL_SERR_DIS is reported to break
+		 * mic bias setting (by Kim.Berts@fisub.mail.abb.com) */
+	        s->ctrl = CTRL_CDC_EN | (DAC2_SRTODIV(8000) << CTRL_SH_PCLKDIV) | (1 << CTRL_SH_WTSRSEL);
+	        if (joystick[index]) {
 			if (check_region(0x200, JOY_EXTENT))
 				printk(KERN_ERR "es1370: io port 0x200 in use\n");
 			else
@@ -2337,7 +2361,7 @@
 		}
 		if (lineout[index])
 			s->ctrl |= CTRL_XCTL0;
-		if (micz[index])
+		if (micbias[index])
 			s->ctrl |= CTRL_XCTL1;
 		s->sctrl = 0;
 		printk(KERN_INFO "es1370: found adapter at io %#lx irq %u\n"
@@ -2361,6 +2385,7 @@
 		wrcodec(s, 0x17, 0); /* CODEC ADC and CODEC DAC use {LR,B}CLK2 and run off the LRCLK2 PLL; program DAC_SYNC=0!!  */
 		wrcodec(s, 0x18, 0); /* recording source is mixer */
 		wrcodec(s, 0x19, s->mix.micpreamp = 1); /* turn on MIC preamp */
+		s->mix.imix = 1;
 		fs = get_fs();
 		set_fs(KERNEL_DS);
 		val = SOUND_MASK_LINE|SOUND_MASK_SYNTH|SOUND_MASK_CD;
@@ -2403,8 +2428,8 @@
 MODULE_PARM_DESC(joystick, "if 1 enables joystick interface (still need separate driver)");
 MODULE_PARM(lineout, "1-" __MODULE_STRING(NR_DEVICE) "i");
 MODULE_PARM_DESC(lineout, "if 1 the LINE input is converted to LINE out");
-MODULE_PARM(micz, "1-" __MODULE_STRING(NR_DEVICE) "i");
-MODULE_PARM_DESC(micz, "changes (??) the microphone impedance");
+MODULE_PARM(micbias, "1-" __MODULE_STRING(NR_DEVICE) "i");
+MODULE_PARM_DESC(micbias, "sets the +5V bias for an electret microphone");
 
 MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
 MODULE_DESCRIPTION("ES1370 AudioPCI Driver");
diff -u --recursive --new-file v2.3.3/linux/drivers/sound/lowlevel/awe_wave.c linux/drivers/sound/lowlevel/awe_wave.c
--- v2.3.3/linux/drivers/sound/lowlevel/awe_wave.c	Mon May 17 09:55:22 1999
+++ linux/drivers/sound/lowlevel/awe_wave.c	Wed May 26 09:35:00 1999
@@ -979,10 +979,10 @@
 }
 #else
 
-static DECLARE_WAIT_QUEUE_HEAD(awe_sleeper);
 static void awe_wait(unsigned short delay)
 {
-	interruptible_sleep_on_timeout(&awe_sleeper, (HZ * (unsigned long)delay + 44099) / 44100);
+	current->state = TASK_INTERRUPTIBLE;
+	schedule_timeout((HZ*(unsigned long)delay + 44099)/44100);
 }
 #endif /* wait by loop */
 
diff -u --recursive --new-file v2.3.3/linux/drivers/sound/sb_common.c linux/drivers/sound/sb_common.c
--- v2.3.3/linux/drivers/sound/sb_common.c	Mon Apr 12 16:18:27 1999
+++ linux/drivers/sound/sb_common.c	Wed May 26 09:35:00 1999
@@ -742,10 +742,7 @@
 					hw_config->name = "Sound Blaster Pro (8 BIT ONLY)";
 				break;
 			case MDL_ESS:
-				if (!ess_dsp_init(devc, hw_config)) {
-					release_region (hw_config->io_base, 16);
-					return 0;
-				}
+				ess_dsp_init(devc, hw_config);
 				break;
 			}
 			break;
@@ -887,6 +884,9 @@
 
 	if (devc && devc->base == hw_config->io_base)
 	{
+		if ((devc->model & MDL_ESS) && devc->pcibase)
+			release_region(devc->pcibase, 8);
+
 		release_region(devc->base, 16);
 
 		if (!(devc->caps & SB_NO_AUDIO))
@@ -1190,7 +1190,16 @@
 
 void attach_sbmpu(struct address_info *hw_config)
 {
-#if defined(CONFIG_MIDI) && defined(CONFIG_UART401)
+	if (last_sb->model == MDL_ESS) {
+#if defined(CONFIG_SOUND_MPU401)
+		attach_mpu401(hw_config);
+		if (last_sb->irq == -hw_config->irq) {
+			last_sb->midi_irq_cookie=(void *)hw_config->slots[1];
+		}
+#endif
+		return;
+	}
+#if defined(CONFIG_UART401)
 	attach_uart401(hw_config);
 	last_sb->midi_irq_cookie=midi_devs[hw_config->slots[4]]->devc;
 #endif
@@ -1198,7 +1207,6 @@
 
 int probe_sbmpu(struct address_info *hw_config)
 {
-#if defined(CONFIG_MIDI) && defined(CONFIG_UART401)
 	sb_devc *devc = last_devc;
 
 	if (last_devc == NULL)
@@ -1209,6 +1217,23 @@
 	if (hw_config->io_base <= 0)
 		return 0;
 
+#if defined(CONFIG_SOUND_MPU401)
+	if (devc->model == MDL_ESS)
+	{
+		if (check_region(hw_config->io_base, 2))
+		{
+			printk(KERN_ERR "sbmpu: I/O port conflict (%x)\n", hw_config->io_base);
+			return 0;
+		}
+		if (!ess_midi_init(devc, hw_config))
+			return 0;
+		hw_config->name = "ESS1xxx MPU";
+		devc->midi_irq_cookie = -1;
+		return probe_mpu401(hw_config);
+	}
+#endif
+
+#if defined(CONFIG_UART401)
 	if (check_region(hw_config->io_base, 4))
 	{
 		printk(KERN_ERR "sbmpu: I/O port conflict (%x)\n", hw_config->io_base);
@@ -1228,14 +1253,6 @@
 				sb16_set_mpu_port(devc, hw_config);
 			break;
 
-		case MDL_ESS:
-			if (hw_config->irq < 3 || hw_config->irq == devc->irq)
-				hw_config->irq = -devc->irq;
-			if (!ess_midi_init(devc, hw_config))
-				return 0;
-			hw_config->name = "ESS ES1688";
-			break;
-
 		case MDL_JAZZ:
 			if (hw_config->irq < 3 || hw_config->irq == devc->irq)
 				hw_config->irq = -devc->irq;
@@ -1258,7 +1275,13 @@
 
 void unload_sbmpu(struct address_info *hw_config)
 {
-#if defined(CONFIG_MIDI) && defined(CONFIG_UART401)
+#if defined(CONFIG_SOUND_MPU401)
+	if (!strcmp (hw_config->name, "ESS1xxx MPU")) {
+		unload_mpu401(hw_config);
+		return;
+	}
+#endif
+#if defined(CONFIG_UART401)
 	unload_uart401(hw_config);
 #endif
 }
diff -u --recursive --new-file v2.3.3/linux/drivers/sound/sb_ess.c linux/drivers/sound/sb_ess.c
--- v2.3.3/linux/drivers/sound/sb_ess.c	Mon May 10 13:01:21 1999
+++ linux/drivers/sound/sb_ess.c	Wed May 26 09:35:00 1999
@@ -40,6 +40,10 @@
  *								recording problems for high samplerates. I
  *								fixed this by removing ess_calc_best_speed ()
  *								and just doing what the documentation says. 
+ *Javier Achirica(May 15 1999): Major cleanup, MPU IRQ sharing, hardware
+ *								volume support, PNP chip configuration,
+ *								full duplex in most cards, sample rate fine
+ *								tuning.
  *
  * This files contains ESS chip specifics. It's based on the existing ESS
  * handling as it resided in sb_common.c, sb_mixer.c and sb_audio.c. This
@@ -192,12 +196,27 @@
 
 int esstype = ESSTYPE_LIKE20; /* module parameter in sb_card.c */
 
+#define SUBMDL_ES688	0x00	/* Subtype ES688 for specific handling */
+#define SUBMDL_ES1688	0x08	/* Subtype ES1688 for specific handling */
 #define SUBMDL_ES1788	0x10	/* Subtype ES1788 for specific handling */
 #define SUBMDL_ES1868	0x11	/* Subtype ES1868 for specific handling */
 #define SUBMDL_ES1869	0x12	/* Subtype ES1869 for specific handling */
 #define SUBMDL_ES1878	0x13	/* Subtype ES1878 for specific handling */
-#define SUBMDL_ES1887	0x14	/* Subtype ES1887 for specific handling */
-#define SUBMDL_ES1888	0x15	/* Subtype ES1888 for specific handling */
+#define SUBMDL_ES1879	0x14	/* Subtype ES1879 for specific handling */
+#define SUBMDL_ES1887	0x15	/* Subtype ES1887 for specific handling */
+#define SUBMDL_ES1888	0x16	/* Subtype ES1888 for specific handling */
+
+	/* Recording mixer, stereo full duplex */
+#define ESSCAP_NEW		0x00000100
+	/* ISA PnP configuration */
+#define ESSCAP_PNP		0x00000200
+	/* Full duplex, 6-bit volume, hardware volume controls */
+#define ESSCAP_ES18		0x00000400
+	/* New interrupt handling system (ESS 1887) */
+#define ESSCAP_IRQ		0x00000800
+
+#define ESSFMT_16		0x00000001
+#define ESSFMT_SIGNED	0x00000004
 
 #ifdef FKS_LOGGING
 static void ess_show_mixerregs (sb_devc *devc);
@@ -213,52 +232,6 @@
  *																			*
  ****************************************************************************/
 
-struct ess_command {short cmd; short data;};
-
-/*
- * Commands for initializing Audio 1 for input (record)
- */
-static struct ess_command ess_i08m[] =		/* input 8 bit mono */
-	{ {0xb7, 0x51}, {0xb7, 0xd0}, {-1, 0} };
-static struct ess_command ess_i16m[] =		/* input 16 bit mono */
-	{ {0xb7, 0x71}, {0xb7, 0xf4}, {-1, 0} };
-static struct ess_command ess_i08s[] =		/* input 8 bit stereo */
-	{ {0xb7, 0x51}, {0xb7, 0x98}, {-1, 0} };
-static struct ess_command ess_i16s[] =		/* input 16 bit stereo */
-	{ {0xb7, 0x71}, {0xb7, 0xbc}, {-1, 0} };
-
-static struct ess_command *ess_inp_cmds[] =
-	{ ess_i08m, ess_i16m, ess_i08s, ess_i16s };
-
-
-/*
- * Commands for initializing Audio 1 for output (playback)
- */
-static struct ess_command ess_o08m[] =		/* output 8 bit mono */
-	{ {0xb6, 0x80}, {0xb7, 0x51}, {0xb7, 0xd0}, {-1, 0} };
-static struct ess_command ess_o16m[] =		/* output 16 bit mono */
-	{ {0xb6, 0x00}, {0xb7, 0x71}, {0xb7, 0xf4}, {-1, 0} };
-static struct ess_command ess_o08s[] =		/* output 8 bit stereo */
-	{ {0xb6, 0x80}, {0xb7, 0x51}, {0xb7, 0x98}, {-1, 0} };
-static struct ess_command ess_o16s[] =		/* output 16 bit stereo */
-	{ {0xb6, 0x00}, {0xb7, 0x71}, {0xb7, 0xbc}, {-1, 0} };
-
-static struct ess_command *ess_out_cmds[] =
-	{ ess_o08m, ess_o16m, ess_o08s, ess_o16s };
-
-static void ess_exec_commands
-	(sb_devc *devc, struct ess_command *cmdtab[])
-{
-	struct ess_command *cmd;
-
-	cmd = cmdtab [ ((devc->channels != 1) << 1) + (devc->bits != AFMT_U8) ];
-
-	while (cmd->cmd != -1) {
-		ess_write (devc, cmd->cmd, cmd->data);
-		cmd++;
-	}
-}
-
 static void ess_change
 	(sb_devc *devc, unsigned int reg, unsigned int mask, unsigned int val)
 {
@@ -298,56 +271,22 @@
 	devc->irq_mode = IMODE_INPUT;
 }
 
-static int ess_calc_div (int clock, int revert, int *speedp, int *diffp)
+static int ess_calc_div (int clock, int *speedp, int *diffp)
 {
 	int divider;
 	int speed, diff;
-	int retval;
 
 	speed   = *speedp;
 	divider = (clock + speed / 2) / speed;
-	retval  = revert - divider;
-	if (retval > revert - 1) {
-		retval  = revert - 1;
-		divider = revert - retval;
-	}
-	/* This line is suggested. Must be wrong I think
-	*speedp = (clock + divider / 2) / divider;
-	So I chose the next one */
-
+	if (divider > 127) {
+		divider = 127;
+	}
 	*speedp	= clock / divider;
 	diff	= speed - *speedp;
-	if (diff < 0) diff =-diff;
-	*diffp  = diff;
-
-	return retval;
-}
-
-#ifdef OBSOLETE
-static int ess_calc_best_speed
-	(int clock1, int rev1, int clock2, int rev2, int *divp, int *speedp)
-{
-	int speed1 = *speedp, speed2 = *speedp;
-	int div1, div2;
-	int diff1, diff2;
-	int retval;
-
-	div1 = ess_calc_div (clock1, rev1, &speed1, &diff1);
-	div2 = ess_calc_div (clock2, rev2, &speed2, &diff2);
-
-	if (diff1 < diff2) {
-		*divp   = div1;
-		*speedp = speed1;
-		retval  = 1;
-	} else {
-		*divp   = div2;
-		*speedp = speed2;
-		retval  = 2;
-	}
+	*diffp = diff < 0 ? -diff : diff;
 
-	return retval;
+	return 128 - divider;
 }
-#endif
 
 /*
  * Depending on the audiochannel ESS devices can
@@ -359,21 +298,30 @@
  */
 static void ess_common_speed (sb_devc *devc, int *speedp, int *divp)
 {
-	int diff = 0, div;
+	int speed1 = *speedp, speed2 = *speedp;
+	int div1, div2;
+	int diff1, diff2;
 
-	if (devc->duplex) {
-		/*
-		 * The 0x80 is important for the first audio channel
-		 */
-		div = 0x80 | ess_calc_div (795500, 128, speedp, &diff);
+	if (devc->caps & ESSCAP_NEW) {
+		div1 = 0x000 | ess_calc_div (793800, &speed1, &diff1);
+		div2 = 0x080 | ess_calc_div (768000, &speed2, &diff2);
 	} else {
 		if (*speedp > 22000) {
-			div = 0x80 | ess_calc_div (795500, 256, speedp, &diff);
+			div1 = 0x080 | ess_calc_div (795444, &speed1, &diff1);
+			div2 = 0x180 | ess_calc_div (793800, &speed2, &diff2);
 		} else {
-			div = 0x00 | ess_calc_div (397700, 128, speedp, &diff);
+			div1 = 0x000 | ess_calc_div (397722, &speed1, &diff1);
+			div2 = 0x100 | ess_calc_div (396900, &speed2, &diff2);
 		}
 	}
-	*divp = div;
+
+	if (diff1 < diff2) {
+		*divp   = div1;
+		*speedp = speed1;
+	} else {
+		*divp   = div2;
+		*speedp = speed2;
+	}
 }
 
 static void ess_speed (sb_devc *devc, int audionum)
@@ -392,21 +340,13 @@
 
 	div2 = 256 - 7160000 / (speed * 82);
 
-	if (!devc->duplex) audionum = 1;
-
-	if (audionum == 1) {
-		/* Change behaviour of register A1 *
-		sb_chg_mixer(devc, 0x71, 0x20, 0x20)
-		* For ES1869 only??? */
-		ess_write (devc, 0xa1, div);
-		ess_write (devc, 0xa2, div2);
-	} else {
+	if ((devc->caps & ESSCAP_NEW) && audionum != 1) {
 		ess_setmixer (devc, 0x70, div);
-		/*
-		 * FKS: fascinating: 0x72 doesn't seem to work.
-		 */
-		ess_write (devc, 0xa2, div2);
 		ess_setmixer (devc, 0x72, div2);
+	} else {
+		ess_change (devc, 0xba, 0x40, (div & 0x100) ? 0x40 : 0x00);
+		ess_write (devc, 0xa1, div & 0xff);
+		ess_write (devc, 0xa2, div2);
 	}
 }
 
@@ -414,77 +354,14 @@
 {
 	sb_devc *devc = audio_devs[dev]->devc;
 
-	ess_speed(devc, 1);
-
-	sb_dsp_command(devc, DSP_CMD_SPKOFF);
-
 	ess_write (devc, 0xb8, 0x0e);	/* Auto init DMA mode */
-	ess_change (devc, 0xa8, 0x03, 3 - devc->channels);	/* Mono/stereo */
-	ess_write (devc, 0xb9, 2);	/* Demand mode (4 bytes/DMA request) */
-
-	ess_exec_commands (devc, ess_inp_cmds);
-
-	ess_change (devc, 0xb1, 0xf0, 0x50);
-	ess_change (devc, 0xb2, 0xf0, 0x50);
+	ess_change (devc, 0xa8, 0x0b, 3 - devc->channels);	/* Mono/stereo */
 
-	devc->trigger_bits = 0;
-	return 0;
-}
-
-static int ess_audio_prepare_for_output_audio1 (int dev, int bsize, int bcount)
-{
-	sb_devc *devc = audio_devs[dev]->devc;
-
-	sb_dsp_reset(devc);
 	ess_speed(devc, 1);
-	ess_write (devc, 0xb8, 4);	/* Auto init DMA mode */
-	ess_change (devc, 0xa8, 0x03, 3 - devc->channels);	/* Mono/stereo */
-	ess_write (devc, 0xb9, 2);	/* Demand mode (4 bytes/request) */
-
-	ess_exec_commands (devc, ess_out_cmds);
-
-	ess_change (devc, 0xb1, 0xf0, 0x50);	/* Enable DMA */
-	ess_change (devc, 0xb2, 0xf0, 0x50);	/* Enable IRQ */
 
-	sb_dsp_command(devc, DSP_CMD_SPKON);	/* There be sound! */
-
-	devc->trigger_bits = 0;
-	return 0;
-}
-
-static int ess_audio_prepare_for_output_audio2 (int dev, int bsize, int bcount)
-{
-	sb_devc *devc = audio_devs[dev]->devc;
-	unsigned char bits;
-
-/* FKS: qqq
-	sb_dsp_reset(devc);
-*/
-
-	/*
-	 * Auto-Initialize:
-	 * DMA mode + demand mode (8 bytes/request, yes I want it all!)
-	 * But leave 16-bit DMA bit untouched!
-	 */
-	ess_chgmixer (devc, 0x78, 0xd0, 0xd0);
-
-	ess_speed(devc, 2);
-
-	/* bits 4:3 on ES1887 represent recording source. Keep them! */
-	bits = ess_getmixer (devc, 0x7a) & 0x18;
-
-	/* Set stereo/mono */
-	if (devc->channels != 1) bits |= 0x02;
-
-	/* Init DACs; UNSIGNED mode for 8 bit; SIGNED mode for 16 bit */
-	if (devc->bits != AFMT_U8) bits |= 0x05;	/* 16 bit */
-
-	/* Enable DMA, IRQ will be shared (hopefully)*/
-	bits |= 0x60;
-
-	ess_setmixer (devc, 0x7a, bits);
-
-	ess_mixer_reload (devc, SOUND_MIXER_PCM);	/* There be sound! */
+    ess_write (devc, 0xb7, (devc->bits & ESSFMT_SIGNED) ? 0x71 : 0x51);
+    ess_write (devc, 0xb7, 0x90 | ((devc->bits & ESSFMT_SIGNED) ? 0x20 : 0) |
+		((devc->bits & ESSFMT_16) ? 4 : 0) | ((devc->channels > 1) ? 8 : 0x40));
 
 	devc->trigger_bits = 0;
 	return 0;
@@ -500,144 +377,79 @@
 #endif
 
 	if (devc->duplex) {
-		return ess_audio_prepare_for_output_audio2 (dev, bsize, bcount);
-	} else {
-		return ess_audio_prepare_for_output_audio1 (dev, bsize, bcount);
-	}
-}
-
-static void ess_audio_halt_xfer(int dev)
-{
-	unsigned long flags;
-	sb_devc *devc = audio_devs[dev]->devc;
-
-	save_flags(flags);
-	cli();
-	sb_dsp_reset(devc);
-	restore_flags(flags);
-
-	/*
-	 * Audio 2 may still be operational! Creates awful sounds!
-	 */
-	if (devc->duplex) ess_chgmixer(devc, 0x78, 0x03, 0x00);
-}
-
-static void ess_audio_start_input
-	(int dev, unsigned long buf, int nr_bytes, int intrflag)
-{
-	int count = nr_bytes;
-	sb_devc *devc = audio_devs[dev]->devc;
-	short c = -nr_bytes;
-
-	/*
-	 * Start a DMA input to the buffer pointed by dmaqtail
-	 */
-
-	if (audio_devs[dev]->dmap_in->dma > 3) count >>= 1;
-	count--;
-
-	devc->irq_mode = IMODE_INPUT;
-
-	ess_write (devc, 0xa4, (unsigned char) ((unsigned short) c & 0xff));
-	ess_write (devc, 0xa5, (unsigned char) (((unsigned short) c >> 8) & 0xff));
-
-	ess_change (devc, 0xb8, 0x0f, 0x0f);	/* Go */
-	devc->intr_active = 1;
-}
+		ess_speed(devc, 2);
 
-static void ess_audio_output_block_audio1
-	(int dev, unsigned long buf, int nr_bytes, int intrflag)
-{
-	int count = nr_bytes;
-	sb_devc *devc = audio_devs[dev]->devc;
-	short c = -nr_bytes;
+	    ess_chgmixer (devc, 0x7a, 0x07, ((devc->bits & ESSFMT_SIGNED) ? 4 : 0) |
+			((devc->bits & ESSFMT_16) ? 1 : 0) | ((devc->channels > 1) ? 2 : 0));
 
-	if (audio_devs[dev]->dmap_out->dma > 3)
-		count >>= 1;
-	count--;
+		if (devc->caps & ESSCAP_NEW)
+			ess_mixer_reload (devc, SOUND_MIXER_PCM);	/* There be sound! */
+		else
+			sb_dsp_command(devc, DSP_CMD_SPKON);	/* There be sound! */
+	} else {
+		ess_write (devc, 0xb8, 4);	/* Auto init DMA mode */
+		ess_change (devc, 0xa8, 0x03, 3 - devc->channels);	/* Mono/stereo */
 
-	devc->irq_mode = IMODE_OUTPUT;
+		ess_speed(devc, 1);
 
-	ess_write (devc, 0xa4, (unsigned char) ((unsigned short) c & 0xff));
-	ess_write (devc, 0xa5, (unsigned char) (((unsigned short) c >> 8) & 0xff));
+	    ess_write (devc, 0xb6, (devc->bits & ESSFMT_SIGNED) ? 0 : 0x80);
+	    ess_write (devc, 0xb7, (devc->bits & ESSFMT_SIGNED) ? 0x71 : 0x51);
+	    ess_write (devc, 0xb7, 0x90 | ((devc->bits & ESSFMT_SIGNED) ? 0x20 : 0) |
+			((devc->bits & ESSFMT_16) ? 4 : 0) | ((devc->channels > 1) ? 8 : 0x40));
 
-	ess_change (devc, 0xb8, 0x05, 0x05);	/* Go */
-	devc->intr_active = 1;
+		sb_dsp_command(devc, DSP_CMD_SPKON);	/* There be sound! */
+	}
+	devc->trigger_bits = 0;
+	return 0;
 }
 
-static void ess_audio_output_block_audio2
-	(int dev, unsigned long buf, int nr_bytes, int intrflag)
+static void ess_audio_halt_xfer(int dev)
 {
-	int count = nr_bytes;
 	sb_devc *devc = audio_devs[dev]->devc;
-	short c = -nr_bytes;
 
-	if (audio_devs[dev]->dmap_out->dma > 3) count >>= 1;
-	count--;
+	sb_dsp_command (devc, DSP_CMD_SPKOFF);
 
-	ess_setmixer (devc, 0x74, (unsigned char) ((unsigned short) c & 0xff));
-	ess_setmixer (devc, 0x76, (unsigned char) (((unsigned short) c >> 8) & 0xff));
-	ess_chgmixer (devc, 0x78, 0x03, 0x03);   /* Go */
-
-	devc->irq_mode_16 = IMODE_OUTPUT;
-		devc->intr_active_16 = 1;
-}
+	if (devc->caps & ESSCAP_NEW) {
+		ess_setmixer (devc, 0x7c, 0);
+	}
 
-static void ess_audio_output_block
-	(int dev, unsigned long buf, int nr_bytes, int intrflag)
-{
-	sb_devc *devc = audio_devs[dev]->devc;
+	ess_change (devc, 0xb8, 0x0f, 0x00);	/* Stop */
 
-	if (devc->duplex) {
-		ess_audio_output_block_audio2 (dev, buf, nr_bytes, intrflag);
-	} else {
-		ess_audio_output_block_audio1 (dev, buf, nr_bytes, intrflag);
+	if (devc->duplex) {			/* Audio 2 may still be operational! */
+		ess_chgmixer (devc, 0x78, 0x03, 0x00);
 	}
 }
 
-/*
- * FKS: the if-statements for both bits and bits_16 are quite alike.
- * Combine this...
- */
 static void ess_audio_trigger(int dev, int bits)
 {
 	sb_devc *devc = audio_devs[dev]->devc;
 
-	int bits_16 = bits & devc->irq_mode_16;
+	int bits_16 = bits & devc->irq_mode_16 & IMODE_OUTPUT;
 	bits &= devc->irq_mode;
 
 	if (!bits && !bits_16) {
-		/* FKS oh oh.... wrong?? for dma 16? */
-		sb_dsp_command(devc, 0xd0);	/* Halt DMA */
+		sb_dsp_command (devc, 0xd0);			/* Halt DMA */
+		ess_chgmixer (devc, 0x78, 0x04, 0x00);	/* Halt DMA 2 */
 	}
 
 	if (bits) {
-		switch (devc->irq_mode)
-		{
-			case IMODE_INPUT:
-				ess_audio_start_input(dev, devc->trg_buf, devc->trg_bytes,
-					devc->trg_intrflag);
-				break;
+		short c = -devc->trg_bytes;
 
-			case IMODE_OUTPUT:
-				ess_audio_output_block(dev, devc->trg_buf, devc->trg_bytes,
-					devc->trg_intrflag);
-				break;
-		}
+		ess_write (devc, 0xa4, (unsigned char)((unsigned short) c & 0xff));
+		ess_write (devc, 0xa5, (unsigned char)((unsigned short) c >> 8));
+		ess_change (devc, 0xb8, 0x0f, (devc->irq_mode==IMODE_INPUT)?0x0f:0x05);
+
+		devc->intr_active = 1;
 	}
 
 	if (bits_16) {
-		switch (devc->irq_mode_16) {
-		case IMODE_INPUT:
-			ess_audio_start_input(dev, devc->trg_buf_16, devc->trg_bytes_16,
-					devc->trg_intrflag_16);
-			break;
+		short c = -devc->trg_bytes_16;
 
-		case IMODE_OUTPUT:
-			ess_audio_output_block(dev, devc->trg_buf_16, devc->trg_bytes_16,
-					devc->trg_intrflag_16);
-			break;
-		}
+		ess_setmixer (devc, 0x74, (unsigned char)((unsigned short) c & 0xff));
+		ess_setmixer (devc, 0x76, (unsigned char)((unsigned short) c >> 8));
+		ess_chgmixer (devc, 0x78, 0x03, 0x03);   /* Go */
+
+		devc->intr_active_16 = 1;
 	}
 
 	devc->trigger_bits = bits | bits_16;
@@ -649,8 +461,8 @@
 	int minspeed, maxspeed, dummydiv;
 
 	if (speed > 0) {
-		minspeed = (devc->duplex ? 6215  : 5000 );
-		maxspeed = (devc->duplex ? 44100 : 48000);
+		minspeed = (devc->caps & ESSCAP_NEW) ? 6047  : 3125;
+		maxspeed = 48000;
 		if (speed < minspeed) speed = minspeed;
 		if (speed > maxspeed) speed = maxspeed;
 
@@ -661,38 +473,46 @@
 	return devc->speed;
 }
 
-/*
- * FKS: This is a one-on-one copy of sb1_audio_set_bits
- */
 static unsigned int ess_audio_set_bits(int dev, unsigned int bits)
 {
 	sb_devc *devc = audio_devs[dev]->devc;
 
-	if (bits != 0) {
-		if (bits == AFMT_U8 || bits == AFMT_S16_LE) {
-			devc->bits = bits;
-		} else {
-			devc->bits = AFMT_U8;
-		}
+	switch (bits) {
+		case 0:
+			break;
+		case AFMT_S16_LE:
+			devc->bits = ESSFMT_16 | ESSFMT_SIGNED;
+			break;
+		case AFMT_U16_LE:
+			devc->bits = ESSFMT_16;
+			break;
+		case AFMT_S8:
+			devc->bits = ESSFMT_SIGNED;
+			break;
+		default:
+			devc->bits = 0;
+			break;
 	}
 
 	return devc->bits;
 }
 
-/*
- * FKS: This is a one-on-one copy of sbpro_audio_set_channels
- * (*) Modified it!!
- */
 static short ess_audio_set_channels(int dev, short channels)
 {
 	sb_devc *devc = audio_devs[dev]->devc;
 
-	if (channels == 1 || channels == 2) devc->channels = channels;
+	if (devc->fullduplex && !(devc->caps & ESSCAP_NEW)) {
+		devc->channels = 1;
+	} else {
+		if (channels == 1 || channels == 2) {
+			devc->channels = channels;
+		}
+	}
 
 	return devc->channels;
 }
 
-static struct audio_driver ess_audio_driver =   /* ESS ES688/1688 */
+static struct audio_driver ess_audio_driver =   /* ESS ES688/1688/18xx */
 {
 	sb_audio_open,
 	sb_audio_close,
@@ -719,7 +539,7 @@
 		(sb_devc *devc, int *audio_flags, int *format_mask)
 {
 	*audio_flags = DMA_AUTOMODE;
-	*format_mask |= AFMT_S16_LE;
+	*format_mask |= AFMT_S16_LE | AFMT_U16_LE | AFMT_S8;
 
 	if (devc->duplex) {
 		int tmp_dma;
@@ -742,10 +562,8 @@
  *								ESS common									*
  *																			*
  ****************************************************************************/
-static void ess_handle_channel
-	(char *channel, int dev, int intr_active, unsigned char flag, int irq_mode)
+static void ess_handle_channel (int dev, int irq_mode)
 {
-	if (!intr_active || !flag) return;
 #ifdef FKS_REG_LOGGING
 printk(KERN_INFO "FKS: ess_handle_channel %s irq_mode=%d\n", channel, irq_mode);
 #endif
@@ -767,48 +585,77 @@
 }
 
 /*
- * FKS: TODO!!! Finish this!
- *
- * I think midi stuff uses uart401, without interrupts.
- * So IMODE_MIDI isn't a value for devc->irq_mode.
+ * In the ESS 1888 model, how do we found out if the MPU interrupted ???
  */
 void ess_intr (sb_devc *devc)
 {
 	int				status;
 	unsigned char	src;
 
-	if (devc->submodel == SUBMDL_ES1887) {
+	if (devc->caps & ESSCAP_PNP) {
+		outb (devc->pcibase + 7, 0);		/* Mask IRQs */
+		src = inb (devc->pcibase + 6) & 0x0f;
+	} else if (devc->caps & ESSCAP_IRQ) {
 		src = ess_getmixer (devc, 0x7f) >> 4;
 	} else {
-		src = 0xff;
+		src = inb (DSP_STATUS) & 0x01;
+		if (devc->duplex && (ess_getmixer (devc, 0x7a) & 0x80)) {
+			src |= 0x02;
+		}
+		if ((devc->caps & ESSCAP_ES18) && (ess_getmixer (devc, 0x64) & 0x10)) {
+			src |= 0x04;
+		}
+#if defined(CONFIG_MIDI) && defined(CONFIG_SOUND_MPU401)
+		/*
+		 * This should work if dev_conf wasn't local to mpu401.c
+		 */
+#if 0
+		if ((int)devc->midi_irq_cookie >= 0 &&
+			!(inb(dev_conf[(int)devc->midi_irq_cookie].base + 1) & 0x80)) {
+			src |= 0x08;
+		}
+#endif
+#endif
 	}
 
 #ifdef FKS_REG_LOGGING
 printk(KERN_INFO "FKS: sbintr src=%x\n",(int)src);
 #endif
-	ess_handle_channel
-		( "Audio 1"
-		, devc->dev, devc->intr_active   , src & 0x01, devc->irq_mode   );
-	ess_handle_channel
-		( "Audio 2"
-		, devc->dev, devc->intr_active_16, src & 0x02, devc->irq_mode_16);
-	/*
-	 * Acknowledge interrupts
-	 */
-	if (devc->submodel == SUBMDL_ES1887 && (src & 0x02)) {
-		ess_chgmixer (devc, 0x7a, 0x80, 0x00);
+	if (src & 0x01) {
+		status = inb(DSP_DATA_AVAIL);	/* Acknowledge interrupt */
+		if (devc->intr_active)
+			ess_handle_channel (devc->dev, devc->irq_mode   );
 	}
 
-	if (src & 0x01) {
-		status = inb(DSP_DATA_AVAIL);
+	if (src & 0x02) {
+		ess_chgmixer (devc, 0x7a, 0x80, 0x00);	/* Acknowledge interrupt */
+		if (devc->intr_active_16)
+			ess_handle_channel (devc->dev, devc->irq_mode_16);
 	}
-}
 
-static void ess_extended (sb_devc * devc)
-{
-	/* Enable extended mode */
+	if (src & 0x04) {
+		int left, right;
+
+		ess_setmixer (devc, 0x66, 0x00);	/* Hardware volume IRQ ack */
+
+		left = ess_getmixer (devc, 0x60);
+		right = ess_getmixer (devc, 0x62);
 
-	sb_dsp_command(devc, 0xc6);
+		left = (left & 0x40) ? 0 : ((left * 100 + 31)/ 63);	/* Mute or scale */
+		right = (right & 0x40) ? 0 : ((right * 100 + 31)/ 63);
+
+		devc->levels[SOUND_MIXER_VOLUME] = left | (right << 8);
+	}
+
+#if defined(CONFIG_MIDI) && defined(CONFIG_SOUND_MPU401)
+	if ((int)devc->midi_irq_cookie >= 0 && (src & 0x08)) {
+		mpuintr (devc->irq, devc->midi_irq_cookie, NULL);
+	}
+#endif
+
+	if (devc->caps & ESSCAP_PNP) {
+		outb (devc->pcibase + 7, 0xff);		/* Unmask IRQs */
+	}
 }
 
 static int ess_write (sb_devc * devc, unsigned char reg, unsigned char data)
@@ -838,7 +685,7 @@
 
 int ess_dsp_reset(sb_devc * devc)
 {
-	int loopc;
+	int loopc, val;
 
 #ifdef FKS_REG_LOGGING
 printk(KERN_INFO "FKS: ess_dsp_reset 1\n");
@@ -859,77 +706,100 @@
 		DDB(printk("sb: No response to RESET\n"));
 		return 0;   /* Sorry */
 	}
-	ess_extended (devc);
-
-	DEB(printk("sb_dsp_reset() OK\n"));
-
-#ifdef FKS_LOGGING
-printk(KERN_INFO "FKS: dsp_reset 2\n");
-ess_show_mixerregs (devc);
-#endif
-
-	return 1;
-}
 
-static int ess_irq_bits (int irq)
-{
-	switch (irq) {
-	case 2:
-	case 9:
-		return 0;
-
-	case 5:
-		return 1;
+	sb_dsp_command(devc, 0xc6);			/* Enable extended mode */
+	if (!(devc->caps & ESSCAP_PNP)) {
+		ess_setmixer (devc, 0x40, 0x03);	/* Enable joystick and OPL3 */
+
+		switch (devc->irq) {
+			case 2:
+			case 9:
+				val = 1;
+				break;
+			case 5:
+				val = 2;
+				break;
+			case 7:
+				val = 3;
+				break;
+			case 10:
+				val = 4;
+				break;
+			case 11:
+				val = 5;
+				break;
+			default:
+				val = 0;
+		}						/* IRQ config */
+		ess_write (devc, 0xb1, 0xf0 | ((val && val != 5) ? val - 1 : 0));
 
-	case 7:
-		return 2;
+		if (devc->caps & ESSCAP_IRQ) {
+			ess_setmixer (devc, 0x7f, 0x01 | (val << 1)); /* IRQ config */
+		}
 
-	case 10:
-		return 3;
+		switch ((devc->duplex) ? devc->dma16 : devc->dma8) {
+			case 0:
+				val = 0x54;
+				break;
+			case 1:
+				val = 0x58;
+				break;
+			case 3:
+				val = 0x5c;
+				break;
+			default:
+				val = 0;
+		}
+		ess_write (devc, 0xb2, val); /* DMA1 config */
 
-	default:
-		printk(KERN_ERR "ESS1688: Invalid IRQ %d\n", irq);
-		return -1;
+		if (devc->duplex) {
+			switch (devc->dma8) {
+				case 0:
+					val = 0x04;
+					break;
+				case 1:
+					val = 0x05;
+					break;
+				case 3:
+					val = 0x06;
+					break;
+				case 5:
+					val = 0x07;
+					break;
+				default:
+					val = 0;
+			}
+			ess_write (devc, 0x7d, val); /* DMA2 config */
+		}
 	}
-}
-
-/*
- *	Set IRQ configuration register for all ESS models
- */
-static int ess_common_set_irq_hw (sb_devc * devc)
-{
-	int irq_bits;
-
-	if ((irq_bits = ess_irq_bits (devc->irq)) == -1) return 0;
-
-	if (!ess_write (devc, 0xb1, 0x50 | (irq_bits << 2))) {
-		printk(KERN_ERR "ES1688: Failed to write to IRQ config register\n");
-		return 0;
+	ess_change (devc, 0xb1, 0xf0, 0x50);	/* Enable IRQ 1 */
+	ess_change (devc, 0xb2, 0xf0, 0x50);	/* Enable DMA 1 */
+	ess_write (devc, 0xb9, 2);			/* Demand mode (4 bytes/DMA request) */
+	ess_setmixer (devc, 0x7a, 0x40);	/* Enable IRQ 2 */
+			/* Auto-Initialize DMA mode + demand mode (8 bytes/request) */
+	if (devc->caps & ESSCAP_PNP) {
+		ess_setmixer (devc, 0x78, 0xd0);
+		ess_setmixer (devc, 0x64, 0x82);		/* Enable HW volume interrupt */
+	} else {
+		ess_setmixer (devc, 0x78, (devc->dma8 > 4) ? 0xf0 : 0xd0);
+		ess_setmixer (devc, 0x64, 0x42);		/* Enable HW volume interrupt */
 	}
-	return 1;
-}
 
-/*
- * I wanna use modern ES1887 mixer irq handling. Funny is the
- * fact that my BIOS wants the same. But suppose someone's BIOS
- * doesn't do this!
- * This is independent of duplex. If there's a 1887 this will
- * prevent it from going into 1888 mode.
- */
-static void ess_es1887_set_irq_hw (sb_devc * devc)
-{
-	int irq_bits;
-
-	if ((irq_bits = ess_irq_bits (devc->irq)) == -1) return;
+    if (devc->caps & ESSCAP_NEW) {
+		ess_setmixer (devc, 0x71, 0x32); /* Change behaviour of register A1 */
+		ess_setmixer (devc, 0x1c, 0x05); /* Recording source is mixer */
+	} else {
+		ess_change (devc, 0xb7, 0x80, 0x80); /* Enable DMA FIFO */
+	}
 
-	ess_chgmixer (devc, 0x7f, 0x0f, 0x01 | ((irq_bits + 1) << 1));
-}
+	DEB(printk("sb_dsp_reset() OK\n"));
 
-static int ess_set_irq_hw (sb_devc * devc)
-{
-	if (devc->submodel == SUBMDL_ES1887) ess_es1887_set_irq_hw (devc);
+#ifdef FKS_LOGGING
+printk(KERN_INFO "FKS: dsp_reset 2\n");
+ess_show_mixerregs (devc);
+#endif
 
-	return ess_common_set_irq_hw (devc);
+	return 1;
 }
 
 #ifdef FKS_TEST
@@ -955,7 +825,7 @@
 };
 #endif
 
-static unsigned int ess_identify (sb_devc * devc)
+static unsigned int ess_identify (sb_devc * devc, int *control)
 {
 	unsigned int val;
 	unsigned long flags;
@@ -969,8 +839,15 @@
 	udelay(20);
 	val |= inb(MIXER_DATA);
 	udelay(20);
+	*control  = inb(MIXER_DATA) << 8;
+	udelay(20);
+	*control |= inb(MIXER_DATA);
+	udelay(20);
 	restore_flags(flags);
 
+	if (*control < 0 || *control > 0x3ff || check_region (*control, 8))
+		*control = 0;
+
 	return val;
 }
 
@@ -997,7 +874,6 @@
 
 int ess_init(sb_devc * devc, struct address_info *hw_config)
 {
-	unsigned char cfg;
 	int ess_major = 0, ess_minor = 0;
 	int i;
 	static char name[100], modelname[10];
@@ -1005,6 +881,7 @@
 	/*
 	 * Try to detect ESS chips.
 	 */
+	devc->pcibase = 0;
 
 	sb_dsp_command(devc, 0xe7); /* Return identification */
 
@@ -1056,10 +933,10 @@
 		case ESSTYPE_LIKE20:
 			break;
 		case 688:
-			submodel = 0x00;
+			submodel = SUBMDL_ES688;
 			break;
 		case 1688:
-			submodel = 0x08;
+			submodel = SUBMDL_ES1688;
 			break;
 		case 1868:
 			submodel = SUBMDL_ES1868;
@@ -1070,6 +947,12 @@
 		case 1788:
 			submodel = SUBMDL_ES1788;
 			break;
+		case 1878:
+			submodel = SUBMDL_ES1878;
+			break;
+		case 1879:
+			submodel = SUBMDL_ES1879;
+			break;
 		case 1887:
 			submodel = SUBMDL_ES1887;
 			break;
@@ -1102,7 +985,7 @@
 		if (chip == NULL) {
 			int type;
 
-			type = ess_identify (devc);
+			type = ess_identify (devc, &devc->pcibase);
 
 			switch (type) {
 			case 0x1868:
@@ -1117,6 +1000,10 @@
 				chip = "ES1878";
 				devc->submodel = SUBMDL_ES1878;
 				break;
+			case 0x1879:
+				chip = "ES1879";
+				devc->submodel = SUBMDL_ES1879;
+				break;
 			default:
 				if ((type & 0x00ff) != ((type >> 8) & 0x00ff)) {
 					printk ("ess_init: Unrecognized %04x\n", type);
@@ -1175,130 +1062,72 @@
 		strcpy(name, "Jazz16");
 	}
 
-	hw_config->name = name;
-	/* FKS: sb_dsp_reset to enable extended mode???? */
-	sb_dsp_reset(devc); /* Turn on extended mode */
+	switch (devc->submodel) {
+	case SUBMDL_ES1869:
+	case SUBMDL_ES1879:
+		devc->caps |= ESSCAP_NEW;
+	case SUBMDL_ES1868:
+	case SUBMDL_ES1878:
+		devc->caps |= ESSCAP_PNP | ESSCAP_ES18;
+		break;
+	case SUBMDL_ES1887:
+		devc->caps |= ESSCAP_IRQ;
+	case SUBMDL_ES1888:
+		devc->caps |= ESSCAP_NEW | ESSCAP_ES18;
+	}
+    if (devc->caps & ESSCAP_PNP) {
+		if (!devc->pcibase) {
+			printk (KERN_ERR "ESS PnP chip without PnP registers. Ignored\n");
+			return 0;
+		}
+		request_region (devc->pcibase, 8, "ESS18xx ctrl");
 
-	/*
-	 *  Enable joystick and OPL3
-	 */
-	cfg = ess_getmixer (devc, 0x40);
-	ess_setmixer (devc, 0x40, cfg | 0x03);
-	if (devc->submodel >= 8) {		/* ES1688 */
-		devc->caps |= SB_NO_MIDI;   /* ES1688 uses MPU401 MIDI mode */
+		outb (0x07, devc->pcibase);		/* Selects logical device #1 */
+		outb (0x01, devc->pcibase + 1);
+		outb (0x28, devc->pcibase);
+		i = inb (devc->pcibase + 1) & 0x0f;
+		outb (0x28, devc->pcibase);		/* Sets HW volume IRQ */
+		outb (devc->irq << 4 | i, devc->pcibase + 1);
+		outb (0x70, devc->pcibase);		/* Sets IRQ 1 */
+		outb (devc->irq, devc->pcibase + 1);
+		outb (0x72, devc->pcibase);		/* Sets IRQ 2 */
+		outb (devc->irq, devc->pcibase + 1);
+		outb (0x74, devc->pcibase);		/* Sets DMA 1 */
+		outb (hw_config->dma, devc->pcibase + 1);
+		outb (0x75, devc->pcibase);		/* Sets DMA 2 */
+		outb (hw_config->dma2 >= 0 ? hw_config->dma2 : 4, devc->pcibase + 1);
+	} else if (devc->pcibase) {
+		printk (KERN_INFO "Non-PnP ESS card with PnP registers at %04Xh, ignoring them.\n", devc->pcibase);
+		devc->pcibase = 0;
 	}
-	sb_dsp_reset (devc);
-
-	/*
-	 * This is important! If it's not done, the IRQ probe in sb_dsp_init
-	 * may fail.
-	 */
-	return ess_set_irq_hw (devc);
-}
-
-static int ess_set_dma_hw(sb_devc * devc)
-{
-	unsigned char cfg, dma_bits = 0, dma16_bits;
-	int dma;
-
-#ifdef FKS_LOGGING
-printk(KERN_INFO "ess_set_dma_hw: dma8=%d,dma16=%d,dup=%d\n"
-, devc->dma8, devc->dma16, devc->duplex);
-#endif
 
-	/*
-	 * FKS: It seems as if this duplex flag isn't set yet. Check it.
-	 */
-	dma = devc->dma8;
+	devc->caps |= SB_NO_MIDI;   /* ES1688 uses MPU401 MIDI mode */
 
-	if (dma > 3 || dma < 0 || dma == 2) {
-		dma_bits = 0;
-		printk(KERN_ERR "ESS1688: Invalid DMA8 %d\n", dma);
-		return 0;
-	} else {
-		/* Extended mode DMA enable */
-		cfg = 0x50;
-
-		if (dma == 3) {
-			dma_bits = 3;
-		} else {
-			dma_bits = dma + 1;
-		}
-	}
+	hw_config->name = name;
 
-	if (!ess_write (devc, 0xb2, cfg | (dma_bits << 2))) {
-		printk(KERN_ERR "ESS1688: Failed to write to DMA config register\n");
-		return 0;
-	}
+	sb_dsp_reset(devc); /* Turn on extended mode */
 
-	if (devc->duplex) {
-		dma = devc->dma16;
-		dma16_bits = 0;
+	ess_setmixer (devc, 0x00, 0x00);	/* Reset mixer registers */
 
-		if (dma >= 0) {
-			switch (dma) {
-			case 0:
-				dma_bits = 0x04;
-				break;
-			case 1:
-				dma_bits = 0x05;
-				break;
-			case 3:
-				dma_bits = 0x06;
-				break;
-			case 5:
-				dma_bits   = 0x07;
-				dma16_bits = 0x20;
-				break;
-			default:
-				printk(KERN_ERR "ESS1887: Invalid DMA16 %d\n", dma);
-				return 0;
-			};
-			ess_chgmixer (devc, 0x78, 0x20, dma16_bits);
-			ess_chgmixer (devc, 0x7d, 0x07, dma_bits);
-		}
-	}
 	return 1;
 }
 
 /*
  * This one is called from sb_dsp_init.
- *
- * Return values:
- *  0: Failed
- *  1: Succeeded or doesn't apply (not SUBMDL_ES1887)
  */
 int ess_dsp_init (sb_devc *devc, struct address_info *hw_config)
 {
 	/*
-	 * This for ES1887 to run Full Duplex. Actually ES1888
-	 * is allowed to do so too. I have no idea yet if this
-	 * will work for ES1888 however.
-	 *
 	 * For SB16 having both dma8 and dma16 means enable
-	 * Full Duplex. Let's try this for ES1887 too
-	 *
+	 * Full Duplex. Let's try this too
 	 */
-	if (devc->submodel == SUBMDL_ES1887) {
-		if (hw_config->dma2 != -1) {
-			devc->dma16 = hw_config->dma2;
-		}
-		/*
-		 * devc->duplex initialization is put here, cause
-		 * ess_set_dma_hw needs it.
-		 */
-		if (devc->dma8 != devc->dma16 && devc->dma16 != -1) {
+	if ((devc->caps & ESSCAP_ES18) && hw_config->dma2 >= 0) {
+		devc->dma16 = hw_config->dma2;
+		if (devc->dma8 != devc->dma16) {
 			devc->duplex = 1;
 		}
-
-		if (!ess_set_dma_hw (devc)) {
-			free_irq(devc->irq, devc);
-			return 0;
-		}
-		return 1;
-	} else {
-		return -1;
 	}
+	return 1;
 }
 
 /****************************************************************************
@@ -1319,13 +1148,13 @@
 #define ES1688_MIXER_DEVICES		\
 			( ES688_MIXER_DEVICES | SOUND_MASK_RECLEV	)
 
-#define ES1887_RECORDING_DEVICES	\
+#define ES_NEW_RECORDING_DEVICES	\
 			( ES1688_RECORDING_DEVICES | SOUND_MASK_LINE2 | SOUND_MASK_SYNTH)
-#define ES1887_MIXER_DEVICES		\
+#define ES_NEW_MIXER_DEVICES		\
 			( ES1688_MIXER_DEVICES											)
 
 /*
- * Mixer registers of ES1887
+ * Mixer registers of ES18xx with new capabilities
  *
  * These registers specifically take care of recording levels. To make the
  * mapping from playback devices to recording devices every recording
@@ -1455,11 +1284,11 @@
 };
 
 /*
- * This one is for ES1887. It's little different from es_rec_mix: it
- * has 0x7c for PCM playback level. This is because ES1887 uses
+ * This one is for new ES's. It's little different from es_rec_mix: it
+ * has 0x7c for PCM playback level. This is because uses
  * Audio 2 for playback.
  */
-static mixer_tab es1887_mix = {
+static mixer_tab es_new_mix = {
 MIX_ENT(SOUND_MIXER_VOLUME,			0x60, 5, 6, 0x62, 5, 6),
 MIX_ENT(SOUND_MIXER_BASS,			0x00, 0, 0, 0x00, 0, 0),
 MIX_ENT(SOUND_MIXER_TREBLE,			0x00, 0, 0, 0x00, 0, 0),
@@ -1493,16 +1322,6 @@
 MIX_ENT(ES_REC_MIXER_RECLINE3,		0x00, 0, 0, 0x00, 0, 0)
 };
 
-static int ess_has_rec_mixer (int submodel)
-{
-	switch (submodel) {
-	case SUBMDL_ES1887:
-		return 1;
-	default:
-		return 0;
-	};
-};
-
 #ifdef FKS_LOGGING
 static int ess_mixer_mon_regs[]
 	= { 0x70, 0x71, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7d, 0x7f
@@ -1533,15 +1352,13 @@
 
 	save_flags(flags);
 	cli();
-	if (port >= 0xa0) {
-		ess_write (devc, port, value);
-	} else {
-		outb(((unsigned char) (port & 0xff)), MIXER_ADDR);
 
-		udelay(20);
-		outb(((unsigned char) (value & 0xff)), MIXER_DATA);
-		udelay(20);
-	};
+	outb(((unsigned char) (port & 0xff)), MIXER_ADDR);
+
+	udelay(20);
+	outb(((unsigned char) (value & 0xff)), MIXER_DATA);
+	udelay(20);
+
 	restore_flags(flags);
 }
 
@@ -1553,15 +1370,12 @@
 	save_flags(flags);
 	cli();
 
-	if (port >= 0xa0) {
-		val = ess_read (devc, port);
-	} else {
-		outb(((unsigned char) (port & 0xff)), MIXER_ADDR);
+	outb(((unsigned char) (port & 0xff)), MIXER_ADDR);
+
+	udelay(20);
+	val = inb(MIXER_DATA);
+	udelay(20);
 
-		udelay(20);
-		val = inb(MIXER_DATA);
-		udelay(20);
-	}
 	restore_flags(flags);
 
 	return val;
@@ -1585,23 +1399,21 @@
 	devc->mixer_caps = SOUND_CAP_EXCL_INPUT;
 
 	/*
-	* Take care of ES1887 specifics...
+	* Take care of new ES's specifics...
 	*/
-	switch (devc->submodel) {
-	case SUBMDL_ES1887:
-		devc->supported_devices		= ES1887_MIXER_DEVICES;
-		devc->supported_rec_devices	= ES1887_RECORDING_DEVICES;
+	if (devc->caps & ESSCAP_NEW) {
+		devc->supported_devices		= ES_NEW_MIXER_DEVICES;
+		devc->supported_rec_devices	= ES_NEW_RECORDING_DEVICES;
 #ifdef FKS_LOGGING
 printk (KERN_INFO "FKS: ess_mixer_init dup = %d\n", devc->duplex);
 #endif
 		if (devc->duplex) {
-			devc->iomap				= &es1887_mix;
+			devc->iomap				= &es_new_mix;
 		} else {
 			devc->iomap				= &es_rec_mix;
 		}
-		break;
-	default:
-		if (devc->submodel < 8) {
+	} else {
+		if (devc->submodel == SUBMDL_ES688) {
 			devc->supported_devices		= ES688_MIXER_DEVICES;
 			devc->supported_rec_devices	= ES688_RECORDING_DEVICES;
 			devc->iomap					= &es688_mix;
@@ -1612,10 +1424,10 @@
 			 */
 			devc->supported_devices		= ES1688_MIXER_DEVICES;
 			devc->supported_rec_devices	= ES1688_RECORDING_DEVICES;
-			if (devc->submodel < 0x10) {
-				devc->iomap				= &es1688_mix;
-			} else {
+			if (devc->caps & ESSCAP_ES18) {
 				devc->iomap				= &es1688later_mix;
+			} else {
+				devc->iomap				= &es1688_mix;
 			}
 		}
 	}
@@ -1627,9 +1439,15 @@
  */
 int ess_mixer_set(sb_devc *devc, int dev, int left, int right)
 {
-	if (ess_has_rec_mixer (devc->submodel) && (devc->recmask & (1 << dev))) {
+	if ((devc->caps & ESSCAP_NEW) && (devc->recmask & (1 << dev))) {
 		sb_common_mixer_set (devc, dev + ES_REC_MIXER_RECDIFF, left, right);
 	}
+	/* Set & unmute master volume */
+	if ((devc->caps & ESSCAP_ES18) && (dev == SOUND_MIXER_VOLUME)) {
+		ess_chgmixer (devc, 0x60, 0x7f, 0x3f & ((left * 0x3f + 50) / 100));
+		ess_chgmixer (devc, 0x62, 0x7f, 0x3f & ((right * 0x3f + 50) / 100));
+		return left | (right << 8);
+	}
 	return sb_common_mixer_set (devc, dev, left, right);
 }
 
@@ -1675,7 +1493,6 @@
 				right = (value & 0x0000ff00) >> 8;
 			} else {				/* Turn it off (3)  */
 				left  = 0;
-				left  = 0;
 				right = 0;
 			}
 			sb_common_mixer_set(devc, i + ES_REC_MIXER_RECDIFF, left, right);
@@ -1688,7 +1505,7 @@
 {
 	/* This applies to ESS chips with record mixers only! */
 
-	if (ess_has_rec_mixer (devc->submodel)) {
+	if (devc->caps & ESSCAP_NEW) {
 		*mask	= es_rec_set_recmask (devc, *mask);
 		return 1;									/* Applied		*/
 	} else {
@@ -1704,18 +1521,7 @@
 	/*
 	 * Separate actions for ESS chips with a record mixer:
 	 */
-	if (ess_has_rec_mixer (devc->submodel)) {
-		switch (devc->submodel) {
-		case SUBMDL_ES1887:
-			/*
-			 * Separate actions for ES1887:
-			 * Change registers 7a and 1c to make the record mixer the
-			 * actual recording source.
-			 */
-			ess_chgmixer(devc, 0x7a, 0x18, 0x08);
-			ess_chgmixer(devc, 0x1c, 0x07, 0x07);
-			break;
-		};
+	if (devc->caps & ESSCAP_NEW) {
 		/*
 		 * Call set_recmask for proper initialization
 		 */
@@ -1735,50 +1541,80 @@
  *																			*
  ****************************************************************************/
 
-/*
- * FKS: IRQ may be shared. Hm. And if so? Then What?
- */
 int ess_midi_init(sb_devc * devc, struct address_info *hw_config)
 {
-	unsigned char   cfg, tmp;
+	int val;
 
-	cfg = ess_getmixer (devc, 0x40) & 0x03;
-
-	if (devc->submodel < 8) {
-		ess_setmixer (devc, 0x40, cfg | 0x03);	/* Enable OPL3 & joystick */
-		return 0;  					 /* ES688 doesn't support MPU401 mode */
+	if (devc->submodel == SUBMDL_ES688) {
+		return 0;				/* ES688 doesn't support MPU401 mode */
 	}
-	tmp = (hw_config->io_base & 0x0f0) >> 4;
 
-	if (tmp > 3) {
-		ess_setmixer (devc, 0x40, cfg);
-		return 0;
+	if (hw_config->irq < 2) {
+		hw_config->irq = devc->irq;
 	}
-	cfg |= tmp << 3;
-
-	tmp = 1;		/* MPU enabled without interrupts */
 
-	/* May be shared: if so the value is -ve */
+	if (devc->caps & ESSCAP_PNP) {
+		outb (0x07, devc->pcibase);		/* Selects logical device #1 */
+		outb (0x01, devc->pcibase + 1);
+		outb (0x28, devc->pcibase);
+		val = inb (devc->pcibase + 1) & 0xf0;
+		outb (0x28, devc->pcibase);		/* Sets MPU IRQ */
+		outb (hw_config->irq | val, devc->pcibase + 1);
+		if (hw_config->io_base) {
+			outb (0x64, devc->pcibase);		/* Sets MPU I/O address */
+			outb ((hw_config->io_base & 0xf00) >> 8, devc->pcibase + 1);
+			outb (0x65, devc->pcibase);		/* Sets MPU I/O address */
+			outb (hw_config->io_base & 0xfc, devc->pcibase + 1);
+		} else {
+			outb (0x64, devc->pcibase);		/* Read MPU I/O address */
+			hw_config->io_base = (inb (devc->pcibase + 1) & 0x0f) << 8;
+			outb (0x65, devc->pcibase);		/* Read MPU I/O address */
+			hw_config->io_base |= inb (devc->pcibase + 1) & 0xfc;
+		}
 
-	switch (abs(hw_config->irq)) {
-		case 9:
-			tmp = 0x4;
-			break;
-		case 5:
-			tmp = 0x5;
-			break;
-		case 7:
-			tmp = 0x6;
-			break;
-		case 10:
-			tmp = 0x7;
-			break;
-		default:
-			return 0;
+		ess_setmixer (devc, 0x64, 0xc2);	/* Enable MPU interrupt */
+	} else {
+		if (devc->irq == hw_config->irq && (devc->caps & ESSCAP_IRQ)) {
+			val = 0x43;
+		}
+		else switch (hw_config->irq) {
+			case 11:
+				if (!(devc->caps & ESSCAP_IRQ)) {
+					return 0;
+				}
+				val = 0x63;
+				break;
+			case 2:
+			case 9:
+				val = 0x83;
+				break;
+			case 5:
+				val = 0xa3;
+				break;
+			case 7:
+				val = 0xc3;
+				break;
+			case 10:
+				val = 0xe3;
+				break;
+			default:
+				return 0;
+		}
+		switch (hw_config->io_base) {
+			case 0x300:
+			case 0x310:
+			case 0x320:
+			case 0x330:
+				ess_setmixer (devc, 0x40, val
+								| ((hw_config->io_base & 0x0f0) >> 1));
+				break;
+			default:
+				return 0;
+		}
 	}
 
-	cfg |= tmp << 5;
-	ess_setmixer (devc, 0x40, cfg | 0x03);
+	if (devc->irq == hw_config->irq)	/* Shared IRQ */
+		hw_config->irq = -devc->irq;
 
 	return 1;
 }
diff -u --recursive --new-file v2.3.3/linux/drivers/sound/sound_core.c linux/drivers/sound/sound_core.c
--- v2.3.3/linux/drivers/sound/sound_core.c	Sun Mar  7 15:22:06 1999
+++ linux/drivers/sound/sound_core.c	Wed May 26 09:35:00 1999
@@ -382,6 +382,9 @@
 #ifdef CONFIG_SOUND_SONICVIBES
 	init_sonicvibes();
 #endif
+#ifdef CONFIG_SOUND_CMPCI
+	init_cmpci();
+#endif
 #ifdef CONFIG_SOUND_ES1370
 	init_es1370();
 #endif
diff -u --recursive --new-file v2.3.3/linux/drivers/usb/Config.in linux/drivers/usb/Config.in
--- v2.3.3/linux/drivers/usb/Config.in	Mon May 17 09:55:22 1999
+++ linux/drivers/usb/Config.in	Mon May 31 09:01:50 1999
@@ -5,30 +5,31 @@
 # Right now hubs, mice and keyboards work - at least with UHCI.
 # But that may be more a lucky coincidence than anything else..
 #
-# This was all developed modularly, but I've been lazy in cleaning
-# it up, so right now they are all bools. 
-#
 mainmenu_option next_comment
 comment 'USB drivers - not for the faint of heart'
 
 tristate 'Support for USB (EXPERIMENTAL!)' CONFIG_USB
 if [ ! "$CONFIG_USB" = "n" ]; then
-  bool 'UHCI (intel PIIX4 and others) support?' CONFIG_USB_UHCI
+  dep_tristate 'UHCI (intel PIIX4 and others) support' CONFIG_USB_UHCI \
+	$CONFIG_USB
+    dep_tristate 'OHCI (compaq and some others) support' CONFIG_USB_OHCI \
+	$CONFIG_USB
 
-  bool 'OHCI (compaq and some others) support?' CONFIG_USB_OHCI
-  if [ "$CONFIG_USB_OHCI" = "y" ]; then
-     bool '  Enable tons of OHCI debugging output?' CONFIG_USB_OHCI_DEBUG
+  if [ "$CONFIG_USB_OHCI" != "n" ]; then
+     bool '  Enable tons of OHCI debugging output' CONFIG_USB_OHCI_DEBUG
   fi
-
-  bool 'OHCI-HCD (other OHCI opt. Virt. Root Hub) support?' CONFIG_USB_OHCI_HCD
-  if [ "$CONFIG_USB_OHCI_HCD" = "y" ]; then
-     bool 'OHCI-HCD Virtual Root Hub' CONFIG_USB_OHCI_VROOTHUB
+  dep_tristate 'OHCI-HCD (other OHCI opt. Virt. Root Hub) support' \
+		CONFIG_USB_OHCI_HCD $CONFIG_USB
+  if [ "$CONFIG_USB_OHCI_HCD" != "n" ]; then
+     	bool '  OHCI-HCD Virtual Root Hub' CONFIG_USB_OHCI_VROOTHUB
   fi
 
-  bool 'USB mouse support' CONFIG_USB_MOUSE
-  bool 'USB keyboard support' CONFIG_USB_KBD
-  bool 'USB audio parsing support' CONFIG_USB_AUDIO
-  bool 'USB Abstract Control Model support' CONFIG_USB_ACM
+  dep_tristate 'USB hub support' CONFIG_USB_HUB $CONFIG_USB
+  dep_tristate 'USB mouse support' CONFIG_USB_MOUSE $CONFIG_USB
+  dep_tristate 'USB keyboard support' CONFIG_USB_KBD $CONFIG_USB
+  dep_tristate 'USB audio parsing support' CONFIG_USB_AUDIO $CONFIG_USB
+  dep_tristate 'USB Abstract Control Model support' CONFIG_USB_ACM $CONFIG_USB
+  dep_tristate 'Preliminary USB Printer support' CONFIG_USB_PRINTER $CONFIG_USB
 fi
 
 endmenu
diff -u --recursive --new-file v2.3.3/linux/drivers/usb/Makefile linux/drivers/usb/Makefile
--- v2.3.3/linux/drivers/usb/Makefile	Sat May 15 23:46:03 1999
+++ linux/drivers/usb/Makefile	Mon May 31 09:01:50 1999
@@ -7,8 +7,6 @@
 #
 # Note 2! The CFLAGS definitions are now inherited from the
 # parent makes..
-#
-# This isn't actually supported yet. Don't try to use it.
 
 SUB_DIRS     :=
 MOD_SUB_DIRS := $(SUB_DIRS)
@@ -17,67 +15,100 @@
 L_TARGET := usb.a
 M_OBJS   :=
 L_OBJS   :=
-LX_OBJS  :=
-USBX_OBJS := usb.o hub.o usb-debug.o
+
+ifeq ($(CONFIG_USB),y)
+  L_OBJS +=usbcore.o
+endif
+ifeq ($(CONFIG_USB),m)
+  M_OBJS +=usbcore.o
+  MIX_OBJS +=usb.o usb-debug.o usb-core.o
+endif
+
+ifeq ($(CONFIG_USB_UHCI),y)
+    L_OBJS += uhci.o uhci-debug.o
+endif
+
+ifeq ($(CONFIG_USB_UHCI),m)
+      M_OBJS += usb-uhci.o
+    MIX_OBJS += uhci.o uhci-debug.o
+endif
+
+ifeq ($(CONFIG_USB_OHCI),y)
+    L_OBJS += ohci.o ohci-debug.o
+endif
+ifeq ($(CONFIG_USB_OHCI),m)
+      M_OBJS += usb-ohci.o
+      MIX_OBJS += ohci.o ohci-debug.o
+endif
+
+ifeq ($(CONFIG_USB_OHCI_HCD),y)
+    L_OBJS += ohci-hcd.o ohci-root-hub.o
+endif
+ifeq ($(CONFIG_USB_OHCI_HCD),m)
+      M_OBJS += usb-ohci-hcd.o
+      MIX_OBJS += ohci-hcd.o ohci-root-hub.o
+endif
 
 ifeq ($(CONFIG_USB_MOUSE),y)
-  USBX_OBJS += mouse.o
+  L_OBJS += mouse.o
+endif
+ifeq ($(CONFIG_USB_MOUSE),m)
+  M_OBJS  +=mouse.o	
+  MIX_OBJS +=mouse.o
+endif
+
+ifeq ($(CONFIG_USB_HUB),y)
+  L_OBJS += hub.o
+endif
+ifeq ($(CONFIG_USB_HUB),m)
+  M_OBJS  +=hub.o
+  MIX_OBJS +=hub.o	
 endif
 
 ifeq ($(CONFIG_USB_ACM),y)
-  USBX_OBJS += acm.o
+  L_OBJS += acm.o
+endif
+ifeq ($(CONFIG_USB_ACM),m)
+  M_OBJS += acm.o
+  MIX_OBJS +=acm.o
 endif
 
-ifeq ($(CONFIG_USB_KBD),y)
-  USBX_OBJS += keyboard.o keymap.o
+ifeq ($(CONFIG_USB_PRINTER),y)
+  L_OBJS += printer.o
 endif
 
-ifeq ($(CONFIG_USB_AUDIO),y)
-  USBX_OBJS += audio.o
+ifeq ($(CONFIG_USB_PRINTER),m)
+  M_OBJS += printer.o
+  MIX_OBJS += printer.o  
 endif
 
-ifeq ($(CONFIG_USB_CPIA),y)
-  USBX_OBJS += cpia.o
+ifeq ($(CONFIG_USB_KBD),y)
+  L_OBJS += keyboard.o keymap.o
 endif
 
-ifeq ($(CONFIG_USB), y)
-  L_OBJS += $(USBX_OBJS)
+ifeq ($(CONFIG_USB_KBD),m)
+  M_OBJS += usb-keyboard.o
+  MIX_OBJS += keyboard.o keymap.o
 endif
 
-ifeq ($(CONFIG_USB_UHCI),y)
-  ifeq ($(CONFIG_USB), y)
-    L_OBJS += uhci.o uhci-debug.o
-  else
-    ifeq ($(CONFIG_USB),m)
-      M_OBJS += usb-uhci.o
-      MIX_OBJS += $(USBX_OBJS)
-    endif
-  endif
+ifeq ($(CONFIG_USB_AUDIO),y)
+  L_OBJS += audio.o
 endif
 
-ifeq ($(CONFIG_USB_OHCI),y)
-  ifeq ($(CONFIG_USB), y)
-    L_OBJS += ohci.o ohci-debug.o
-  else
-    ifeq ($(CONFIG_USB),m)
-      USBO_OBJS += ohci.o ohci-debug.o
-      M_OBJS += usb-ohci.o
-      MIX_OBJS += $(USBX_OBJS)
-    endif
-  endif
+ifeq ($(CONFIG_USB_AUDIO),m)
+  M_OBJS += audio.o
+  MIX_OBJS += audio.o
 endif
 
-ifeq ($(CONFIG_USB_OHCI_HCD),y)
-  ifeq ($(CONFIG_USB), y)
-    L_OBJS += ohci-hcd.o ohci-root-hub.o
-  else
-    ifeq ($(CONFIG_USB),m)
-      USBO_OBJS += ohci-hcd.o ohci-root-hub.o
-      M_OBJS += usb-ohci-hcd.o
-      MIX_OBJS += $(USBX_OBJS)
-    endif
-  endif
+ifeq ($(CONFIG_USB_CPIA),y)
+  L_OBJS += cpia.o
+endif
+
+ifeq ($(CONFIG_USB_CPIA),m)
+  M_OBJS += cpia.o
+  MIX_OBJS += cpia.o
 endif
+
 include $(TOPDIR)/Rules.make
 
 keymap.o: keymap.c
@@ -85,12 +116,19 @@
 keymap.c: maps/serial.map maps/usb.map maps/fixup.map
 	./mkmap > $@
 
-usb-uhci.o: uhci.o uhci-debug.o $(USBX_OBJS)
-	$(LD) $(LD_RFLAG) -r -o $@  uhci.o uhci-debug.o $(USBX_OBJS)
+usb-keyboard.o: keymap.o keyboard.o
+	$(LD) $(LD_RFLAG) -r -o $@  keymap.o keyboard.o 
+
+usb-uhci.o: uhci.o uhci-debug.o 
+	$(LD) $(LD_RFLAG) -r -o $@  uhci.o uhci-debug.o 
 
-usb-ohci.o: ohci.o ohci-debug.o $(USBX_OBJS)
-	$(LD) $(LD_RFLAG) -r -o $@ ohci.o ohci-debug.o $(USBX_OBJS)
+usb-ohci.o: ohci.o ohci-debug.o 
+	$(LD) $(LD_RFLAG) -r -o $@ ohci.o ohci-debug.o 
+       
+usb-ohci-hcd.o: ohci-hcd.o ohci-root-hub.o 
+	$(LD) $(LD_RFLAG) -r -o $@ ohci-hcd.o ohci-root-hub.o 
+       
+usbcore.o: usb.o usb-debug.o usb-core.o
+	$(LD) $(LD_RFLAG) -r -o $@ usb.o usb-debug.o usb-core.o
 
-usb-ohci-hcd.o: ohci-hcd.o ohci-root-hub.o $(USBX_OBJS)
-	$(LD) $(LD_RFLAG) -r -o $@ ohci-hcd.o ohci-root-hub.o $(USBX_OBJS)
        
diff -u --recursive --new-file v2.3.3/linux/drivers/usb/acm.c linux/drivers/usb/acm.c
--- v2.3.3/linux/drivers/usb/acm.c	Sat May 15 23:46:03 1999
+++ linux/drivers/usb/acm.c	Mon May 31 09:01:50 1999
@@ -3,9 +3,19 @@
  *
  * Armin Fuerst 5/8/1999
  *
- * version 0.0: Driver sets up configuration, setus up data pipes, opens misc
+ * version 0.2: Improved Bulk transfer. TX led now flashes every time data is
+ * sent. Send Encapsulated Data is not needed, nor does it do anything.
+ * Why's that ?!? Thanks to Thomas Sailer for his close look at the bulk code.
+ * He told me about some importand bugs. (5/21/99)
+ *
+ * version 0.1: Bulk transfer for uhci seems to work now, no dangling tds any
+ * more. TX led of the ISDN TA flashed the first time. Does this mean it works?
+ * The interrupt of the ctrl endpoint crashes the kernel => no read possible
+ * (5/19/99)
+ *
+ * version 0.0: Driver sets up configuration, sets up data pipes, opens misc
  * device. No actual data transfer is done, since we don't have bulk transfer,
- * yet. Purely skeleton for now.
+ * yet. Purely skeleton for now. (5/8/99)
  */
 
 #include <linux/kernel.h>
@@ -13,10 +23,11 @@
 #include <linux/signal.h>
 #include <linux/errno.h>
 #include <linux/miscdevice.h>
-#include <linux/random.h>
 #include <linux/poll.h>
 #include <linux/init.h>
 #include <linux/malloc.h>
+#include <linux/config.h>
+#include <linux/module.h>
 
 #include <asm/spinlock.h>
 
@@ -27,8 +38,12 @@
 struct acm_state {
 	int present; /* this acm is plugged in */
 	int active; /* someone is has this acm's device open */
+	int serstate; /* Status of the serial port (rate, handshakelines,...) */
 	struct usb_device *dev;
-	unsigned int readpipe,writepipe;
+	unsigned ctrlbuffer;	/*buffer for control messages*/
+	unsigned int readendp,writeendp,ctrlendp;
+	unsigned int readpipe,writepipe,ctrlpipe;
+	char buffer;
 };
 
 static struct acm_state static_acm_state;
@@ -37,16 +52,44 @@
 
 static int acm_irq(int state, void *__buffer, void *dev_id)
 {
-/*
-	signed char *data = __buffer;
+//	unsigned char *data = __buffer;
         struct acm_state *acm = &static_acm_state; 
+        devrequest *dr;
+
+        dr=__buffer;
+	printk("ACM_USB_IRQ\n");
+        printk("reqtype: %02X\n",dr->requesttype);
+        printk("request: %02X\n",dr->request);
+	printk("wValue: %02X\n",dr->value);
+	printk("wIndex: %02X\n",dr->index);
+	printk("wLength: %02X\n",dr->length);
+	
+	switch(dr->request) {
+	  //Network connection 
+	  case 0x00:
+	    printk("Network connection: ");
+	    if (dr->request==0) printk("disconnected\n");
+	    if (dr->request==1) printk("connected\n");
+	    break;
+
+    	  //Response available
+	  case 0x01:
+	    printk("Response available\n");
+	    acm->buffer=1;
+	    break;
+	
+	  //Set serial line state
+	  case 0x20:
+	    if ((dr->index==1)&&(dr->length==2)) {
+	      acm->serstate=acm->ctrlbuffer;
+	      printk("Serstate: %02X\n",acm->ctrlbuffer);
+	    }
+	    break;
+	}
+/*
 	if(!acm->active)
 		return 1;
 */
-
-	/*We should so something useful here*/
-	printk("ACM_USB_IRQ\n");
-
 	return 1;
 }
 
@@ -76,22 +119,48 @@
 static ssize_t write_acm(struct file * file,
        const char * buffer, size_t count, loff_t *ppos)
 {
-        char * buffer="ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+        devrequest dr;
 	struct acm_state *acm = &static_acm_state;
+	unsigned long retval;	        
+
 	printk("USB_FILE_WRITE\n");
-		printk("writing:>%s<\n",buffer);
-		acm->dev->bus->op->bulk_msg(acm->dev,acm->writepipe,buffer, 26);
-		printk("done:>%s<\n",buffer);
-		printk("reading:>%s<\n",buffer);
-		acm->dev->bus->op->bulk_msg(acm->dev,acm->readpipe,buffer, 26);
-		printk("done:>%s<\n",buffer);
+//Huh, i seem to got that wrong, we don't need this ?!?
+/*
+	dr.requesttype = USB_TYPE_CLASS | USB_RT_ENDPOINT;
+	dr.request = 0;
+	dr.value = 0;
+	dr.index = acm->writeendp;
+	dr.length = count;
+	acm->dev->bus->op->control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0), &dr, NULL, 0);
+*/	
+
+	acm->dev->bus->op->bulk_msg(acm->dev,&acm->writepipe,buffer, count, &retval);
 	return -EINVAL;
 }
 
-static ssize_t read_acm(struct file * file, char * buffer, size_t count, loff_t *ppos)
+
+static ssize_t read_acm(struct file * file, const char * buffer, size_t count, loff_t *ppos)
 {
+	devrequest dr;
+        struct acm_state *acm = &static_acm_state;
+	unsigned long retval;
 	printk("USB_FILE_READ\n");
-	return -EINVAL;
+//        if (!acm->buffer) return -1;
+     	acm->buffer=0;
+//We don't need this
+/*
+	printk("writing control msg\n");
+	dr.requesttype = USB_TYPE_CLASS | USB_RT_ENDPOINT | 0x80;
+	dr.request = 1;
+	dr.value = 0;
+	dr.index = acm->readendp;
+	dr.length = 0;
+	acm->dev->bus->op->control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0), &dr, NULL, 0);
+*/
+	printk("reading:>%s<\n",buffer);
+	acm->dev->bus->op->bulk_msg(acm->dev,&acm->readpipe,buffer, 1,&retval);
+	printk("done:>%s<\n",buffer);
+	return 1;
 }
 
 struct file_operations usb_acm_fops = {
@@ -165,10 +234,14 @@
 		printk("USB ACM found\n");
 		usb_set_configuration(dev, dev->config[cfgnum].bConfigurationValue);
 		acm->dev=dev;
-		acm->readpipe=__create_pipe(dev,&dev->config[cfgnum].interface[1].endpoint[0]);
-		acm->writepipe=__create_pipe(dev,&dev->config[cfgnum].interface[1].endpoint[1]);
-		usb_request_irq(dev, usb_rcvctrlpipe(dev,&dev->config[cfgnum].interface[0].endpoint[0]), acm_irq, endpoint->bInterval, NULL);
+		acm->readendp=dev->config[cfgnum].interface[1].endpoint[0].bEndpointAddress;
+		acm->writeendp=dev->config[cfgnum].interface[1].endpoint[1].bEndpointAddress;
+		acm->ctrlendp=dev->config[cfgnum].interface[0].endpoint[0].bEndpointAddress;
+		acm->readpipe=usb_rcvbulkpipe(dev,acm->readendp);
+		acm->writepipe=usb_sndbulkpipe(dev,acm->writeendp);
+		usb_request_irq(dev,acm->ctrlpipe=usb_rcvctrlpipe(dev,acm->ctrlendp), acm_irq, dev->config[cfgnum].interface[0].endpoint[0].bInterval, &acm->ctrlbuffer);
 		acm->present = 1;
+		acm->buffer=0;
 		return 0;
 	}
 
@@ -203,7 +276,7 @@
 	return 0;
 }
 
-#if 0
+#ifdef MODULE
 
 int init_module(void)
 {
diff -u --recursive --new-file v2.3.3/linux/drivers/usb/audio.c linux/drivers/usb/audio.c
--- v2.3.3/linux/drivers/usb/audio.c	Wed Apr 28 11:14:03 1999
+++ linux/drivers/usb/audio.c	Mon May 31 09:01:50 1999
@@ -3,6 +3,9 @@
 #include <linux/string.h>
 #include <linux/timer.h>
 #include <linux/sched.h>
+#include <linux/config.h>
+#include <linux/module.h>
+
 #include "usb.h"
 
 static int usb_audio_probe(struct usb_device *dev);
@@ -124,3 +127,15 @@
 {
 }
 
+#ifdef MODULE
+int init_module(void)
+{
+	return usb_audio_init();
+}
+
+void module_cleanup(void)
+{
+	usb_deregister(&usb_audio_driver);
+}
+
+#endif
diff -u --recursive --new-file v2.3.3/linux/drivers/usb/cpia.c linux/drivers/usb/cpia.c
--- v2.3.3/linux/drivers/usb/cpia.c	Sat May 15 23:46:03 1999
+++ linux/drivers/usb/cpia.c	Mon May 31 09:01:50 1999
@@ -17,6 +17,8 @@
 #include <linux/videodev.h>
 #include <linux/vmalloc.h>
 #include <linux/wrapper.h>
+#include <linux/config.h>
+#include <linux/module.h>
 
 #include <asm/spinlock.h>
 #include <asm/io.h>
@@ -1252,4 +1254,14 @@
 
 	return 0;
 }
+
+#ifdef MODULE
+int init_module(void)
+{
+	return usb_cpia_init();
+}
+void module_cleanup(void)
+{
+}
+#endif
 
diff -u --recursive --new-file v2.3.3/linux/drivers/usb/hub.c linux/drivers/usb/hub.c
--- v2.3.3/linux/drivers/usb/hub.c	Sat May 15 23:46:03 1999
+++ linux/drivers/usb/hub.c	Mon May 31 09:01:50 1999
@@ -1,10 +1,6 @@
 /*
  * USB hub driver.
  *
- * This is horrible, it knows about the UHCI driver
- * internals, but it's just meant as a rough example,
- * let's do the virtualization later when this works.
- *
  * (C) Copyright 1999 Linus Torvalds
  * (C) Copyright 1999 Johannes Erdfelt
  */
@@ -14,15 +10,14 @@
 #include <linux/list.h>
 #include <linux/malloc.h>
 #include <linux/smp_lock.h>
+#include <linux/config.h>
+#include <linux/module.h>
 
 #include <asm/spinlock.h>
 
 #include "usb.h"
-#include "uhci.h"
 #include "hub.h"
 
-extern struct usb_operations uhci_device_operations;
-
 /* Wakes up khubd */
 static DECLARE_WAIT_QUEUE_HEAD(usb_hub_wait);
 static spinlock_t hub_event_lock = SPIN_LOCK_UNLOCKED;
@@ -413,10 +408,20 @@
 	return 0;
 }
 
-void hub_cleanup(void)
+void usb_hub_cleanup(void)
 {
 	if (khubd_pid >= 0)
 		kill_proc(khubd_pid, SIGINT, 1);
 
 	usb_deregister(&hub_driver);
 }
+
+#ifdef MODULE
+int init_module(void){
+	return usb_hub_init();
+}
+
+void module_cleanup(void){
+	usb_hub_cleanup();
+}
+#endif
diff -u --recursive --new-file v2.3.3/linux/drivers/usb/inits.h linux/drivers/usb/inits.h
--- v2.3.3/linux/drivers/usb/inits.h	Sat May 15 23:46:03 1999
+++ linux/drivers/usb/inits.h	Mon May 31 09:01:50 1999
@@ -1,7 +1,7 @@
-int bp_mouse_init(void);
 int usb_kbd_init(void);
 int usb_audio_init(void);
-int hub_init(void);
+int usb_hub_init(void);
 int usb_acm_init(void);
-void hub_cleanup(void);
+int usb_printer_init(void);
+void usb_hub_cleanup(void);
 void usb_mouse_cleanup(void);
diff -u --recursive --new-file v2.3.3/linux/drivers/usb/keyboard.c linux/drivers/usb/keyboard.c
--- v2.3.3/linux/drivers/usb/keyboard.c	Mon Apr 26 13:35:01 1999
+++ linux/drivers/usb/keyboard.c	Mon May 31 09:01:50 1999
@@ -3,6 +3,9 @@
 #include <linux/string.h>
 #include <linux/timer.h>
 #include <linux/sched.h>
+#include <linux/config.h>
+#include <linux/module.h>
+
 #include <linux/kbd_ll.h>
 #include "usb.h"
 
@@ -218,9 +221,21 @@
     printk(KERN_INFO "USB HID boot protocol keyboard removed.\n");
 }
 
-int
-usb_kbd_init(void)
+int usb_kbd_init(void)
 {
     usb_register(&usb_kbd_driver);
     return 0;
 }
+
+#ifdef MODULE
+int init_module(void)
+{
+	return usb_kbd_init();
+}
+
+void module_cleanup(void)
+{
+	usb_deregister(&usb_kbd_driver);
+}
+#endif
+
diff -u --recursive --new-file v2.3.3/linux/drivers/usb/keymap.c linux/drivers/usb/keymap.c
--- v2.3.3/linux/drivers/usb/keymap.c	Mon Apr 26 12:19:05 1999
+++ linux/drivers/usb/keymap.c	Mon May 24 09:31:36 1999
@@ -7,13 +7,13 @@
     0x16,  0x2f,  0x11,  0x2d,  0x15,  0x2c,  0x02,  0x03,
 
     0x04,  0x05,  0x06,  0x07,  0x08,  0x09,  0x0a,  0x0b,
-    0x1c,  0x01,  0xd3,  0x0f,  0x39,  0x0c,  0x0d,  0x1a,
+    0x1c,  0x01,  0x0e,  0x0f,  0x39,  0x0c,  0x0d,  0x1a,
 
     0x1b,  0x2b,  0x00,  0x27,  0x28,  0x29,  0x33,  0x34,
     0x35,  0x3a,  0x3b,  0x3c,  0x3d,  0x3e,  0x3f,  0x40,
 
     0x41,  0x42,  0x43,  0x44,  0x57,  0x58,  0xb7,  0x46,
-    0x00,  0xd2,  0xc7,  0xc9,  0x63,  0xcf,  0xd1,  0xcd,
+    0x00,  0xd2,  0xc7,  0xc9,  0xd3,  0xcf,  0xd1,  0xcd,
 
     0xcb,  0xd0,  0xc8,  0x45,  0xb5,  0x37,  0x4a,  0x4e,
     0x9c,  0x4f,  0x50,  0x51,  0x4b,  0x4c,  0x4d,  0x47,
diff -u --recursive --new-file v2.3.3/linux/drivers/usb/maps/fixup.map linux/drivers/usb/maps/fixup.map
--- v2.3.3/linux/drivers/usb/maps/fixup.map	Fri Apr 16 15:38:24 1999
+++ linux/drivers/usb/maps/fixup.map	Sat May 22 13:06:37 1999
@@ -1,7 +1,6 @@
 # misc fixes
 keycode   0 = Pause
 keycode  29 = Control
-keycode  99 = Remove
 keycode  42 = Shift
 keycode  54 = Shift_R
 keycode 109 = Application
@@ -25,7 +24,7 @@
 keycode 208 = Down
 keycode 209 = Next
 keycode 210 = Insert
-keycode 211 = Delete
+keycode 211 = Remove
 keycode 219 = Window
 keycode 220 = Window_R
 keycode 221 = Menu
diff -u --recursive --new-file v2.3.3/linux/drivers/usb/mouse.c linux/drivers/usb/mouse.c
--- v2.3.3/linux/drivers/usb/mouse.c	Fri May 14 18:55:23 1999
+++ linux/drivers/usb/mouse.c	Mon May 31 09:01:50 1999
@@ -33,6 +33,8 @@
 #include <linux/poll.h>
 #include <linux/init.h>
 #include <linux/malloc.h>
+#include <linux/config.h>
+#include <linux/module.h>
 
 #include <asm/spinlock.h>
 
@@ -172,6 +174,30 @@
 			buffer++;
 			retval++;
 			state = 0;
+			if (!--count)
+				break;
+		}
+
+		/*
+		 * SUBTLE:
+		 *
+		 * The only way to get here is to do a read() of
+		 * more than 3 bytes: if you read a byte at a time
+		 * you will just ever see states 0-2, for backwards
+		 * compatibility.
+		 *
+		 * So you can think of this as a packet interface,
+		 * where you have arbitrary-sized packets, and you
+		 * only ever see the first three bytes when you read
+		 * them in small chunks.
+		 */
+		{ /* fallthrough - dz */
+			int dz = mouse->dz;
+			mouse->dz = 0;
+			put_user(dz, buffer);
+			buffer++;
+			retval++;
+			state = 0;
 		}
 		break;
 		}
@@ -261,6 +287,7 @@
 
 	/* this might need work */
 	mouse->present = 0;
+	printk("Mouse disconnected\n");
 }
 
 static struct usb_driver mouse_driver = {
@@ -291,3 +318,15 @@
 	usb_deregister(&mouse_driver);
 	misc_deregister(&usb_mouse);
 }
+
+#ifdef MODULE
+int init_module(void)
+{
+	return usb_mouse_init();
+}
+
+void cleanup_module(void)
+{
+	usb_mouse_cleanup();
+}
+#endif
diff -u --recursive --new-file v2.3.3/linux/drivers/usb/ohci-hcd.c linux/drivers/usb/ohci-hcd.c
--- v2.3.3/linux/drivers/usb/ohci-hcd.c	Sat May 15 23:46:03 1999
+++ linux/drivers/usb/ohci-hcd.c	Mon May 31 09:01:50 1999
@@ -3,12 +3,14 @@
  *
  * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
  *
- * The OHCI HCD layer is a simple but nearly complete implementation of what the
- * USB people would call a HCD  for the OHCI. 
+ * The OHCI HCD layer is a simple but nearly complete implementation of what 
+ * the USB people would call a HCD  for the OHCI. 
  * (ISO comming soon, Bulk disabled, INT u. CTRL transfers enabled)
- * The layer on top of it, is for interfacing to the alternate-usb device-drivers.
+ * The layer on top of it, is for interfacing to the alternate-usb 
+ * device-drivers.
  * 
- * [ This is based on Linus' UHCI code and gregs OHCI fragments (0.03c source tree). ]
+ * [ This is based on Linus' UHCI code and gregs OHCI fragments 
+ * (0.03c source tree). ]
  * [ Open Host Controller Interface driver for USB. ]
  * [ (C) Copyright 1999 Linus Torvalds (uhci.c) ]
  * [ (C) Copyright 1999 Gregory P. Smith <greg@electricrain.com> ]
@@ -51,9 +53,6 @@
 
 #include "usb.h"
 #include "ohci-hcd.h"
-#include "inits.h" 
-
- 
 
 #ifdef CONFIG_APM
 #include <linux/apm_bios.h>
@@ -62,7 +61,7 @@
 #endif
 
  
-
+static DECLARE_WAIT_QUEUE_HEAD(bulk_wakeup);
 static DECLARE_WAIT_QUEUE_HEAD(control_wakeup);
 static DECLARE_WAIT_QUEUE_HEAD(root_hub);
  
@@ -122,7 +121,19 @@
 	OHCI_DEBUG( printk(" ret_status: %x\n", status); })
     return 0;                       
 } 
-                                                             
+
+static int sohci_bulk_handler(void * ohci_in, unsigned int ep_addr, int ctrl_len, void * ctrl, void * data, int data_len, int status, __OHCI_BAG lw0, __OHCI_BAG lw)
+{  
+	*(int * )lw0 = status;
+	wake_up(&bulk_wakeup);
+
+	OHCI_DEBUG( { int i; printk("USB HC BULK<<<: %x:", ep_addr, ctrl_len);)
+	OHCI_DEBUG( printk(" data(%d):", data_len);) 
+	OHCI_DEBUG( for(i=0; i < data_len; i++ ) printk(" %02x", ((__u8 *) data)[i]);)
+	OHCI_DEBUG( printk(" ret_status: %x\n", status); })
+    return 0;                       
+}
+                                                           
 static int sohci_request_irq(struct usb_device *usb_dev, unsigned int pipe, usb_device_irq handler, int period, void *dev_id)
 {
 	struct ohci * ohci = usb_dev->bus->hcpriv;
@@ -179,6 +190,40 @@
 	return cc_to_status[status & 0x0f];
 }
 
+static int sohci_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	struct ohci * ohci = usb_dev->bus->hcpriv;
+	int status;
+	union ep_addr_ ep_addr;
+
+    ep_addr.iep = 0;
+	ep_addr.bep.ep = ((pipe >> 15) & 0x0f) 		/* endpoint address */		
+				| (pipe  & 0x80)  				/* direction */
+				|  (11 << 5);					/* type = bulk*/
+	ep_addr.bep.fa	= ((pipe >> 8) & 0x7f);				/* device address */
+	
+	status = 0xf; /* CC not Accessed */
+	OHCI_DEBUG( { int i; printk("USB HC BULK>>>: %x:", ep_addr.iep);)
+	OHCI_DEBUG( printk(" data(%d):", len);) 
+	OHCI_DEBUG( for(i=0; i < len; i++ ) printk(" %02x", ((__u8 *) data)[i]);)
+	OHCI_DEBUG( printk("\n"); })
+	
+	usb_ohci_add_ep(ohci, ep_addr.iep, 0, 1, sohci_bulk_handler, 1 << ((pipe & 0x03) + 3) , (pipe >> 26) & 0x01);
+	
+	current->state = TASK_UNINTERRUPTIBLE;
+    add_wait_queue(&bulk_wakeup, &wait);   
+ 
+	ohci_trans_req(ohci, ep_addr.iep, 0, NULL, data, len, (__OHCI_BAG) &status, 0);
+
+	schedule_timeout(HZ/10);
+
+    remove_wait_queue(&bulk_wakeup, &wait); 
+     
+    OHCI_DEBUG(printk("USB HC status::: %x\n", cc_to_status[status & 0x0f]);)
+     
+	return cc_to_status[status & 0x0f];
+}
 
 static int sohci_usb_deallocate(struct usb_device *usb_dev) {
     struct ohci_device *dev = usb_to_ohci(usb_dev); 
@@ -234,8 +279,6 @@
 	return usb_dev;
 }
 
-/* FIXME! */
-#define sohci_bulk_msg NULL
 
 struct usb_operations sohci_device_operations = {
 	sohci_usb_allocate,
@@ -567,7 +610,7 @@
   }
   writel(0, &ohci->regs->ed_controlcurrent); /* reset CTRL list */
   writel(0, &ohci->regs->ed_bulkcurrent);    /* reset BULK list */
-  writel_set((0x01<<4), &ohci->regs->control);   /* start CTRL u. (BULK list) */ 
+  writel_set((0x03<<4), &ohci->regs->control);   /* start CTRL u. (BULK list) */ 
 
   spin_unlock_irqrestore(&usb_ed_lock, flags);
   
@@ -915,6 +958,7 @@
 {
    /*  int fminterval; */
     unsigned int mask;
+    int port_nr;
   /*  fminterval = readl(&ohci->regs->fminterval) & 0x3fff;
 	reset_hc(ohci); */
  
@@ -926,16 +970,19 @@
 	/* | OHCI_INTR_SO | OHCI_INTR_UE |OHCI_INTR_RHSC |OHCI_INTR_SF|  
 		OHCI_INTR_FNO */
 		
-
+	if(readl(&ohci->regs->roothub.a) & 0x100) /* global power on */
+ 		writel( 0x10000, &ohci->regs->roothub.status); /* root hub power on */
+	else { /* port power on */
+ 		for(port_nr=0; port_nr < (ohci->regs->roothub.a & 0xff); port_nr++)
+	     		 writel(0x100, &ohci->regs->roothub.portstatus[port_nr]);
+	}
+	wait_ms(50); 		
 	 
 	writel((0x00), &ohci->regs->control); /* USB Reset BUS */
 	wait_ms(10);
 	  
-	writel((0x97), &ohci->regs->control); /* USB Operational  */
-	
- 	writel( 0x10000, &ohci->regs->roothub.status); /* root hub power on */
-	wait_ms(50); 
-		
+	writel((0xB7), &ohci->regs->control); /* USB Operational  */
+ 		
 	OHCI_DEBUG(printk("USB HC rstart_hc_operational: %x\n", readl(&ohci->regs->control)); )
  	OHCI_DEBUG(printk("USB HC roothubstata: %x \n", readl( &(ohci->regs->roothub.a) )); )
  	OHCI_DEBUG(printk("USB HC roothubstatb: %x \n", readl( &(ohci->regs->roothub.b) )); )
@@ -952,15 +999,15 @@
 
 	struct usb_device * usb_dev;
 	struct ohci_device *dev;
-
-	usb_dev = sohci_usb_allocate(ohci->root_hub->usb);
+	struct ohci_device *tmp_root_hub= usb_to_ohci(ohci->bus->root_hub);
+	usb_dev = sohci_usb_allocate(tmp_root_hub->usb);
 	dev = usb_dev->hcpriv; 
 
 	dev->ohci = ohci; 
 
 	usb_connect(usb_dev);
 
-	ohci->root_hub->usb->children[0] = usb_dev;
+	tmp_root_hub->usb->children[0] = usb_dev;
 
 	usb_new_device(usb_dev);
 	}
@@ -1037,6 +1084,7 @@
 {
 	struct usb_device *usb_dev;
     struct ohci_device *dev;
+	struct ohci_device *tmp_root_hub=usb_to_ohci(ohci->bus->root_hub);
 	OHCI_DEBUG(printk("uhci_connect_change: called for %d stat %x\n", port_nr,readl(&ohci->regs->roothub.portstatus[port_nr]) );)
 
 	/*
@@ -1046,7 +1094,7 @@
 	 *
 	 * So start off by getting rid of any old devices..
 	 */
-	usb_disconnect(&ohci->root_hub->usb->children[port_nr]);
+	usb_disconnect(&tmp_root_hub->usb->children[port_nr]);
 
 	 if(!(readl(&ohci->regs->roothub.portstatus[port_nr]) & RH_PS_CCS)) {
 	 	writel(RH_PS_CCS, &ohci->regs->roothub.portstatus[port_nr]);
@@ -1056,11 +1104,11 @@
 	 * Ok, we got a new connection. Allocate a device to it,
 	 * and find out what it wants to do..
 	 */
-	usb_dev = sohci_usb_allocate(ohci->root_hub->usb);
+	usb_dev = sohci_usb_allocate(tmp_root_hub->usb);
 	dev = usb_dev->hcpriv; 
 	dev->ohci = ohci; 
 	usb_connect(dev->usb);
-	ohci->root_hub->usb->children[port_nr] = usb_dev;
+	tmp_root_hub->usb->children[port_nr] = usb_dev;
 	wait_ms(200); /* wait for powerup */
     /* reset port/device */
  	writel(RH_PS_PRS, &ohci->regs->roothub.portstatus[port_nr]); /* reset port */
@@ -1205,10 +1253,9 @@
 	if (!usb)
 		return NULL;
 
-	dev = ohci->root_hub = usb_to_ohci(usb);
-
+	dev = usb_to_ohci(usb);
 	usb->bus = bus;
-	/* bus->root_hub = ohci_to_usb(ohci->root_hub); */
+	bus->root_hub = usb;
 	dev->ohci = ohci;
 	
 	/* Initialize the root hub */
@@ -1227,10 +1274,11 @@
 static void release_ohci(struct ohci *ohci)
 {
 	int i;
+	struct ohci_device *tmp_root_hub=usb_to_ohci(ohci->bus->root_hub);
 	union ep_addr_ ep_addr;
 	ep_addr.iep = 0;
 	
-    OHCI_DEBUG(printk("USB HC release ohci \n");)
+	OHCI_DEBUG(printk("USB HC release ohci \n"););
     
 	if (ohci->irq >= 0) {
 		free_irq(ohci->irq, ohci);
@@ -1248,25 +1296,22 @@
 	ohci_rm_eds(ohci); /* remove eds */
 	
     /* disconnect all devices */    
-	if(ohci->root_hub)
-		for(i = 0; i < ohci->root_hub->usb->maxchild; i++)
-			  usb_disconnect(ohci->root_hub->usb->children + i);
+	if(ohci->bus->root_hub)
+		for(i = 0; i < tmp_root_hub->usb->maxchild; i++)
+			usb_disconnect(tmp_root_hub->usb->children + i);
 	    
-    USB_FREE(ohci->root_hub->usb);
-    USB_FREE(ohci->root_hub);
+    usb_deregister_bus(ohci->bus);
+    USB_FREE(tmp_root_hub->usb);
+    USB_FREE(tmp_root_hub);
     USB_FREE(ohci->bus);
     
 	/* unmap the IO address space */
 	iounmap(ohci->regs);
        
-	
 	free_pages((unsigned int) ohci->hc_area, 1);
 	
 }
 
- 
-void cleanup_drivers(void);
-
 static int ohci_roothub_thread(void * __ohci)
 {
         struct ohci *ohci = (struct ohci *)__ohci;
@@ -1288,6 +1333,7 @@
 		start_hc(ohci);
 		writel( 0x10000, &ohci->regs->roothub.status);
 		wait_ms(50); /* root hub power on */
+	usb_register_bus(ohci->bus);
          do {
 #ifdef CONFIG_APM
 		if (apm_resume) {
@@ -1297,7 +1343,7 @@
 		}
 #endif
  	 
-         OHCI_DEBUG(printk("USB RH tasks: int: %x\n", ohci->intrstatus); )
+		OHCI_DEBUG(printk("USB RH tasks: int: %x\n",ohci->intrstatus););
 #ifndef VROOTHUB
 		/*	if (ohci->intrstatus & OHCI_INTR_RHSC)  */
 			{
@@ -1332,9 +1378,6 @@
         return 0;
 }
 
-
- 
-
 /*
  * Increment the module usage count, start the control thread and
  * return success.
@@ -1432,20 +1475,6 @@
 }
 #endif
 
-
-#ifdef MODULE
-
-void cleanup_module(void)
-{
-#ifdef CONFIG_APM
-	apm_unregister_callback(&handle_apm_event);
-#endif
-}
-
-#define ohci_hcd_init init_module
-
-#endif
-
 #define PCI_CLASS_SERIAL_USB_OHCI 0x0C0310
 #define PCI_CLASS_SERIAL_USB_OHCI_PG 0x10
  
@@ -1471,3 +1500,17 @@
   }
   return retval;
 }
+
+#ifdef MODULE
+int init_module(void){
+	return ohci_hcd_init();
+}
+
+void cleanup_module(void)
+{
+#	ifdef CONFIG_APM
+	apm_unregister_callback(&handle_apm_event);
+#	endif
+}
+#endif //MODULE
+
diff -u --recursive --new-file v2.3.3/linux/drivers/usb/ohci-hcd.h linux/drivers/usb/ohci-hcd.h
--- v2.3.3/linux/drivers/usb/ohci-hcd.h	Tue May 11 09:55:45 1999
+++ linux/drivers/usb/ohci-hcd.h	Mon May 31 09:01:50 1999
@@ -307,7 +307,6 @@
         struct usb_ohci_ed * ed_bulktail;       /* last endpoint of bulk list */
         struct usb_ohci_ed * ed_controltail;    /* last endpoint of control list */
         struct usb_ohci_ed * ed_isotail;        /* last endpoint of iso list */
-        struct ohci_device * root_hub;
         struct usb_ohci_ed ed_rh_ep0;
         struct usb_ohci_ed ed_rh_epi;
         struct ohci_rep_td *td_rh_epi;
diff -u --recursive --new-file v2.3.3/linux/drivers/usb/ohci.c linux/drivers/usb/ohci.c
--- v2.3.3/linux/drivers/usb/ohci.c	Mon May 17 09:55:22 1999
+++ linux/drivers/usb/ohci.c	Mon May 31 09:01:50 1999
@@ -49,7 +49,6 @@
 #include <asm/system.h>
 
 #include "ohci.h"
-#include "inits.h"
 
 #ifdef CONFIG_APM
 #include <linux/apm_bios.h>
@@ -63,9 +62,7 @@
 #define OHCI_DEBUG    /* to make typing it easier.. */
 #endif
 
-#ifdef OHCI_DEBUG
 int MegaDebug = 0;	/* SIGUSR2 to the control thread toggles this */
-#endif
 
 
 #ifdef OHCI_TIMER
@@ -207,12 +204,13 @@
 void ohci_add_periodic_ed(struct ohci *ohci, struct ohci_ed *ed, int period)
 {
 	struct ohci_ed *int_ed;
+	struct ohci_device *root_hub=usb_to_ohci(ohci->bus->root_hub);
 	unsigned long flags;
 
 	/*
 	 * Pick a good frequency endpoint based on the requested period
 	 */
-	int_ed = &ohci->root_hub->ed[ms_to_ed_int(period)];
+	int_ed = &root_hub->ed[ms_to_ed_int(period)];
 #ifdef OHCI_DEBUG
 	printk("usb-ohci: Using INT ED queue %d for %dms period\n",
 			ms_to_ed_int(period), period);
@@ -632,7 +630,7 @@
 	 * Set the max packet size, device speed, endpoint number, usb
 	 * device number (function address), and type of TD.
 	 */
-	ohci_fill_ed(dev, interrupt_ed, usb_maxpacket(pipe), usb_pipeslow(pipe),
+	ohci_fill_ed(dev, interrupt_ed, usb_maxpacket(usb,pipe), usb_pipeslow(pipe),
 		usb_pipe_endpdev(pipe), 0 /* normal TDs */);
 
 	/* Fill in the TD */
@@ -732,7 +730,7 @@
 	 * device number (function address), and type of TD.
 	 *
 	 */
-	ohci_fill_ed(dev, control_ed, usb_maxpacket(pipe), usb_pipeslow(pipe),
+	ohci_fill_ed(dev, control_ed, usb_maxpacket(usb,pipe), usb_pipeslow(pipe),
 		usb_pipe_endpdev(pipe), 0 /* normal TDs */);
 
 	/*
@@ -1015,6 +1013,8 @@
 	int fminterval;
 	__u32 what_to_enable;
 
+	struct ohci_device *root_hub=usb_to_ohci(ohci->bus->root_hub);
+
 	fminterval = readl(&ohci->regs->fminterval) & 0x3fff;
 #if 0
 	printk(KERN_DEBUG "entering start_hc %p\n", ohci);
@@ -1024,7 +1024,7 @@
 		return -1;
 
 	/* restore registers cleared by the reset */
-	writel(virt_to_bus(ohci->root_hub->hcca), &ohci->regs->hcca);
+	writel(virt_to_bus(root_hub->hcca), &ohci->regs->hcca);
 
 	/*
 	 * XXX Should fminterval also be set here?
@@ -1120,6 +1120,7 @@
 {
 	struct usb_device *usb_dev;
 	struct ohci_device *dev;
+	struct ohci_device *root_hub=usb_to_ohci(ohci->bus->root_hub);
 	/* memory I/O address of the port status register */
 	__u32 *portaddr = &ohci->regs->roothub.portstatus[port];
 	int portstatus;	
@@ -1133,7 +1134,7 @@
 	 * everything we think we know about the device
 	 * on this root hub port.  It may have changed.
 	 */
-	usb_disconnect(ohci->root_hub->usb->children + port);
+	usb_disconnect(root_hub->usb->children + port);
 
 	portstatus = readl(portaddr);
 
@@ -1154,7 +1155,7 @@
 	/*
 	 * Allocate a device for the new thingy that's been attached
 	 */
-	usb_dev = ohci_usb_allocate(ohci->root_hub->usb);
+	usb_dev = ohci_usb_allocate(root_hub->usb);
 	dev = usb_dev->hcpriv;
 
 	dev->ohci = ohci;
@@ -1162,7 +1163,7 @@
 	usb_connect(dev->usb);
 
 	/* link it into the bus's device tree */
-	ohci->root_hub->usb->children[port] = usb_dev;
+	root_hub->usb->children[port] = usb_dev;
 
 	wait_ms(200); /* wait for powerup; XXX is this needed? */
 	ohci_reset_port(ohci, port);
@@ -1220,10 +1221,12 @@
  */
 static void ohci_root_hub_events(struct ohci *ohci)
 {
-	if (waitqueue_active(&ohci_configure)) {
 		int num = 0;
-		int maxport = ohci->root_hub->usb->maxchild;
+	struct ohci_device *root_hub=usb_to_ohci(ohci->bus->root_hub);
+	int maxport = root_hub->usb->maxchild;
 
+	if (!waitqueue_active(&ohci_configure))
+		return;
 		do {
 			__u32 *portstatus_p = &ohci->regs->roothub.portstatus[num];
 			if (readl(portstatus_p) & PORT_CSC) {
@@ -1232,7 +1235,7 @@
 				return;
 			}
 		} while (++num < maxport);
-	}
+	
 } /* ohci_root_hub_events() */
 
 
@@ -1248,7 +1251,8 @@
 static struct ohci_td * ohci_reverse_donelist(struct ohci * ohci)
 {
 	__u32 td_list_hc;
-	struct ohci_hcca *hcca = ohci->root_hub->hcca;
+	struct ohci_device *root_hub=usb_to_ohci(ohci->bus->root_hub);
+	struct ohci_hcca *hcca = root_hub->hcca;
 	struct ohci_td *td_list = NULL;
 	struct ohci_td *td_rev = NULL;
   	
@@ -1326,7 +1330,8 @@
 {
 	struct ohci *ohci = __ohci;
 	struct ohci_regs *regs = ohci->regs;
-	struct ohci_hcca *hcca = ohci->root_hub->hcca;
+	struct ohci_device *root_hub=usb_to_ohci(ohci->bus->root_hub);
+	struct ohci_hcca *hcca = root_hub->hcca;
 	__u32 status, context;
 
 	/* Save the status of the interrupts that are enabled */
@@ -1481,8 +1486,8 @@
 	if (!usb)
 		return NULL;
 
-	dev = ohci->root_hub = usb_to_ohci(usb);
-
+	dev = usb_to_ohci(usb);
+	ohci->bus->root_hub= ohci_to_usb(dev);
 	usb->bus = bus;
 
 	/* Initialize the root hub */
@@ -1588,13 +1593,14 @@
 	/* stop all OHCI interrupts */
 	writel(~0x0, &ohci->regs->intrdisable);
 
-	if (ohci->root_hub) {
+	if (ohci->bus->root_hub) {
+		struct ohci_device *root_hub=usb_to_ohci(ohci->bus->root_hub);
 		/* ensure that HC is stopped before releasing the HCCA */
 		writel(OHCI_USB_SUSPEND, &ohci->regs->control);
-		free_page((unsigned long) ohci->root_hub->hcca);
-		kfree(ohci->root_hub);
-		ohci->root_hub->hcca = NULL;
-		ohci->root_hub = NULL;
+		free_page((unsigned long) root_hub->hcca);
+		kfree(ohci->bus->root_hub);
+		root_hub->hcca = NULL;
+		ohci->bus->root_hub = NULL;
 	}
 
 	/* unmap the IO address space */
@@ -1634,12 +1640,15 @@
 
 	strcpy(current->comm, "ohci-control");
 
+	usb_register_bus(ohci->bus);
+
 	/*
 	 * Damn the torpedoes, full speed ahead
 	 */
 	if (start_hc(ohci) < 0) {
 		printk("usb-ohci: failed to start the controller\n");
 		release_ohci(ohci);
+		usb_deregister_bus(ohci->bus);
 		printk(KERN_INFO "leaving ohci_control_thread %p\n", __ohci);
 		return 0;
 	}
@@ -1697,7 +1706,7 @@
 
 	reset_hc(ohci);
 	release_ohci(ohci);
-
+	usb_deregister_bus(ohci->bus);
 	printk(KERN_INFO "ohci-control thread for 0x%p exiting\n", __ohci);
 
 	return 0;
@@ -1864,26 +1873,6 @@
 	return 0;
 } /* init_ohci() */
 
-#ifdef MODULE
-/*
- *  Clean up when unloading the module
- */
-void cleanup_module(void)
-{
-#ifdef CONFIG_APM
-	apm_unregister_callback(&handle_apm_event);
-#endif
-#ifdef CONFIG_USB_MOUSE
-	usb_mouse_cleanup();
-#endif
-	printk("usb-ohci: module unloaded\n");
-}
-
-#define ohci_init init_module
-
-#endif
-
-
 /* TODO this should be named following Linux convention and go in pci.h */
 #define PCI_CLASS_SERIAL_USB_OHCI ((PCI_CLASS_SERIAL_USB << 8) | 0x0010)
 
@@ -1935,3 +1924,21 @@
 
 /* vim:sw=8
  */
+
+#ifdef MODULE
+/*
+ *  Clean up when unloading the module
+ */
+void module_cleanup(void){
+#	ifdef CONFIG_APM
+	apm_unregister_callback(&handle_apm_event);
+#	endif
+	printk("usb-ohci: module unloaded\n");
+}
+
+int init_module(void){
+	return ohci_init();
+}
+#endif //MODULE
+
+
diff -u --recursive --new-file v2.3.3/linux/drivers/usb/ohci.h linux/drivers/usb/ohci.h
--- v2.3.3/linux/drivers/usb/ohci.h	Mon May 17 09:55:22 1999
+++ linux/drivers/usb/ohci.h	Mon May 31 09:01:50 1999
@@ -367,7 +367,6 @@
     	int irq;
 	struct ohci_regs *regs;			/* OHCI controller's memory */
 	struct usb_bus *bus;
-	struct ohci_device *root_hub;		/* Root hub & controller */
 	struct list_head interrupt_list;	/* List of interrupt active TDs for this OHCI */
 };
 
diff -u --recursive --new-file v2.3.3/linux/drivers/usb/printer.c linux/drivers/usb/printer.c
--- v2.3.3/linux/drivers/usb/printer.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/usb/printer.c	Sat May 22 14:45:56 1999
@@ -0,0 +1,412 @@
+
+/* Driver for USB Printers
+ * 
+ * (C) Michael Gee (michael@linuxspecific.com) 1999
+ * 
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/random.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+#include <linux/lp.h>
+
+#include <asm/spinlock.h>
+
+#include "usb.h"
+
+#define NAK_TIMEOUT (HZ)				/* stall wait for printer */
+#define MAX_RETRY_COUNT ((60*60*HZ)/NAK_TIMEOUT)	/* should not take 1 minute a page! */
+
+#ifndef USB_PRINTER_MAJOR
+#define USB_PRINTER_MAJOR 0
+#endif
+
+static int mymajor = USB_PRINTER_MAJOR;
+
+#define MAX_PRINTERS	8
+
+struct pp_usb_data {
+	struct usb_device 	*pusb_dev;
+	__u8			isopen;			/* nz if open */
+	__u8			noinput;		/* nz if no input stream */
+	__u8			minor;			/* minor number of device */
+	__u8			status;			/* last status from device */
+	int			maxin, maxout;		/* max transfer size in and out */
+	char			*obuf;			/* transfer buffer (out only) */
+	wait_queue_head_t	wait_q;			/* for timeouts */
+	unsigned int		last_error;		/* save for checking */
+};
+
+static struct pp_usb_data *minor_data[MAX_PRINTERS];
+
+#define PPDATA(x) ((struct pp_usb_data *)(x))
+
+unsigned char printer_read_status(struct pp_usb_data *p)
+{
+	__u8 status;
+	devrequest dr;
+	struct usb_device *dev = p->pusb_dev;
+
+	dr.requesttype = USB_TYPE_CLASS | USB_RT_INTERFACE | 0x80;
+	dr.request = 1;
+	dr.value = 0;
+	dr.index = 0;
+	dr.length = 1;
+	if (dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, &status, 1)) {
+		return 0;
+	}
+	return status;
+}
+
+static int printer_check_status(struct pp_usb_data *p)
+{
+	unsigned int last = p->last_error;
+	unsigned char status = printer_read_status(p);
+
+	if (status & LP_PERRORP)
+		/* No error. */
+		last = 0;
+	else if ((status & LP_POUTPA)) {
+		if (last != LP_POUTPA) {
+			last = LP_POUTPA;
+			printk(KERN_INFO "usblp%d out of paper\n", p->minor);
+		}
+	} else if (!(status & LP_PSELECD)) {
+		if (last != LP_PSELECD) {
+			last = LP_PSELECD;
+			printk(KERN_INFO "usblp%d off-line\n", p->minor);
+		}
+	} else {
+		if (last != LP_PERRORP) {
+			last = LP_PERRORP;
+			printk(KERN_INFO "usblp%d on fire\n", p->minor);
+		}
+	}
+
+	p->last_error = last;
+
+	return status;
+}
+
+void printer_reset(struct pp_usb_data *p)
+{
+	devrequest dr;
+	struct usb_device *dev = p->pusb_dev;
+
+	dr.requesttype = USB_TYPE_CLASS | USB_RECIP_OTHER;
+	dr.request = 2;
+	dr.value = 0;
+	dr.index = 0;
+	dr.length = 0;
+	dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0);
+}
+
+static int open_printer(struct inode * inode, struct file * file)
+{
+	struct pp_usb_data *p;
+
+	if(MINOR(inode->i_rdev) >= MAX_PRINTERS ||
+	   !minor_data[MINOR(inode->i_rdev)]) {
+		return -ENODEV;
+	}
+
+	p = minor_data[MINOR(inode->i_rdev)];
+	p->minor = MINOR(inode->i_rdev);
+
+	if (p->isopen++) {
+		return -EBUSY;
+	}
+	if (!(p->obuf = (char *)__get_free_page(GFP_KERNEL))) {
+		return -ENOMEM;
+	}
+
+	printer_check_status(p);
+
+
+	file->private_data = p;
+//	printer_reset(p);
+	init_waitqueue_head(&p->wait_q);
+	return 0;
+}
+
+static int close_printer(struct inode * inode, struct file * file)
+{
+	struct pp_usb_data *p = file->private_data;
+
+	free_page((unsigned long)p->obuf);
+	p->isopen = 0;
+	file->private_data = NULL;
+	if(!p->pusb_dev) {
+		minor_data[p->minor] = NULL;
+		kfree(p);
+
+		MOD_DEC_USE_COUNT;
+
+	}
+	return 0;
+}
+
+static ssize_t write_printer(struct file * file,
+       const char * buffer, size_t count, loff_t *ppos)
+{
+	struct pp_usb_data *p = file->private_data;
+	unsigned long copy_size;
+	unsigned long bytes_written = 0;
+	unsigned long partial;
+	int result;
+	int maxretry;
+
+	do {
+		char *obuf = p->obuf;
+		unsigned long thistime;
+
+		thistime = copy_size = (count > p->maxout) ? p->maxout : count;
+		if (copy_from_user(p->obuf, buffer, copy_size))
+			return -EFAULT;
+		maxretry = MAX_RETRY_COUNT;
+		while (thistime) {
+			if (!p->pusb_dev)
+				return -ENODEV;
+			if (signal_pending(current)) {
+				return bytes_written ? bytes_written : -EINTR;
+			}
+			result = p->pusb_dev->bus->op->bulk_msg(p->pusb_dev,
+					 usb_sndbulkpipe(p->pusb_dev, 1), obuf, thistime, &partial);
+			if (result & 0x08) {	/* NAK - so hold for a while */
+				obuf += partial;
+				thistime -= partial;
+				if(!maxretry--)
+					return -ETIME;
+                                interruptible_sleep_on_timeout(&p->wait_q, NAK_TIMEOUT);
+				continue;
+			} else
+				break;
+		};
+		if (result) {
+			/* whoops - let's reset and fail the request */
+//			printk("Whoops - %x\n", result);
+			printer_reset(p);
+			interruptible_sleep_on_timeout(&p->wait_q, 5*HZ);  /* let reset do its stuff */
+			return -EIO;
+		}
+		bytes_written += copy_size;
+		count -= copy_size;
+		buffer += copy_size;
+	} while ( count > 0 );
+
+	return bytes_written ? bytes_written : -EIO;
+}
+
+static ssize_t read_printer(struct file * file,
+       char * buffer, size_t count, loff_t *ppos)
+{
+	struct pp_usb_data *p = file->private_data;
+	int read_count;
+	int this_read;
+	char buf[64];
+	unsigned long partial;
+	int result;
+
+	if (p->noinput)
+		return -EINVAL;
+
+	read_count = 0;
+	while (count) {
+		if (signal_pending(current)) {
+			return read_count ? read_count : -EINTR;
+		}
+		if (!p->pusb_dev)
+			return -ENODEV;
+		this_read = (count > sizeof(buf)) ? sizeof(buf) : count;
+
+		result = p->pusb_dev->bus->op->bulk_msg(p->pusb_dev,
+			  usb_rcvbulkpipe(p->pusb_dev, 2), buf, this_read, &partial);
+
+		/* unlike writes, we don't retry a NAK, just stop now */
+		if (result & 0x08)
+			count = this_read = partial;
+		else if (result)
+			return -EIO;
+
+		if (this_read) {
+			if (copy_to_user(buffer, p->obuf, this_read))
+				return -EFAULT;
+			count -= this_read;
+			read_count += this_read;
+			buffer += this_read;
+		}
+	}
+	return read_count;
+}
+
+static int printer_probe(struct usb_device *dev)
+{
+	struct usb_interface_descriptor *interface;
+	int i;
+
+	/*
+	 * FIXME - this will not cope with combined printer/scanners
+	 */
+	if (dev->descriptor.bDeviceClass != 7 ||
+	    dev->descriptor.bNumConfigurations != 1 ||
+	    dev->config[0].bNumInterfaces != 1) {
+		return -1;
+	}
+
+	interface = dev->config->interface;
+
+	/* Lets be paranoid (for the moment)*/
+	if (interface->bInterfaceClass != 7 ||
+	    interface->bInterfaceSubClass != 1 ||
+	    (interface->bInterfaceProtocol != 2 && interface->bInterfaceProtocol != 1)||
+	    interface->bNumEndpoints > 2) {
+		return -1;
+	}
+
+	if (interface->endpoint[0].bEndpointAddress != 0x01 ||
+	    interface->endpoint[0].bmAttributes != 0x02 ||
+	    (interface->bNumEndpoints > 1 && (
+		    interface->endpoint[1].bEndpointAddress != 0x82 ||
+		    interface->endpoint[1].bmAttributes != 0x02))) {
+		return -1;
+	}
+
+	for (i=0; i<MAX_PRINTERS; i++) {
+		if (!minor_data[i])
+			break;
+	}
+	if (i >= MAX_PRINTERS) {
+		return -1;
+	}
+
+	printk(KERN_INFO "USB Printer found at address %d\n", dev->devnum);
+
+	if (!(dev->private = kmalloc(sizeof(struct pp_usb_data), GFP_KERNEL))) {
+		printk( KERN_DEBUG "usb_printer: no memory!\n");
+		return -1;
+	}
+
+	memset(dev->private, 0, sizeof(struct pp_usb_data));
+	minor_data[i] = PPDATA(dev->private);
+	minor_data[i]->minor = i;
+	minor_data[i]->pusb_dev = dev;
+	minor_data[i]->maxout = interface->endpoint[0].wMaxPacketSize * 16;
+	if (minor_data[i]->maxout > PAGE_SIZE) {
+                minor_data[i]->maxout = PAGE_SIZE;
+	}
+	if (interface->bInterfaceProtocol != 2)
+		minor_data[i]->noinput = 1;
+	else {
+		minor_data[i]->maxin = interface->endpoint[1].wMaxPacketSize;
+	}
+
+        if (usb_set_configuration(dev, dev->config[0].bConfigurationValue)) {
+		printk(KERN_INFO "  Failed to set configuration\n");
+		return -1;
+	}
+#if 0
+	{
+		__u8 status;
+		__u8 ieee_id[64];
+		devrequest dr;
+
+		/* Lets get the device id if possible */
+		dr.requesttype = USB_TYPE_CLASS | USB_RT_INTERFACE | 0x80;
+		dr.request = 0;
+		dr.value = 0;
+		dr.index = 0;
+		dr.length = sizeof(ieee_id) - 1;
+		if (dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, ieee_id, sizeof(ieee_id)-1) == 0) {
+			if (ieee_id[1] < sizeof(ieee_id) - 1)
+				ieee_id[ieee_id[1]+2] = '\0';
+			else
+				ieee_id[sizeof(ieee_id)-1] = '\0';
+			printk(KERN_INFO "  Printer ID is %s\n", &ieee_id[2]);
+		}
+		status = printer_read_status(PPDATA(dev->private));
+		printk(KERN_INFO "  Status is %s,%s,%s\n",
+		       (status & 0x10) ? "Selected" : "Not Selected",
+		       (status & 0x20) ? "No Paper" : "Paper",
+		       (status & 0x08) ? "No Error" : "Error");
+	}
+#endif
+	return 0;
+}
+
+static void printer_disconnect(struct usb_device *dev)
+{
+	struct pp_usb_data *pp = dev->private;
+
+	if (pp->isopen) {
+		/* better let it finish - the release will do whats needed */
+		pp->pusb_dev = NULL;
+		return;
+	}
+	minor_data[pp->minor] = NULL;
+	kfree(pp);
+	dev->private = NULL;		/* just in case */
+	MOD_DEC_USE_COUNT;
+}
+
+static struct usb_driver printer_driver = {
+	"printer",
+	printer_probe,
+	printer_disconnect,
+	{ NULL, NULL }
+};
+
+static struct file_operations usb_printer_fops = {
+	NULL,		/* seek */
+	read_printer,
+	write_printer,
+	NULL,		/* readdir */
+	NULL,		/* poll - out for the moment */
+	NULL,		/* ioctl */
+	NULL,		/* mmap */
+	open_printer,
+	NULL,		/* flush ? */
+	close_printer,
+	NULL,
+	NULL
+};
+
+int usb_printer_init(void)
+{
+	int result;
+
+	MOD_INC_USE_COUNT;
+	
+	if ((result = register_chrdev(USB_PRINTER_MAJOR, "usblp", &usb_printer_fops)) < 0) {
+		printk(KERN_WARNING "usbprinter: Cannot register device\n");
+		return result;
+	}
+	if (mymajor == 0) {
+		mymajor = result;
+	}
+	usb_register(&printer_driver);
+	printk(KERN_INFO "USB Printer support registered.\n");
+	return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+
+	return usb_printer_init();
+}
+
+void cleanup_module(void)
+{
+	unsigned int offset;
+
+	usb_deregister(&printer_driver);
+	unregister_chrdev(mymajor, "usblplp");
+}
+#endif
diff -u --recursive --new-file v2.3.3/linux/drivers/usb/uhci-debug.c linux/drivers/usb/uhci-debug.c
--- v2.3.3/linux/drivers/usb/uhci-debug.c	Mon Apr 26 12:22:58 1999
+++ linux/drivers/usb/uhci-debug.c	Mon May 31 09:01:50 1999
@@ -12,6 +12,8 @@
 
 void show_td(struct uhci_td * td)
 {
+	char *spid;
+
 	printk("%08x ", td->link);
 	printk("%se%d %s%s%s%s%s%s%s%s%s%sLength=%x ",
 		((td->status >> 29) & 1) ? "SPD " : "",
@@ -27,12 +29,27 @@
 		((td->status >> 18) & 1) ? "CRC/Timeo " : "",
 		((td->status >> 17) & 1) ? "BitStuff " : "",
 		td->status & 0x7ff);
-	printk("MaxLen=%x %sEndPt=%x Dev=%x, PID=%x ",
+	switch (td->info & 0xff) {
+	case 0x2d:
+		spid = "SETUP";
+		break;
+	case 0xe1:
+		spid = "OUT";
+		break;
+	case 0x69:
+		spid = "IN";
+		break;
+	default:
+		spid = "?";
+		break;
+	}
+	printk("MaxLen=%x %sEndPt=%x Dev=%x, PID=%x(%s) ",
 		td->info >> 21,
 		 ((td->info >> 19) & 1) ? "DT " : "",
 		 (td->info >> 15) & 15,
 		 (td->info >> 8) & 127,
-		 td->info & 0xff);
+		 (td->info & 0xff),
+		 spid);
 	printk("(buf=%08x)\n", td->buffer);
 }
 
@@ -130,9 +147,9 @@
 int is_skeleton_qh(struct uhci *uhci, struct uhci_qh *qh)
 {
 	int j;
-
+	struct uhci_device * root_hub=usb_to_uhci(uhci->bus->root_hub);
 	for (j = 0; j < UHCI_MAXQH; j++)
-		if (qh == uhci->root_hub->qh + j)
+		if (qh == root_hub->qh + j)
 			return 1;
 
 	return 0;
@@ -148,15 +165,16 @@
 {
 	int i;
 	struct uhci_qh *qh;
+	struct uhci_device * root_hub=usb_to_uhci(uhci->bus->root_hub);
 
 	for (i = 0; i < UHCI_MAXQH; ++i) {
 		printk("  %s:\n", qh_names[i]);
 #if 0
-		printk("  qh #%d, %p\n", i, virt_to_bus(uhci->root_hub->qh + i));
+		printk("  qh #%d, %p\n", i, virt_to_bus(root_hub->qh + i));
 		show_queue(uhci->root_hub->qh + i);
 #endif
 
-		qh = uhci_link_to_qh(uhci->root_hub->qh[i].link);
+		qh = uhci_link_to_qh(root_hub->qh[i].link);
 		for (; qh; qh = uhci_link_to_qh(qh->link)) {
 			if (is_skeleton_qh(uhci, qh))
 				break;
@@ -165,4 +183,3 @@
 		}
 	}
 }
-
diff -u --recursive --new-file v2.3.3/linux/drivers/usb/uhci.c linux/drivers/usb/uhci.c
--- v2.3.3/linux/drivers/usb/uhci.c	Sat May 15 23:46:04 1999
+++ linux/drivers/usb/uhci.c	Mon May 31 09:06:44 1999
@@ -19,6 +19,7 @@
  */
 
 /* 4/4/1999 added data toggle for interrupt pipes -keryan */
+/* 5/16/1999 added global toggles for bulk and control */
 
 #include <linux/config.h>
 #include <linux/module.h>
@@ -31,13 +32,19 @@
 #include <linux/smp_lock.h>
 #include <linux/errno.h>
 
+#include <linux/sched.h>
+#include <linux/unistd.h>
+#include <linux/smp_lock.h>
+
+#include <asm/uaccess.h>
+
+
 #include <asm/spinlock.h>
 #include <asm/io.h>
 #include <asm/irq.h>
 #include <asm/system.h>
 
 #include "uhci.h"
-#include "inits.h"
 
 #ifdef CONFIG_APM
 #include <linux/apm_bios.h>
@@ -52,17 +59,30 @@
 /*
  * Return the result of a TD..
  */
-static int uhci_td_result(struct uhci_device *dev, struct uhci_td *td)
+static int uhci_td_result(struct uhci_device *dev, struct uhci_td *td, unsigned long *rval)
 {
 	unsigned int status;
+	struct uhci_td *tmp = td->first;
 
-	status = (td->status >> 16) & 0xff;
+	/* locate the first failing td, if any */
 
+	do {
+		status = (tmp->status >> 16) & 0xff;
+		if (status)
+			break;
+		if ((tmp->link & 1) || (tmp->link & 2))
+			break;
+		tmp = bus_to_virt(tmp->link & ~0xF);
+	} while (1);
+
+	if(rval)
+		*rval = 0;
 	/* Some debugging code */
-	if (status) {
+	if (status && (!usb_pipeendpoint(tmp->info) || !(status & 0x08)) ) {
 		int i = 10;
-		struct uhci_td *tmp = td->first;
-		printk("uhci_td_result() failed with status %d\n", status);
+
+		tmp = td->first;
+		printk("uhci_td_result() failed with status %x\n", status);
 		show_status(dev->uhci);
 		do {
 			show_td(tmp);
@@ -73,6 +93,34 @@
 				break;
 		} while (1);
 	}
+	if (usb_pipeendpoint(tmp->info) && (status & 0x08)) {
+//		printk("uhci_td_result() - NAK\n");
+		/* find total length xferred and reset toggle on failed packets */
+		tmp = td->first;
+		do {
+			/* sum up packets that did not fail */
+			if(rval && !((tmp->status >> 16) & 0xff))
+				*rval += (tmp->status & 0x3ff) + 1;
+
+			/* 
+			 * Note - only the first to fail will be marked NAK
+			 */
+			if (tmp->status & 0xFF0000) {
+				/* must reset the toggle on any error */
+				usb_settoggle(dev->usb, usb_pipeendpoint(tmp->info), (tmp->info >> 19) & 1);
+				break;
+			} 
+
+			if ((tmp->link & 1) || (tmp->link & 2))
+				break;
+			tmp = bus_to_virt(tmp->link & ~0xF);
+		} while (1);
+#if 0
+		if (rval) {
+			printk("uhci_td_result returning partial count %d\n", *rval);
+		}
+#endif
+	}
 	return status;		
 }
 
@@ -207,8 +255,8 @@
 
 static void uhci_qh_deallocate(struct uhci_qh *qh)
 {
-	if (qh->element != 1)
-		printk("qh %p leaving dangling entries? (%X)\n", qh, qh->element);
+//	if (qh->element != 1)
+//		printk("qh %p leaving dangling entries? (%X)\n", qh, qh->element);
 
 	qh->element = 1;
 	qh->link = 1;
@@ -275,6 +323,7 @@
 static int uhci_request_irq(struct usb_device *usb_dev, unsigned int pipe, usb_device_irq handler, int period, void *dev_id)
 {
 	struct uhci_device *dev = usb_to_uhci(usb_dev);
+	struct uhci_device *root_hub=usb_to_uhci(dev->uhci->bus->root_hub);
 	struct uhci_td *td = uhci_td_allocate(dev);
 	struct uhci_qh *interrupt_qh = uhci_qh_allocate(dev);
 
@@ -296,14 +345,14 @@
 	td->buffer = virt_to_bus(dev->data);
 	td->first = td;
 	td->qh = interrupt_qh;
-	interrupt_qh->skel = &dev->uhci->root_hub->skel_int8_qh;
+	interrupt_qh->skel = &root_hub->skel_int8_qh;
 
 	uhci_add_irq_list(dev->uhci, td, handler, dev_id);
 
 	uhci_insert_td_in_qh(interrupt_qh, td);
 
 	/* Add it into the skeleton */
-	uhci_insert_qh(&dev->uhci->root_hub->skel_int8_qh, interrupt_qh);
+	uhci_insert_qh(&root_hub->skel_int8_qh, interrupt_qh);
 	return 0;
 }
 
@@ -525,7 +574,7 @@
 	DECLARE_WAITQUEUE(wait, current);
 	struct uhci_qh *ctrl_qh = uhci_qh_allocate(dev);
 	struct uhci_td *curtd;
-
+	struct uhci_device *root_hub=usb_to_uhci(dev->uhci->bus->root_hub);
 	current->state = TASK_UNINTERRUPTIBLE;
 	add_wait_queue(&control_wakeup, &wait);
 
@@ -553,10 +602,19 @@
 	uhci_insert_tds_in_qh(ctrl_qh, first, last);
 
 	/* Add it into the skeleton */
-	uhci_insert_qh(&dev->uhci->root_hub->skel_control_qh, ctrl_qh);
+	uhci_insert_qh(&root_hub->skel_control_qh, ctrl_qh);
+
+//	control should be full here...	
+//	printk("control\n");
+//	show_status(dev->uhci);
+//	show_queues(dev->uhci);
 
 	schedule_timeout(HZ/10);
 
+//	control should be empty here...	
+//	show_status(dev->uhci);
+//	show_queues(dev->uhci);
+
 	remove_wait_queue(&control_wakeup, &wait);
 
 	/* Clean up in case it failed.. */
@@ -567,11 +625,11 @@
 #endif
 
 	/* Remove it from the skeleton */
-	uhci_remove_qh(&dev->uhci->root_hub->skel_control_qh, ctrl_qh);
+	uhci_remove_qh(&root_hub->skel_control_qh, ctrl_qh);
 
 	uhci_qh_deallocate(ctrl_qh);
 
-	return uhci_td_result(dev, last);
+	return uhci_td_result(dev, last, NULL);
 }
 
 /*
@@ -601,8 +659,9 @@
 	struct uhci_td *first, *td, *prevtd;
 	unsigned long destination, status;
 	int ret;
+	int maxsze = usb_maxpacket(usb_dev, pipe);
 
-	if (len > usb_maxpacket(usb_dev->maxpacketsize) * 29)
+	if (len > maxsze * 29)
 		printk("Warning, too much data for a control packet, crashing\n");
 
 	first = td = uhci_td_allocate(dev);
@@ -639,7 +698,6 @@
 	while (len > 0) {
 		/* Build the TD for control status */
 		int pktsze = len;
-		int maxsze = usb_maxpacket(pipe);
 
 		if (pktsze > maxsze)
 			pktsze = maxsze;
@@ -653,12 +711,13 @@
 		td->first = first;
 		td->backptr = &prevtd->link;
 
+		data += pktsze;
+		len -= pktsze;
+
 		prevtd = td;
 		td = uhci_td_allocate(dev);
 		prevtd->link = 4 | virt_to_bus(td);			/* Update previous TD */
 
-		data += maxsze;
-		len -= maxsze;
 	}
 
 	/*
@@ -697,6 +756,12 @@
 		} while (1);
 	}
 
+	if (ret) {
+		__u8 *p = cmd;
+
+		printk("Failed cmd - %02X %02X %02X %02X %02X %02X %02X %02X\n",
+		       p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
+	}
 	return ret;
 }
 
@@ -718,13 +783,12 @@
 }
 
 /* td points to the last td in the list, which interrupts on completion */
-static int uhci_run_bulk(struct uhci_device *dev, struct uhci_td *first, struct uhci_td *last)
+static int uhci_run_bulk(struct uhci_device *dev, struct uhci_td *first, struct uhci_td *last, unsigned long *rval)
 {
 	DECLARE_WAITQUEUE(wait, current);
 	struct uhci_qh *bulk_qh = uhci_qh_allocate(dev);
 	struct uhci_td *curtd;
-
-
+	struct uhci_device *root_hub=usb_to_uhci(dev->uhci->bus->root_hub);
 
 	current->state = TASK_UNINTERRUPTIBLE;
 	add_wait_queue(&bulk_wakeup, &wait);
@@ -752,19 +816,17 @@
 
 	uhci_insert_tds_in_qh(bulk_qh, first, last);
 
-//	bulk0 is empty here...	
-//	show_status(dev->uhci);
-//	show_queues(dev->uhci);
-	
 	/* Add it into the skeleton */
-	/*WARNING! HUB HAS NO BULK QH TIL NOW!!!!!!!!!!!*/
-	uhci_insert_qh(&dev->uhci->root_hub->skel_bulk0_qh, bulk_qh);
+	uhci_insert_qh(&root_hub->skel_bulk0_qh, bulk_qh);
 
 //	now we're in the queue... but don't ask WHAT is in there ;-(
+//	printk("bulk\n");
 //	show_status(dev->uhci);
 //	show_queues(dev->uhci);
 
 	schedule_timeout(HZ/10);
+//	show_status(dev->uhci);
+//	show_queues(dev->uhci);
 
 	remove_wait_queue(&bulk_wakeup, &wait);
 
@@ -776,11 +838,11 @@
 #endif
 
 	/* Remove it from the skeleton */
-	uhci_remove_qh(&dev->uhci->root_hub->skel_bulk0_qh, bulk_qh);
+	uhci_remove_qh(&root_hub->skel_bulk0_qh, bulk_qh);
 
 	uhci_qh_deallocate(bulk_qh);
 
-	return uhci_td_result(dev, last);
+	return uhci_td_result(dev, last, rval);
 }
 
 /*
@@ -799,15 +861,16 @@
  * 31 TD's is a minimum of 248 bytes worth of bulk
  * information.
  */
-static int uhci_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len)
+static int uhci_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, unsigned long *rval)
 {
 	struct uhci_device *dev = usb_to_uhci(usb_dev);
 	struct uhci_td *first, *td, *prevtd;
 	unsigned long destination, status;
 	int ret;
+	int maxsze = usb_maxpacket(usb_dev, pipe);
 
-	if (len > usb_maxpacket(usb_dev->maxpacketsize) * 31)
-		printk("Warning, too much data for a bulk packet, crashing\n");
+	if (len > maxsze * 31)
+		printk("Warning, too much data for a bulk packet, crashing (%d/%d)\n", len, maxsze);
 
 	/* The "pipe" thing contains the destination in bits 8--18, 0x69 is IN */
 	/*
@@ -816,10 +879,10 @@
 	  I FORGOT WHAT IT EXACTLY DOES
 	*/
 	if (usb_pipeout(pipe)) {
-		destination = (pipe & 0x0007ff00) | 0xE1;
+		destination = (pipe & 0x000Aff00) | 0xE1;
 	}
 	else {
-		destination = (pipe & 0x0007ff00) | 0x69;
+		destination = (pipe & 0x000Aff00) | 0x69;
 	}
 
 	/* Status:    slow/fast,       Active,    Short Packet Detect     Three Errors */
@@ -833,29 +896,37 @@
 	while (len > 0) {
 		/* Build the TD for control status */
 		int pktsze = len;
-		int maxsze = usb_maxpacket(pipe);
-
 		if (pktsze > maxsze)
 			pktsze = maxsze;
 
 		td->status = status;					/* Status */
-		td->info = destination | ((pktsze-1) << 21);		/* pktsze bytes of data */
+		td->info = destination | ((pktsze-1) << 21) |
+			 (usb_gettoggle(usb_dev, usb_pipeendpoint(pipe)) << 19); /* pktsze bytes of data */
 		td->buffer = virt_to_bus(data);
 		td->backptr = &prevtd->link;
-
-		prevtd = td;
-		td = uhci_td_allocate(dev);
-		prevtd->link = 4 | virt_to_bus(td);			/* Update previous TD */
+		td->first = first;
 
 		data += maxsze;
 		len -= maxsze;
+
+		if (len > 0) {
+			prevtd = td;
+			td = uhci_td_allocate(dev);
+			prevtd->link = 4 | virt_to_bus(td);			/* Update previous TD */
+		}
+
 		/* Alternate Data0/1 (start with Data0) */
-		destination ^= 1 << 19;
+		usb_dotoggle(usb_dev, usb_pipeendpoint(pipe));
 	}
-	td->link = 1;					/* Terminate */
+	prevtd->link = 1;				/* Terminate */
+	prevtd->status = status | (1 << 24);		/* IOC */
+	prevtd->first = first;
+	uhci_td_deallocate(td);
+
+	/* CHANGE DIRECTION HERE! SAVE IT SOMEWHERE IN THE ENDPOINT!!! */
 
 	/* Start it up.. */
-	ret = uhci_run_bulk(dev, first, td);
+	ret = uhci_run_bulk(dev, first, td, rval);
 
 	{
 		int maxcount = 100;
@@ -1010,7 +1081,7 @@
 	struct usb_device *usb_dev;
 	struct uhci_device *dev;
 	unsigned short status;
-
+	struct uhci_device *root_hub=usb_to_uhci(uhci->bus->root_hub);
 	printk("uhci_connect_change: called for %d\n", nr);
 
 	/*
@@ -1020,7 +1091,7 @@
 	 *
 	 * So start off by getting rid of any old devices..
 	 */
-	usb_disconnect(&uhci->root_hub->usb->children[nr]);
+	usb_disconnect(&root_hub->usb->children[nr]);
 
 	status = inw(port);
 
@@ -1035,14 +1106,14 @@
 	 * Ok, we got a new connection. Allocate a device to it,
 	 * and find out what it wants to do..
 	 */
-	usb_dev = uhci_usb_allocate(uhci->root_hub->usb);
+	usb_dev = uhci_usb_allocate(root_hub->usb);
 	dev = usb_dev->hcpriv;
 
 	dev->uhci = uhci;
 
 	usb_connect(usb_dev);
 
-	uhci->root_hub->usb->children[nr] = usb_dev;
+	root_hub->usb->children[nr] = usb_dev;
 
 	wait_ms(200); /* wait for powerup */
 	uhci_reset_port(port);
@@ -1065,8 +1136,9 @@
  */
 static void uhci_check_configuration(struct uhci *uhci)
 {
+	struct uhci_device * root_hub=usb_to_uhci(uhci->bus->root_hub);
 	unsigned int io_addr = uhci->io_addr + USBPORTSC1;
-	int maxchild = uhci->root_hub->usb->maxchild;
+	int maxchild = root_hub->usb->maxchild;
 	int nr = 0;
 
 	do {
@@ -1130,7 +1202,8 @@
 static void uhci_root_hub_events(struct uhci *uhci, unsigned int io_addr)
 {
 	if (waitqueue_active(&uhci_configure)) {
-		int ports = uhci->root_hub->usb->maxchild;
+		struct uhci_device * root_hub=usb_to_uhci(uhci->bus->root_hub);
+		int ports = root_hub->usb->maxchild;
 		io_addr += USBPORTSC1;
 		do {
 			if (inw(io_addr) & USBPORTSC_CSC) {
@@ -1154,8 +1227,8 @@
 	status = inw(io_addr + USBSTS);
 	outw(status, io_addr + USBSTS);
 
-	if ((status & ~0x21) != 0)
-		printk("interrupt: %X\n", status);
+//	if ((status & ~0x21) != 0)
+//		printk("interrupt: %X\n", status);
 
 	/* Walk the list of pending TD's to see which ones completed.. */
 	uhci_interrupt_notify(uhci);
@@ -1173,7 +1246,7 @@
  */
 static void uhci_init_ticktd(struct uhci *uhci)
 {
-	struct uhci_device *dev = uhci->root_hub;
+	struct uhci_device *dev = usb_to_uhci(uhci->bus->root_hub);
 	struct uhci_td *td = uhci_td_allocate(dev);
 
 	td->link = 1;
@@ -1218,12 +1291,13 @@
 		}
 	}
 
+
 	outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP, io_addr + USBINTR);
 	outw(0, io_addr + USBFRNUM);
 	outl(virt_to_bus(uhci->fl), io_addr + USBFLBASEADD);
 
 	/* Run and mark it configured with a 64-byte max packet */
-	outw(USBCMD_RS | USBCMD_CF, io_addr + USBCMD);
+	outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD);
 }
 
 /*
@@ -1289,10 +1363,9 @@
 	if (!usb)
 		return NULL;
 
-	dev = uhci->root_hub = usb_to_uhci(usb);
-
 	usb->bus = bus;
-
+	dev = usb_to_uhci(usb);
+	uhci->bus->root_hub=uhci_to_usb(dev);
 	/* Initialize the root hub */
 	/* UHCI specs says devices must have 2 ports, but goes on to say */
 	/* they may have more but give no way to determine how many they */
@@ -1373,9 +1446,9 @@
 	}
 
 #if 0
-	if (uhci->root_hub) {
-		uhci_usb_deallocate(uhci_to_usb(uhci->root_hub));
-		uhci->root_hub = NULL;
+	if (uhci->bus->root_hub) {
+		uhci_usb_deallocate(uhci_to_usb(uhci->bus->root_hub));
+		uhci->bus->root_hub = NULL;
 	}
 #endif
 
@@ -1388,12 +1461,10 @@
 	kfree(uhci);
 }
 
-void cleanup_drivers(void);
-
 static int uhci_control_thread(void * __uhci)
 {
 	struct uhci *uhci = (struct uhci *)__uhci;
-
+	struct uhci_device * root_hub =usb_to_uhci(uhci->bus->root_hub);
 	lock_kernel();
 	request_region(uhci->io_addr, 32, "usb-uhci");
 
@@ -1412,6 +1483,7 @@
 	 * Ok, all systems are go..
 	 */
 	start_hc(uhci);
+	usb_register_bus(uhci->bus);
 	for(;;) {
 		siginfo_t info;
 		int unsigned long signr;
@@ -1443,12 +1515,12 @@
 
 	{
 	int i;
-	if(uhci->root_hub)
-		for(i = 0; i < uhci->root_hub->usb->maxchild; i++)
-			usb_disconnect(uhci->root_hub->usb->children + i);
+	if(root_hub)
+		for(i = 0; i < root_hub->usb->maxchild; i++)
+			usb_disconnect(root_hub->usb->children + i);
 	}
 
-	cleanup_drivers();
+	usb_deregister_bus(uhci->bus);
 
 	reset_hc(uhci);
 	release_region(uhci->io_addr, 32);
@@ -1479,10 +1551,10 @@
 	retval = -EBUSY;
 	if (request_irq(irq, uhci_interrupt, SA_SHIRQ, "usb", uhci) == 0) {
 		int pid;
-
 		MOD_INC_USE_COUNT;
 		uhci->irq = irq;
-		pid = kernel_thread(uhci_control_thread, uhci, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+		pid = kernel_thread(uhci_control_thread, uhci,
+			CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
 		if (pid >= 0)
 			return 0;
 
@@ -1547,18 +1619,6 @@
 }
 #endif
 
-#ifdef MODULE
-
-void cleanup_module(void)
-{
-#ifdef CONFIG_APM
-	apm_unregister_callback(&handle_apm_event);
-#endif
-}
-
-#define uhci_init init_module
-
-#endif
 
 int uhci_init(void)
 {
@@ -1587,3 +1647,17 @@
 	}
 	return retval;
 }
+
+#ifdef MODULE
+int init_module(void)
+{
+	return uhci_init();
+}
+
+void cleanup_module(void)
+{
+#ifdef CONFIG_APM
+	apm_unregister_callback(&handle_apm_event);
+#endif
+}
+#endif //MODULE
diff -u --recursive --new-file v2.3.3/linux/drivers/usb/uhci.h linux/drivers/usb/uhci.h
--- v2.3.3/linux/drivers/usb/uhci.h	Fri May 14 18:55:23 1999
+++ linux/drivers/usb/uhci.h	Mon May 31 09:01:50 1999
@@ -225,8 +225,6 @@
 	/* These are "standard" QH's for the entire bus */
 	struct uhci_qh qh[UHCI_MAXQH];
 #endif
-	struct uhci_device *root_hub;		/* Root hub device descriptor.. */
-
 	struct uhci_framelist *fl;		/* Frame list */
 	struct list_head interrupt_list;	/* List of interrupt-active TD's for this uhci */
 };
diff -u --recursive --new-file v2.3.3/linux/drivers/usb/usb-core.c linux/drivers/usb/usb-core.c
--- v2.3.3/linux/drivers/usb/usb-core.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/usb/usb-core.c	Mon May 31 09:01:50 1999
@@ -0,0 +1,93 @@
+/*
+ * driver/usb/usb-core.c
+ *
+ * (C) Copyright David Waite 1999
+ * based on code from usb.c, by Linus Torvolds
+ *
+ * The purpose of this file is to pull any and all generic modular code from
+ * usb.c and put it in a separate file. This way usb.c is kept as a generic
+ * library, while this file handles starting drivers, etc.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include "inits.h"
+#include "usb.h"
+
+#ifndef CONFIG_USB_MODULE
+#	ifdef CONFIG_USB_UHCI
+		int uhci_init(void);
+#	endif
+#	ifdef CONFIG_USB_OHCI
+		int ohci_init(void);
+#	endif
+#	ifdef CONFIG_USB_OHCI_HCD
+		int ohci_hcd_init(void);
+#	endif
+#endif
+
+int usb_init(void)
+{
+#ifndef CONFIG_USB_MODULE
+#	ifdef CONFIG_USB_UHCI
+		uhci_init();
+#	endif
+#	ifdef CONFIG_USB_OHCI
+		ohci_init();
+#	endif
+#	ifdef CONFIG_USB_OHCI_HCD
+		ohci_hcd_init(); 
+#	endif
+#	ifdef CONFIG_USB_MOUSE
+		usb_mouse_init();
+#	endif
+#	ifdef CONFIG_USB_KBD
+		usb_kbd_init();
+#	endif
+#	ifdef CONFIG_USB_AUDIO
+		usb_audio_init();
+#	endif
+#	ifdef CONFIG_USB_ACM
+		usb_acm_init();
+#	endif
+#	ifdef CONFIG_USB_PRINTER
+		usb_print_init();
+#	endif
+#	ifdef CONFIG_USB_CPIA
+		usb_cpia_init();
+#	endif
+#	ifdef CONFIG_USB_HUB
+		usb_hub_init();
+#	endif
+#endif
+	return 0;
+}
+/*
+ *  Clean up when unloading the module
+ */
+void cleanup_drivers(void)
+{
+#ifndef MODULE
+#	ifdef CONFIG_USB_HUB
+		usb_hub_cleanup();
+#	endif
+#	ifdef CONFIG_USB_MOUSE
+        	usb_mouse_cleanup();
+#	endif
+#endif
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+	return usb_init();
+}
+void module_cleanup(void)
+{
+	cleanup_drivers();
+}
+#endif
+
+
diff -u --recursive --new-file v2.3.3/linux/drivers/usb/usb-debug.c linux/drivers/usb/usb-debug.c
--- v2.3.3/linux/drivers/usb/usb-debug.c	Wed Apr 21 02:32:59 1999
+++ linux/drivers/usb/usb-debug.c	Sat May 22 14:45:56 1999
@@ -124,4 +124,8 @@
 	printk("\n");
 }
 
-
+void usb_show_string(struct usb_device* dev, char *id, int index)
+{
+	if (index <= dev->maxstring && dev->stringindex && dev->stringindex[index])
+		printk("%s: %s\n", id, dev->stringindex[index]);
+}
diff -u --recursive --new-file v2.3.3/linux/drivers/usb/usb.c linux/drivers/usb/usb.c
--- v2.3.3/linux/drivers/usb/usb.c	Sat May 15 23:46:04 1999
+++ linux/drivers/usb/usb.c	Mon May 31 09:01:50 1999
@@ -36,110 +36,149 @@
  * 6		wLength		2	Count		Bytes for data
  */
 
+#include <linux/config.h>
 #include <linux/string.h>
 #include <linux/bitops.h>
 #include <linux/malloc.h>
 
 #include "usb.h"
 
-#ifdef CONFIG_USB_UHCI
-int uhci_init(void);
-#endif
-#ifdef CONFIG_USB_OHCI
-int ohci_init(void);
-#endif
-#ifdef CONFIG_USB_OHCI_HCD
-int ohci_hcd_init(void);
-#endif
-
-int usb_init(void)
-{
-#ifdef CONFIG_USB_UHCI
-	uhci_init();
-#endif
-#ifdef CONFIG_USB_OHCI
-	ohci_init();
-#endif
-#ifdef CONFIG_USB_OHCI_HCD
-	ohci_hcd_init(); 
-#endif
-#ifdef CONFIG_USB_MOUSE
-	usb_mouse_init();
-#endif
-#ifdef CONFIG_USB_KBD
-	usb_kbd_init();
-#endif
-#ifdef CONFIG_USB_AUDIO
-	usb_audio_init();
-#endif
-#ifdef CONFIG_USB_ACM
-	usb_acm_init();
-#endif
-#ifdef CONFIG_USB_CPIA
-	usb_cpia_init();
-#endif
-
-	usb_hub_init();
-	return 0;
-}
-
-void cleanup_drivers(void)
-{
-	hub_cleanup();
-#ifdef CONFIG_USB_MOUSE
-	usb_mouse_cleanup();
-#endif
-}
-
 /*
  * We have a per-interface "registered driver" list.
  */
 static LIST_HEAD(usb_driver_list);
+static LIST_HEAD(usb_bus_list);
 
 int usb_register(struct usb_driver *new_driver)
 {
+	struct list_head *tmp = usb_bus_list.next;
 	/* Add it to the list of known drivers */
 	list_add(&new_driver->driver_list, &usb_driver_list);
 
 	/*
-	 * We should go through all existing devices, and see if any of
-	 * them would be acceptable to the new driver.. Let's do that
-	 * in version 2.0.
+         * We go through all existing devices, and see if any of them would
+         * be acceptable to the new driver.. This is done using a depth-first
+         * search for devices without a registered driver already, then 
+	 * running 'probe' with each of the drivers registered on every one 
+	 * of these.
 	 */
+        while (tmp!= &usb_bus_list) {
+		struct usb_bus * bus = list_entry(tmp,struct
+			usb_bus,bus_list);
+	        tmp=tmp->next;  
+		usb_check_support(bus->root_hub);
+        }
 	return 0;
 }
 
 void usb_deregister(struct usb_driver *driver)
 {
+      struct list_head *tmp = usb_bus_list.next;
+      /*first we remove the driver, to be sure it doesn't get used by
+       *another thread while we are stepping through removing entries
+       */
 	list_del(&driver->driver_list);
+      printk("usbcore: deregistering driver\n");
+      while (tmp!= &usb_bus_list) {
+              struct usb_bus * bus = list_entry(tmp,struct 
+            		usb_bus,bus_list);
+              tmp=tmp->next;
+              usb_driver_purge(driver,bus->root_hub);
+      }
+}
+
+/* This function is part of a depth-first search down the device tree,
+ * removing any instances of a device driver.
+ */
+void usb_driver_purge(struct usb_driver *driver,struct usb_device *dev)
+{
+       int i;
+       if (dev==NULL){
+               printk("null device being passed in!!!\n");
+               return;
+       }
+       for (i=0;i<USB_MAXCHILDREN;i++)
+               if (dev->children[i]!=NULL)
+                       usb_driver_purge(driver,dev->children[i]);
+       /*now we check this device*/
+       if(dev->driver==driver) {
+               /*
+                * Note: this is not the correct way to do this, this
+                * uninitializes and reinitializes EVERY driver
+                */
+               printk("disconnecting driverless device\n");
+               dev->driver->disconnect(dev);
+               dev->driver=NULL;
+               /* This will go back through the list looking for a driver
+                * that can handle the device
+		*/
+               usb_device_descriptor(dev);
+      }
+}
+
+/*
+ * New functions for (de)registering a controller
+ */
+void usb_register_bus(struct usb_bus *new_bus)
+{
+      /* Add it to the list of buses */
+      list_add(&new_bus->bus_list, &usb_bus_list);
+      printk("New bus registered");
+}
+
+void usb_deregister_bus(struct usb_bus *bus)
+{
+        /* NOTE: make sure that all the devices are removed by the
+         * controller code, as well as having it call this when cleaning
+	 * itself up
+         */
+	list_del(&bus->bus_list);
 }
 
 /*
+ * This function is for doing a depth-first search for devices which
+ * have support, for dynamic loading of driver modules.
+ */
+void usb_check_support(struct usb_device *dev)
+{
+      int i;
+      if (dev==NULL)
+      {
+              printk("null device being passed in!!!\n");
+              return;
+      }
+      for (i=0;i<USB_MAXCHILDREN;i++)
+              if ((dev->children[i]!=NULL)&&
+                      (dev->children[i]->driver==NULL))
+                      usb_check_support(dev->children[i]);
+      /*now we check this device*/
+      usb_device_descriptor(dev);
+}
+/*
  * This entrypoint gets called for each new device.
  *
  * We now walk the list of registered USB drivers,
  * looking for one that will accept this device as
  * his..
  */
-void usb_device_descriptor(struct usb_device *dev)
+int usb_device_descriptor(struct usb_device *dev)
 {
 	struct list_head *tmp = usb_driver_list.next;
 
 	while (tmp != &usb_driver_list) {
-		struct usb_driver *driver = list_entry(tmp, struct usb_driver, driver_list);
+		struct usb_driver *driver = list_entry(tmp, struct usb_driver,
+						       driver_list);
 		tmp = tmp->next;
 		if (driver->probe(dev))
 			continue;
 		dev->driver = driver;
-		return;
+		return 1;
 	}
-
 	/*
 	 * Ok, no driver accepted the device, so show the info
 	 * for debugging..
 	 */
-	printk("Unknown new USB device:\n");
-	usb_show_device(dev);
+	return 0;
 }
 
 /*
@@ -370,6 +409,7 @@
 	
 	if(dev->config==NULL)
 		return;
+
 	for(c=0;c<dev->descriptor.bNumConfigurations;c++)
 	{
 		cf=&dev->config[c];
@@ -385,6 +425,11 @@
 		kfree(cf->interface);
 	}
 	kfree(dev->config);
+
+	if (dev->stringindex)
+		kfree(dev->stringindex);
+	if (dev->stringtable)
+		kfree(dev->stringtable);
 }
 			
 void usb_init_root_hub(struct usb_device *dev)
@@ -462,6 +507,8 @@
 int usb_get_descriptor(struct usb_device *dev, unsigned char type, unsigned char index, void *buf, int size)
 {
 	devrequest dr;
+	int i = 5;
+	int result;
 
 	dr.requesttype = 0x80;
 	dr.request = USB_REQ_GET_DESCRIPTOR;
@@ -469,7 +516,30 @@
 	dr.index = 0;
 	dr.length = size;
 
-	return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, buf, size);
+	while (i--) {
+		if (!(result = dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, buf, size)))
+			break;
+	}
+	return result;
+}
+
+int usb_get_string(struct usb_device *dev, unsigned short langid, unsigned char index, void *buf, int size)
+{
+	devrequest dr;
+	int i = 5;
+	int result;
+
+	dr.requesttype = 0x80;
+	dr.request = USB_REQ_GET_DESCRIPTOR;
+	dr.value = (USB_DT_STRING << 8) + index;
+	dr.index = langid;
+	dr.length = size;
+
+	while (i--) {
+		if (!(result = dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, buf, size)))
+			break;
+	}
+	return result;
 }
 
 int usb_get_device_descriptor(struct usb_device *dev)
@@ -593,6 +663,24 @@
 	return 0;
 }
 
+static void usb_set_maxpacket(struct usb_device *dev)
+{
+	struct usb_endpoint_descriptor *ep;
+	struct usb_interface_descriptor *ip = dev->actconfig->interface;
+	int i;
+
+	for (i=0; i<dev->actconfig->bNumInterfaces; i++) {
+		if (dev->actconfig->interface[i].bInterfaceNumber == dev->ifnum) {
+			ip = &dev->actconfig->interface[i];
+			break;
+		}
+	}
+	ep = ip->endpoint;
+	for (i=0; i<ip->bNumEndpoints; i++) {
+		dev->epmaxpacket[ep[i].bEndpointAddress & 0x0f] = ep[i].wMaxPacketSize;
+	}
+}
+
 int usb_set_interface(struct usb_device *dev, int interface, int alternate)
 {
 	devrequest dr;
@@ -606,6 +694,8 @@
 	if (dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), &dr, NULL, 0))
 		return -1;
 
+	dev->ifnum = interface;
+	usb_set_maxpacket(dev);
 	return 0;
 }
 
@@ -613,16 +703,31 @@
 int usb_set_configuration(struct usb_device *dev, int configuration)
 {
 	devrequest dr;
-
+	int i;
+	struct usb_config_descriptor *cp = NULL;
+	
 	dr.requesttype = 0;
 	dr.request = USB_REQ_SET_CONFIGURATION;
 	dr.value = configuration;
 	dr.index = 0;
 	dr.length = 0;
 
+	for (i=0; i<dev->descriptor.bNumConfigurations; i++) {
+		if (dev->config[i].bConfigurationValue == configuration) {
+			cp = &dev->config[i];
+			break;
+		}
+	}
+	if (!cp) {
+		printk(KERN_INFO "usb: selecting invalid configuration %d\n", configuration);
+		return -1;
+	}
 	if (dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), &dr, NULL, 0))
 		return -1;
 
+	dev->actconfig = cp;
+	dev->toggle = 0;
+	usb_set_maxpacket(dev);
 	return 0;
 }
 
@@ -671,6 +776,61 @@
 	return usb_parse_configuration(dev, buffer, bufptr - buffer);
 }
 
+int usb_get_stringtable(struct usb_device *dev)
+{
+	int i;
+	int maxindex;
+	int langid;
+	unsigned char buffer[256];
+	int totalchars;
+	struct usb_string_descriptor *sd = (struct usb_string_descriptor *)buffer;
+	char *string;
+	__u8 bLengths[USB_MAXSTRINGS+1];
+	int j;
+
+	dev->maxstring = 0;
+	if(usb_get_string(dev, 0, 0, buffer, 2) ||
+	   usb_get_string(dev, 0, 0, buffer, sd->bLength))
+		return -1;
+	/* we are going to assume that the first ID is good */
+	langid = sd->wData[0];
+
+	/* whip through and find total length and max index */
+	for (maxindex = 1, totalchars = 0; maxindex<=USB_MAXSTRINGS; maxindex++) {
+		if(usb_get_string(dev, langid, maxindex, buffer, 2))
+			break;
+		totalchars += (sd->bLength - 2)/2 + 1;
+		bLengths[maxindex] = sd->bLength;
+	}
+	if (--maxindex <= 0)
+		return -1;
+
+	/* get space for strings and index */
+	dev->stringindex = kmalloc(sizeof(char *)*maxindex, GFP_KERNEL);
+	if (!dev->stringindex)
+		return -1;
+	dev->stringtable = kmalloc(totalchars, GFP_KERNEL);
+	if (!dev->stringtable) {
+		kfree(dev->stringindex);
+		dev->stringindex = NULL;
+		return -1;
+	}
+
+	/* fill them in */
+	memset(dev->stringindex, 0, sizeof(char *)*maxindex);
+	for (i=1, string = dev->stringtable; i <= maxindex; i++) {
+		if (usb_get_string(dev, langid, i, buffer, bLengths[i]))
+			continue;
+		dev->stringindex[i] = string;
+		for (j=0; j < (bLengths[i] - 2)/2; j++) {
+			*string++ =  sd->wData[j];
+		}
+		*string++ = '\0';
+	}
+	dev->maxstring = maxindex;
+	return 0;
+}
+
 /*
  * By the time we get here, the device has gotten a new device ID
  * and is in the default state. We need to identify the thing and
@@ -684,10 +844,12 @@
 		dev->devnum);
 
 	dev->maxpacketsize = 0;		/* Default to 8 byte max packet size */
+        dev->epmaxpacket[0] = 8;
 
 	addr = dev->devnum;
 	dev->devnum = 0;
 
+#if 1
 	/* Slow devices */
 	for (i = 0; i < 5; i++) {
 		if (!usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, 8))
@@ -700,10 +862,12 @@
 		printk("giving up\n");
 		return;
 	}
+#endif
 
 #if 0
 	printk("maxpacketsize: %d\n", dev->descriptor.bMaxPacketSize0);
 #endif
+	dev->epmaxpacket[0] = dev->descriptor.bMaxPacketSize0;
 	switch (dev->descriptor.bMaxPacketSize0) {
 		case 8: dev->maxpacketsize = 0; break;
 		case 16: dev->maxpacketsize = 1; break;
@@ -716,11 +880,15 @@
 
 	dev->devnum = addr;
 
+#if 1
 	if (usb_set_address(dev)) {
 		printk("Unable to set address\n");
 		/* FIXME: We should disable the port */
 		return;
 	}
+#else
+	usb_set_address(dev);
+#endif
 
 	wait_ms(10);	/* Let the SET_ADDRESS settle */
 
@@ -734,12 +902,30 @@
 		return;
 	}
 
+	usb_get_stringtable(dev);
+
+	dev->actconfig = dev->config;
+	dev->ifnum = 0;
+	usb_set_maxpacket(dev);
+
+	usb_show_string(dev, "Manufacturer", dev->descriptor.iManufacturer);
+	usb_show_string(dev, "Product", dev->descriptor.iProduct);
+	usb_show_string(dev, "SerialNumber", dev->descriptor.iSerialNumber);
+
 #if 0
 	printk("Vendor: %X\n", dev->descriptor.idVendor);
 	printk("Product: %X\n", dev->descriptor.idProduct);
 #endif
 
-	usb_device_descriptor(dev);
+	if (usb_device_descriptor(dev)==0)
+	{
+		/*
+		 * Ok, no driver accepted the device, so show the info for
+		 * debugging
+		 */
+		printk ("Unknown new USB device:\n");
+		usb_show_device(dev);
+	}
 }
 
 int usb_request_irq(struct usb_device *dev, unsigned int pipe, usb_device_irq handler, int period, void *dev_id)
diff -u --recursive --new-file v2.3.3/linux/drivers/usb/usb.h linux/drivers/usb/usb.h
--- v2.3.3/linux/drivers/usb/usb.h	Sat May 15 23:46:04 1999
+++ linux/drivers/usb/usb.h	Mon May 31 09:02:48 1999
@@ -10,6 +10,7 @@
 extern int usb_kbd_init(void);
 extern int usb_cpia_init(void);
 extern int usb_mouse_init(void);
+extern int usb_printer_init(void);
 
 extern void hub_cleanup(void);
 extern void usb_mouse_cleanup(void);
@@ -113,6 +114,7 @@
 #define USB_MAXCONFIG		8
 #define USB_MAXINTERFACES	32
 #define USB_MAXENDPOINTS	32
+#define USB_MAXSTRINGS		16
 
 struct usb_device_descriptor {
 	__u8  bLength;
@@ -176,6 +178,7 @@
 struct usb_string_descriptor {
 	__u8  bLength;
 	__u8  bDescriptorType;
+	__u16 wData[1];
 };
 
 /* Hub descriptor */
@@ -217,7 +220,7 @@
 	struct usb_device *(*allocate)(struct usb_device *);
 	int (*deallocate)(struct usb_device *);
 	int (*control_msg)(struct usb_device *, unsigned int, void *, void *, int);
-	int (*bulk_msg)(struct usb_device *, unsigned int, void *, int);
+	int (*bulk_msg)(struct usb_device *, unsigned int, void *, int,unsigned long *);
 	int (*request_irq)(struct usb_device *, unsigned int, usb_device_irq, int, void *);
 };
 
@@ -228,6 +231,7 @@
 	struct usb_devmap devmap;       /* Device map */
 	struct usb_operations *op;      /* Operations (specific to the HC) */
 	struct usb_device *root_hub;    /* Root hub */
+	struct list_head bus_list;
 	void *hcpriv;                   /* Host Controller private data */
 };
 
@@ -238,19 +242,25 @@
 	int devnum;						/* Device number on USB bus */
 	int slow;						/* Slow device? */
 	int maxpacketsize;					/* Maximum packet size */
-
+	__u16 toggle;						/* one bit for each endpoint */
+	struct usb_config_descriptor *actconfig;		/* the active configuration */
+	int epmaxpacket[16];					/* endpoint specific maximums */
+	int ifnum;						/* active interface number */
 	struct usb_bus *bus;					/* Bus we're apart of */
 	struct usb_driver *driver;				/* Driver */
 	struct usb_device_descriptor descriptor;		/* Descriptor */
 	struct usb_config_descriptor *config;			/* All of the configs */
 	struct usb_device *parent;
+	char *stringtable;					/* Strings (multiple, null term) */
+	char **stringindex;					/* pointers to strings */
+	int maxstring;						/* max valid index */
   
 	/*
 	 * Child devices - these can be either new devices
 	 * (if this is a hub device), or different instances
 	 * of this same device.
 	 *
-	 * Each instance needs its own set of data structuctures.
+	 * Each instance needs its own set of data structures.
 	 */
 
 	int maxchild;			/* Number of ports if hub */
@@ -263,13 +273,18 @@
 extern int usb_register(struct usb_driver *);
 extern void usb_deregister(struct usb_driver *);
 
+extern void usb_register_bus(struct usb_bus *);
+extern void usb_deregister_bus(struct usb_bus *);
+
 extern int usb_request_irq(struct usb_device *, unsigned int, usb_device_irq, int, void *);
 
 extern void usb_init_root_hub(struct usb_device *dev);
 extern void usb_connect(struct usb_device *dev);
 extern void usb_disconnect(struct usb_device **);
-extern void usb_device_descriptor(struct usb_device *dev);
 
+extern int usb_device_descriptor(struct usb_device *dev);
+void usb_check_support(struct usb_device *);
+void usb_driver_purge(struct usb_driver *,struct usb_device *);
 extern int  usb_parse_configuration(struct usb_device *dev, void *buf, int len);
 extern void usb_destroy_configuration(struct usb_device *dev);
 
@@ -306,7 +321,7 @@
  * appropriately.
  */
 
-#define usb_maxpacket(pipe)	(8 << ((pipe) & 3))
+#define usb_maxpacket(dev,pipe)	((dev)->epmaxpacket[usb_pipeendpoint(pipe)])
 #define usb_packetid(pipe)	(((pipe) & 0x80) ? 0x69 : 0xE1)
 
 #define usb_pipedevice(pipe)	(((pipe) >> 8) & 0x7f)
@@ -323,6 +338,11 @@
 
 #define usb_pipe_endpdev(pipe)	(((pipe) >> 8) & 0x7ff)
 
+/* The D0/D1 toggle bits */
+#define usb_gettoggle(dev, ep) (((dev)->toggle >> ep) & 1)
+#define	usb_dotoggle(dev, ep)	((dev)->toggle ^= (1 <<	ep))
+#define usb_settoggle(dev, ep, bit) ((dev)->toggle = ((dev)->toggle & (0xfffe << ep)) | (bit << ep))
+
 static inline unsigned int __create_pipe(struct usb_device *dev, unsigned int endpoint)
 {
 	return (dev->devnum << 8) | (endpoint << 15) | (dev->slow << 26) | dev->maxpacketsize;
@@ -338,11 +358,11 @@
 #define usb_rcvctrlpipe(dev,endpoint)	((2 << 30) | __create_pipe(dev,endpoint) | 0x80)
 #define usb_sndisocpipe(dev,endpoint)	((0 << 30) | __create_pipe(dev,endpoint))
 #define usb_rcvisocpipe(dev,endpoint)	((0 << 30) | __create_pipe(dev,endpoint) | 0x80)
+#define usb_sndbulkpipe(dev,endpoint)	((3 << 30) | __create_pipe(dev,endpoint))
+#define usb_rcvbulkpipe(dev,endpoint)	((3 << 30) | __create_pipe(dev,endpoint) | 0x80)
 #define usb_snddefctrl(dev)		((2 << 30) | __default_pipe(dev))
 #define usb_rcvdefctrl(dev)		((2 << 30) | __default_pipe(dev) | 0x80)
 
-/* Create .. */
-
 /*
  * Send and receive control messages..
  */
@@ -371,6 +391,7 @@
 void usb_show_endpoint_descriptor(struct usb_endpoint_descriptor *);
 void usb_show_hub_descriptor(struct usb_hub_descriptor *);
 void usb_show_device(struct usb_device *);
+void usb_show_string(struct usb_device* dev, char *id, int index);
 
 /*
  * Audio parsing helpers
diff -u --recursive --new-file v2.3.3/linux/drivers/video/cgsixfb.c linux/drivers/video/cgsixfb.c
--- v2.3.3/linux/drivers/video/cgsixfb.c	Mon Mar 15 16:11:31 1999
+++ linux/drivers/video/cgsixfb.c	Thu May 27 09:55:21 1999
@@ -1,4 +1,4 @@
-/* $Id: cgsixfb.c,v 1.16 1999/03/09 14:01:49 davem Exp $
+/* $Id: cgsixfb.c,v 1.17 1999/05/25 01:00:31 davem Exp $
  * cgsixfb.c: CGsix (GX,GXplus) frame buffer driver
  *
  * Copyright (C) 1996,1998 Jakub Jelinek (jj@ultra.linux.cz)
@@ -588,7 +588,7 @@
 	p->screen_base += (y_margin - fb->y_margin) * p->line_length + (x_margin - fb->x_margin);
 }
 
-static char idstring[60] __initdata = { 0 };
+static char idstring[70] __initdata = { 0 };
 
 __initfunc(char *cgsixfb_init(struct fb_info_sbusfb *fb))
 {
@@ -599,6 +599,7 @@
 	unsigned long phys = fb->sbdp->reg_addrs[0].phys_addr;
 	u32 conf;
 	char *p;
+	char *cardtype;
 	struct bt_regs *bt;
 
 	strcpy(fb->info.modename, "CGsix");
@@ -656,15 +657,29 @@
 	case CG6_FHC_CPU_68020: p = "68020"; break;
 	default: p = "i386"; break;
 	}
+
+	if (((conf >> CG6_FHC_REV_SHIFT) & CG6_FHC_REV_MASK) >= 11) {
+		if (fix->smem_len <= 0x100000) {
+			cardtype = "TurboGX";
+		} else {
+			cardtype = "TurboGX+";
+		}
+	} else {
+		if (fix->smem_len <= 0x100000) {
+			cardtype = "GX";
+		} else {
+			cardtype = "GX+";
+		}
+	}
 	                                                                        
 	sprintf(idstring, 
 #ifdef __sparc_v9__
-		    "cgsix at %016lx TEC Rev %x CPU %s Rev %x", phys, 
+		    "cgsix at %016lx TEC Rev %x CPU %s Rev %x [%s]", phys, 
 #else	
-		    "cgsix at %x.%08lx TEC Rev %x CPU %s Rev %x", fb->iospace, phys, 
+		    "cgsix at %x.%08lx TEC Rev %x CPU %s Rev %x [%s]", fb->iospace, phys, 
 #endif
 		    (fb->s.cg6.thc->thc_misc >> CG6_THC_MISC_REV_SHIFT) & CG6_THC_MISC_REV_MASK,
-		    p, conf >> CG6_FHC_REV_SHIFT & CG6_FHC_REV_MASK);
+		    p, conf >> CG6_FHC_REV_SHIFT & CG6_FHC_REV_MASK, cardtype);
 		    
 	cg6_reset(fb);
 		    
diff -u --recursive --new-file v2.3.3/linux/drivers/video/vga16fb.c linux/drivers/video/vga16fb.c
--- v2.3.3/linux/drivers/video/vga16fb.c	Sat May 15 23:46:04 1999
+++ linux/drivers/video/vga16fb.c	Wed May 26 16:55:40 1999
@@ -22,7 +22,6 @@
 #include <linux/selection.h>
 #include <linux/ioport.h>
 #include <linux/init.h>
-#include <linux/config.h>
 
 #include <asm/io.h>
 
diff -u --recursive --new-file v2.3.3/linux/fs/Config.in linux/fs/Config.in
--- v2.3.3/linux/fs/Config.in	Mon May 17 09:55:22 1999
+++ linux/fs/Config.in	Wed May 26 09:37:03 1999
@@ -111,6 +111,7 @@
 bool 'Macintosh partition map support' CONFIG_MAC_PARTITION
 bool 'SMD disklabel (Sun partition tables) support' CONFIG_SMD_DISKLABEL
 bool 'Solaris (x86) partition table support' CONFIG_SOLARIS_X86_PARTITION
+bool 'SGI disklabel support' CONFIG_SGI_DISKLABEL
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
   bool 'Unixware slices support (EXPERIMENTAL)' CONFIG_UNIXWARE_DISKLABEL
 fi
diff -u --recursive --new-file v2.3.3/linux/fs/binfmt_aout.c linux/fs/binfmt_aout.c
--- v2.3.3/linux/fs/binfmt_aout.c	Mon May 10 13:01:21 1999
+++ linux/fs/binfmt_aout.c	Mon May 24 08:48:34 1999
@@ -413,7 +413,14 @@
 			return fd;
 		file = fcheck(fd);
 
-		if (!file->f_op || !file->f_op->mmap) {
+		if ((fd_offset & ~PAGE_MASK) != 0) {
+			printk(KERN_WARNING 
+			       "fd_offset is not page aligned. Please convert program: %s\n",
+			       file->f_dentry->d_name.name
+			       );
+		}
+
+		if (!file->f_op || !file->f_op->mmap || ((fd_offset & ~PAGE_MASK) != 0)) {
 			sys_close(fd);
 			do_mmap(NULL, 0, ex.a_text+ex.a_data,
 				PROT_READ|PROT_WRITE|PROT_EXEC,
@@ -534,6 +541,24 @@
 
 	start_addr =  ex.a_entry & 0xfffff000;
 
+	if ((N_TXTOFF(ex) & ~PAGE_MASK) != 0) {
+		printk(KERN_WARNING 
+		       "N_TXTOFF is not page aligned. Please convert library: %s\n",
+		       file->f_dentry->d_name.name
+		       );
+		
+		do_mmap(NULL, start_addr & PAGE_MASK, ex.a_text + ex.a_data + ex.a_bss,
+			PROT_READ | PROT_WRITE | PROT_EXEC,
+			MAP_FIXED| MAP_PRIVATE, 0);
+		
+		read_exec(file->f_dentry, N_TXTOFF(ex),
+			  (char *)start_addr, ex.a_text + ex.a_data, 0);
+		flush_icache_range((unsigned long) start_addr,
+				   (unsigned long) start_addr + ex.a_text + ex.a_data);
+
+		retval = 0;
+		goto out_putf;
+	}
 	/* Now use mmap to map the library into memory. */
 	error = do_mmap(file, start_addr, ex.a_text + ex.a_data,
 			PROT_READ | PROT_WRITE | PROT_EXEC,
diff -u --recursive --new-file v2.3.3/linux/fs/binfmt_elf.c linux/fs/binfmt_elf.c
--- v2.3.3/linux/fs/binfmt_elf.c	Mon May 10 13:01:21 1999
+++ linux/fs/binfmt_elf.c	Mon May 24 22:47:43 1999
@@ -739,12 +739,12 @@
 	padzero(elf_bss);
 
 #if 0
-	printk("(start_brk) %x\n" , current->mm->start_brk);
-	printk("(end_code) %x\n" , current->mm->end_code);
-	printk("(start_code) %x\n" , current->mm->start_code);
-	printk("(end_data) %x\n" , current->mm->end_data);
-	printk("(start_stack) %x\n" , current->mm->start_stack);
-	printk("(brk) %x\n" , current->mm->brk);
+	printk("(start_brk) %lx\n" , (long) current->mm->start_brk);
+	printk("(end_code) %lx\n" , (long) current->mm->end_code);
+	printk("(start_code) %lx\n" , (long) current->mm->start_code);
+	printk("(end_data) %lx\n" , (long) current->mm->end_data);
+	printk("(start_stack) %lx\n" , (long) current->mm->start_stack);
+	printk("(brk) %lx\n" , (long) current->mm->brk);
 #endif
 
 	if ( current->personality == PER_SVR4 )
diff -u --recursive --new-file v2.3.3/linux/fs/block_dev.c linux/fs/block_dev.c
--- v2.3.3/linux/fs/block_dev.c	Fri Nov 13 10:07:26 1998
+++ linux/fs/block_dev.c	Fri May 28 09:20:31 1999
@@ -273,6 +273,8 @@
 			if (++bhe == &buflist[NBUF])
 				bhe = buflist;
 		} while (left > 0 && bhe != bhb && (!*bhe || !buffer_locked(*bhe)));
+		if (bhe == bhb && !blocks)
+			break;
 	} while (left > 0);
 
 /* Release the read-ahead blocks */
diff -u --recursive --new-file v2.3.3/linux/fs/efs/.cvsignore linux/fs/efs/.cvsignore
--- v2.3.3/linux/fs/efs/.cvsignore	Sat May 15 23:46:04 1999
+++ linux/fs/efs/.cvsignore	Wed Dec 31 16:00:00 1969
@@ -1,2 +0,0 @@
-.depend
-.*.flags
diff -u --recursive --new-file v2.3.3/linux/fs/efs/super.c linux/fs/efs/super.c
--- v2.3.3/linux/fs/efs/super.c	Sat May 15 23:46:04 1999
+++ linux/fs/efs/super.c	Wed May 26 09:35:00 1999
@@ -119,11 +119,13 @@
 
 	if (slice == -1) {
 		printk(KERN_NOTICE "EFS: partition table contained no EFS partitions\n");
+#ifdef DEBUG
 	} else {
 		printk(KERN_INFO "EFS: using slice %d (type %s, offset 0x%x)\n",
 			slice,
 			(pt_entry->pt_name) ? pt_entry->pt_name : "unknown",
 			sblock);
+#endif
 	}
 	return(sblock);
 }
@@ -178,12 +180,14 @@
 
 	bh = bread(dev, sb->fs_start + EFS_SUPER, EFS_BLOCKSIZE);
 	if (!bh) {
-		printk(KERN_ERR "EFS: unable to read superblock\n");
+		printk(KERN_ERR "EFS: cannot read superblock\n");
 		goto out_no_fs_ul;
 	}
 		
 	if (efs_validate_super(sb, (struct efs_super *) bh->b_data)) {
+#ifdef DEBUG
 		printk(KERN_WARNING "EFS: invalid superblock at block %u\n", sb->fs_start + EFS_SUPER);
+#endif
 		brelse(bh);
 		goto out_no_fs_ul;
 	}
diff -u --recursive --new-file v2.3.3/linux/fs/exec.c linux/fs/exec.c
--- v2.3.3/linux/fs/exec.c	Thu Apr 29 22:10:12 1999
+++ linux/fs/exec.c	Wed May 26 11:15:36 1999
@@ -415,12 +415,11 @@
 	 * Failure ... restore the prior mm_struct.
 	 */
 fail_restore:
-	/* The pgd belongs to the parent ... don't free it! */
-	mm->pgd = NULL;
 	current->mm = old_mm;
 	/* restore the ldt for this task */
 	copy_segments(nr, current, NULL);
-	mmput(mm);
+	release_segments(mm);
+	kmem_cache_free(mm_cachep, mm);
 
 fail_nomem:
 	return retval;
diff -u --recursive --new-file v2.3.3/linux/fs/isofs/inode.c linux/fs/isofs/inode.c
--- v2.3.3/linux/fs/isofs/inode.c	Fri May 14 18:55:24 1999
+++ linux/fs/isofs/inode.c	Wed May 26 10:01:43 1999
@@ -126,6 +126,9 @@
 	uid_t uid;
 	char *iocharset;
 	unsigned char utf8;
+        /* LVE */
+        s32 session;
+        s32 sbsector;
 };
 
 /*
@@ -294,6 +297,8 @@
 	popt->uid = 0;
 	popt->iocharset = NULL;
 	popt->utf8 = 0;
+	popt->session=-1;
+	popt->sbsector=-1;
 	if (!options) return 1;
 	for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) {
 	        if (strncmp(this_char,"norock",6) == 0) {
@@ -337,6 +342,18 @@
 			else if (!strcmp(value,"acorn")) popt->map = 'a';
 			else return 0;
 		}
+		if (!strcmp(this_char,"session") && value) {
+			char * vpnt = value;
+			unsigned int ivalue = simple_strtoul(vpnt, &vpnt, 0);
+			if(ivalue < 0 || ivalue >99) return 0;
+			popt->session=ivalue+1;
+		}
+		if (!strcmp(this_char,"sbsector") && value) {
+			char * vpnt = value;
+			unsigned int ivalue = simple_strtoul(vpnt, &vpnt, 0);
+			if(ivalue < 0 || ivalue >660*512) return 0;
+			popt->sbsector=ivalue;
+		}
 		else if (!strcmp(this_char,"check") && value) {
 			if (value[0] && !value[1] && strchr("rs",*value))
 				popt->check = *value;
@@ -404,7 +421,7 @@
  */
 #define WE_OBEY_THE_WRITTEN_STANDARDS 1
 
-static unsigned int isofs_get_last_session(kdev_t dev)
+static unsigned int isofs_get_last_session(kdev_t dev,s32 session )
 {
   struct cdrom_multisession ms_info;
   unsigned int vol_desc_start;
@@ -426,11 +443,26 @@
       init_waitqueue_head(&inode_fake.i_wait);
       ms_info.addr_format=CDROM_LBA;
       set_fs(KERNEL_DS);
+      if(session >= 0 && session <= 99) {
+	      struct cdrom_tocentry Te;
+	      Te.cdte_track=session;
+	      Te.cdte_format=CDROM_LBA;
+	      i=get_blkfops(MAJOR(dev))->ioctl(&inode_fake,
+				       NULL,
+				       CDROMREADTOCENTRY,
+				       (unsigned long) &Te);
+	      set_fs(old_fs);
+	      if(!i) printk(KERN_ERR"Session %d start %d type %d\n",session,Te.cdte_addr.lba,Te.cdte_ctrl&CDROM_DATA_TRACK);
+	      if(i || (Te.cdte_ctrl&CDROM_DATA_TRACK) != 4)
+			printk(KERN_ERR"Invalid session number or type of track\n");
+		else return Te.cdte_addr.lba;
+      }
       i=get_blkfops(MAJOR(dev))->ioctl(&inode_fake,
 				       NULL,
 				       CDROMMULTISESSION,
 				       (unsigned long) &ms_info);
       set_fs(old_fs);
+      if(session > 0) printk(KERN_ERR"Invalid session number\n");
 #if 0
       printk("isofs.inode: CDROMMULTISESSION: rc=%d\n",i);
       if (i==0)
@@ -524,7 +556,8 @@
 
 	s->u.isofs_sb.s_high_sierra = high_sierra = 0; /* default is iso9660 */
 
-	vol_desc_start = isofs_get_last_session(dev);
+	vol_desc_start = (opt.sbsector != -1) ?
+		opt.sbsector : isofs_get_last_session(dev,opt.session);
 
   	for (iso_blknum = vol_desc_start+16;
              iso_blknum < vol_desc_start+100; iso_blknum++)
@@ -1117,7 +1150,7 @@
 	   .. but a DVD may be up to 1Gig (Ulrich Habel) */
 	if((inode->i_size < 0 || inode->i_size > 1073741824) &&
 	    inode->i_sb->u.isofs_sb.s_cruft == 'n') {
-	  printk("Warning: defective cdrom.  Enabling \"cruft\" mount option.\n");
+	  printk(KERN_WARNING "Warning: defective CD-ROM.  Enabling \"cruft\" mount option.\n");
 	  inode->i_sb->u.isofs_sb.s_cruft = 'y';
 	}
 
@@ -1192,7 +1225,7 @@
 	 */
 	if (inode->i_sb->u.isofs_sb.s_cruft == 'n' &&
 	    (volume_seq_no != 0) && (volume_seq_no != 1)) {
-	  printk("Warning: defective cdrom (volume sequence number). Enabling \"cruft\" mount option.\n");
+	  printk(KERN_WARNING "Warning: defective CD-ROM (volume sequence number). Enabling \"cruft\" mount option.\n");
 	  inode->i_sb->u.isofs_sb.s_cruft = 'y';
 	}
 
@@ -1201,7 +1234,7 @@
 #ifndef IGNORE_WRONG_MULTI_VOLUME_SPECS
 	if (inode->i_sb->u.isofs_sb.s_cruft != 'y' &&
 	    (volume_seq_no != 0) && (volume_seq_no != 1)) {
-		printk("Multi volume CD somehow got mounted.\n");
+		printk(KERN_WARNING "Multi-volume CD somehow got mounted.\n");
 	} else
 #endif IGNORE_WRONG_MULTI_VOLUME_SPECS
 	{
diff -u --recursive --new-file v2.3.3/linux/fs/ncpfs/ioctl.c linux/fs/ncpfs/ioctl.c
--- v2.3.3/linux/fs/ncpfs/ioctl.c	Sat May 15 23:46:04 1999
+++ linux/fs/ncpfs/ioctl.c	Wed May 26 09:27:16 1999
@@ -89,7 +89,7 @@
 
 		result = ncp_request2(server, request.function, 
 			bouncebuffer, NCP_PACKET_SIZE);
-		if (result)
+		if (result < 0)
 			result = -EIO;
 		else
 			result = server->reply_size;
diff -u --recursive --new-file v2.3.3/linux/fs/open.c linux/fs/open.c
--- v2.3.3/linux/fs/open.c	Fri Apr 16 14:21:39 1999
+++ linux/fs/open.c	Mon May 24 22:47:43 1999
@@ -171,7 +171,7 @@
 	return error;
 }
 
-#ifndef __alpha__
+#if !(defined(__alpha__) || defined(__ia64__))
 
 /*
  * sys_utime() can be implemented in user-level using sys_utimes().
diff -u --recursive --new-file v2.3.3/linux/fs/select.c linux/fs/select.c
--- v2.3.3/linux/fs/select.c	Fri May 14 18:55:26 1999
+++ linux/fs/select.c	Sun May 23 23:53:45 1999
@@ -267,8 +267,12 @@
 	}
 
 	ret = -EINVAL;
-	if (n < 0 || n > KFDS_NR)
+	if (n < 0)
 		goto out_nofds;
+
+	if (n > KFDS_NR)
+		n = KFDS_NR;
+
 	/*
 	 * We need 6 bitmaps (in/out/ex for both incoming and outgoing),
 	 * since we used fdset we need to allocate memory in units of
diff -u --recursive --new-file v2.3.3/linux/fs/stat.c linux/fs/stat.c
--- v2.3.3/linux/fs/stat.c	Fri Nov 13 10:07:26 1998
+++ linux/fs/stat.c	Mon May 24 22:47:43 1999
@@ -24,7 +24,7 @@
 }
 
 
-#if !defined(__alpha__) && !defined(__sparc__)
+#if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__)
 
 /*
  * For backward compatibility?  Maybe this should be moved
@@ -114,7 +114,7 @@
 }
 
 
-#if !defined(__alpha__) && !defined(__sparc__)
+#if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__)
 /*
  * For backward compatibility?  Maybe this should be moved
  * into arch/i386 instead?
@@ -160,7 +160,7 @@
 	return error;
 }
 
-#if !defined(__alpha__) && !defined(__sparc__)
+#if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__)
 
 /*
  * For backward compatibility?  Maybe this should be moved
@@ -208,7 +208,7 @@
 	return error;
 }
 
-#if !defined(__alpha__) && !defined(__sparc__)
+#if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__)
 
 /*
  * For backward compatibility?  Maybe this should be moved
diff -u --recursive --new-file v2.3.3/linux/include/asm-alpha/processor.h linux/include/asm-alpha/processor.h
--- v2.3.3/linux/include/asm-alpha/processor.h	Mon May 17 09:55:23 1999
+++ linux/include/asm-alpha/processor.h	Tue May 25 14:55:05 1999
@@ -8,6 +8,12 @@
 #define __ASM_ALPHA_PROCESSOR_H
 
 /*
+ * Default implementation of macro that returns current
+ * instruction pointer ("program counter").
+ */
+#define current_text_addr() ({ __label__ _l; _l: &&_l;})
+
+/*
  * We have a 42-bit user address space: 4TB user VM...
  */
 #define TASK_SIZE (0x40000000000UL)
diff -u --recursive --new-file v2.3.3/linux/include/asm-alpha/semaphore.h linux/include/asm-alpha/semaphore.h
--- v2.3.3/linux/include/asm-alpha/semaphore.h	Mon May 17 09:55:23 1999
+++ linux/include/asm-alpha/semaphore.h	Sat May 22 13:46:08 1999
@@ -17,14 +17,57 @@
 	atomic_t count;
 	atomic_t waking;		/* biased by -1 */
 	wait_queue_head_t wait;
+#if WAITQUEUE_DEBUG
+	long __magic;
+#endif
 };
 
-#define MUTEX ((struct semaphore) \
- { ATOMIC_INIT(1), ATOMIC_INIT(-1), NULL })
-#define MUTEX_LOCKED ((struct semaphore) \
- { ATOMIC_INIT(0), ATOMIC_INIT(-1), NULL })
+#if WAITQUEUE_DEBUG
+# define __SEM_DEBUG_INIT(name)		, (long)&(name).__magic
+#else
+# define __SEM_DEBUG_INIT(name)
+#endif
+
+#define __SEMAPHORE_INITIALIZER(name,count)		\
+	{ ATOMIC_INIT(count), ATOMIC_INIT(-1),		\
+	  __WAIT_QUEUE_HEAD_INITIALIZER((name).wait)	\
+	  __SEM_DEBUG_INIT(name) }
+
+#define __MUTEX_INITIALIZER(name) \
+	__SEMAPHORE_INITIALIZER(name,1)
+
+#define __DECLARE_SEMAPHORE_GENERIC(name,count) \
+	struct semaphore name = __SEMAPHORE_INITIALIZER(name,count)
+
+#define DECLARE_MUTEX(name) __DECLARE_SEMAPHORE_GENERIC(name,1)
+#define DECLARE_MUTEX_LOCKED(name) __DECLARE_SEMAPHORE_GENERIC(name,0)
+
+extern inline void sema_init (struct semaphore *sem, int val)
+{
+	/*
+	 * Logically, 
+	 *   *sem = (struct semaphore)__SEMAPHORE_INITIALIZER((*sem),val);
+	 * except that gcc produces better initializing by parts yet.
+	 */
+
+	atomic_set(&sem->count, val);
+	atomic_set(&sem->waking, -1);
+	init_waitqueue_head(&sem->wait);
+#if WAITQUEUE_DEBUG
+	sem->__magic = (long)&sem->__magic;
+#endif
+}
+
+static inline void init_MUTEX (struct semaphore *sem)
+{
+	sema_init(sem, 1);
+}
+
+static inline void init_MUTEX_LOCKED (struct semaphore *sem)
+{
+	sema_init(sem, 0);
+}
 
-#define sema_init(sem, val)	atomic_set(&((sem)->count), val)
 
 extern void __down(struct semaphore * sem);
 extern int  __down_interruptible(struct semaphore * sem);
@@ -57,8 +100,13 @@
 	   a function that ordinarily wouldn't.  Otherwise we could
 	   have it done by the macro directly, which can be optimized
 	   the linker.  */
-	register void *pv __asm__("$27") = __down_failed;
+	register void *pv __asm__("$27");
+
+#if WAITQUEUE_DEBUG
+	CHECK_MAGIC(sem->__magic);
+#endif
 	
+	pv = __down_failed;
 	__asm__ __volatile__ (
 		"/* semaphore down operation */\n"
 		"1:	ldl_l	$24,%1\n"
@@ -88,8 +136,13 @@
 	   value is in $24.  */
 
 	register int ret __asm__("$24");
-	register void *pv __asm__("$27") = __down_failed_interruptible;
+	register void *pv __asm__("$27");
 
+#if WAITQUEUE_DEBUG
+	CHECK_MAGIC(sem->__magic);
+#endif
+	
+	pv = __down_failed_interruptible;
 	__asm__ __volatile__ (
 		"/* semaphore down interruptible operation */\n"
 		"1:	ldl_l	$24,%2\n"
@@ -144,6 +197,10 @@
 	   } while (tmp == 0);
 	*/
 
+#if WAITQUEUE_DEBUG
+	CHECK_MAGIC(sem->__magic);
+#endif
+	
 	__asm__ __volatile__(
 		"1:	ldq_l	%1,%4\n"
 		"	lda	%3,1\n"
@@ -179,8 +236,13 @@
 	   it's return address in $28.  The pv is loaded as usual.
 	   The gp is clobbered (in the module case) as usual.  */
 
-	register void *pv __asm__("$27") = __up_wakeup;
+	register void *pv __asm__("$27");
 
+#if WAITQUEUE_DEBUG
+	CHECK_MAGIC(sem->__magic);
+#endif
+	
+	pv = __up_wakeup;
 	__asm__ __volatile__ (
 		"/* semaphore up operation */\n"
 		"	mb\n"
diff -u --recursive --new-file v2.3.3/linux/include/asm-arm/processor.h linux/include/asm-arm/processor.h
--- v2.3.3/linux/include/asm-arm/processor.h	Sat May  8 11:06:58 1999
+++ linux/include/asm-arm/processor.h	Tue May 25 14:55:05 1999
@@ -7,6 +7,12 @@
 #ifndef __ASM_ARM_PROCESSOR_H
 #define __ASM_ARM_PROCESSOR_H
 
+/*
+ * Default implementation of macro that returns current
+ * instruction pointer ("program counter").
+ */
+#define current_text_addr() ({ __label__ _l; _l: &&_l;})
+
 #define FP_SIZE 35
 
 struct fp_hard_struct {
diff -u --recursive --new-file v2.3.3/linux/include/asm-i386/processor.h linux/include/asm-i386/processor.h
--- v2.3.3/linux/include/asm-i386/processor.h	Tue May 11 13:03:58 1999
+++ linux/include/asm-i386/processor.h	Tue May 25 14:56:06 1999
@@ -11,6 +11,13 @@
 #include <asm/math_emu.h>
 #include <asm/segment.h>
 #include <asm/page.h>
+#include <asm/types.h>
+
+/*
+ * Default implementation of macro that returns current
+ * instruction pointer ("program counter").
+ */
+#define current_text_addr() ({ void *pc; __asm__("movl $1f,%0\n1:":"=g" (pc)); pc; })
 
 /*
  *  CPU type and hardware bug flags. Kept separately for each CPU.
@@ -279,6 +286,7 @@
 } while (0)
 
 /* Forward declaration, a strange C thing */
+struct task_struct;
 struct mm_struct;
 
 /* Free all resources held by a thread. */
diff -u --recursive --new-file v2.3.3/linux/include/asm-i386/softirq.h linux/include/asm-i386/softirq.h
--- v2.3.3/linux/include/asm-i386/softirq.h	Tue May 11 13:04:00 1999
+++ linux/include/asm-i386/softirq.h	Fri May 28 09:19:24 1999
@@ -40,6 +40,13 @@
 
 extern void synchronize_bh(void);
 
+/*
+ * This is suboptimal. We only need to disable bh's locally
+ * on this CPU...
+ */
+#define local_bh_disable()	atomic_inc(&global_bh_lock)
+#define local_bh_enable()	atomic_dec(&global_bh_lock)
+
 static inline void start_bh_atomic(void)
 {
 	atomic_inc(&global_bh_lock);
@@ -83,6 +90,9 @@
 	barrier();
 	local_bh_count[smp_processor_id()]--;
 }
+
+#define local_bh_disable()	(local_bh_count[smp_processor_id()]++)
+#define local_bh_enable()	(local_bh_count[smp_processor_id()]--)
 
 /* These are for the irq's testing the lock */
 #define softirq_trylock(cpu)	(local_bh_count[cpu] ? 0 : (local_bh_count[cpu]=1))
diff -u --recursive --new-file v2.3.3/linux/include/asm-i386/spinlock.h linux/include/asm-i386/spinlock.h
--- v2.3.3/linux/include/asm-i386/spinlock.h	Tue May 11 13:03:58 1999
+++ linux/include/asm-i386/spinlock.h	Tue May 25 14:56:58 1999
@@ -1,6 +1,35 @@
 #ifndef __ASM_SPINLOCK_H
 #define __ASM_SPINLOCK_H
 
+/*
+ * These are the generic versions of the spinlocks
+ * and read-write locks.. We should actually do a
+ * <linux/spinlock.h> with all of this. Oh, well.
+ */
+#define spin_lock_irqsave(lock, flags)		do { local_irq_save(flags);       spin_lock(lock); } while (0)
+#define spin_lock_irq(lock)			do { local_irq_disable();         spin_lock(lock); } while (0)
+#define spin_lock_bh(lock)			do { local_bh_disable();          spin_lock(lock); } while (0)
+
+#define read_lock_irqsave(lock, flags)		do { local_irq_save(flags);       read_lock(lock); } while (0)
+#define read_lock_irq(lock)			do { local_irq_disable();         read_lock(lock); } while (0)
+#define read_lock_bh(lock)			do { local_bh_disable();          read_lock(lock); } while (0)
+
+#define write_lock_irqsave(lock, flags)		do { local_irq_save(flags);      write_lock(lock); } while (0)
+#define write_lock_irq(lock)			do { local_irq_disable();        write_lock(lock); } while (0)
+#define write_lock_bh(lock)			do { local_bh_disable();         write_lock(lock); } while (0)
+
+#define spin_unlock_irqrestore(lock, flags)	do { spin_unlock(lock);  local_irq_restore(flags); } while (0)
+#define spin_unlock_irq(lock)			do { spin_unlock(lock);  local_irq_enable();       } while (0)
+#define spin_unlock_bh(lock)			do { spin_unlock(lock);  local_bh_enable();        } while (0)
+
+#define read_unlock_irqrestore(lock, flags)	do { read_unlock(lock);  local_irq_restore(flags); } while (0)
+#define read_unlock_irq(lock)			do { read_unlock(lock);  local_irq_enable();       } while (0)
+#define read_unlock_bh(lock)			do { read_unlock(lock);  local_bh_enable();        } while (0)
+
+#define write_unlock_irqrestore(lock, flags)	do { write_unlock(lock); local_irq_restore(flags); } while (0)
+#define write_unlock_irq(lock)			do { write_unlock(lock); local_irq_enable();       } while (0)
+#define write_unlock_bh(lock)			do { write_unlock(lock); local_bh_enable();        } while (0)
+
 #ifndef __SMP__
 
 #define DEBUG_SPINLOCKS	0	/* 0 == no debugging, 1 == maintain lock state, 2 == full debug */
@@ -25,13 +54,6 @@
 #define spin_trylock(lock)	(1)
 #define spin_unlock_wait(lock)	do { } while(0)
 #define spin_unlock(lock)	do { } while(0)
-#define spin_lock_irq(lock)	cli()
-#define spin_unlock_irq(lock)	sti()
-
-#define spin_lock_irqsave(lock, flags) \
-	do { save_flags(flags); cli(); } while (0)
-#define spin_unlock_irqrestore(lock, flags) \
-	restore_flags(flags)
 
 #elif (DEBUG_SPINLOCKS < 2)
 
@@ -46,13 +68,6 @@
 #define spin_lock(x)		do { (x)->lock = 1; } while (0)
 #define spin_unlock_wait(x)	do { } while (0)
 #define spin_unlock(x)		do { (x)->lock = 0; } while (0)
-#define spin_lock_irq(x)	do { cli(); spin_lock(x); } while (0)
-#define spin_unlock_irq(x)	do { spin_unlock(x); sti(); } while (0)
-
-#define spin_lock_irqsave(x, flags) \
-	do { save_flags(flags); spin_lock_irq(x); } while (0)
-#define spin_unlock_irqrestore(x, flags) \
-	do { spin_unlock(x); restore_flags(flags); } while (0)
 
 #else /* (DEBUG_SPINLOCKS >= 2) */
 
@@ -71,11 +86,6 @@
 #define spin_lock(x)		do {unsigned long __spinflags; save_flags(__spinflags); cli(); if ((x)->lock&&(x)->babble) {printk("%s:%d: spin_lock(%s:%p) already locked\n", __BASE_FILE__,__LINE__, (x)->module, (x));(x)->babble--;} (x)->lock = 1; restore_flags(__spinflags);} while (0)
 #define spin_unlock_wait(x)	do {unsigned long __spinflags; save_flags(__spinflags); cli(); if ((x)->lock&&(x)->babble) {printk("%s:%d: spin_unlock_wait(%s:%p) deadlock\n", __BASE_FILE__,__LINE__, (x)->module, (x));(x)->babble--;} restore_flags(__spinflags);} while (0)
 #define spin_unlock(x)		do {unsigned long __spinflags; save_flags(__spinflags); cli(); if (!(x)->lock&&(x)->babble) {printk("%s:%d: spin_unlock(%s:%p) not locked\n", __BASE_FILE__,__LINE__, (x)->module, (x));(x)->babble--;} (x)->lock = 0; restore_flags(__spinflags);} while (0)
-#define spin_lock_irq(x)	do {cli(); if ((x)->lock&&(x)->babble) {printk("%s:%d: spin_lock_irq(%s:%p) already locked\n", __BASE_FILE__,__LINE__, (x)->module, (x));(x)->babble--;} (x)->lock = 1;} while (0)
-#define spin_unlock_irq(x)	do {cli(); if (!(x)->lock&&(x)->babble) {printk("%s:%d: spin_lock(%s:%p) not locked\n", __BASE_FILE__,__LINE__, (x)->module, (x));(x)->babble--;} (x)->lock = 0; sti();} while (0)
-
-#define spin_lock_irqsave(x,flags)      do {save_flags(flags); cli(); if ((x)->lock&&(x)->babble) {printk("%s:%d: spin_lock_irqsave(%s:%p) already locked\n", __BASE_FILE__,__LINE__, (x)->module, (x));(x)->babble--;} (x)->lock = 1;} while (0)
-#define spin_unlock_irqrestore(x,flags) do {cli(); if (!(x)->lock&&(x)->babble) {printk("%s:%d: spin_unlock_irqrestore(%s:%p) not locked\n", __BASE_FILE__,__LINE__, (x)->module, (x));(x)->babble--;} (x)->lock = 0; restore_flags(flags);} while (0)
 
 #endif	/* DEBUG_SPINLOCKS */
 
@@ -103,19 +113,6 @@
 #define read_unlock(lock)	do { } while(0)
 #define write_lock(lock)	do { } while(0)
 #define write_unlock(lock)	do { } while(0)
-#define read_lock_irq(lock)	cli()
-#define read_unlock_irq(lock)	sti()
-#define write_lock_irq(lock)	cli()
-#define write_unlock_irq(lock)	sti()
-
-#define read_lock_irqsave(lock, flags)	\
-	do { save_flags(flags); cli(); } while (0)
-#define read_unlock_irqrestore(lock, flags) \
-	restore_flags(flags)
-#define write_lock_irqsave(lock, flags)	\
-	do { save_flags(flags); cli(); } while (0)
-#define write_unlock_irqrestore(lock, flags) \
-	restore_flags(flags)
 
 #else	/* __SMP__ */
 
@@ -168,18 +165,6 @@
 
 #define spin_trylock(lock) (!test_and_set_bit(0,(lock)))
 
-#define spin_lock_irq(lock) \
-	do { __cli(); spin_lock(lock); } while (0)
-
-#define spin_unlock_irq(lock) \
-	do { spin_unlock(lock); __sti(); } while (0)
-
-#define spin_lock_irqsave(lock, flags) \
-	do { __save_flags(flags); __cli(); spin_lock(lock); } while (0)
-
-#define spin_unlock_irqrestore(lock, flags) \
-	do { spin_unlock(lock); __restore_flags(flags); } while (0)
-
 /*
  * Read-write spinlocks, allowing multiple readers
  * but only one writer.
@@ -235,20 +220,6 @@
 
 #define write_unlock(rw) \
 	asm volatile("lock ; btrl $31,%0":"=m" (__dummy_lock(&(rw)->lock)))
-
-#define read_lock_irq(lock)	do { __cli(); read_lock(lock); } while (0)
-#define read_unlock_irq(lock)	do { read_unlock(lock); __sti(); } while (0)
-#define write_lock_irq(lock)	do { __cli(); write_lock(lock); } while (0)
-#define write_unlock_irq(lock)	do { write_unlock(lock); __sti(); } while (0)
-
-#define read_lock_irqsave(lock, flags)	\
-	do { __save_flags(flags); __cli(); read_lock(lock); } while (0)
-#define read_unlock_irqrestore(lock, flags) \
-	do { read_unlock(lock); __restore_flags(flags); } while (0)
-#define write_lock_irqsave(lock, flags)	\
-	do { __save_flags(flags); __cli(); write_lock(lock); } while (0)
-#define write_unlock_irqrestore(lock, flags) \
-	do { write_unlock(lock); __restore_flags(flags); } while (0)
 
 #endif /* __SMP__ */
 #endif /* __ASM_SPINLOCK_H */
diff -u --recursive --new-file v2.3.3/linux/include/asm-i386/system.h linux/include/asm-i386/system.h
--- v2.3.3/linux/include/asm-i386/system.h	Tue May 11 13:03:58 1999
+++ linux/include/asm-i386/system.h	Tue May 25 14:56:58 1999
@@ -181,6 +181,11 @@
 #define __restore_flags(x) \
 __asm__ __volatile__("pushl %0 ; popfl": /* no output */ :"g" (x):"memory")
 
+/* For spinlocks etc */
+#define local_irq_save(x)	__asm__ __volatile__("pushfl ; popl %0 ; cli":"=g" (x): /* no input */ :"memory")
+#define local_irq_restore(x)	__asm__ __volatile__("pushl %0 ; popfl": /* no output */ :"g" (x):"memory")
+#define local_irq_disable()	__asm__ __volatile__("cli": : :"memory")
+#define local_irq_enable()	__asm__ __volatile__("sti": : :"memory")
 
 #ifdef __SMP__
 
diff -u --recursive --new-file v2.3.3/linux/include/asm-m68k/processor.h linux/include/asm-m68k/processor.h
--- v2.3.3/linux/include/asm-m68k/processor.h	Tue Jan 19 10:58:34 1999
+++ linux/include/asm-m68k/processor.h	Tue May 25 14:55:05 1999
@@ -7,6 +7,12 @@
 #ifndef __ASM_M68K_PROCESSOR_H
 #define __ASM_M68K_PROCESSOR_H
 
+/*
+ * Default implementation of macro that returns current
+ * instruction pointer ("program counter").
+ */
+#define current_text_addr() ({ __label__ _l; _l: &&_l;})
+
 #include <asm/segment.h>
 #include <asm/fpu.h>
 
diff -u --recursive --new-file v2.3.3/linux/include/asm-mips/processor.h linux/include/asm-mips/processor.h
--- v2.3.3/linux/include/asm-mips/processor.h	Mon Jan 18 09:55:30 1999
+++ linux/include/asm-mips/processor.h	Tue May 25 14:55:05 1999
@@ -11,6 +11,12 @@
 #ifndef __ASM_MIPS_PROCESSOR_H
 #define __ASM_MIPS_PROCESSOR_H
 
+/*
+ * Default implementation of macro that returns current
+ * instruction pointer ("program counter").
+ */
+#define current_text_addr() ({ __label__ _l; _l: &&_l;})
+
 #if !defined (_LANGUAGE_ASSEMBLY)
 #include <asm/cachectl.h>
 #include <asm/mipsregs.h>
diff -u --recursive --new-file v2.3.3/linux/include/asm-ppc/ide.h linux/include/asm-ppc/ide.h
--- v2.3.3/linux/include/asm-ppc/ide.h	Fri May 14 18:55:28 1999
+++ linux/include/asm-ppc/ide.h	Sat May 22 13:03:00 1999
@@ -83,12 +83,18 @@
 
 static __inline__ int ide_default_irq(ide_ioreg_t base)
 {
-	return ppc_ide_md.default_irq(base);
+	if ( ppc_ide_md.default_irq )
+		return ppc_ide_md.default_irq(base);
+	else
+		return -1;
 }
 
 static __inline__ ide_ioreg_t ide_default_io_base(int index)
 {
-	return ppc_ide_md.default_io_base(index);
+	if ( ppc_ide_md.default_io_base )
+		return ppc_ide_md.default_io_base(index);
+	else
+		return -1;
 }
 
 static __inline__ void ide_init_default_hwifs(void)
@@ -107,21 +113,28 @@
 
 static __inline__ int ide_check_region (ide_ioreg_t from, unsigned int extent)
 {
-	return ppc_ide_md.check_region(from, extent);
+	if ( ppc_ide_md.check_region )
+		return ppc_ide_md.check_region(from, extent);
+	else
+		return -1;
 }
 
 static __inline__ void ide_request_region (ide_ioreg_t from, unsigned int extent, const char *name)
 {
-	ppc_ide_md.request_region(from, extent, name);
+	if ( ppc_ide_md.request_region )
+		ppc_ide_md.request_region(from, extent, name);
 }
 
 static __inline__ void ide_release_region (ide_ioreg_t from, unsigned int extent)
 {
-	ppc_ide_md.release_region(from, extent);
+	if ( ppc_ide_md.release_region )
+		ppc_ide_md.release_region(from, extent);
 }
 
-static __inline__ void ide_fix_driveid (struct hd_driveid *id) {
-        ppc_ide_md.fix_driveid(id);
+static __inline__ void ide_fix_driveid (struct hd_driveid *id)
+{
+        if ( ppc_ide_md.fix_driveid )
+		ppc_ide_md.fix_driveid(id);
 }
 
 #undef inb
diff -u --recursive --new-file v2.3.3/linux/include/asm-ppc/mmu.h linux/include/asm-ppc/mmu.h
--- v2.3.3/linux/include/asm-ppc/mmu.h	Fri Mar 19 10:50:09 1999
+++ linux/include/asm-ppc/mmu.h	Wed May 26 16:55:40 1999
@@ -5,14 +5,25 @@
 #ifndef _PPC_MMU_H_
 #define _PPC_MMU_H_
 
+#include <linux/config.h>
+
 #ifndef __ASSEMBLY__
 /* Hardware Page Table Entry */
 typedef struct _PTE {
+#ifdef CONFIG_PPC64
+	unsigned long long vsid:52;
+	unsigned long api:5;
+	unsigned long :5;
+	unsigned long h:1;
+	unsigned long v:1;
+	unsigned long long rpn:52;
+#else /* CONFIG_PPC64 */
 	unsigned long v:1;	/* Entry is valid */
 	unsigned long vsid:24;	/* Virtual segment identifier */
 	unsigned long h:1;	/* Hash algorithm indicator */
 	unsigned long api:6;	/* Abbreviated page index */
 	unsigned long rpn:20;	/* Real (physical) page number */
+#endif /* CONFIG_PPC64 */
 	unsigned long    :3;	/* Unused */
 	unsigned long r:1;	/* Referenced */
 	unsigned long c:1;	/* Changed */
@@ -53,7 +64,11 @@
 } P601_BATU;
 
 typedef struct _BATU {		/* Upper part of BAT (all except 601) */
+#ifdef CONFIG_PPC64
+	unsigned long long bepi:47;
+#else /* CONFIG_PPC64 */
 	unsigned long bepi:15;	/* Effective page index (virtual address) */
+#endif /* CONFIG_PPC64 */
 	unsigned long :4;	/* Unused */
 	unsigned long bl:11;	/* Block size mask */
 	unsigned long vs:1;	/* Supervisor valid */
@@ -68,7 +83,11 @@
 } P601_BATL;
 
 typedef struct _BATL {		/* Lower part of BAT (all except 601) */
+#ifdef CONFIG_PPC64
+	unsigned long long brpn:47;
+#else /* CONFIG_PPC64 */
 	unsigned long brpn:15;	/* Real page index (physical address) */
+#endif /* CONFIG_PPC64 */
 	unsigned long :10;	/* Unused */
 	unsigned long w:1;	/* Write-thru cache */
 	unsigned long i:1;	/* Cache inhibit */
diff -u --recursive --new-file v2.3.3/linux/include/asm-ppc/pgtable.h linux/include/asm-ppc/pgtable.h
--- v2.3.3/linux/include/asm-ppc/pgtable.h	Thu Apr 29 12:39:01 1999
+++ linux/include/asm-ppc/pgtable.h	Sat May 22 13:03:00 1999
@@ -20,6 +20,11 @@
 #define flush_tlb_page local_flush_tlb_page
 #define flush_tlb_range local_flush_tlb_range
 
+extern __inline__ void flush_tlb_pgtables(struct mm_struct *mm, unsigned long start, unsigned long end)
+{
+	/* PPC has hw page tables. */
+}
+
 /*
  * No cache flushing is required when address mappings are
  * changed, because the caches on PowerPCs are physically
diff -u --recursive --new-file v2.3.3/linux/include/asm-ppc/processor.h linux/include/asm-ppc/processor.h
--- v2.3.3/linux/include/asm-ppc/processor.h	Tue May 11 08:24:32 1999
+++ linux/include/asm-ppc/processor.h	Tue May 25 14:55:05 1999
@@ -1,12 +1,22 @@
 #ifndef __ASM_PPC_PROCESSOR_H
 #define __ASM_PPC_PROCESSOR_H
 
+/*
+ * Default implementation of macro that returns current
+ * instruction pointer ("program counter").
+ */
+#define current_text_addr() ({ __label__ _l; _l: &&_l;})
+
 #include <linux/config.h>
 
 #include <asm/ptrace.h>
 #include <asm/residual.h>
 
 /* Bit encodings for Machine State Register (MSR) */
+#ifdef CONFIG_PPC64
+#define MSR_SF		(1<<63)
+#define MSR_ISF		(1<<61)
+#endif /* CONFIG_PPC64 */
 #define MSR_POW		(1<<18)		/* Enable Power Management */
 #define MSR_TGPR	(1<<17)		/* TLB Update registers in use */
 #define MSR_ILE		(1<<16)		/* Interrupt Little-Endian enable */
diff -u --recursive --new-file v2.3.3/linux/include/asm-ppc/ptrace.h linux/include/asm-ppc/ptrace.h
--- v2.3.3/linux/include/asm-ppc/ptrace.h	Tue Apr 14 17:34:00 1998
+++ linux/include/asm-ppc/ptrace.h	Wed May 26 16:55:40 1999
@@ -17,22 +17,29 @@
  * the PT_* values below.  This simplifies arch/ppc/kernel/ptrace.c.
  */
 
+#include <linux/config.h>
+
 #ifndef __ASSEMBLY__
+#ifdef CONFIG_PPC64
+#define REG unsigned long /*long*/
+#else
+#define REG unsigned long
+#endif
 struct pt_regs {
-	unsigned long gpr[32];
-	unsigned long nip;
-	unsigned long msr;
-	unsigned long orig_gpr3; /* Used for restarting system calls */
-	unsigned long ctr;
-	unsigned long link;
-	unsigned long xer;
-	unsigned long ccr;
-	unsigned long mq;	/* 601 only (not used at present) */
-				/* Used on APUS to hold IPL value. */
-	unsigned long trap;	/* Reason for being here */
-	unsigned long dar;	/* Fault registers */
-	unsigned long dsisr;
-	unsigned long result;   /* Result of a system call */
+	REG gpr[32];
+	REG nip;
+	REG msr;
+	REG orig_gpr3;	/* Used for restarting system calls */
+	REG ctr;
+	REG link;
+	REG xer;
+	REG ccr;
+	REG mq;		/* 601 only (not used at present) */
+			/* Used on APUS to hold IPL value. */
+	REG trap;	/* Reason for being here */
+	REG dar;	/* Fault registers */
+	REG dsisr;
+	REG result;   	/* Result of a system call */
 };
 #endif
 
diff -u --recursive --new-file v2.3.3/linux/include/asm-ppc/semaphore.h linux/include/asm-ppc/semaphore.h
--- v2.3.3/linux/include/asm-ppc/semaphore.h	Mon May 17 09:55:23 1999
+++ linux/include/asm-ppc/semaphore.h	Sat May 22 13:03:00 1999
@@ -1,27 +1,64 @@
-#ifndef _PPC_SEMAPHORE_H
-#define _PPC_SEMAPHORE_H
+#ifndef _SPARC_SEMAPHORE_H
+#define _SPARC_SEMAPHORE_H
 
 /*
- * SMP- and interrupt-safe semaphores..
- *
- * (C) Copyright 1996 Linus Torvalds
- * Adapted for PowerPC by Gary Thomas and Paul Mackerras
+ * Swiped from asm-sparc/semaphore.h and modified
+ * -- Cort (cort@cs.nmt.edu)
  */
 
+#ifdef __KERNEL__
+
 #include <asm/atomic.h>
+#include <linux/wait.h>
 
 struct semaphore {
 	atomic_t count;
 	atomic_t waking;
 	wait_queue_head_t wait;
+#if WAITQUEUE_DEBUG
+	long __magic;
+#endif
 };
 
-#define sema_init(sem, val)	atomic_set(&((sem)->count), (val))
+#if WAITQUEUE_DEBUG
+# define __SEM_DEBUG_INIT(name) \
+		, (long)&(name).__magic
+#else
+# define __SEM_DEBUG_INIT(name)
+#endif
+
+#define __SEMAPHORE_INITIALIZER(name,count) \
+{ ATOMIC_INIT(count), ATOMIC_INIT(0), __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \
+	__SEM_DEBUG_INIT(name) }
+
+#define __MUTEX_INITIALIZER(name) \
+	__SEMAPHORE_INITIALIZER(name,1)
+
+#define __DECLARE_SEMAPHORE_GENERIC(name,count) \
+	struct semaphore name = __SEMAPHORE_INITIALIZER(name,count)
+
+#define DECLARE_MUTEX(name) __DECLARE_SEMAPHORE_GENERIC(name,1)
+#define DECLARE_MUTEX_LOCKED(name) __DECLARE_SEMAPHORE_GENERIC(name,0)
+
+extern inline void sema_init (struct semaphore *sem, int val)
+{
+	atomic_set(&sem->count, val);
+	atomic_set(&sem->waking, 0);
+	init_waitqueue_head(&sem->wait);
+#if WAITQUEUE_DEBUG
+	sem->__magic = (long)&sem->__magic;
+#endif
+}
+
+static inline void init_MUTEX (struct semaphore *sem)
+{
+	sema_init(sem, 1);
+}
 
-#define MUTEX		((struct semaphore) \
-			 { ATOMIC_INIT(1), ATOMIC_INIT(0), NULL })
-#define MUTEX_LOCKED	((struct semaphore) \
-			 { ATOMIC_INIT(0), ATOMIC_INIT(0), NULL })
+static inline void init_MUTEX_LOCKED (struct semaphore *sem)
+{
+	sema_init(sem, 0);
+}
 
 extern void __down(struct semaphore * sem);
 extern int  __down_interruptible(struct semaphore * sem);
@@ -65,4 +102,6 @@
 		__up(sem);
 }	
 
-#endif /* !(_PPC_SEMAPHORE_H) */
+#endif /* __KERNEL__ */
+
+#endif /* !(_SPARC_SEMAPHORE_H) */
diff -u --recursive --new-file v2.3.3/linux/include/asm-ppc/system.h linux/include/asm-ppc/system.h
--- v2.3.3/linux/include/asm-ppc/system.h	Tue May 11 08:24:32 1999
+++ linux/include/asm-ppc/system.h	Sat May 22 13:03:00 1999
@@ -58,7 +58,6 @@
 extern int _disable_interrupts(void);
 extern void _enable_interrupts(int);
 
-extern void instruction_dump(unsigned long *);
 extern void print_backtrace(unsigned long *);
 extern void show_regs(struct pt_regs * regs);
 extern void flush_instruction_cache(void);
diff -u --recursive --new-file v2.3.3/linux/include/asm-sparc/pgtable.h linux/include/asm-sparc/pgtable.h
--- v2.3.3/linux/include/asm-sparc/pgtable.h	Wed Mar 10 16:53:37 1999
+++ linux/include/asm-sparc/pgtable.h	Thu May 27 09:55:21 1999
@@ -1,4 +1,4 @@
-/* $Id: pgtable.h,v 1.78 1999/01/07 14:14:05 jj Exp $ */
+/* $Id: pgtable.h,v 1.80 1999/05/27 04:52:40 davem Exp $ */
 #ifndef _SPARC_PGTABLE_H
 #define _SPARC_PGTABLE_H
 
diff -u --recursive --new-file v2.3.3/linux/include/asm-sparc/processor.h linux/include/asm-sparc/processor.h
--- v2.3.3/linux/include/asm-sparc/processor.h	Thu Mar 25 09:23:34 1999
+++ linux/include/asm-sparc/processor.h	Thu May 27 09:55:22 1999
@@ -1,4 +1,4 @@
-/* $Id: processor.h,v 1.70 1999/03/24 11:42:44 davem Exp $
+/* $Id: processor.h,v 1.71 1999/05/27 04:52:43 davem Exp $
  * include/asm-sparc/processor.h
  *
  * Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu)
@@ -6,6 +6,12 @@
 
 #ifndef __ASM_SPARC_PROCESSOR_H
 #define __ASM_SPARC_PROCESSOR_H
+
+/*
+ * Sparc32 implementation of macro that returns current
+ * instruction pointer ("program counter").
+ */
+#define current_text_addr() ({ void *pc; __asm__("sethi %%hi(1f), %0; or %0, %%lo(1f), %0;\n1:" : "=r" (pc)); pc; })
 
 #include <linux/a.out.h>
 
diff -u --recursive --new-file v2.3.3/linux/include/asm-sparc/softirq.h linux/include/asm-sparc/softirq.h
--- v2.3.3/linux/include/asm-sparc/softirq.h	Thu Apr 22 19:24:52 1999
+++ linux/include/asm-sparc/softirq.h	Thu May 27 09:55:22 1999
@@ -103,7 +103,8 @@
 static inline int softirq_trylock(int cpu)
 {
 	if (spin_trylock(&global_bh_count)) {
-		if (atomic_read(&global_bh_lock) == 0) {
+		if (atomic_read(&global_bh_lock) == 0 &&
+		    local_bh_count[cpu] == 0) {
 			++local_bh_count[cpu];
 			return 1;
 		}
@@ -118,6 +119,9 @@
 	spin_unlock(&global_bh_count);
 }
 
+#define local_bh_disable()	(local_bh_count[smp_processor_id()]++)
+#define local_bh_enable()	(local_bh_count[smp_processor_id()]--)
+
 #else
 extern unsigned int local_bh_count;
 
@@ -128,6 +132,9 @@
 #define softirq_trylock(cpu)	(local_bh_count ? 0 : (local_bh_count=1))
 #define softirq_endlock(cpu)	(local_bh_count = 0)
 #define synchronize_bh()	barrier()
+
+#define local_bh_disable()	(local_bh_count++)
+#define local_bh_enable()	(local_bh_count--)
 
 /*
  * These use a mask count to correctly handle
diff -u --recursive --new-file v2.3.3/linux/include/asm-sparc/spinlock.h linux/include/asm-sparc/spinlock.h
--- v2.3.3/linux/include/asm-sparc/spinlock.h	Thu Apr 22 19:24:52 1999
+++ linux/include/asm-sparc/spinlock.h	Thu May 27 09:55:22 1999
@@ -22,6 +22,8 @@
 #define spin_unlock(lock)	do { } while(0)
 #define spin_lock_irq(lock)	cli()
 #define spin_unlock_irq(lock)	sti()
+#define spin_lock_bh(lock)	local_bh_disable()
+#define spin_unlock_bh(lock)	local_bh_enable()
 
 #define spin_lock_irqsave(lock, flags)		save_and_cli(flags)
 #define spin_unlock_irqrestore(lock, flags)	restore_flags(flags)
@@ -47,6 +49,10 @@
 #define read_unlock_irq(lock)	sti()
 #define write_lock_irq(lock)	cli()
 #define write_unlock_irq(lock)	sti()
+#define read_lock_bh(lock)	local_bh_disable()
+#define read_unlock_bh(lock)	local_bh_enable()
+#define write_lock_bh(lock)	local_bh_disable()
+#define write_unlock_bh(lock)	local_bh_enable()
 
 #define read_lock_irqsave(lock, flags)		save_and_cli(flags)
 #define read_unlock_irqrestore(lock, flags)	restore_flags(flags)
@@ -80,10 +86,12 @@
 
 #define spin_lock(lock)		_do_spin_lock(lock, "spin_lock")
 #define spin_lock_irq(lock)	do { __cli(); _do_spin_lock(lock, "spin_lock_irq"); } while(0)
+#define spin_lock_bh(lock)	do { local_bh_disable(); _do_spin_lock(lock, "spin_lock_irq"); } while(0)
 #define spin_lock_irqsave(lock, flags) do { __save_and_cli(flags); _do_spin_lock(lock, "spin_lock_irqsave"); } while(0)
 
 #define spin_unlock(lock)	_do_spin_unlock(lock)
 #define spin_unlock_irq(lock)	do { _do_spin_unlock(lock); __sti(); } while(0)
+#define spin_unlock_bh(lock)	do { _do_spin_unlock(lock); local_bh_enable(); } while(0)
 #define spin_unlock_irqrestore(lock, flags) do { _do_spin_unlock(lock); __restore_flags(flags); } while(0)
 
 struct _rwlock_debug {
@@ -107,6 +115,7 @@
 	__restore_flags(flags); \
 } while(0)
 #define read_lock_irq(lock)	do { __cli(); _do_read_lock(lock, "read_lock_irq"); } while(0)
+#define read_lock_bh(lock)	do { local_bh_disable(); _do_read_lock(lock, "read_lock_irq"); } while(0)
 #define read_lock_irqsave(lock, flags) do { __save_and_cli(flags); _do_read_lock(lock, "read_lock_irqsave"); } while(0)
 
 #define read_unlock(lock) \
@@ -116,6 +125,7 @@
 	__restore_flags(flags); \
 } while(0)
 #define read_unlock_irq(lock)	do { _do_read_unlock(lock, "read_unlock_irq"); __sti(); } while(0)
+#define read_unlock_bh(lock)	do { _do_read_unlock(lock, "read_unlock_irq"); local_bh_enable(); } while(0)
 #define read_unlock_irqrestore(lock, flags) do { _do_read_unlock(lock, "read_unlock_irqrestore"); __restore_flags(flags); } while(0)
 
 #define write_lock(lock) \
@@ -125,6 +135,7 @@
 	__restore_flags(flags); \
 } while(0)
 #define write_lock_irq(lock)	do { __cli(); _do_write_lock(lock, "write_lock_irq"); } while(0)
+#define write_lock_bh(lock)	do { local_bh_disable(); _do_write_lock(lock, "write_lock_irq"); } while(0)
 #define write_lock_irqsave(lock, flags) do { __save_and_cli(flags); _do_write_lock(lock, "write_lock_irqsave"); } while(0)
 
 #define write_unlock(lock) \
@@ -134,6 +145,7 @@
 	__restore_flags(flags); \
 } while(0)
 #define write_unlock_irq(lock)	do { _do_write_unlock(lock); __sti(); } while(0)
+#define write_unlock_bh(lock)	do { _do_write_unlock(lock); local_bh_enable(); } while(0)
 #define write_unlock_irqrestore(lock, flags) do { _do_write_unlock(lock); __restore_flags(flags); } while(0)
 
 #else /* !SPIN_LOCK_DEBUG */
@@ -200,6 +212,8 @@
 	: "g2", "memory", "cc");
 }
 
+#define spin_lock_bh(___lk) do { local_bh_disable(); spin_lock(___lk); } while(0)
+
 extern __inline__ void spin_unlock_irq(spinlock_t *lock)
 {
 	__asm__ __volatile__("
@@ -213,6 +227,8 @@
 	: "g2", "memory");
 }
 
+#define spin_unlock_bh(___lk) do { spin_unlock(___lk); local_bh_enable(); } while(0)
+
 #define spin_lock_irqsave(__lock, flags)	\
 do {						\
 	register spinlock_t *__lp asm("g1");	\
@@ -338,6 +354,11 @@
 #define read_unlock_irq(lock)	do { _read_unlock(lock); __sti(); } while (0)
 #define write_lock_irq(lock)	do { __cli(); write_lock(lock); } while (0)
 #define write_unlock_irq(lock)	do { write_unlock(lock); __sti(); } while (0)
+
+#define read_lock_bh(lock)	do { local_bh_disable(); _read_lock(lock); } while (0)
+#define read_unlock_bh(lock)	do { _read_unlock(lock); local_bh_enable(); } while (0)
+#define write_lock_bh(lock)	do { local_bh_disable(); write_lock(lock); } while (0)
+#define write_unlock_bh(lock)	do { write_unlock(lock); local_bh_enable(); } while (0)
 
 #define read_lock_irqsave(lock, flags)	\
 	do { __save_and_cli(flags); _read_lock(lock); } while (0)
diff -u --recursive --new-file v2.3.3/linux/include/asm-sparc64/asm_offsets.h linux/include/asm-sparc64/asm_offsets.h
--- v2.3.3/linux/include/asm-sparc64/asm_offsets.h	Fri May 14 18:55:28 1999
+++ linux/include/asm-sparc64/asm_offsets.h	Thu May 27 09:55:22 1999
@@ -12,177 +12,177 @@
 #define ASIZ_task_flags	0x00000008
 #define AOFF_task_sigpending	0x00000010
 #define ASIZ_task_sigpending	0x00000004
-#define AOFF_task_addr_limit	0x00000018
-#define ASIZ_task_addr_limit	0x00000008
-#define AOFF_task_exec_domain	0x00000020
+#define AOFF_task_addr_limit	0x00000014
+#define ASIZ_task_addr_limit	0x00000001
+#define AOFF_task_exec_domain	0x00000018
 #define ASIZ_task_exec_domain	0x00000008
-#define AOFF_task_need_resched	0x00000028
+#define AOFF_task_need_resched	0x00000020
 #define ASIZ_task_need_resched	0x00000008
-#define AOFF_task_counter	0x00000030
+#define AOFF_task_counter	0x00000028
 #define ASIZ_task_counter	0x00000008
-#define AOFF_task_priority	0x00000038
+#define AOFF_task_priority	0x00000030
 #define ASIZ_task_priority	0x00000008
-#define AOFF_task_avg_slice	0x00000040
+#define AOFF_task_avg_slice	0x00000038
 #define ASIZ_task_avg_slice	0x00000008
-#define AOFF_task_has_cpu	0x00000048
+#define AOFF_task_has_cpu	0x00000040
 #define ASIZ_task_has_cpu	0x00000004
-#define AOFF_task_processor	0x0000004c
+#define AOFF_task_processor	0x00000044
 #define ASIZ_task_processor	0x00000004
-#define AOFF_task_last_processor	0x00000050
+#define AOFF_task_last_processor	0x00000048
 #define ASIZ_task_last_processor	0x00000004
-#define AOFF_task_lock_depth	0x00000054
+#define AOFF_task_lock_depth	0x0000004c
 #define ASIZ_task_lock_depth	0x00000004
-#define AOFF_task_next_task	0x00000058
+#define AOFF_task_next_task	0x00000050
 #define ASIZ_task_next_task	0x00000008
-#define AOFF_task_prev_task	0x00000060
+#define AOFF_task_prev_task	0x00000058
 #define ASIZ_task_prev_task	0x00000008
-#define AOFF_task_next_run	0x00000068
+#define AOFF_task_next_run	0x00000060
 #define ASIZ_task_next_run	0x00000008
-#define AOFF_task_prev_run	0x00000070
+#define AOFF_task_prev_run	0x00000068
 #define ASIZ_task_prev_run	0x00000008
-#define AOFF_task_binfmt	0x00000078
+#define AOFF_task_binfmt	0x00000070
 #define ASIZ_task_binfmt	0x00000008
-#define AOFF_task_exit_code	0x00000080
+#define AOFF_task_exit_code	0x00000078
 #define ASIZ_task_exit_code	0x00000004
-#define AOFF_task_exit_signal	0x00000084
+#define AOFF_task_exit_signal	0x0000007c
 #define ASIZ_task_exit_signal	0x00000004
-#define AOFF_task_pdeath_signal	0x00000088
+#define AOFF_task_pdeath_signal	0x00000080
 #define ASIZ_task_pdeath_signal	0x00000004
-#define AOFF_task_personality	0x00000090
+#define AOFF_task_personality	0x00000088
 #define ASIZ_task_personality	0x00000008
-#define AOFF_task_pid	0x0000009c
+#define AOFF_task_pid	0x00000094
 #define ASIZ_task_pid	0x00000004
-#define AOFF_task_pgrp	0x000000a0
+#define AOFF_task_pgrp	0x00000098
 #define ASIZ_task_pgrp	0x00000004
-#define AOFF_task_tty_old_pgrp	0x000000a4
+#define AOFF_task_tty_old_pgrp	0x0000009c
 #define ASIZ_task_tty_old_pgrp	0x00000004
-#define AOFF_task_session	0x000000a8
+#define AOFF_task_session	0x000000a0
 #define ASIZ_task_session	0x00000004
-#define AOFF_task_leader	0x000000ac
+#define AOFF_task_leader	0x000000a4
 #define ASIZ_task_leader	0x00000004
-#define AOFF_task_p_opptr	0x000000b0
+#define AOFF_task_p_opptr	0x000000a8
 #define ASIZ_task_p_opptr	0x00000008
-#define AOFF_task_p_pptr	0x000000b8
+#define AOFF_task_p_pptr	0x000000b0
 #define ASIZ_task_p_pptr	0x00000008
-#define AOFF_task_p_cptr	0x000000c0
+#define AOFF_task_p_cptr	0x000000b8
 #define ASIZ_task_p_cptr	0x00000008
-#define AOFF_task_p_ysptr	0x000000c8
+#define AOFF_task_p_ysptr	0x000000c0
 #define ASIZ_task_p_ysptr	0x00000008
-#define AOFF_task_p_osptr	0x000000d0
+#define AOFF_task_p_osptr	0x000000c8
 #define ASIZ_task_p_osptr	0x00000008
-#define AOFF_task_pidhash_next	0x000000d8
+#define AOFF_task_pidhash_next	0x000000d0
 #define ASIZ_task_pidhash_next	0x00000008
-#define AOFF_task_pidhash_pprev	0x000000e0
+#define AOFF_task_pidhash_pprev	0x000000d8
 #define ASIZ_task_pidhash_pprev	0x00000008
-#define AOFF_task_tarray_ptr	0x000000e8
+#define AOFF_task_tarray_ptr	0x000000e0
 #define ASIZ_task_tarray_ptr	0x00000008
-#define AOFF_task_wait_chldexit	0x000000f0
+#define AOFF_task_wait_chldexit	0x000000e8
 #define ASIZ_task_wait_chldexit	0x00000028
-#define AOFF_task_vfork_sem	0x00000118
+#define AOFF_task_vfork_sem	0x00000110
 #define ASIZ_task_vfork_sem	0x00000008
-#define AOFF_task_policy	0x00000120
+#define AOFF_task_policy	0x00000118
 #define ASIZ_task_policy	0x00000008
-#define AOFF_task_rt_priority	0x00000128
+#define AOFF_task_rt_priority	0x00000120
 #define ASIZ_task_rt_priority	0x00000008
-#define AOFF_task_it_real_value	0x00000130
+#define AOFF_task_it_real_value	0x00000128
 #define ASIZ_task_it_real_value	0x00000008
-#define AOFF_task_it_prof_value	0x00000138
+#define AOFF_task_it_prof_value	0x00000130
 #define ASIZ_task_it_prof_value	0x00000008
-#define AOFF_task_it_virt_value	0x00000140
+#define AOFF_task_it_virt_value	0x00000138
 #define ASIZ_task_it_virt_value	0x00000008
-#define AOFF_task_it_real_incr	0x00000148
+#define AOFF_task_it_real_incr	0x00000140
 #define ASIZ_task_it_real_incr	0x00000008
-#define AOFF_task_it_prof_incr	0x00000150
+#define AOFF_task_it_prof_incr	0x00000148
 #define ASIZ_task_it_prof_incr	0x00000008
-#define AOFF_task_it_virt_incr	0x00000158
+#define AOFF_task_it_virt_incr	0x00000150
 #define ASIZ_task_it_virt_incr	0x00000008
-#define AOFF_task_real_timer	0x00000160
+#define AOFF_task_real_timer	0x00000158
 #define ASIZ_task_real_timer	0x00000028
-#define AOFF_task_times	0x00000188
+#define AOFF_task_times	0x00000180
 #define ASIZ_task_times	0x00000020
-#define AOFF_task_start_time	0x000001a8
+#define AOFF_task_start_time	0x000001a0
 #define ASIZ_task_start_time	0x00000008
-#define AOFF_task_per_cpu_utime	0x000001b0
+#define AOFF_task_per_cpu_utime	0x000001a8
 #define ASIZ_task_per_cpu_utime	0x00000008
-#define AOFF_task_min_flt	0x000001c0
+#define AOFF_task_min_flt	0x000001b8
 #define ASIZ_task_min_flt	0x00000008
-#define AOFF_task_maj_flt	0x000001c8
+#define AOFF_task_maj_flt	0x000001c0
 #define ASIZ_task_maj_flt	0x00000008
-#define AOFF_task_nswap	0x000001d0
+#define AOFF_task_nswap	0x000001c8
 #define ASIZ_task_nswap	0x00000008
-#define AOFF_task_cmin_flt	0x000001d8
+#define AOFF_task_cmin_flt	0x000001d0
 #define ASIZ_task_cmin_flt	0x00000008
-#define AOFF_task_cmaj_flt	0x000001e0
+#define AOFF_task_cmaj_flt	0x000001d8
 #define ASIZ_task_cmaj_flt	0x00000008
-#define AOFF_task_cnswap	0x000001e8
+#define AOFF_task_cnswap	0x000001e0
 #define ASIZ_task_cnswap	0x00000008
-#define AOFF_task_uid	0x000001f4
+#define AOFF_task_uid	0x000001ec
 #define ASIZ_task_uid	0x00000004
-#define AOFF_task_euid	0x000001f8
+#define AOFF_task_euid	0x000001f0
 #define ASIZ_task_euid	0x00000004
-#define AOFF_task_suid	0x000001fc
+#define AOFF_task_suid	0x000001f4
 #define ASIZ_task_suid	0x00000004
-#define AOFF_task_fsuid	0x00000200
+#define AOFF_task_fsuid	0x000001f8
 #define ASIZ_task_fsuid	0x00000004
-#define AOFF_task_gid	0x00000204
+#define AOFF_task_gid	0x000001fc
 #define ASIZ_task_gid	0x00000004
-#define AOFF_task_egid	0x00000208
+#define AOFF_task_egid	0x00000200
 #define ASIZ_task_egid	0x00000004
-#define AOFF_task_sgid	0x0000020c
+#define AOFF_task_sgid	0x00000204
 #define ASIZ_task_sgid	0x00000004
-#define AOFF_task_fsgid	0x00000210
+#define AOFF_task_fsgid	0x00000208
 #define ASIZ_task_fsgid	0x00000004
-#define AOFF_task_ngroups	0x00000214
+#define AOFF_task_ngroups	0x0000020c
 #define ASIZ_task_ngroups	0x00000004
-#define AOFF_task_groups	0x00000218
+#define AOFF_task_groups	0x00000210
 #define ASIZ_task_groups	0x00000080
-#define AOFF_task_cap_effective	0x00000298
+#define AOFF_task_cap_effective	0x00000290
 #define ASIZ_task_cap_effective	0x00000004
-#define AOFF_task_cap_inheritable	0x0000029c
+#define AOFF_task_cap_inheritable	0x00000294
 #define ASIZ_task_cap_inheritable	0x00000004
-#define AOFF_task_cap_permitted	0x000002a0
+#define AOFF_task_cap_permitted	0x00000298
 #define ASIZ_task_cap_permitted	0x00000004
-#define AOFF_task_user	0x000002a8
+#define AOFF_task_user	0x000002a0
 #define ASIZ_task_user	0x00000008
-#define AOFF_task_rlim	0x000002b0
+#define AOFF_task_rlim	0x000002a8
 #define ASIZ_task_rlim	0x000000a0
-#define AOFF_task_used_math	0x00000350
+#define AOFF_task_used_math	0x00000348
 #define ASIZ_task_used_math	0x00000002
-#define AOFF_task_comm	0x00000352
+#define AOFF_task_comm	0x0000034a
 #define ASIZ_task_comm	0x00000010
-#define AOFF_task_link_count	0x00000364
+#define AOFF_task_link_count	0x0000035c
 #define ASIZ_task_link_count	0x00000004
-#define AOFF_task_tty	0x00000368
+#define AOFF_task_tty	0x00000360
 #define ASIZ_task_tty	0x00000008
-#define AOFF_task_semundo	0x00000370
+#define AOFF_task_semundo	0x00000368
 #define ASIZ_task_semundo	0x00000008
-#define AOFF_task_semsleeping	0x00000378
+#define AOFF_task_semsleeping	0x00000370
 #define ASIZ_task_semsleeping	0x00000008
 #define AOFF_task_tss	0x00000380
-#define ASIZ_task_tss	0x00000470
-#define AOFF_task_fs	0x000007f0
+#define ASIZ_task_tss	0x00000460
+#define AOFF_task_fs	0x000007e0
 #define ASIZ_task_fs	0x00000008
-#define AOFF_task_files	0x000007f8
+#define AOFF_task_files	0x000007e8
 #define ASIZ_task_files	0x00000008
-#define AOFF_task_mm	0x00000800
+#define AOFF_task_mm	0x000007f0
 #define ASIZ_task_mm	0x00000008
-#define AOFF_task_sigmask_lock	0x00000808
+#define AOFF_task_sigmask_lock	0x000007f8
 #define ASIZ_task_sigmask_lock	0x00000001
-#define AOFF_task_sig	0x00000810
+#define AOFF_task_sig	0x00000800
 #define ASIZ_task_sig	0x00000008
-#define AOFF_task_signal	0x00000818
+#define AOFF_task_signal	0x00000808
 #define ASIZ_task_signal	0x00000008
-#define AOFF_task_blocked	0x00000820
+#define AOFF_task_blocked	0x00000810
 #define ASIZ_task_blocked	0x00000008
-#define AOFF_task_sigqueue	0x00000828
+#define AOFF_task_sigqueue	0x00000818
 #define ASIZ_task_sigqueue	0x00000008
-#define AOFF_task_sigqueue_tail	0x00000830
+#define AOFF_task_sigqueue_tail	0x00000820
 #define ASIZ_task_sigqueue_tail	0x00000008
-#define AOFF_task_sas_ss_sp	0x00000838
+#define AOFF_task_sas_ss_sp	0x00000828
 #define ASIZ_task_sas_ss_sp	0x00000008
-#define AOFF_task_sas_ss_size	0x00000840
+#define AOFF_task_sas_ss_size	0x00000830
 #define ASIZ_task_sas_ss_size	0x00000008
-#define ASIZ_task	0x00000850
+#define ASIZ_task	0x00000840
 #define AOFF_mm_mmap	0x00000000
 #define ASIZ_mm_mmap	0x00000008
 #define AOFF_mm_mmap_avl	0x00000008
@@ -246,47 +246,45 @@
 #define ASIZ_thread_cwp	0x00000002
 #define AOFF_thread_flags	0x0000000c
 #define ASIZ_thread_flags	0x00000002
-#define AOFF_thread_ctx	0x0000000e
-#define ASIZ_thread_ctx	0x00000002
+#define AOFF_thread_current_ds	0x0000000e
+#define ASIZ_thread_current_ds	0x00000001
 #define AOFF_thread_w_saved	0x00000010
 #define ASIZ_thread_w_saved	0x00000002
 #define AOFF_thread_new_signal	0x00000012
 #define ASIZ_thread_new_signal	0x00000002
-#define AOFF_thread____pad	0x00000014
-#define ASIZ_thread____pad	0x00000004
-#define AOFF_thread_current_ds	0x00000018
-#define ASIZ_thread_current_ds	0x00000008
-#define AOFF_thread_kregs	0x00000020
+#define AOFF_thread_ctx	0x00000014
+#define ASIZ_thread_ctx	0x00000002
+#define AOFF_thread_kregs	0x00000018
 #define ASIZ_thread_kregs	0x00000008
-#define AOFF_thread_utraps	0x00000028
+#define AOFF_thread_utraps	0x00000020
 #define ASIZ_thread_utraps	0x00000008
-#define AOFF_thread_reg_window	0x00000030
+#define AOFF_thread_fpdepth	0x00000028
+#define ASIZ_thread_fpdepth	0x00000001
+#define AOFF_thread_fpsaved	0x00000029
+#define ASIZ_thread_fpsaved	0x00000007
+#define AOFF_thread_gsr	0x00000030
+#define ASIZ_thread_gsr	0x00000007
+#define AOFF_thread_xfsr	0x00000038
+#define ASIZ_thread_xfsr	0x00000038
+#define AOFF_thread_reg_window	0x00000070
 #define ASIZ_thread_reg_window	0x00000380
-#define AOFF_thread_rwbuf_stkptrs	0x000003b0
+#define AOFF_thread_rwbuf_stkptrs	0x000003f0
 #define ASIZ_thread_rwbuf_stkptrs	0x00000038
-#define AOFF_thread_sig_address	0x000003e8
+#define AOFF_thread_sig_address	0x00000428
 #define ASIZ_thread_sig_address	0x00000008
-#define AOFF_thread_sig_desc	0x000003f0
+#define AOFF_thread_sig_desc	0x00000430
 #define ASIZ_thread_sig_desc	0x00000008
-#define AOFF_thread_user_cntd0	0x000003f8
+#define AOFF_thread_user_cntd0	0x00000438
 #define ASIZ_thread_user_cntd0	0x00000008
-#define AOFF_thread_user_cntd1	0x00000400
+#define AOFF_thread_user_cntd1	0x00000440
 #define ASIZ_thread_user_cntd1	0x00000008
-#define AOFF_thread_kernel_cntd0	0x00000408
+#define AOFF_thread_kernel_cntd0	0x00000448
 #define ASIZ_thread_kernel_cntd0	0x00000008
-#define AOFF_thread_kernel_cntd1	0x00000410
+#define AOFF_thread_kernel_cntd1	0x00000450
 #define ASIZ_thread_kernel_cntd1	0x00000008
-#define AOFF_thread_pcr_reg	0x00000418
+#define AOFF_thread_pcr_reg	0x00000458
 #define ASIZ_thread_pcr_reg	0x00000008
-#define AOFF_thread_fpdepth	0x00000420
-#define ASIZ_thread_fpdepth	0x00000001
-#define AOFF_thread_fpsaved	0x00000421
-#define ASIZ_thread_fpsaved	0x00000007
-#define AOFF_thread_gsr	0x00000428
-#define ASIZ_thread_gsr	0x00000007
-#define AOFF_thread_xfsr	0x00000430
-#define ASIZ_thread_xfsr	0x00000038
-#define ASIZ_thread	0x00000470
+#define ASIZ_thread	0x00000460
 
 #else /* CONFIG_SMP */
 
@@ -298,177 +296,177 @@
 #define ASIZ_task_flags	0x00000008
 #define AOFF_task_sigpending	0x00000010
 #define ASIZ_task_sigpending	0x00000004
-#define AOFF_task_addr_limit	0x00000018
-#define ASIZ_task_addr_limit	0x00000008
-#define AOFF_task_exec_domain	0x00000020
+#define AOFF_task_addr_limit	0x00000014
+#define ASIZ_task_addr_limit	0x00000001
+#define AOFF_task_exec_domain	0x00000018
 #define ASIZ_task_exec_domain	0x00000008
-#define AOFF_task_need_resched	0x00000028
+#define AOFF_task_need_resched	0x00000020
 #define ASIZ_task_need_resched	0x00000008
-#define AOFF_task_counter	0x00000030
+#define AOFF_task_counter	0x00000028
 #define ASIZ_task_counter	0x00000008
-#define AOFF_task_priority	0x00000038
+#define AOFF_task_priority	0x00000030
 #define ASIZ_task_priority	0x00000008
-#define AOFF_task_avg_slice	0x00000040
+#define AOFF_task_avg_slice	0x00000038
 #define ASIZ_task_avg_slice	0x00000008
-#define AOFF_task_has_cpu	0x00000048
+#define AOFF_task_has_cpu	0x00000040
 #define ASIZ_task_has_cpu	0x00000004
-#define AOFF_task_processor	0x0000004c
+#define AOFF_task_processor	0x00000044
 #define ASIZ_task_processor	0x00000004
-#define AOFF_task_last_processor	0x00000050
+#define AOFF_task_last_processor	0x00000048
 #define ASIZ_task_last_processor	0x00000004
-#define AOFF_task_lock_depth	0x00000054
+#define AOFF_task_lock_depth	0x0000004c
 #define ASIZ_task_lock_depth	0x00000004
-#define AOFF_task_next_task	0x00000058
+#define AOFF_task_next_task	0x00000050
 #define ASIZ_task_next_task	0x00000008
-#define AOFF_task_prev_task	0x00000060
+#define AOFF_task_prev_task	0x00000058
 #define ASIZ_task_prev_task	0x00000008
-#define AOFF_task_next_run	0x00000068
+#define AOFF_task_next_run	0x00000060
 #define ASIZ_task_next_run	0x00000008
-#define AOFF_task_prev_run	0x00000070
+#define AOFF_task_prev_run	0x00000068
 #define ASIZ_task_prev_run	0x00000008
-#define AOFF_task_binfmt	0x00000078
+#define AOFF_task_binfmt	0x00000070
 #define ASIZ_task_binfmt	0x00000008
-#define AOFF_task_exit_code	0x00000080
+#define AOFF_task_exit_code	0x00000078
 #define ASIZ_task_exit_code	0x00000004
-#define AOFF_task_exit_signal	0x00000084
+#define AOFF_task_exit_signal	0x0000007c
 #define ASIZ_task_exit_signal	0x00000004
-#define AOFF_task_pdeath_signal	0x00000088
+#define AOFF_task_pdeath_signal	0x00000080
 #define ASIZ_task_pdeath_signal	0x00000004
-#define AOFF_task_personality	0x00000090
+#define AOFF_task_personality	0x00000088
 #define ASIZ_task_personality	0x00000008
-#define AOFF_task_pid	0x0000009c
+#define AOFF_task_pid	0x00000094
 #define ASIZ_task_pid	0x00000004
-#define AOFF_task_pgrp	0x000000a0
+#define AOFF_task_pgrp	0x00000098
 #define ASIZ_task_pgrp	0x00000004
-#define AOFF_task_tty_old_pgrp	0x000000a4
+#define AOFF_task_tty_old_pgrp	0x0000009c
 #define ASIZ_task_tty_old_pgrp	0x00000004
-#define AOFF_task_session	0x000000a8
+#define AOFF_task_session	0x000000a0
 #define ASIZ_task_session	0x00000004
-#define AOFF_task_leader	0x000000ac
+#define AOFF_task_leader	0x000000a4
 #define ASIZ_task_leader	0x00000004
-#define AOFF_task_p_opptr	0x000000b0
+#define AOFF_task_p_opptr	0x000000a8
 #define ASIZ_task_p_opptr	0x00000008
-#define AOFF_task_p_pptr	0x000000b8
+#define AOFF_task_p_pptr	0x000000b0
 #define ASIZ_task_p_pptr	0x00000008
-#define AOFF_task_p_cptr	0x000000c0
+#define AOFF_task_p_cptr	0x000000b8
 #define ASIZ_task_p_cptr	0x00000008
-#define AOFF_task_p_ysptr	0x000000c8
+#define AOFF_task_p_ysptr	0x000000c0
 #define ASIZ_task_p_ysptr	0x00000008
-#define AOFF_task_p_osptr	0x000000d0
+#define AOFF_task_p_osptr	0x000000c8
 #define ASIZ_task_p_osptr	0x00000008
-#define AOFF_task_pidhash_next	0x000000d8
+#define AOFF_task_pidhash_next	0x000000d0
 #define ASIZ_task_pidhash_next	0x00000008
-#define AOFF_task_pidhash_pprev	0x000000e0
+#define AOFF_task_pidhash_pprev	0x000000d8
 #define ASIZ_task_pidhash_pprev	0x00000008
-#define AOFF_task_tarray_ptr	0x000000e8
+#define AOFF_task_tarray_ptr	0x000000e0
 #define ASIZ_task_tarray_ptr	0x00000008
-#define AOFF_task_wait_chldexit	0x000000f0
+#define AOFF_task_wait_chldexit	0x000000e8
 #define ASIZ_task_wait_chldexit	0x00000028
-#define AOFF_task_vfork_sem	0x00000118
+#define AOFF_task_vfork_sem	0x00000110
 #define ASIZ_task_vfork_sem	0x00000008
-#define AOFF_task_policy	0x00000120
+#define AOFF_task_policy	0x00000118
 #define ASIZ_task_policy	0x00000008
-#define AOFF_task_rt_priority	0x00000128
+#define AOFF_task_rt_priority	0x00000120
 #define ASIZ_task_rt_priority	0x00000008
-#define AOFF_task_it_real_value	0x00000130
+#define AOFF_task_it_real_value	0x00000128
 #define ASIZ_task_it_real_value	0x00000008
-#define AOFF_task_it_prof_value	0x00000138
+#define AOFF_task_it_prof_value	0x00000130
 #define ASIZ_task_it_prof_value	0x00000008
-#define AOFF_task_it_virt_value	0x00000140
+#define AOFF_task_it_virt_value	0x00000138
 #define ASIZ_task_it_virt_value	0x00000008
-#define AOFF_task_it_real_incr	0x00000148
+#define AOFF_task_it_real_incr	0x00000140
 #define ASIZ_task_it_real_incr	0x00000008
-#define AOFF_task_it_prof_incr	0x00000150
+#define AOFF_task_it_prof_incr	0x00000148
 #define ASIZ_task_it_prof_incr	0x00000008
-#define AOFF_task_it_virt_incr	0x00000158
+#define AOFF_task_it_virt_incr	0x00000150
 #define ASIZ_task_it_virt_incr	0x00000008
-#define AOFF_task_real_timer	0x00000160
+#define AOFF_task_real_timer	0x00000158
 #define ASIZ_task_real_timer	0x00000028
-#define AOFF_task_times	0x00000188
+#define AOFF_task_times	0x00000180
 #define ASIZ_task_times	0x00000020
-#define AOFF_task_start_time	0x000001a8
+#define AOFF_task_start_time	0x000001a0
 #define ASIZ_task_start_time	0x00000008
-#define AOFF_task_per_cpu_utime	0x000001b0
+#define AOFF_task_per_cpu_utime	0x000001a8
 #define ASIZ_task_per_cpu_utime	0x00000100
-#define AOFF_task_min_flt	0x000003b0
+#define AOFF_task_min_flt	0x000003a8
 #define ASIZ_task_min_flt	0x00000008
-#define AOFF_task_maj_flt	0x000003b8
+#define AOFF_task_maj_flt	0x000003b0
 #define ASIZ_task_maj_flt	0x00000008
-#define AOFF_task_nswap	0x000003c0
+#define AOFF_task_nswap	0x000003b8
 #define ASIZ_task_nswap	0x00000008
-#define AOFF_task_cmin_flt	0x000003c8
+#define AOFF_task_cmin_flt	0x000003c0
 #define ASIZ_task_cmin_flt	0x00000008
-#define AOFF_task_cmaj_flt	0x000003d0
+#define AOFF_task_cmaj_flt	0x000003c8
 #define ASIZ_task_cmaj_flt	0x00000008
-#define AOFF_task_cnswap	0x000003d8
+#define AOFF_task_cnswap	0x000003d0
 #define ASIZ_task_cnswap	0x00000008
-#define AOFF_task_uid	0x000003e4
+#define AOFF_task_uid	0x000003dc
 #define ASIZ_task_uid	0x00000004
-#define AOFF_task_euid	0x000003e8
+#define AOFF_task_euid	0x000003e0
 #define ASIZ_task_euid	0x00000004
-#define AOFF_task_suid	0x000003ec
+#define AOFF_task_suid	0x000003e4
 #define ASIZ_task_suid	0x00000004
-#define AOFF_task_fsuid	0x000003f0
+#define AOFF_task_fsuid	0x000003e8
 #define ASIZ_task_fsuid	0x00000004
-#define AOFF_task_gid	0x000003f4
+#define AOFF_task_gid	0x000003ec
 #define ASIZ_task_gid	0x00000004
-#define AOFF_task_egid	0x000003f8
+#define AOFF_task_egid	0x000003f0
 #define ASIZ_task_egid	0x00000004
-#define AOFF_task_sgid	0x000003fc
+#define AOFF_task_sgid	0x000003f4
 #define ASIZ_task_sgid	0x00000004
-#define AOFF_task_fsgid	0x00000400
+#define AOFF_task_fsgid	0x000003f8
 #define ASIZ_task_fsgid	0x00000004
-#define AOFF_task_ngroups	0x00000404
+#define AOFF_task_ngroups	0x000003fc
 #define ASIZ_task_ngroups	0x00000004
-#define AOFF_task_groups	0x00000408
+#define AOFF_task_groups	0x00000400
 #define ASIZ_task_groups	0x00000080
-#define AOFF_task_cap_effective	0x00000488
+#define AOFF_task_cap_effective	0x00000480
 #define ASIZ_task_cap_effective	0x00000004
-#define AOFF_task_cap_inheritable	0x0000048c
+#define AOFF_task_cap_inheritable	0x00000484
 #define ASIZ_task_cap_inheritable	0x00000004
-#define AOFF_task_cap_permitted	0x00000490
+#define AOFF_task_cap_permitted	0x00000488
 #define ASIZ_task_cap_permitted	0x00000004
-#define AOFF_task_user	0x00000498
+#define AOFF_task_user	0x00000490
 #define ASIZ_task_user	0x00000008
-#define AOFF_task_rlim	0x000004a0
+#define AOFF_task_rlim	0x00000498
 #define ASIZ_task_rlim	0x000000a0
-#define AOFF_task_used_math	0x00000540
+#define AOFF_task_used_math	0x00000538
 #define ASIZ_task_used_math	0x00000002
-#define AOFF_task_comm	0x00000542
+#define AOFF_task_comm	0x0000053a
 #define ASIZ_task_comm	0x00000010
-#define AOFF_task_link_count	0x00000554
+#define AOFF_task_link_count	0x0000054c
 #define ASIZ_task_link_count	0x00000004
-#define AOFF_task_tty	0x00000558
+#define AOFF_task_tty	0x00000550
 #define ASIZ_task_tty	0x00000008
-#define AOFF_task_semundo	0x00000560
+#define AOFF_task_semundo	0x00000558
 #define ASIZ_task_semundo	0x00000008
-#define AOFF_task_semsleeping	0x00000568
+#define AOFF_task_semsleeping	0x00000560
 #define ASIZ_task_semsleeping	0x00000008
 #define AOFF_task_tss	0x00000570
-#define ASIZ_task_tss	0x00000470
-#define AOFF_task_fs	0x000009e0
+#define ASIZ_task_tss	0x00000460
+#define AOFF_task_fs	0x000009d0
 #define ASIZ_task_fs	0x00000008
-#define AOFF_task_files	0x000009e8
+#define AOFF_task_files	0x000009d8
 #define ASIZ_task_files	0x00000008
-#define AOFF_task_mm	0x000009f0
+#define AOFF_task_mm	0x000009e0
 #define ASIZ_task_mm	0x00000008
-#define AOFF_task_sigmask_lock	0x000009f8
+#define AOFF_task_sigmask_lock	0x000009e8
 #define ASIZ_task_sigmask_lock	0x00000001
-#define AOFF_task_sig	0x00000a00
+#define AOFF_task_sig	0x000009f0
 #define ASIZ_task_sig	0x00000008
-#define AOFF_task_signal	0x00000a08
+#define AOFF_task_signal	0x000009f8
 #define ASIZ_task_signal	0x00000008
-#define AOFF_task_blocked	0x00000a10
+#define AOFF_task_blocked	0x00000a00
 #define ASIZ_task_blocked	0x00000008
-#define AOFF_task_sigqueue	0x00000a18
+#define AOFF_task_sigqueue	0x00000a08
 #define ASIZ_task_sigqueue	0x00000008
-#define AOFF_task_sigqueue_tail	0x00000a20
+#define AOFF_task_sigqueue_tail	0x00000a10
 #define ASIZ_task_sigqueue_tail	0x00000008
-#define AOFF_task_sas_ss_sp	0x00000a28
+#define AOFF_task_sas_ss_sp	0x00000a18
 #define ASIZ_task_sas_ss_sp	0x00000008
-#define AOFF_task_sas_ss_size	0x00000a30
+#define AOFF_task_sas_ss_size	0x00000a20
 #define ASIZ_task_sas_ss_size	0x00000008
-#define ASIZ_task	0x00000a40
+#define ASIZ_task	0x00000a30
 #define AOFF_mm_mmap	0x00000000
 #define ASIZ_mm_mmap	0x00000008
 #define AOFF_mm_mmap_avl	0x00000008
@@ -532,47 +530,45 @@
 #define ASIZ_thread_cwp	0x00000002
 #define AOFF_thread_flags	0x0000000c
 #define ASIZ_thread_flags	0x00000002
-#define AOFF_thread_ctx	0x0000000e
-#define ASIZ_thread_ctx	0x00000002
+#define AOFF_thread_current_ds	0x0000000e
+#define ASIZ_thread_current_ds	0x00000001
 #define AOFF_thread_w_saved	0x00000010
 #define ASIZ_thread_w_saved	0x00000002
 #define AOFF_thread_new_signal	0x00000012
 #define ASIZ_thread_new_signal	0x00000002
-#define AOFF_thread____pad	0x00000014
-#define ASIZ_thread____pad	0x00000004
-#define AOFF_thread_current_ds	0x00000018
-#define ASIZ_thread_current_ds	0x00000008
-#define AOFF_thread_kregs	0x00000020
+#define AOFF_thread_ctx	0x00000014
+#define ASIZ_thread_ctx	0x00000002
+#define AOFF_thread_kregs	0x00000018
 #define ASIZ_thread_kregs	0x00000008
-#define AOFF_thread_utraps	0x00000028
+#define AOFF_thread_utraps	0x00000020
 #define ASIZ_thread_utraps	0x00000008
-#define AOFF_thread_reg_window	0x00000030
+#define AOFF_thread_fpdepth	0x00000028
+#define ASIZ_thread_fpdepth	0x00000001
+#define AOFF_thread_fpsaved	0x00000029
+#define ASIZ_thread_fpsaved	0x00000007
+#define AOFF_thread_gsr	0x00000030
+#define ASIZ_thread_gsr	0x00000007
+#define AOFF_thread_xfsr	0x00000038
+#define ASIZ_thread_xfsr	0x00000038
+#define AOFF_thread_reg_window	0x00000070
 #define ASIZ_thread_reg_window	0x00000380
-#define AOFF_thread_rwbuf_stkptrs	0x000003b0
+#define AOFF_thread_rwbuf_stkptrs	0x000003f0
 #define ASIZ_thread_rwbuf_stkptrs	0x00000038
-#define AOFF_thread_sig_address	0x000003e8
+#define AOFF_thread_sig_address	0x00000428
 #define ASIZ_thread_sig_address	0x00000008
-#define AOFF_thread_sig_desc	0x000003f0
+#define AOFF_thread_sig_desc	0x00000430
 #define ASIZ_thread_sig_desc	0x00000008
-#define AOFF_thread_user_cntd0	0x000003f8
+#define AOFF_thread_user_cntd0	0x00000438
 #define ASIZ_thread_user_cntd0	0x00000008
-#define AOFF_thread_user_cntd1	0x00000400
+#define AOFF_thread_user_cntd1	0x00000440
 #define ASIZ_thread_user_cntd1	0x00000008
-#define AOFF_thread_kernel_cntd0	0x00000408
+#define AOFF_thread_kernel_cntd0	0x00000448
 #define ASIZ_thread_kernel_cntd0	0x00000008
-#define AOFF_thread_kernel_cntd1	0x00000410
+#define AOFF_thread_kernel_cntd1	0x00000450
 #define ASIZ_thread_kernel_cntd1	0x00000008
-#define AOFF_thread_pcr_reg	0x00000418
+#define AOFF_thread_pcr_reg	0x00000458
 #define ASIZ_thread_pcr_reg	0x00000008
-#define AOFF_thread_fpdepth	0x00000420
-#define ASIZ_thread_fpdepth	0x00000001
-#define AOFF_thread_fpsaved	0x00000421
-#define ASIZ_thread_fpsaved	0x00000007
-#define AOFF_thread_gsr	0x00000428
-#define ASIZ_thread_gsr	0x00000007
-#define AOFF_thread_xfsr	0x00000430
-#define ASIZ_thread_xfsr	0x00000038
-#define ASIZ_thread	0x00000470
+#define ASIZ_thread	0x00000460
 
 #else /* SPIN_LOCK_DEBUG */
 
@@ -582,177 +578,177 @@
 #define ASIZ_task_flags	0x00000008
 #define AOFF_task_sigpending	0x00000010
 #define ASIZ_task_sigpending	0x00000004
-#define AOFF_task_addr_limit	0x00000018
-#define ASIZ_task_addr_limit	0x00000008
-#define AOFF_task_exec_domain	0x00000020
+#define AOFF_task_addr_limit	0x00000014
+#define ASIZ_task_addr_limit	0x00000001
+#define AOFF_task_exec_domain	0x00000018
 #define ASIZ_task_exec_domain	0x00000008
-#define AOFF_task_need_resched	0x00000028
+#define AOFF_task_need_resched	0x00000020
 #define ASIZ_task_need_resched	0x00000008
-#define AOFF_task_counter	0x00000030
+#define AOFF_task_counter	0x00000028
 #define ASIZ_task_counter	0x00000008
-#define AOFF_task_priority	0x00000038
+#define AOFF_task_priority	0x00000030
 #define ASIZ_task_priority	0x00000008
-#define AOFF_task_avg_slice	0x00000040
+#define AOFF_task_avg_slice	0x00000038
 #define ASIZ_task_avg_slice	0x00000008
-#define AOFF_task_has_cpu	0x00000048
+#define AOFF_task_has_cpu	0x00000040
 #define ASIZ_task_has_cpu	0x00000004
-#define AOFF_task_processor	0x0000004c
+#define AOFF_task_processor	0x00000044
 #define ASIZ_task_processor	0x00000004
-#define AOFF_task_last_processor	0x00000050
+#define AOFF_task_last_processor	0x00000048
 #define ASIZ_task_last_processor	0x00000004
-#define AOFF_task_lock_depth	0x00000054
+#define AOFF_task_lock_depth	0x0000004c
 #define ASIZ_task_lock_depth	0x00000004
-#define AOFF_task_next_task	0x00000058
+#define AOFF_task_next_task	0x00000050
 #define ASIZ_task_next_task	0x00000008
-#define AOFF_task_prev_task	0x00000060
+#define AOFF_task_prev_task	0x00000058
 #define ASIZ_task_prev_task	0x00000008
-#define AOFF_task_next_run	0x00000068
+#define AOFF_task_next_run	0x00000060
 #define ASIZ_task_next_run	0x00000008
-#define AOFF_task_prev_run	0x00000070
+#define AOFF_task_prev_run	0x00000068
 #define ASIZ_task_prev_run	0x00000008
-#define AOFF_task_binfmt	0x00000078
+#define AOFF_task_binfmt	0x00000070
 #define ASIZ_task_binfmt	0x00000008
-#define AOFF_task_exit_code	0x00000080
+#define AOFF_task_exit_code	0x00000078
 #define ASIZ_task_exit_code	0x00000004
-#define AOFF_task_exit_signal	0x00000084
+#define AOFF_task_exit_signal	0x0000007c
 #define ASIZ_task_exit_signal	0x00000004
-#define AOFF_task_pdeath_signal	0x00000088
+#define AOFF_task_pdeath_signal	0x00000080
 #define ASIZ_task_pdeath_signal	0x00000004
-#define AOFF_task_personality	0x00000090
+#define AOFF_task_personality	0x00000088
 #define ASIZ_task_personality	0x00000008
-#define AOFF_task_pid	0x0000009c
+#define AOFF_task_pid	0x00000094
 #define ASIZ_task_pid	0x00000004
-#define AOFF_task_pgrp	0x000000a0
+#define AOFF_task_pgrp	0x00000098
 #define ASIZ_task_pgrp	0x00000004
-#define AOFF_task_tty_old_pgrp	0x000000a4
+#define AOFF_task_tty_old_pgrp	0x0000009c
 #define ASIZ_task_tty_old_pgrp	0x00000004
-#define AOFF_task_session	0x000000a8
+#define AOFF_task_session	0x000000a0
 #define ASIZ_task_session	0x00000004
-#define AOFF_task_leader	0x000000ac
+#define AOFF_task_leader	0x000000a4
 #define ASIZ_task_leader	0x00000004
-#define AOFF_task_p_opptr	0x000000b0
+#define AOFF_task_p_opptr	0x000000a8
 #define ASIZ_task_p_opptr	0x00000008
-#define AOFF_task_p_pptr	0x000000b8
+#define AOFF_task_p_pptr	0x000000b0
 #define ASIZ_task_p_pptr	0x00000008
-#define AOFF_task_p_cptr	0x000000c0
+#define AOFF_task_p_cptr	0x000000b8
 #define ASIZ_task_p_cptr	0x00000008
-#define AOFF_task_p_ysptr	0x000000c8
+#define AOFF_task_p_ysptr	0x000000c0
 #define ASIZ_task_p_ysptr	0x00000008
-#define AOFF_task_p_osptr	0x000000d0
+#define AOFF_task_p_osptr	0x000000c8
 #define ASIZ_task_p_osptr	0x00000008
-#define AOFF_task_pidhash_next	0x000000d8
+#define AOFF_task_pidhash_next	0x000000d0
 #define ASIZ_task_pidhash_next	0x00000008
-#define AOFF_task_pidhash_pprev	0x000000e0
+#define AOFF_task_pidhash_pprev	0x000000d8
 #define ASIZ_task_pidhash_pprev	0x00000008
-#define AOFF_task_tarray_ptr	0x000000e8
+#define AOFF_task_tarray_ptr	0x000000e0
 #define ASIZ_task_tarray_ptr	0x00000008
-#define AOFF_task_wait_chldexit	0x000000f0
+#define AOFF_task_wait_chldexit	0x000000e8
 #define ASIZ_task_wait_chldexit	0x00000030
-#define AOFF_task_vfork_sem	0x00000120
+#define AOFF_task_vfork_sem	0x00000118
 #define ASIZ_task_vfork_sem	0x00000008
-#define AOFF_task_policy	0x00000128
+#define AOFF_task_policy	0x00000120
 #define ASIZ_task_policy	0x00000008
-#define AOFF_task_rt_priority	0x00000130
+#define AOFF_task_rt_priority	0x00000128
 #define ASIZ_task_rt_priority	0x00000008
-#define AOFF_task_it_real_value	0x00000138
+#define AOFF_task_it_real_value	0x00000130
 #define ASIZ_task_it_real_value	0x00000008
-#define AOFF_task_it_prof_value	0x00000140
+#define AOFF_task_it_prof_value	0x00000138
 #define ASIZ_task_it_prof_value	0x00000008
-#define AOFF_task_it_virt_value	0x00000148
+#define AOFF_task_it_virt_value	0x00000140
 #define ASIZ_task_it_virt_value	0x00000008
-#define AOFF_task_it_real_incr	0x00000150
+#define AOFF_task_it_real_incr	0x00000148
 #define ASIZ_task_it_real_incr	0x00000008
-#define AOFF_task_it_prof_incr	0x00000158
+#define AOFF_task_it_prof_incr	0x00000150
 #define ASIZ_task_it_prof_incr	0x00000008
-#define AOFF_task_it_virt_incr	0x00000160
+#define AOFF_task_it_virt_incr	0x00000158
 #define ASIZ_task_it_virt_incr	0x00000008
-#define AOFF_task_real_timer	0x00000168
+#define AOFF_task_real_timer	0x00000160
 #define ASIZ_task_real_timer	0x00000028
-#define AOFF_task_times	0x00000190
+#define AOFF_task_times	0x00000188
 #define ASIZ_task_times	0x00000020
-#define AOFF_task_start_time	0x000001b0
+#define AOFF_task_start_time	0x000001a8
 #define ASIZ_task_start_time	0x00000008
-#define AOFF_task_per_cpu_utime	0x000001b8
+#define AOFF_task_per_cpu_utime	0x000001b0
 #define ASIZ_task_per_cpu_utime	0x00000100
-#define AOFF_task_min_flt	0x000003b8
+#define AOFF_task_min_flt	0x000003b0
 #define ASIZ_task_min_flt	0x00000008
-#define AOFF_task_maj_flt	0x000003c0
+#define AOFF_task_maj_flt	0x000003b8
 #define ASIZ_task_maj_flt	0x00000008
-#define AOFF_task_nswap	0x000003c8
+#define AOFF_task_nswap	0x000003c0
 #define ASIZ_task_nswap	0x00000008
-#define AOFF_task_cmin_flt	0x000003d0
+#define AOFF_task_cmin_flt	0x000003c8
 #define ASIZ_task_cmin_flt	0x00000008
-#define AOFF_task_cmaj_flt	0x000003d8
+#define AOFF_task_cmaj_flt	0x000003d0
 #define ASIZ_task_cmaj_flt	0x00000008
-#define AOFF_task_cnswap	0x000003e0
+#define AOFF_task_cnswap	0x000003d8
 #define ASIZ_task_cnswap	0x00000008
-#define AOFF_task_uid	0x000003ec
+#define AOFF_task_uid	0x000003e4
 #define ASIZ_task_uid	0x00000004
-#define AOFF_task_euid	0x000003f0
+#define AOFF_task_euid	0x000003e8
 #define ASIZ_task_euid	0x00000004
-#define AOFF_task_suid	0x000003f4
+#define AOFF_task_suid	0x000003ec
 #define ASIZ_task_suid	0x00000004
-#define AOFF_task_fsuid	0x000003f8
+#define AOFF_task_fsuid	0x000003f0
 #define ASIZ_task_fsuid	0x00000004
-#define AOFF_task_gid	0x000003fc
+#define AOFF_task_gid	0x000003f4
 #define ASIZ_task_gid	0x00000004
-#define AOFF_task_egid	0x00000400
+#define AOFF_task_egid	0x000003f8
 #define ASIZ_task_egid	0x00000004
-#define AOFF_task_sgid	0x00000404
+#define AOFF_task_sgid	0x000003fc
 #define ASIZ_task_sgid	0x00000004
-#define AOFF_task_fsgid	0x00000408
+#define AOFF_task_fsgid	0x00000400
 #define ASIZ_task_fsgid	0x00000004
-#define AOFF_task_ngroups	0x0000040c
+#define AOFF_task_ngroups	0x00000404
 #define ASIZ_task_ngroups	0x00000004
-#define AOFF_task_groups	0x00000410
+#define AOFF_task_groups	0x00000408
 #define ASIZ_task_groups	0x00000080
-#define AOFF_task_cap_effective	0x00000490
+#define AOFF_task_cap_effective	0x00000488
 #define ASIZ_task_cap_effective	0x00000004
-#define AOFF_task_cap_inheritable	0x00000494
+#define AOFF_task_cap_inheritable	0x0000048c
 #define ASIZ_task_cap_inheritable	0x00000004
-#define AOFF_task_cap_permitted	0x00000498
+#define AOFF_task_cap_permitted	0x00000490
 #define ASIZ_task_cap_permitted	0x00000004
-#define AOFF_task_user	0x000004a0
+#define AOFF_task_user	0x00000498
 #define ASIZ_task_user	0x00000008
-#define AOFF_task_rlim	0x000004a8
+#define AOFF_task_rlim	0x000004a0
 #define ASIZ_task_rlim	0x000000a0
-#define AOFF_task_used_math	0x00000548
+#define AOFF_task_used_math	0x00000540
 #define ASIZ_task_used_math	0x00000002
-#define AOFF_task_comm	0x0000054a
+#define AOFF_task_comm	0x00000542
 #define ASIZ_task_comm	0x00000010
-#define AOFF_task_link_count	0x0000055c
+#define AOFF_task_link_count	0x00000554
 #define ASIZ_task_link_count	0x00000004
-#define AOFF_task_tty	0x00000560
+#define AOFF_task_tty	0x00000558
 #define ASIZ_task_tty	0x00000008
-#define AOFF_task_semundo	0x00000568
+#define AOFF_task_semundo	0x00000560
 #define ASIZ_task_semundo	0x00000008
-#define AOFF_task_semsleeping	0x00000570
+#define AOFF_task_semsleeping	0x00000568
 #define ASIZ_task_semsleeping	0x00000008
-#define AOFF_task_tss	0x00000580
-#define ASIZ_task_tss	0x00000470
-#define AOFF_task_fs	0x000009f0
+#define AOFF_task_tss	0x00000570
+#define ASIZ_task_tss	0x00000460
+#define AOFF_task_fs	0x000009d0
 #define ASIZ_task_fs	0x00000008
-#define AOFF_task_files	0x000009f8
+#define AOFF_task_files	0x000009d8
 #define ASIZ_task_files	0x00000008
-#define AOFF_task_mm	0x00000a00
+#define AOFF_task_mm	0x000009e0
 #define ASIZ_task_mm	0x00000008
-#define AOFF_task_sigmask_lock	0x00000a08
+#define AOFF_task_sigmask_lock	0x000009e8
 #define ASIZ_task_sigmask_lock	0x0000000c
-#define AOFF_task_sig	0x00000a18
+#define AOFF_task_sig	0x000009f8
 #define ASIZ_task_sig	0x00000008
-#define AOFF_task_signal	0x00000a20
+#define AOFF_task_signal	0x00000a00
 #define ASIZ_task_signal	0x00000008
-#define AOFF_task_blocked	0x00000a28
+#define AOFF_task_blocked	0x00000a08
 #define ASIZ_task_blocked	0x00000008
-#define AOFF_task_sigqueue	0x00000a30
+#define AOFF_task_sigqueue	0x00000a10
 #define ASIZ_task_sigqueue	0x00000008
-#define AOFF_task_sigqueue_tail	0x00000a38
+#define AOFF_task_sigqueue_tail	0x00000a18
 #define ASIZ_task_sigqueue_tail	0x00000008
-#define AOFF_task_sas_ss_sp	0x00000a40
+#define AOFF_task_sas_ss_sp	0x00000a20
 #define ASIZ_task_sas_ss_sp	0x00000008
-#define AOFF_task_sas_ss_size	0x00000a48
+#define AOFF_task_sas_ss_size	0x00000a28
 #define ASIZ_task_sas_ss_size	0x00000008
-#define ASIZ_task	0x00000a50
+#define ASIZ_task	0x00000a30
 #define AOFF_mm_mmap	0x00000000
 #define ASIZ_mm_mmap	0x00000008
 #define AOFF_mm_mmap_avl	0x00000008
@@ -816,47 +812,45 @@
 #define ASIZ_thread_cwp	0x00000002
 #define AOFF_thread_flags	0x0000000c
 #define ASIZ_thread_flags	0x00000002
-#define AOFF_thread_ctx	0x0000000e
-#define ASIZ_thread_ctx	0x00000002
+#define AOFF_thread_current_ds	0x0000000e
+#define ASIZ_thread_current_ds	0x00000001
 #define AOFF_thread_w_saved	0x00000010
 #define ASIZ_thread_w_saved	0x00000002
 #define AOFF_thread_new_signal	0x00000012
 #define ASIZ_thread_new_signal	0x00000002
-#define AOFF_thread____pad	0x00000014
-#define ASIZ_thread____pad	0x00000004
-#define AOFF_thread_current_ds	0x00000018
-#define ASIZ_thread_current_ds	0x00000008
-#define AOFF_thread_kregs	0x00000020
+#define AOFF_thread_ctx	0x00000014
+#define ASIZ_thread_ctx	0x00000002
+#define AOFF_thread_kregs	0x00000018
 #define ASIZ_thread_kregs	0x00000008
-#define AOFF_thread_utraps	0x00000028
+#define AOFF_thread_utraps	0x00000020
 #define ASIZ_thread_utraps	0x00000008
-#define AOFF_thread_reg_window	0x00000030
+#define AOFF_thread_fpdepth	0x00000028
+#define ASIZ_thread_fpdepth	0x00000001
+#define AOFF_thread_fpsaved	0x00000029
+#define ASIZ_thread_fpsaved	0x00000007
+#define AOFF_thread_gsr	0x00000030
+#define ASIZ_thread_gsr	0x00000007
+#define AOFF_thread_xfsr	0x00000038
+#define ASIZ_thread_xfsr	0x00000038
+#define AOFF_thread_reg_window	0x00000070
 #define ASIZ_thread_reg_window	0x00000380
-#define AOFF_thread_rwbuf_stkptrs	0x000003b0
+#define AOFF_thread_rwbuf_stkptrs	0x000003f0
 #define ASIZ_thread_rwbuf_stkptrs	0x00000038
-#define AOFF_thread_sig_address	0x000003e8
+#define AOFF_thread_sig_address	0x00000428
 #define ASIZ_thread_sig_address	0x00000008
-#define AOFF_thread_sig_desc	0x000003f0
+#define AOFF_thread_sig_desc	0x00000430
 #define ASIZ_thread_sig_desc	0x00000008
-#define AOFF_thread_user_cntd0	0x000003f8
+#define AOFF_thread_user_cntd0	0x00000438
 #define ASIZ_thread_user_cntd0	0x00000008
-#define AOFF_thread_user_cntd1	0x00000400
+#define AOFF_thread_user_cntd1	0x00000440
 #define ASIZ_thread_user_cntd1	0x00000008
-#define AOFF_thread_kernel_cntd0	0x00000408
+#define AOFF_thread_kernel_cntd0	0x00000448
 #define ASIZ_thread_kernel_cntd0	0x00000008
-#define AOFF_thread_kernel_cntd1	0x00000410
+#define AOFF_thread_kernel_cntd1	0x00000450
 #define ASIZ_thread_kernel_cntd1	0x00000008
-#define AOFF_thread_pcr_reg	0x00000418
+#define AOFF_thread_pcr_reg	0x00000458
 #define ASIZ_thread_pcr_reg	0x00000008
-#define AOFF_thread_fpdepth	0x00000420
-#define ASIZ_thread_fpdepth	0x00000001
-#define AOFF_thread_fpsaved	0x00000421
-#define ASIZ_thread_fpsaved	0x00000007
-#define AOFF_thread_gsr	0x00000428
-#define ASIZ_thread_gsr	0x00000007
-#define AOFF_thread_xfsr	0x00000430
-#define ASIZ_thread_xfsr	0x00000038
-#define ASIZ_thread	0x00000470
+#define ASIZ_thread	0x00000460
 #endif /* SPIN_LOCK_DEBUG */
 
 #endif /* CONFIG_SMP */
diff -u --recursive --new-file v2.3.3/linux/include/asm-sparc64/checksum.h linux/include/asm-sparc64/checksum.h
--- v2.3.3/linux/include/asm-sparc64/checksum.h	Tue Apr 28 22:28:10 1998
+++ linux/include/asm-sparc64/checksum.h	Thu May 27 09:55:22 1999
@@ -1,4 +1,4 @@
-/* $Id: checksum.h,v 1.11 1998/04/17 02:37:22 davem Exp $ */
+/* $Id: checksum.h,v 1.12 1999/05/25 16:53:36 jj Exp $ */
 #ifndef __SPARC64_CHECKSUM_H
 #define __SPARC64_CHECKSUM_H
 
@@ -49,17 +49,20 @@
 csum_partial_copy_nocheck (const char *src, char *dst, int len, 
 			   unsigned int sum)
 {
-	__asm__ __volatile__ ("wr	%%g0, %0, %%asi" : : "i" (ASI_P));
-	return csum_partial_copy_sparc64(src, dst, len, sum);
+	int ret;
+	unsigned char cur_ds = current->tss.current_ds.seg;
+	__asm__ __volatile__ ("wr %%g0, %0, %%asi" : : "i" (ASI_P));
+	ret = csum_partial_copy_sparc64(src, dst, len, sum);
+	__asm__ __volatile__ ("wr %%g0, %0, %%asi" : : "r" (cur_ds));
+	return ret;
 }
 
 extern __inline__ unsigned int 
 csum_partial_copy_from_user(const char *src, char *dst, int len, 
 			    unsigned int sum, int *err)
 {
-	__asm__ __volatile__ ("wr	%%g0, %0, %%asi
-			       stx	%1, [%%sp + 0x7ff + 128]
-			      " : : "i" (ASI_S), "r" (err));
+	__asm__ __volatile__ ("stx	%0, [%%sp + 0x7ff + 128]"
+			      : : "r" (err));
 	return csum_partial_copy_sparc64(src, dst, len, sum);
 }
 
diff -u --recursive --new-file v2.3.3/linux/include/asm-sparc64/fpumacro.h linux/include/asm-sparc64/fpumacro.h
--- v2.3.3/linux/include/asm-sparc64/fpumacro.h	Tue Aug  4 16:03:35 1998
+++ linux/include/asm-sparc64/fpumacro.h	Thu May 27 09:55:22 1999
@@ -30,44 +30,4 @@
 	__asm__ __volatile__("wr %0, 0x0, %%fprs" : : "r" (val));
 }
 
-extern __inline__ void fpsave(unsigned long *fpregs,
-			      unsigned long *fsr,
-			      unsigned long *gsr)
-{
-	__asm__ __volatile__ ("
-	wr	%%g0, %3, %%asi
-	rd	%%gsr, %%g1
-	membar	#LoadStore | #StoreStore
-	stx	%%fsr, [%1]
-	stx	%%g1, [%2]
-	stda	%%f0, [%0] %%asi
-	stda	%%f16, [%0 + 64] %%asi
-	stda	%%f32, [%0 + 128] %%asi
-	stda	%%f48, [%0 + 192] %%asi
-	membar	#Sync
-"	: /* No outputs */
-	: "r" (fpregs), "r" (fsr), "r" (gsr), "i" (ASI_BLK_P)
-	: "g1");
-}
-
-extern __inline__ void fpload(unsigned long *fpregs,
-			      unsigned long *fsr,
-			      unsigned long *gsr)
-{
-	__asm__ __volatile__ ("
-	wr	%%g0, %3, %%asi
-	membar	#StoreLoad | #LoadLoad
-	ldda	[%0] %%asi, %%f0
-	ldda	[%0 + 64] %%asi, %%f16
-	ldda	[%0 + 128] %%asi, %%f32
-	ldda	[%0 + 192] %%asi, %%f48
-	ldx	[%1], %%fsr
-	ldx	[%2], %%g1
-	wr	%%g1, 0, %%gsr
-	membar	#Sync
-"	: /* No outputs */
-	: "r" (fpregs), "r" (fsr), "r" (gsr), "i" (ASI_BLK_P)
-	: "g1");
-}
-
 #endif /* !(_SPARC64_FPUMACRO_H) */
diff -u --recursive --new-file v2.3.3/linux/include/asm-sparc64/md.h linux/include/asm-sparc64/md.h
--- v2.3.3/linux/include/asm-sparc64/md.h	Mon Jan 12 15:15:58 1998
+++ linux/include/asm-sparc64/md.h	Thu May 27 09:55:22 1999
@@ -1,4 +1,4 @@
-/* $Id: md.h,v 1.2 1997/12/27 16:28:38 jj Exp $
+/* $Id: md.h,v 1.3 1999/05/25 16:53:28 jj Exp $
  * md.h: High speed xor_block operation for RAID4/5 
  *            utilizing the UltraSparc Visual Instruction Set.
  *
@@ -11,7 +11,7 @@
 #include <asm/head.h>
 #include <asm/asi.h>
 
-#define HAVE_ARCH_XORBLOCK
+#undef HAVE_ARCH_XORBLOCK
 
 #define MD_XORBLOCK_ALIGNMENT	64
 
diff -u --recursive --new-file v2.3.3/linux/include/asm-sparc64/mmu_context.h linux/include/asm-sparc64/mmu_context.h
--- v2.3.3/linux/include/asm-sparc64/mmu_context.h	Tue May 11 08:24:32 1999
+++ linux/include/asm-sparc64/mmu_context.h	Thu May 27 09:55:22 1999
@@ -1,4 +1,4 @@
-/* $Id: mmu_context.h,v 1.35 1999/05/08 03:03:20 davem Exp $ */
+/* $Id: mmu_context.h,v 1.36 1999/05/25 16:53:34 jj Exp $ */
 #ifndef __SPARC64_MMU_CONTEXT_H
 #define __SPARC64_MMU_CONTEXT_H
 
@@ -27,7 +27,7 @@
 #define init_new_context(__mm)	((__mm)->context = NO_CONTEXT)
 
 /* Kernel threads like rpciod and nfsd drop their mm, and then use
- * init_mm, when this happens we must make sure the tsk->tss.ctx is
+ * init_mm, when this happens we must make sure the secondary context is
  * updated as well.  Otherwise we have disasters relating to
  * set_fs/get_fs usage later on.
  *
@@ -49,12 +49,7 @@
 	} 									\
 } while (0)
 
-/* This routine must called with interrupts off,
- * this is necessary to guarentee that the current->tss.ctx
- * to CPU secontary context register relationship is maintained
- * when traps can happen.
- *
- * Also the caller must flush the current set of user windows
+/* The caller must flush the current set of user windows
  * to the stack (if necessary) before we get here.
  */
 extern __inline__ void __get_mmu_context(struct task_struct *tsk)
@@ -62,27 +57,30 @@
 	register unsigned long paddr asm("o5");
 	register unsigned long pgd_cache asm("o4");
 	struct mm_struct *mm = tsk->mm;
+	unsigned long asi;
 
 	if(!(tsk->tss.flags & SPARC_FLAG_KTHREAD)	&&
 	   !(tsk->flags & PF_EXITING)) {
 		unsigned long ctx = tlb_context_cache;
 		if((mm->context ^ ctx) & CTX_VERSION_MASK)
 			get_new_mmu_context(mm);
+		tsk->tss.ctx = mm->context & 0x3ff;
+		spitfire_set_secondary_context(mm->context & 0x3ff);
+		__asm__ __volatile__("flush %g6");
 		if(!(mm->cpu_vm_mask & (1UL<<smp_processor_id()))) {
-			spitfire_set_secondary_context(mm->context & 0x3ff);
-			__asm__ __volatile__("flush %g6");
 			spitfire_flush_dtlb_secondary_context();
 			spitfire_flush_itlb_secondary_context();
 			__asm__ __volatile__("flush %g6");
 		}
-		/* Don't worry, set_fs() will restore it... */
-		/* Sigh, damned include loops... just poke seg directly.  */
-		tsk->tss.ctx = (tsk->tss.current_ds.seg ?
-				(mm->context & 0x3ff) : 0);
-	} else
+		asi = tsk->tss.current_ds.seg;
+	} else {
 		tsk->tss.ctx = 0;
-	spitfire_set_secondary_context(tsk->tss.ctx);
-	__asm__ __volatile__("flush %g6");
+		spitfire_set_secondary_context(0);
+		__asm__ __volatile__("flush %g6");
+		asi = ASI_P;
+	}
+	/* Sigh, damned include loops... just poke seg directly.  */
+	__asm__ __volatile__ ("wr %%g0, %0, %%asi" : : "r" (asi));
 	paddr = __pa(mm->pgd);
 	if((tsk->tss.flags & (SPARC_FLAG_32BIT|SPARC_FLAG_KTHREAD)) ==
 	   (SPARC_FLAG_32BIT))
diff -u --recursive --new-file v2.3.3/linux/include/asm-sparc64/pgtable.h linux/include/asm-sparc64/pgtable.h
--- v2.3.3/linux/include/asm-sparc64/pgtable.h	Sun Mar 28 09:07:47 1999
+++ linux/include/asm-sparc64/pgtable.h	Thu May 27 09:55:22 1999
@@ -1,4 +1,4 @@
-/* $Id: pgtable.h,v 1.103 1999/03/28 08:40:04 davem Exp $
+/* $Id: pgtable.h,v 1.105 1999/05/27 04:52:51 davem Exp $
  * pgtable.h: SpitFire page table operations.
  *
  * Copyright 1996,1997 David S. Miller (davem@caip.rutgers.edu)
diff -u --recursive --new-file v2.3.3/linux/include/asm-sparc64/processor.h linux/include/asm-sparc64/processor.h
--- v2.3.3/linux/include/asm-sparc64/processor.h	Wed Mar 10 16:53:38 1999
+++ linux/include/asm-sparc64/processor.h	Thu May 27 09:55:22 1999
@@ -1,4 +1,4 @@
-/* $Id: processor.h,v 1.53 1999/01/19 07:57:51 davem Exp $
+/* $Id: processor.h,v 1.55 1999/05/27 04:52:54 davem Exp $
  * include/asm-sparc64/processor.h
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -7,6 +7,12 @@
 #ifndef __ASM_SPARC64_PROCESSOR_H
 #define __ASM_SPARC64_PROCESSOR_H
 
+/*
+ * Sparc64 implementation of macro that returns current
+ * instruction pointer ("program counter").
+ */
+#define current_text_addr() ({ void *pc; __asm__("rd %%pc, %0" : "=r" (pc)); pc; })
+
 #include <asm/asi.h>
 #include <asm/a.out.h>
 #include <asm/pstate.h>
@@ -36,7 +42,7 @@
 #define NSWINS		7
 
 typedef struct {
-	unsigned long seg;
+	unsigned char seg;
 } mm_segment_t;
 
 /* The Sparc processor specific thread struct. */
@@ -45,15 +51,19 @@
 	unsigned short wstate;
 	unsigned short cwp;
 	unsigned short flags;
-	unsigned short ctx;
+	mm_segment_t current_ds;
 
 /*DC2*/	unsigned short w_saved;
 	unsigned short new_signal;
-	unsigned int ___pad;
-	mm_segment_t current_ds;
+	unsigned short ctx;
+	struct pt_regs *kregs;
 
-/*DC3*/	struct pt_regs *kregs;
-	unsigned long *utraps;
+/*DC3*/ unsigned long *utraps;
+	unsigned char fpdepth;
+	unsigned char fpsaved[7];
+	
+/*DC4*/	unsigned char gsr[7];
+	unsigned long xfsr[7];
 
 	struct reg_window reg_window[NSWINS] __attribute__ ((aligned (16)));
 	unsigned long rwbuf_stkptrs[NSWINS] __attribute__ ((aligned (8)));
@@ -66,10 +76,6 @@
 	u64 kernel_cntd0, kernel_cntd1;
 	u64 pcr_reg;
 
-	unsigned char fpdepth;
-	unsigned char fpsaved[7];
-	unsigned char gsr[7];
-	unsigned long xfsr[7];
 };
 
 #endif /* !(__ASSEMBLY__) */
@@ -85,12 +91,14 @@
 		    NULL, PAGE_SHARED , VM_READ | VM_WRITE | VM_EXEC, 1, NULL, NULL }
 
 #define INIT_TSS  {						\
-/* ksp, wstate, cwp, flags,              ctx, */ 		\
-   0,   0,      0,   SPARC_FLAG_KTHREAD, 0,			\
-/* w_saved, new_signal, padding, current_ds, */			\
-   0,       0,          0,       KERNEL_DS,			\
-/* kregs,   utraps, */						\
-   0,       0,							\
+/* ksp, wstate, cwp, flags,              current_ds, */ 	\
+   0,   0,      0,   SPARC_FLAG_KTHREAD, KERNEL_DS,		\
+/* w_saved, new_signal, ctx,		 kregs, */		\
+   0,       0,          0,		 0,			\
+/* utraps, */							\
+   0,								\
+/* fpdepth, fpsaved, gsr,   xfsr */				\
+   0,       { 0 },   { 0 }, { 0 },				\
 /* reg_window */						\
    { { { 0, }, { 0, } }, }, 					\
 /* rwbuf_stkptrs */						\
@@ -99,8 +107,6 @@
    0,           0,						\
 /* user_cntd0, user_cndd1, kernel_cntd0, kernel_cntd0, pcr_reg */ \
    0,          0,          0,		 0,            0,	\
-/* fpdepth, fpsaved, gsr,   xfsr */				\
-   0,       { 0 },   { 0 }, { 0 },				\
 }
 
 #ifndef __ASSEMBLY__
diff -u --recursive --new-file v2.3.3/linux/include/asm-sparc64/softirq.h linux/include/asm-sparc64/softirq.h
--- v2.3.3/linux/include/asm-sparc64/softirq.h	Tue Mar 16 21:52:06 1999
+++ linux/include/asm-sparc64/softirq.h	Mon May 31 22:08:10 1999
@@ -16,6 +16,9 @@
 #define local_bh_count		(cpu_data[smp_processor_id()].bh_count)
 #endif
 
+#define local_bh_disable()	(local_bh_count++)
+#define local_bh_enable()	(local_bh_count--)
+
 /* The locking mechanism for base handlers, to prevent re-entrancy,
  * is entirely private to an implementation, it should not be
  * referenced at all outside of this file.
@@ -94,7 +97,8 @@
 static inline int softirq_trylock(int cpu)
 {
 	if (spin_trylock(&global_bh_count)) {
-		if (atomic_read(&global_bh_lock) == 0) {
+		if (atomic_read(&global_bh_lock) == 0 &&
+		    cpu_data[cpu].bh_count == 0) {
 			++(cpu_data[cpu].bh_count);
 			return 1;
 		}
diff -u --recursive --new-file v2.3.3/linux/include/asm-sparc64/spinlock.h linux/include/asm-sparc64/spinlock.h
--- v2.3.3/linux/include/asm-sparc64/spinlock.h	Fri May 14 18:55:29 1999
+++ linux/include/asm-sparc64/spinlock.h	Tue May 25 13:06:34 1999
@@ -20,6 +20,14 @@
 #define spin_unlock(lock)	do { } while(0)
 #define spin_lock_irq(lock)	cli()
 #define spin_unlock_irq(lock)	sti()
+#define spin_lock_bh(lock) \
+do {	local_bh_count++; \
+	barrier(); \
+} while(0)
+#define spin_unlock_bh(lock) \
+do {	barrier(); \
+	local_bh_count--; \
+} while(0)
 
 #define spin_lock_irqsave(lock, flags)		save_and_cli(flags)
 #define spin_unlock_irqrestore(lock, flags)	restore_flags(flags)
@@ -43,9 +51,28 @@
 #define write_unlock(lock)	do { } while(0)
 #define read_lock_irq(lock)	cli()
 #define read_unlock_irq(lock)	sti()
+#define read_lock_bh(lock) \
+do {	local_bh_count++; \
+	barrier(); \
+} while(0)
+#define read_unlock_bh(lock) \
+do {	barrier(); \
+	local_bh_count--; \
+} while(0)
+
 #define write_lock_irq(lock)	cli()
 #define write_unlock_irq(lock)	sti()
 
+#define write_lock_bh(lock) \
+do {	local_bh_count++; \
+	barrier(); \
+} while(0)
+ 
+#define write_unlock_bh(lock) \
+do {	barrier(); \
+	local_bh_count--; \
+} while(0)
+
 #define read_lock_irqsave(lock, flags)		save_and_cli(flags)
 #define read_unlock_irqrestore(lock, flags)	restore_flags(flags)
 #define write_lock_irqsave(lock, flags)		save_and_cli(flags)
@@ -149,6 +176,16 @@
 	: "memory");
 }
 
+#define spin_lock_bh(__lock)	\
+do {	local_bh_count++;	\
+	spin_lock(__lock);	\
+} while(0)
+
+#define spin_unlock_bh(__lock)		\
+do {	spin_unlock(__lock);		\
+	local_bh_count--;		\
+} while(0)
+
 #define spin_lock_irqsave(__lock, flags)			\
 do {	register spinlock_t *__lp asm("g1");			\
 	__lp = (__lock);					\
@@ -205,9 +242,11 @@
 #define spin_trylock(lp)	_spin_trylock(lp)
 #define spin_lock(lock)		_do_spin_lock(lock, "spin_lock")
 #define spin_lock_irq(lock)	do { __cli(); _do_spin_lock(lock, "spin_lock_irq"); } while(0)
+#define spin_lock_bh(lock)	do { local_bh_count++; _do_spin_lock(lock, "spin_lock_bh"); } while(0)
 #define spin_lock_irqsave(lock, flags) do { __save_and_cli(flags); _do_spin_lock(lock, "spin_lock_irqsave"); } while(0)
 #define spin_unlock(lock)	_do_spin_unlock(lock)
 #define spin_unlock_irq(lock)	do { _do_spin_unlock(lock); __sti(); } while(0)
+#define spin_unlock_bh(lock)	do { _do_spin_unlock(lock); local_bh_count--; } while(0)
 #define spin_unlock_irqrestore(lock, flags) do { _do_spin_unlock(lock); __restore_flags(flags); } while(0)
 
 #endif /* SPIN_LOCK_DEBUG */
@@ -303,8 +342,12 @@
 
 #define read_lock_irq(lock)	do { __cli(); read_lock(lock); } while (0)
 #define read_unlock_irq(lock)	do { read_unlock(lock); __sti(); } while (0)
+#define read_lock_bh(lock)	do { local_bh_count++; read_lock(lock); } while (0)
+#define read_unlock_bh(lock)	do { read_unlock(lock); local_bh_count--; } while (0)
 #define write_lock_irq(lock)	do { __cli(); write_lock(lock); } while (0)
 #define write_unlock_irq(lock)	do { write_unlock(lock); __sti(); } while (0)
+#define write_lock_bh(lock)	do { local_bh_count++; write_lock(lock); } while (0)
+#define write_unlock_bh(lock)	do { write_unlock(lock); local_bh_count--; } while (0)
 
 #define read_lock_irqsave(lock, flags)	\
 	do { __save_and_cli(flags); read_lock(lock); } while (0)
@@ -336,6 +379,7 @@
 	__restore_flags(flags); \
 } while(0)
 #define read_lock_irq(lock)	do { __cli(); _do_read_lock(lock, "read_lock_irq"); } while(0)
+#define read_lock_bh(lock)	do { local_bh_count++; _do_read_lock(lock, "read_lock_bh"); } while(0)
 #define read_lock_irqsave(lock, flags) do { __save_and_cli(flags); _do_read_lock(lock, "read_lock_irqsave"); } while(0)
 
 #define read_unlock(lock) \
@@ -345,6 +389,7 @@
 	__restore_flags(flags); \
 } while(0)
 #define read_unlock_irq(lock)	do { _do_read_unlock(lock, "read_unlock_irq"); __sti() } while(0)
+#define read_unlock_bh(lock)	do { _do_read_unlock(lock, "read_unlock_bh"); local_bh_count--; } while(0)
 #define read_unlock_irqrestore(lock, flags) do { _do_read_unlock(lock, "read_unlock_irqrestore"); __restore_flags(flags); } while(0)
 
 #define write_lock(lock) \
@@ -354,6 +399,7 @@
 	__restore_flags(flags); \
 } while(0)
 #define write_lock_irq(lock)	do { __cli(); _do_write_lock(lock, "write_lock_irq"); } while(0)
+#define write_lock_bh(lock)	do { local_bh_count++; _do_write_lock(lock, "write_lock_bh"); } while(0)
 #define write_lock_irqsave(lock, flags) do { __save_and_cli(flags); _do_write_lock(lock, "write_lock_irqsave"); } while(0)
 
 #define write_unlock(lock) \
@@ -363,6 +409,7 @@
 	__restore_flags(flags); \
 } while(0)
 #define write_unlock_irq(lock)	do { _do_write_unlock(lock); __sti(); } while(0)
+#define write_unlock_bh(lock)	do { _do_write_unlock(lock); local_bh_count--; } while(0)
 #define write_unlock_irqrestore(lock, flags) do { _do_write_unlock(lock); __restore_flags(flags); } while(0)
 
 #endif /* SPIN_LOCK_DEBUG */
diff -u --recursive --new-file v2.3.3/linux/include/asm-sparc64/uaccess.h linux/include/asm-sparc64/uaccess.h
--- v2.3.3/linux/include/asm-sparc64/uaccess.h	Tue May 11 08:24:32 1999
+++ linux/include/asm-sparc64/uaccess.h	Thu May 27 09:55:22 1999
@@ -1,4 +1,4 @@
-/* $Id: uaccess.h,v 1.29 1999/05/08 03:03:25 davem Exp $ */
+/* $Id: uaccess.h,v 1.30 1999/05/25 16:53:32 jj Exp $ */
 #ifndef _ASM_UACCESS_H
 #define _ASM_UACCESS_H
 
@@ -30,8 +30,8 @@
  * "For historical reasons, these macros are grossly misnamed." -Linus
  */
 
-#define KERNEL_DS   ((mm_segment_t) { 0x00 })
-#define USER_DS     ((mm_segment_t) { 0x2B })	/* har har har */
+#define KERNEL_DS   ((mm_segment_t) { ASI_P })
+#define USER_DS     ((mm_segment_t) { ASI_AIUS })	/* har har har */
 
 #define VERIFY_READ	0
 #define VERIFY_WRITE	1
@@ -43,17 +43,8 @@
 
 #define set_fs(val)								\
 do {										\
-	if (current->tss.current_ds.seg != val.seg) {				\
-		current->tss.current_ds = (val);				\
-		if (segment_eq((val), KERNEL_DS)) {				\
-			flushw_user ();						\
-			current->tss.ctx = 0;					\
-		} else {							\
-			current->tss.ctx = (current->mm->context & 0x3ff);	\
-		}								\
-		spitfire_set_secondary_context(current->tss.ctx); 		\
-		__asm__ __volatile__("flush %g6");				\
-	}									\
+	current->tss.current_ds = (val);					\
+	__asm__ __volatile__ ("wr %%g0, %0, %%asi" : : "r" ((val).seg));	\
 } while(0)
 
 #define __user_ok(addr,size) 1
@@ -154,7 +145,7 @@
 #define __put_user_asm(x,size,addr,ret)					\
 __asm__ __volatile__(							\
 	"/* Put user asm, inline. */\n"					\
-"1:\t"	"st"#size "a %1, [%2] %4\n\t"					\
+"1:\t"	"st"#size "a %1, [%2] %%asi\n\t"				\
 	"clr	%0\n"							\
 "2:\n\n\t"								\
 	".section .fixup,#alloc,#execinstr\n\t"				\
@@ -168,22 +159,22 @@
 	".word	1b, 3b\n\t"						\
 	".previous\n\n\t"						\
        : "=r" (ret) : "r" (x), "r" (__m(addr)),				\
-	 "i" (-EFAULT), "i" (ASI_S))
+	 "i" (-EFAULT))
 
 #define __put_user_asm_ret(x,size,addr,ret,foo)				\
 if (__builtin_constant_p(ret) && ret == -EFAULT)			\
 __asm__ __volatile__(							\
 	"/* Put user asm ret, inline. */\n"				\
-"1:\t"	"st"#size "a %1, [%2] %3\n\n\t"					\
+"1:\t"	"st"#size "a %1, [%2] %%asi\n\n\t"				\
 	".section __ex_table,#alloc\n\t"				\
 	".align	4\n\t"							\
 	".word	1b, __ret_efault\n\n\t"					\
 	".previous\n\n\t"						\
-       : "=r" (foo) : "r" (x), "r" (__m(addr)), "i" (ASI_S));		\
+       : "=r" (foo) : "r" (x), "r" (__m(addr)));			\
 else									\
 __asm__ __volatile(							\
 	"/* Put user asm ret, inline. */\n"				\
-"1:\t"	"st"#size "a %1, [%2] %4\n\n\t"					\
+"1:\t"	"st"#size "a %1, [%2] %%asi\n\n\t"				\
 	".section .fixup,#alloc,#execinstr\n\t"				\
 	".align	4\n"							\
 "3:\n\t"								\
@@ -195,7 +186,7 @@
 	".word	1b, 3b\n\n\t"						\
 	".previous\n\n\t"						\
        : "=r" (foo) : "r" (x), "r" (__m(addr)),				\
-         "i" (ret), "i" (ASI_S))
+         "i" (ret))
 
 extern int __put_user_bad(void);
 
@@ -223,7 +214,7 @@
 #define __get_user_asm(x,size,addr,ret)					\
 __asm__ __volatile__(							\
 	"/* Get user asm, inline. */\n"					\
-"1:\t"	"ld"#size "a [%2] %4, %1\n\t"					\
+"1:\t"	"ld"#size "a [%2] %%asi, %1\n\t"				\
 	"clr	%0\n"							\
 "2:\n\n\t"								\
 	".section .fixup,#alloc,#execinstr\n\t"				\
@@ -238,33 +229,33 @@
 	".word	1b, 3b\n\n\t"						\
 	".previous\n\t"							\
        : "=r" (ret), "=r" (x) : "r" (__m(addr)),			\
-	 "i" (-EFAULT), "i" (ASI_S))
+	 "i" (-EFAULT))
 
 #define __get_user_asm_ret(x,size,addr,retval)				\
 if (__builtin_constant_p(retval) && retval == -EFAULT)			\
 __asm__ __volatile__(							\
 	"/* Get user asm ret, inline. */\n"				\
-"1:\t"	"ld"#size "a [%1] %2, %0\n\n\t"					\
+"1:\t"	"ld"#size "a [%1] %%asi, %0\n\n\t"				\
 	".section __ex_table,#alloc\n\t"				\
 	".align	4\n\t"							\
 	".word	1b,__ret_efault\n\n\t"					\
 	".previous\n\t"							\
-       : "=r" (x) : "r" (__m(addr)), "i" (ASI_S));			\
+       : "=r" (x) : "r" (__m(addr)));					\
 else									\
 __asm__ __volatile__(							\
 	"/* Get user asm ret, inline. */\n"				\
-"1:\t"	"ld"#size "a [%1] %2, %0\n\n\t"					\
+"1:\t"	"ld"#size "a [%1] %%asi, %0\n\n\t"				\
 	".section .fixup,#alloc,#execinstr\n\t"				\
 	".align	4\n"							\
 "3:\n\t"								\
 	"ret\n\t"							\
-	" restore %%g0, %3, %%o0\n\n\t"					\
+	" restore %%g0, %2, %%o0\n\n\t"					\
 	".previous\n\t"							\
 	".section __ex_table,#alloc\n\t"				\
 	".align	4\n\t"							\
 	".word	1b, 3b\n\n\t"						\
 	".previous\n\t"							\
-       : "=r" (x) : "r" (__m(addr)), "i" (retval), "i" (ASI_S))
+       : "=r" (x) : "r" (__m(addr)), "i" (retval))
 
 extern int __get_user_bad(void);
 
@@ -339,8 +330,6 @@
 {
 	extern __kernel_size_t __bzero_noasi(void *addr, __kernel_size_t size);
 	
-	
-	__asm__ __volatile__ ("wr %%g0, %0, %%asi" : : "i" (ASI_S));
 	return __bzero_noasi(addr, size);
 }
 
diff -u --recursive --new-file v2.3.3/linux/include/linux/b1lli.h linux/include/linux/b1lli.h
--- v2.3.3/linux/include/linux/b1lli.h	Wed Apr  1 16:20:56 1998
+++ linux/include/linux/b1lli.h	Sun May 23 10:03:42 1999
@@ -1,11 +1,33 @@
 /*
- * $Id: b1lli.h,v 1.3 1998/01/31 10:54:37 calle Exp $
+ * $Id: b1lli.h,v 1.6 1999/04/15 19:49:36 calle Exp $
  *
  * ISDN lowlevel-module for AVM B1-card.
  *
  * Copyright 1996 by Carsten Paeth (calle@calle.in-berlin.de)
  *
  * $Log: b1lli.h,v $
+ * Revision 1.6  1999/04/15 19:49:36  calle
+ * fix fuer die B1-PCI. Jetzt geht z.B. auch IRQ 17 ...
+ *
+ * Revision 1.5  1998/10/25 14:50:28  fritz
+ * Backported from MIPS (Cobalt).
+ *
+ * Revision 1.4  1998/03/29 16:05:02  calle
+ * changes from 2.0 tree merged.
+ *
+ * Revision 1.1.2.9  1998/03/20 14:30:02  calle
+ * added cardnr to detect if you try to add same T1 to different io address.
+ * change number of nccis depending on number of channels.
+ *
+ * Revision 1.1.2.8  1998/03/04 17:32:33  calle
+ * Changes for T1.
+ *
+ * Revision 1.1.2.7  1998/02/27 15:38:29  calle
+ * T1 running with slow link.
+ *
+ * Revision 1.1.2.6  1998/02/24 17:57:36  calle
+ * changes for T1.
+ *
  * Revision 1.3  1998/01/31 10:54:37  calle
  * include changes for PCMCIA cards from 2.0 version
  *
@@ -70,15 +92,17 @@
 	int irq;
 } avmb1_carddef;
 
-#define AVM_CARDTYPE_B1	0
-#define AVM_CARDTYPE_T1	1
-#define AVM_CARDTYPE_M1	2
-#define AVM_CARDTYPE_M2	3
+#define AVM_CARDTYPE_B1		0
+#define AVM_CARDTYPE_T1		1
+#define AVM_CARDTYPE_M1		2
+#define AVM_CARDTYPE_M2		3
+#define AVM_CARDTYPE_B1PCI	4
 
 typedef struct avmb1_extcarddef {
 	int port;
 	int irq;
         int cardtype;
+        int cardnr;  /* for HEMA/T1 */
 } avmb1_extcarddef;
 
 #define	AVMB1_LOAD		0	/* load image to card */
@@ -87,6 +111,7 @@
 #define	AVMB1_LOAD_AND_CONFIG	3	/* load image and config to card */
 #define	AVMB1_ADDCARD_WITH_TYPE	4	/* add a new card, with cardtype */
 #define AVMB1_GET_CARDINFO	5	/* get cardtype */
+#define AVMB1_REMOVECARD	6	/* remove a card (usefull for T1) */
 
 
 
@@ -103,14 +128,12 @@
 
 #ifdef __KERNEL__
 
-#define	AVMB1_PORTLEN	0x1f
+#define	AVMB1_PORTLEN		0x1f
 
-#define AVM_MAXVERSION	8
-#define AVM_NBCHAN	2
+#define AVM_MAXVERSION		8
 
-#define AVM_NAPPS	30
-#define AVM_NPLCI	5
-#define AVM_NNCCI	6
+#define AVM_NAPPS		30
+#define AVM_NNCCI_PER_CHANNEL	4
 
 /*
  * Main driver data
@@ -119,9 +142,10 @@
 typedef struct avmb1_card {
 	struct avmb1_card *next;
 	int cnr;
-	unsigned short port;
+	unsigned int port;
 	unsigned irq;
 	int cardtype;
+	int cardnr; /* for T1-HEMA */
 	volatile unsigned short cardstate;
 	int interrupt;
 	int blocked;
@@ -149,23 +173,26 @@
 
 
 /* b1lli.c */
-int B1_detect(unsigned short base, int cardtype);
-void B1_reset(unsigned short base);
-int B1_load_t4file(unsigned short base, avmb1_t4file * t4file);
-int B1_load_config(unsigned short base, avmb1_t4file * config);
-int B1_loaded(unsigned short base);
-unsigned char B1_assign_irq(unsigned short base, unsigned irq, int cardtype);
-unsigned char B1_enable_irq(unsigned short base);
-unsigned char B1_disable_irq(unsigned short base);
+int B1_detect(unsigned int base, int cardtype);
+int T1_detectandinit(unsigned int base, unsigned irq, int cardnr);
+void B1_reset(unsigned int base);
+void T1_reset(unsigned int base);
+int B1_load_t4file(unsigned int base, avmb1_t4file * t4file);
+int B1_load_config(unsigned int base, avmb1_t4file * config);
+int B1_loaded(unsigned int base);
+void B1_setinterrupt(unsigned int base, unsigned irq, int cardtype);
+unsigned char B1_disable_irq(unsigned int base);
+void T1_disable_irq(unsigned int base);
 int B1_valid_irq(unsigned irq, int cardtype);
+int B1_valid_port(unsigned port, int cardtype);
 void B1_handle_interrupt(avmb1_card * card);
-void B1_send_init(unsigned short port,
+void B1_send_init(unsigned int port,
 	    unsigned int napps, unsigned int nncci, unsigned int cardnr);
-void B1_send_register(unsigned short port,
+void B1_send_register(unsigned int port,
 		      __u16 appid, __u32 nmsg,
 		      __u32 nb3conn, __u32 nb3blocks, __u32 b3bsize);
-void B1_send_release(unsigned short port, __u16 appid);
-void B1_send_message(unsigned short port, struct sk_buff *skb);
+void B1_send_release(unsigned int port, __u16 appid);
+void B1_send_message(unsigned int port, struct sk_buff *skb);
 
 /* b1capi.c */
 void avmb1_handle_new_ncci(avmb1_card * card,
diff -u --recursive --new-file v2.3.3/linux/include/linux/blkpg.h linux/include/linux/blkpg.h
--- v2.3.3/linux/include/linux/blkpg.h	Mon May 17 09:55:23 1999
+++ linux/include/linux/blkpg.h	Sat May 22 13:38:37 1999
@@ -34,7 +34,7 @@
         int flags;
         int datalen;
         void *data;
-} *p;
+};
 
 /* The subfunctions (for the op field) */
 #define BLKPG_ADD_PARTITION	1
diff -u --recursive --new-file v2.3.3/linux/include/linux/cyclades.h linux/include/linux/cyclades.h
--- v2.3.3/linux/include/linux/cyclades.h	Fri May 14 18:55:29 1999
+++ linux/include/linux/cyclades.h	Mon May 24 22:38:07 1999
@@ -557,7 +557,8 @@
 	unsigned long		event;
 	unsigned long		last_active;
 	int			count;	/* # of fd on device */
-	int                     x_break;
+	int                     breakon;
+	int                     breakoff;
 	int			blocked_open; /* # of blocked opens */
 	long			session; /* Session of opening process */
 	long			pgrp; /* pgrp of opening process */
diff -u --recursive --new-file v2.3.3/linux/include/linux/dn.h linux/include/linux/dn.h
--- v2.3.3/linux/include/linux/dn.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/dn.h	Wed May 26 09:37:03 1999
@@ -0,0 +1,161 @@
+#ifndef _LINUX_DN_H
+#define _LINUX_DN_H
+
+/*
+
+	DECnet Data Structures and Constants
+
+*/
+
+/* 
+ * DNPROTO_NSP can't be the same as SOL_SOCKET, 
+ * so increment each by one (compared to ULTRIX)
+ */
+#define DNPROTO_NSP     2                       /* NSP protocol number       */
+#define DNPROTO_ROU     3                       /* Routing protocol number   */
+#define DNPROTO_NML     4                       /* Net mgt protocol number   */
+#define DNPROTO_EVL     5                       /* Evl protocol number (usr) */
+#define DNPROTO_EVR     6                       /* Evl protocol number (evl) */
+#define DNPROTO_NSPT    7                       /* NSP trace protocol number */
+
+
+#define DN_ADDL		2
+#define DN_MAXADDL	2 /* ULTRIX headers have 20 here, but pathworks has 2 */
+#define DN_MAXOPTL	16
+#define DN_MAXOBJL	16
+#define DN_MAXACCL	40
+#define DN_MAXALIASL	128
+#define DN_MAXNODEL	256
+#define DNBUFSIZE	65023
+
+/* 
+ * SET/GET Socket options  - must match the DSO_ numbers below
+ */
+#define SO_CONDATA      1
+#define SO_CONACCESS    2
+#define SO_PROXYUSR     3
+#define SO_LINKINFO     7
+
+#define DSO_CONDATA     1        /* Set/Get connect data                */
+#define DSO_DISDATA     10       /* Set/Get disconnect data             */
+#define DSO_CONACCESS   2        /* Set/Get connect access data         */
+#define DSO_ACCEPTMODE  4        /* Set/Get accept mode                 */
+#define DSO_CONACCEPT   5        /* Accept deferred connection          */
+#define DSO_CONREJECT   6        /* Reject deferred connection          */
+#define DSO_LINKINFO    7        /* Set/Get link information            */
+#define DSO_STREAM      8        /* Set socket type to stream           */
+#define DSO_SEQPACKET   9        /* Set socket type to sequenced packet */
+#define DSO_MAX         10       /* Maximum option number               */
+
+
+/* LINK States */
+#define LL_INACTIVE	0
+#define LL_CONNECTING	1
+#define LL_RUNNING	2
+#define LL_DISCONNECTING 3
+
+#define ACC_IMMED 0
+#define ACC_DEFER 1
+
+#define SDF_WILD        1                  /* Wild card object          */
+#define SDF_PROXY       2                  /* Addr eligible for proxy   */
+#define SDF_UICPROXY    4                  /* Use uic-based proxy       */
+
+/* Structures */
+
+
+struct dn_naddr 
+{
+	unsigned short		a_len;
+	unsigned char a_addr[DN_MAXADDL];
+};
+
+struct sockaddr_dn
+{
+	unsigned short		sdn_family;
+	unsigned char		sdn_flags;
+	unsigned char		sdn_objnum;
+	unsigned short		sdn_objnamel;
+	unsigned char		sdn_objname[DN_MAXOBJL];
+	struct   dn_naddr	sdn_add;
+};
+#define sdn_nodeaddrl   sdn_add.a_len   /* Node address length  */
+#define sdn_nodeaddr    sdn_add.a_addr  /* Node address         */
+
+
+
+/*
+ * DECnet set/get DSO_CONDATA, DSO_DISDATA (optional data) structure
+ */
+struct optdata_dn {
+        unsigned short  opt_status;     /* Extended status return */
+#define opt_sts opt_status
+        unsigned short  opt_optl;       /* Length of user data    */
+        unsigned char   opt_data[16];   /* User data              */
+};
+
+struct accessdata_dn
+{
+	unsigned char		acc_accl;
+	unsigned char		acc_acc[DN_MAXACCL];
+	unsigned char 		acc_passl;
+	unsigned char		acc_pass[DN_MAXACCL];
+	unsigned char 		acc_userl;
+	unsigned char		acc_user[DN_MAXACCL];
+};
+
+/*
+ * DECnet logical link information structure
+ */
+struct linkinfo_dn {
+        unsigned short  idn_segsize;    /* Segment size for link */
+        unsigned char   idn_linkstate;  /* Logical link state    */
+};
+
+/*
+ * Ethernet address format (for DECnet)
+ */
+union etheraddress {
+        unsigned char dne_addr[6];             /* Full ethernet address */
+  struct {
+                unsigned char dne_hiord[4];    /* DECnet HIORD prefix   */
+                unsigned char dne_nodeaddr[2]; /* DECnet node address   */
+  } dne_remote;
+};
+
+
+/*
+ * DECnet physical socket address format
+ */
+struct dn_addr {
+        unsigned short dna_family;      /* AF_DECnet               */
+        union etheraddress dna_netaddr; /* DECnet ethernet address */
+};
+
+#define DECNET_IOCTL_BASE 0x89 /* PROTOPRIVATE range */
+
+#define SIOCSNETADDR  _IOW(DECNET_IOCTL_BASE, 0xe0, struct dn_naddr)
+#define SIOCGNETADDR  _IOR(DECNET_IOCTL_BASE, 0xe1, struct dn_naddr)
+#define OSIOCSNETADDR _IOW(DECNET_IOCTL_BASE, 0xe0, int)
+#define OSIOCGNETADDR _IOR(DECNET_IOCTL_BASE, 0xe1, int)
+
+/*
+ * An unofficial structure used to set/get routes.
+ * Be warned, this will probably change as the routing
+ * evolves. Also this is only for use with the ioctl()
+ * and the routing will use rtnetlink eventually.
+ */
+struct dn_fib_rtinfo {
+	unsigned long flags; /* Flags */
+#define DN_FIB_RTINFO_F_REPLACE 0x0001  /* Replace any existing route */
+#define DN_FIB_RTINFO_F_DEVCOST 0x0002  /* Add cost of device         */
+	unsigned long timeout; /* Time in seconds route should last */
+	unsigned short src;  /* Source Address, 0 = any */
+	unsigned short dst;  /* Destination Address     */
+	unsigned short nhp;  /* Next Hop Address        */
+	unsigned short hops; /* Hops on path            */
+	unsigned short cost; /* Cost of path            */
+	char device[16];
+};
+
+#endif /* _LINUX_DN_H */
diff -u --recursive --new-file v2.3.3/linux/include/linux/i2c.h linux/include/linux/i2c.h
--- v2.3.3/linux/include/linux/i2c.h	Fri Jan 15 14:36:21 1999
+++ linux/include/linux/i2c.h	Tue May 25 13:57:55 1999
@@ -22,6 +22,8 @@
  *
  */
 
+#include <linux/version.h>
+
 #define I2C_BUS_MAX       4    /* max # of bus drivers  */
 #define I2C_DRIVER_MAX    8    /* max # of chip drivers */
 #define I2C_DEVICE_MAX    8    /* max # if devices per bus/driver */
@@ -35,6 +37,7 @@
 #define I2C_DRIVERID_VIDEOTEXT	 3
 
 #define I2C_BUSID_BT848		1	/* I2C bus on a BT848 */
+#define I2C_BUSID_PARPORT	2	/* Bit banging on a parallel port */
 
 /*
  * struct for a driver for a i2c chip (tuner, soundprocessor,
diff -u --recursive --new-file v2.3.3/linux/include/linux/ide.h linux/include/linux/ide.h
--- v2.3.3/linux/include/linux/ide.h	Fri May 14 18:55:29 1999
+++ linux/include/linux/ide.h	Fri May 28 09:34:41 1999
@@ -810,12 +810,4 @@
 unsigned long ide_get_or_set_dma_base (ide_hwif_t *hwif, int extra, const char *name) __init;
 #endif
 
-/* This is too ugly to live! */
-#ifdef CONFIG_BLK_DEV_PDC4030
-#include "pdc4030.h"
-#define IS_PDC4030_DRIVE (HWIF(drive)->chipset == ide_pdc4030)
-#else
-#define IS_PDC4030_DRIVE (0)	/* auto-NULLs out pdc4030 code */
-#endif /* CONFIG_BLK_DEV_PDC4030 */
-
 #endif /* _IDE_H */
diff -u --recursive --new-file v2.3.3/linux/include/linux/if_arp.h linux/include/linux/if_arp.h
--- v2.3.3/linux/include/linux/if_arp.h	Tue May 11 13:05:12 1999
+++ linux/include/linux/if_arp.h	Wed May 26 09:37:03 1999
@@ -49,6 +49,7 @@
 #define ARPHRD_PPP	512
 #define ARPHRD_HDLC	513		/* (Cisco) HDLC 		*/
 #define ARPHRD_LAPB	516		/* LAPB				*/
+#define ARPHRD_DDCMP    517		/* Digital's DDCMP protocol     */
 
 #define ARPHRD_TUNNEL	768		/* IPIP tunnel			*/
 #define ARPHRD_TUNNEL6	769		/* IPIP6 tunnel			*/
diff -u --recursive --new-file v2.3.3/linux/include/linux/interrupt.h linux/include/linux/interrupt.h
--- v2.3.3/linux/include/linux/interrupt.h	Tue May 11 13:04:00 1999
+++ linux/include/linux/interrupt.h	Mon May 31 22:08:10 1999
@@ -35,6 +35,7 @@
 	SERIAL_BH,
 	RISCOM8_BH,
 	SPECIALIX_BH,
+	AURORA_BH,
 	ESP_BH,
 	NET_BH,
 	SCSI_BH,
diff -u --recursive --new-file v2.3.3/linux/include/linux/isdn.h linux/include/linux/isdn.h
--- v2.3.3/linux/include/linux/isdn.h	Fri May 14 18:55:29 1999
+++ linux/include/linux/isdn.h	Sun May 23 10:03:42 1999
@@ -1,4 +1,4 @@
-/* $Id: isdn.h,v 1.37 1998/02/22 19:45:24 fritz Exp $
+/* $Id: isdn.h,v 1.64 1999/04/18 14:57:14 fritz Exp $
  *
  * Main header for the Linux ISDN subsystem (linklevel).
  *
@@ -20,11 +20,102 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
- * Note: This file differs from the corresponding revision as present in the
- * isdn4linux CVS repository because some later bug fixes have been extracted
- * from the repository and merged into this file. -- Henner Eisen
- *
  * $Log: isdn.h,v $
+ * Revision 1.64  1999/04/18 14:57:14  fritz
+ * Removed TIMRU stuff
+ *
+ * Revision 1.63  1999/04/18 14:07:18  fritz
+ * Removed TIMRU stuff.
+ *
+ * Revision 1.62  1999/04/12 13:16:54  fritz
+ * Changes from 2.0 tree.
+ *
+ * Revision 1.61  1999/03/02 11:43:21  armin
+ * Added variable to store connect-message of Modem.
+ * Added Timer-define for RegS7 (Wait for Carrier).
+ *
+ * Revision 1.60  1998/10/25 14:50:29  fritz
+ * Backported from MIPS (Cobalt).
+ *
+ * Revision 1.59  1998/10/23 10:18:55  paul
+ * Implementation of "dialmode" (successor of "status")
+ * You also need current isdnctrl for this!
+ *
+ * Revision 1.58  1998/10/23 10:10:06  fritz
+ * Test-Checkin
+ *
+ * Revision 1.57  1998/08/31 21:10:01  he
+ * new ioctl IIOCNETGPN for /dev/isdninfo (get network interface'
+ *     peer phone number)
+ *
+ * Revision 1.56  1998/07/26 18:46:52  armin
+ * Added silence detection in voice receive mode.
+ *
+ * Revision 1.55  1998/06/26 15:13:17  fritz
+ * Added handling of STAT_ICALL with incomplete CPN.
+ * Added AT&L for ttyI emulator.
+ * Added more locking stuff in tty_write.
+ *
+ * Revision 1.54  1998/06/18 23:32:01  fritz
+ * Replaced cli()/restore_flags() in isdn_tty_write() by locking.
+ * Removed direct-senddown feature in isdn_tty_write because it will
+ * never succeed with locking and is useless anyway.
+ *
+ * Revision 1.53  1998/06/17 19:51:51  he
+ * merged with 2.1.10[34] (cosmetics and udelay() -> mdelay())
+ * brute force fix to avoid Ugh's in isdn_tty_write()
+ * cleaned up some dead code
+ *
+ * Revision 1.52  1998/06/12 11:42:18  detabc
+ * cleanup abc
+ *
+ * Revision 1.51  1998/06/02 12:10:30  detabc
+ * wegen einer einstweiliger verfuegung gegen DW ist zur zeit
+ * die abc-extension bis zur klaerung der rechtslage nicht verfuegbar
+ *
+ * Revision 1.50  1998/05/05 23:11:51  detabc
+ * add Item to stop icmp-unreach (max. 6 times of dialwait delay)
+ *
+ * Revision 1.49  1998/05/03 17:45:00  detabc
+ * Add Item to send icmp-host-unreach to all packets
+ *
+ * Revision 1.48  1998/04/26 19:58:14  detabc
+ * include the new abc-extension-items from 2.0.xx kernels
+ * remove some unused code
+ *
+ * Revision 1.47  1998/04/21 18:00:25  detabc
+ * Add items for secure-callback (abc-extension only)
+ *
+ * Revision 1.46  1998/04/14 16:28:59  he
+ * Fixed user space access with interrupts off and remaining
+ * copy_{to,from}_user() -> -EFAULT return codes
+ *
+ * Revision 1.45  1998/03/24 16:33:12  hipp
+ * More CCP changes. BSD compression now "works" on a local loopback link.
+ * Moved some isdn_ppp stuff from isdn.h to isdn_ppp.h
+ *
+ * Revision 1.44  1998/03/22 18:50:56  hipp
+ * Added BSD Compression for syncPPP .. UNTESTED at the moment
+ *
+ * Revision 1.43  1998/03/09 17:46:44  he
+ * merged in 2.1.89 changes
+ *
+ * Revision 1.42  1998/03/08 13:53:46  detabc
+ * add ABC-variables in structur isdn_devt
+ *
+ * Revision 1.41  1998/03/08 13:14:37  detabc
+ * abc-extension support for kernels > 2.1.x
+ * first try (sorry experimental)
+ *
+ * Revision 1.40  1998/03/08 01:08:29  fritz
+ * Increased NET_DV because of TIMRU
+ *
+ * Revision 1.39  1998/03/07 22:42:49  fritz
+ * Starting generic module support (Nothing usable yet).
+ *
+ * Revision 1.38  1998/03/07 18:21:29  cal
+ * Dynamic Timeout-Rule-Handling vs. 971110 included
+ *
  * Revision 1.37  1998/02/22 19:45:24  fritz
  * Some changes regarding V.110
  *
@@ -163,6 +254,7 @@
 #ifndef isdn_h
 #define isdn_h
 
+#include <linux/config.h>
 #include <linux/ioctl.h>
 
 #define ISDN_TTY_MAJOR    43
@@ -174,14 +266,20 @@
  * the correspondent code in isdn.c
  */
 
+#ifdef CONFIG_COBALT_MICRO_SERVER
+/* Save memory */
+#define ISDN_MAX_DRIVERS    2
+#define ISDN_MAX_CHANNELS   8
+#else
 #define ISDN_MAX_DRIVERS    32
 #define ISDN_MAX_CHANNELS   64
+#endif
 #define ISDN_MINOR_B        0
 #define ISDN_MINOR_BMAX     (ISDN_MAX_CHANNELS-1)
-#define ISDN_MINOR_CTRL     ISDN_MAX_CHANNELS
-#define ISDN_MINOR_CTRLMAX  (2*ISDN_MAX_CHANNELS-1)
-#define ISDN_MINOR_PPP      (2*ISDN_MAX_CHANNELS)
-#define ISDN_MINOR_PPPMAX   (3*ISDN_MAX_CHANNELS-1)
+#define ISDN_MINOR_CTRL     64
+#define ISDN_MINOR_CTRLMAX  (64 + (ISDN_MAX_CHANNELS-1))
+#define ISDN_MINOR_PPP      128
+#define ISDN_MINOR_PPPMAX   (128 + (ISDN_MAX_CHANNELS-1))
 #define ISDN_MINOR_STATUS   255
 
 /* New ioctl-codes */
@@ -192,8 +290,8 @@
 #define IIOCNETANM  _IO('I',5)
 #define IIOCNETDNM  _IO('I',6)
 #define IIOCNETGNM  _IO('I',7)
-#define IIOCGETSET  _IO('I',8)
-#define IIOCSETSET  _IO('I',9)
+#define IIOCGETSET  _IO('I',8) /* no longer supported */
+#define IIOCSETSET  _IO('I',9) /* no longer supported */
 #define IIOCSETVER  _IO('I',10)
 #define IIOCNETHUP  _IO('I',11)
 #define IIOCSETGST  _IO('I',12)
@@ -211,6 +309,8 @@
 #define IIOCNETALN  _IO('I',32)
 #define IIOCNETDLN  _IO('I',33)
 
+#define IIOCNETGPN  _IO('I',34)
+
 #define IIOCDBGVAR  _IO('I',127)
 
 #define IIOCDRVCTL  _IO('I',128)
@@ -238,6 +338,8 @@
 
 #define ISDN_MODEM_ANZREG    23        /* Number of Modem-Registers        */
 #define ISDN_MSNLEN          20
+#define ISDN_LMSNLEN         255 /* Length of tty's Listen-MSN string */
+#define ISDN_CMSGLEN	     50	 /* Length of CONNECT-Message to add for Modem */
 
 typedef struct {
   char drvid[25];
@@ -256,8 +358,9 @@
   int  outgoing;
 } isdn_net_ioctl_phone;
 
-#define NET_DV 0x04 /* Data version for net_cfg     */
-#define TTY_DV 0x04 /* Data version for iprofd etc. */
+#define NET_DV 0x05 /* Data version for net_cfg       */
+#define TTY_DV 0x05 /* Data version for iprofd etc.   */
+#define INF_DV 0x01 /* Data version for /dev/isdninfo */
 
 typedef struct {
   char name[10];     /* Name of interface                     */
@@ -282,8 +385,17 @@
   int  pppbind;      /* ippp device for bindings              */
   int  chargeint;    /* Use fixed charge interval length      */
   int  triggercps;   /* BogoCPS needed for triggering slave   */
+  int  dialtimeout;  /* Dial-Timeout                          */
+  int  dialwait;     /* Time to wait after failed dial        */
+  int  dialmode;     /* Flag: off / on / auto                 */
 } isdn_net_ioctl_cfg;
 
+#define ISDN_NET_DIALMODE_MASK 0xC0  /* bits for status                   */
+#define  ISDN_NET_DM_OFF	0x00    /* this interface is stopped      */
+#define  ISDN_NET_DM_MANUAL	0x40    /* this interface is on (manual)  */
+#define  ISDN_NET_DM_AUTO	0x80    /* this interface is autodial     */
+#define ISDN_NET_DIALMODE(x) ((&(x))->flags & ISDN_NET_DIALMODE_MASK)
+
 #ifdef __KERNEL__
 
 #ifndef STANDALONE
@@ -331,6 +443,7 @@
 
 #include <linux/isdnif.h>
 
+
 #define ISDN_DRVIOCTL_MASK       0x7f  /* Mask for Device-ioctl */
 
 /* Until now unused */
@@ -372,14 +485,17 @@
 #define ISDN_TIMER_NETHANGUP  32
 #define ISDN_TIMER_IPPP       64 
 #define ISDN_TIMER_KEEPALIVE 128 /* Cisco-Keepalive */
+#define ISDN_TIMER_CARRIER   256 /* Wait for Carrier */
 #define ISDN_TIMER_FAST      (ISDN_TIMER_MODEMREAD | ISDN_TIMER_MODEMPLUS | \
                               ISDN_TIMER_MODEMXMIT)
 #define ISDN_TIMER_SLOW      (ISDN_TIMER_MODEMRING | ISDN_TIMER_NETHANGUP | \
-                              ISDN_TIMER_NETDIAL | ISDN_TIMER_KEEPALIVE)
+                              ISDN_TIMER_NETDIAL | ISDN_TIMER_KEEPALIVE | \
+                              ISDN_TIMER_CARRIER)
 
 /* Timeout-Values for isdn_net_dial() */
 #define ISDN_TIMER_DTIMEOUT10 (10*HZ/(ISDN_TIMER_02SEC*(ISDN_TIMER_RES+1)))
 #define ISDN_TIMER_DTIMEOUT15 (15*HZ/(ISDN_TIMER_02SEC*(ISDN_TIMER_RES+1)))
+#define ISDN_TIMER_DTIMEOUT60 (60*HZ/(ISDN_TIMER_02SEC*(ISDN_TIMER_RES+1)))
 
 /* GLOBAL_FLAGS */
 #define ISDN_GLOBAL_STOPPED 1
@@ -398,6 +514,7 @@
 #define ISDN_NET_TMP        0x10       /* tmp interface until getting an IP */
 #define ISDN_NET_DYNAMIC    0x20       /* this link is dynamically allocated */
 #endif
+
 #define ISDN_NET_MAGIC      0x49344C02 /* for paranoia-checking             */
 
 /* Phone-list-element */
@@ -488,6 +605,11 @@
 				    struct device *,
                                     unsigned char *);
   int  pppbind;                        /* ippp device for bindings         */
+  int					dialtimeout;	/* How long shall we try on dialing? (jiffies) */
+  int					dialwait;		/* How long shall we wait after failed attempt? (jiffies) */
+  ulong					dialstarted;	/* jiffies of first dialing-attempt */
+  ulong					dialwait_timer;	/* jiffies of earliest next dialing-attempt */
+  int					huptimeout;		/* How long will the connection be up? (seconds) */
 #ifdef CONFIG_ISDN_X25
   struct concap_device_ops *dops;      /* callbacks used by encapsulator   */
 #endif
@@ -496,18 +618,6 @@
   ulong cisco_yourseq;                 /* Remote keepalive seq. for Cisco  */
 } 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;
@@ -567,6 +677,10 @@
 	u_char       mdmreg[ISDN_MODEM_ANZREG];  /* Modem-Registers                    */
 	char         pmsn[ISDN_MSNLEN];          /* EAZ/MSNs Profile 0                 */
 	char         msn[ISDN_MSNLEN];           /* EAZ/MSN                            */
+	char         plmsn[ISDN_LMSNLEN];        /* Listening MSNs Profile 0           */
+	char         lmsn[ISDN_LMSNLEN];         /* Listening MSNs                     */
+	char         cpn[ISDN_MSNLEN];           /* CalledPartyNumber on incoming call */
+	char         connmsg[ISDN_CMSGLEN];	 /* CONNECT-Msg from HL-Driver	       */
 #ifdef CONFIG_ISDN_AUDIO
 	u_char       vpar[10];                   /* Voice-parameters                   */
 	int          lastDLE;                    /* Flag for voice-coding: DLE seen    */
@@ -574,6 +688,7 @@
 	int          mdmcmdl;                    /* Length of Modem-Commandbuffer      */
 	int          pluscount;                  /* Counter for +++ sequence           */
 	int          lastplus;                   /* Timestamp of last +                */
+	int	     carrierwait;                /* Seconds of carrier waiting         */
 	char         mdmcmd[255];                /* Modem-Commandbuffer                */
 	unsigned int charge;                     /* Charge units of current connection */
 } atemu;
@@ -593,7 +708,7 @@
   long			pgrp;		 /* pgrp of opening process        */
   int                   online;          /* 1 = B-Channel is up, drop data */
 					 /* 2 = B-Channel is up, deliver d.*/
-  int                   dialing;         /* Dial in progress               */
+  int                   dialing;         /* Dial in progress or ATA        */
   int                   rcvsched;        /* Receive needs schedule         */
   int                   isdn_driver;	 /* Index to isdn-driver           */
   int                   isdn_channel;    /* Index to isdn-channel          */
@@ -612,6 +727,7 @@
   int                   xmit_count;      /* # of chars in xmit_buf         */
   unsigned char         *xmit_buf;       /* transmit buffer                */
   struct sk_buff_head   xmit_queue;      /* transmit queue                 */
+  atomic_t              xmit_lock;       /* Semaphore for isdn_tty_write   */
 #ifdef CONFIG_ISDN_AUDIO
   int                   vonline;         /* Voice-channel status           */
 					 /* Bit 0 = recording              */
@@ -621,12 +737,19 @@
   void                  *adpcms;         /* state for adpcm decompression  */
   void                  *adpcmr;         /* state for adpcm compression    */
   void                  *dtmf_state;     /* state for dtmf decoder         */
+  void                  *silence_state;  /* state for silence detection    */
 #endif
   struct tty_struct 	*tty;            /* Pointer to corresponding tty   */
   atemu                 emu;             /* AT-emulator data               */
   struct termios	normal_termios;  /* For saving termios structs     */
   struct termios	callout_termios;
-  wait_queue_head_t 	open_wait, close_wait;
+#if LINUX_VERSION_CODE < 131841
+  struct wait_queue	*open_wait;
+  struct wait_queue	*close_wait;
+#else
+  wait_queue_head_t	open_wait, close_wait;
+#endif
+  struct semaphore      write_sem;
 } modem_info;
 
 #define ISDN_MODEM_WINSIZE 8
@@ -644,72 +767,6 @@
 
 /*======================= End of ISDN-tty stuff ============================*/
 
-/*======================= Start of sync-ppp stuff ==========================*/
-
-
-#define NUM_RCV_BUFFS     64
-#define PPP_HARD_HDR_LEN 4
-
-#ifdef CONFIG_ISDN_PPP
-
-struct sqqueue {
-  struct sqqueue *next;
-  long sqno_start;
-  long sqno_end;
-  struct sk_buff *skb;
-  long timer;
-};
-  
-struct mpqueue {
-  struct mpqueue *next;
-  struct mpqueue *last;
-  long 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 */
-  wait_queue_head_t wq;
-  wait_queue_head_t wql;
-  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; 
-  int minor;
-  long last_link_seqno;
-  long mp_seqno;
-  long range;
-#ifdef CONFIG_ISDN_PPP_VJ
-  unsigned char *cbuf;
-  struct slcompress *slcomp;
-#endif
-  unsigned long debug;
-  struct isdn_ppp_compressor *compressor,*link_compressor;
-  void *decomp_stat,*comp_stat,*link_decomp_stat,*link_comp_stat;
-};
-
-#endif
-
-/*======================== End of sync-ppp stuff ===========================*/
-
 /*======================== Start of V.110 stuff ============================*/
 #define V110_BUFSIZE 1024
 
@@ -742,69 +799,103 @@
 	char *private;
 } infostruct;
 
+typedef struct isdn_module {
+	struct isdn_module *prev;
+	struct isdn_module *next;
+	char *name;
+	int (*get_free_channel)(int, int, int, int, int);
+	int (*free_channel)(int, int, int);
+	int (*status_callback)(isdn_ctrl *);
+	int (*command)(isdn_ctrl *);
+	int (*receive_callback)(int, int, struct sk_buff *);
+	int (*writebuf_skb)(int, int, int, struct sk_buff *);
+	int (*net_start_xmit)(struct sk_buff *, struct device *);
+	int (*net_receive)(struct device *, struct sk_buff *);
+	int (*net_open)(struct device *);
+	int (*net_close)(struct device *);
+	int priority;
+} isdn_module;
+
+#define DRV_FLAG_RUNNING 1
+#define DRV_FLAG_REJBUS  2
+#define DRV_FLAG_LOADED  4
+
 /* Description of hardware-level-driver */
 typedef struct {
-  ulong               flags;            /* Flags                            */
-  int                 channels;         /* Number of channels               */
-  int                 reject_bus;       /* Flag: Reject rejected call on bus*/
-  wait_queue_head_t   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  */
+	ulong               online;           /* Channel-Online flags             */
+	ulong               flags;            /* Misc driver Flags                */
+	int                 locks;            /* Number of locks for this driver  */
+	int                 channels;         /* Number of channels               */
+#if LINUX_VERSION_CODE < 131841
+	struct wait_queue  *st_waitq;         /* Wait-Queue for status-read's     */
+#else
+	wait_queue_head_t   st_waitq;         /* Wait-Queue for status-read's     */
+#endif
+	int                 maxbufsize;       /* Maximum Buffersize supported     */
+	unsigned long       pktcount;         /* Until now: unused                */
+	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  */
 #ifdef CONFIG_ISDN_AUDIO
-  unsigned long      DLEflag;           /* Flags: Insert DLE at next read   */
+	unsigned long      DLEflag;           /* Flags: Insert DLE at next read   */
 #endif
-  struct sk_buff_head *rpqueue;         /* Pointers to start of Rcv-Queue   */
-  wait_queue_head_t  *rcv_waitq;  /* array of Wait-Queues for B-Channel-Reads  */
-  wait_queue_head_t  *snd_waitq;  /* array of Wait-Queue for B-Channel-Sends  */
-  char               msn2eaz[10][ISDN_MSNLEN];  /* Mapping-Table MSN->EAZ   */
+	struct sk_buff_head *rpqueue;         /* Pointers to start of Rcv-Queue   */
+#if LINUX_VERSION_CODE < 131841
+	struct wait_queue  **rcv_waitq;       /* Wait-Queues for B-Channel-Reads  */
+	struct wait_queue  **snd_waitq;       /* Wait-Queue for B-Channel-Send's  */
+#else
+	wait_queue_head_t  *rcv_waitq;       /* Wait-Queues for B-Channel-Reads  */
+	wait_queue_head_t  *snd_waitq;       /* Wait-Queue for B-Channel-Send's  */
+#endif
+	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.    */
-  wait_queue_head_t 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][ISDN_MSNLEN];
-                                               /* Remote number of active ch.*/
-  int               m_idx[ISDN_MAX_CHANNELS];  /* Index for mdm....          */
-  driver            *drv[ISDN_MAX_DRIVERS];    /* Array of drivers           */
-  isdn_net_dev      *netdev;		       /* Linked list of net-if's    */
-  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   */
-  ulong             ibytes[ISDN_MAX_CHANNELS]; /* Statistics incoming bytes  */
-  ulong             obytes[ISDN_MAX_CHANNELS]; /* Statistics outgoing bytes  */
-  int               v110emu[ISDN_MAX_CHANNELS];/* V.110 emulator-mode 0=none */
-  atomic_t          v110use[ISDN_MAX_CHANNELS];/* Usage-Semaphore for stream */
-  isdn_v110_stream  *v110[ISDN_MAX_CHANNELS];  /* V.110 private data         */
-  struct semaphore  sem;                       /* serialize list access*/
+	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.    */
+#if LINUX_VERSION_CODE < 131841
+	struct wait_queue *info_waitq;               /* Wait-Queue for isdninfo    */
+#else
+	wait_queue_head_t info_waitq;               /* Wait-Queue for isdninfo    */
+#endif
+	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][ISDN_MSNLEN];
+	/* Remote number of active ch.*/
+	int               m_idx[ISDN_MAX_CHANNELS];  /* Index for mdm....          */
+	driver            *drv[ISDN_MAX_DRIVERS];    /* Array of drivers           */
+	isdn_net_dev      *netdev;		       /* Linked list of net-if's    */
+	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   */
+	ulong             ibytes[ISDN_MAX_CHANNELS]; /* Statistics incoming bytes  */
+	ulong             obytes[ISDN_MAX_CHANNELS]; /* Statistics outgoing bytes  */
+	int               v110emu[ISDN_MAX_CHANNELS];/* V.110 emulator-mode 0=none */
+	atomic_t          v110use[ISDN_MAX_CHANNELS];/* Usage-Semaphore for stream */
+	isdn_v110_stream  *v110[ISDN_MAX_CHANNELS];  /* V.110 private data         */
+	struct semaphore  sem;                       /* serialize list access*/
+	isdn_module       *modules;
 } 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 v2.3.3/linux/include/linux/isdn_budget.h linux/include/linux/isdn_budget.h
--- v2.3.3/linux/include/linux/isdn_budget.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/isdn_budget.h	Sun May 23 10:03:42 1999
@@ -0,0 +1,62 @@
+/* isdn_budget.h
+ *
+ * Linux ISDN subsystem, budget-accounting for network interfaces.
+ *
+ * Copyright 1997       by Christian Lademann <cal@zls.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. 
+ *
+ */
+
+/*
+30.06.97:cal:angelegt
+04.11.97:cal:budget.period: int --> time_t
+*/
+
+#ifndef __isdn_budget_h__
+#define __isdn_budget_h__
+
+#include	<linux/types.h>
+
+#define	ISDN_BUDGET_DIAL		0
+#define	ISDN_BUDGET_CHARGE		1
+#define	ISDN_BUDGET_ONLINE		2
+#define	ISDN_BUDGET_NUM_BUDGET		3
+
+#define	ISDN_BUDGET_INIT		0
+#define	ISDN_BUDGET_CHECK_DIAL		1
+#define	ISDN_BUDGET_CHECK_CHARGE	2
+#define	ISDN_BUDGET_CHECK_ONLINE	3
+#define	ISDN_BUDGET_START_ONLINE	10
+
+#define	ISDN_BUDGET_SET_BUDGET	0
+#define	ISDN_BUDGET_GET_BUDGET	1
+
+typedef struct {
+	char	name [9];		/* Interface */
+	int	command,		/* subcommand */
+		budget,			/* budget-nr. */
+		amount,			/* set/get budget-amount */
+		used;			/* set/get used amount */
+	time_t	period,			/* set/get length of period */
+		period_started;		/* set/get startpoint of period */
+}	isdn_ioctl_budget;
+
+#ifdef __KERNEL__
+extern int	isdn_net_budget(int, struct device *);
+extern int	isdn_budget_ioctl(isdn_ioctl_budget *);
+#endif /* __KERNEL__ */
+
+#endif /* __isdn_budget_h__ */
diff -u --recursive --new-file v2.3.3/linux/include/linux/isdn_lzscomp.h linux/include/linux/isdn_lzscomp.h
--- v2.3.3/linux/include/linux/isdn_lzscomp.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/isdn_lzscomp.h	Sun May 23 10:03:42 1999
@@ -0,0 +1,26 @@
+/*
+ * $Id: isdn_lzscomp.h,v 1.1 1998/07/08 16:52:33 hipp Exp $
+ *
+ * Header for isdn_lzscomp.c
+ * Concentrated here to not mess up half a dozen kernel headers with code
+ * snippets
+ *
+ */
+
+#define CI_LZS_COMPRESS		17
+#define CILEN_LZS_COMPRESS	5
+
+#define LZS_CMODE_NONE		0
+#define LZS_CMODE_LCB		1
+#define LZS_CMODE_CRC		2
+#define LZS_CMODE_SEQNO		3	/* MUST be implemented (default) */
+#define LZS_CMODE_EXT		4	/* Seems to be what Win0.95 uses */
+
+#define LZS_COMP_MAX_HISTS	1	/* Don't waste peers ressources */
+#define LZS_COMP_DEF_HISTS	1	/* Most likely to negotiate */
+#define LZS_DECOMP_MAX_HISTS	32	/* More is really nonsense */
+#define LZS_DECOMP_DEF_HISTS	8	/* If we get it, this may be optimal */
+
+#define LZS_HIST_BYTE1(word)   	(word>>8)	/* Just for better reading */
+#define LZS_HIST_BYTE2(word)	(word&0xff)	/* of this big endian stuff */
+#define LZS_HIST_WORD(b1,b2)	((b1<<8)|b2)	/* (network byte order rulez) */
diff -u --recursive --new-file v2.3.3/linux/include/linux/isdn_ppp.h linux/include/linux/isdn_ppp.h
--- v2.3.3/linux/include/linux/isdn_ppp.h	Wed Apr  1 16:20:56 1998
+++ linux/include/linux/isdn_ppp.h	Wed May 26 16:55:40 1999
@@ -1,19 +1,22 @@
+/* -*- mode: c; c-basic-offset: 2 -*- */
+
 #ifndef _LINUX_ISDN_PPP_H
 #define _LINUX_ISDN_PPP_H
 
-extern int isdn_ppp_dial_slave(char *);
-extern int isdn_ppp_hangup_slave(char *);
+#include <linux/config.h>
 
 #define CALLTYPE_INCOMING 0x1
 #define CALLTYPE_OUTGOING 0x2
 #define CALLTYPE_CALLBACK 0x4
 
+#define IPPP_VERSION    "2.2.0"
+
 struct pppcallinfo
 {
-	int calltype;
-	unsigned char local_num[64];
-	unsigned char remote_num[64];
-	int charge_units;
+  int calltype;
+  unsigned char local_num[64];
+  unsigned char remote_num[64];
+  int charge_units;
 };
 
 #define PPPIOCGCALLINFO _IOWR('t',128,struct pppcallinfo)
@@ -22,48 +25,216 @@
 #define PPPIOCSMPFLAGS _IOW('t',131,int)
 #define PPPIOCSMPMTU   _IOW('t',132,int)
 #define PPPIOCSMPMRU   _IOW('t',133,int)
-#define PPPIOCGCOMPRESSORS _IOR('t',134,unsigned long)
+#define PPPIOCGCOMPRESSORS _IOR('t',134,unsigned long [8])
 #define PPPIOCSCOMPRESSOR _IOW('t',135,int)
+#define PPPIOCGIFNAME      _IOR('t',136, char [IFNAMSIZ] )
 
 #define PPP_MP          0x003d
 #define PPP_LINK_COMP   0x00fb
+#define PPP_LINK_CCP    0x80fb
 
 #define SC_MP_PROT       0x00000200
 #define SC_REJ_MP_PROT   0x00000400
 #define SC_OUT_SHORT_SEQ 0x00000800
 #define SC_IN_SHORT_SEQ  0x00004000
 
+#define SC_DECOMP_ON		0x01
+#define SC_COMP_ON		0x02
+#define SC_DECOMP_DISCARD	0x04
+#define SC_COMP_DISCARD		0x08
+#define SC_LINK_DECOMP_ON	0x10
+#define SC_LINK_COMP_ON		0x20
+#define SC_LINK_DECOMP_DISCARD	0x40
+#define SC_LINK_COMP_DISCARD	0x80
+
+#define DECOMP_ERR_NOMEM	(-10)
+
 #define MP_END_FRAG    0x40
 #define MP_BEGIN_FRAG  0x80
 
+#define ISDN_PPP_COMP_MAX_OPTIONS 16
+
+#define IPPP_COMP_FLAG_XMIT 0x1
+#define IPPP_COMP_FLAG_LINK 0x2
+
+struct isdn_ppp_comp_data {
+  int num;
+  unsigned char options[ISDN_PPP_COMP_MAX_OPTIONS];
+  int optlen;
+  int flags;
+};
+
 #ifdef __KERNEL__
+
+/*
+ * We need a way for the decompressor to influence the generation of CCP
+ * Reset-Requests in a variety of ways. The decompressor is already returning
+ * a lot of information (generated skb length, error conditions) so we use
+ * another parameter. This parameter is a pointer to a structure which is
+ * to be marked valid by the decompressor and only in this case is ever used.
+ * Furthermore, the only case where this data is used is when the decom-
+ * pressor returns DECOMP_ERROR.
+ *
+ * We use this same struct for the reset entry of the compressor to commu-
+ * nicate to its caller how to deal with sending of a Reset Ack. In this
+ * case, expra is not used, but other options still apply (supressing
+ * sending with rsend, appending arbitrary data, etc).
+ */
+
+#define IPPP_RESET_MAXDATABYTES	32
+
+struct isdn_ppp_resetparams {
+  unsigned char valid:1;	/* rw Is this structure filled at all ? */
+  unsigned char rsend:1;	/* rw Should we send one at all ? */
+  unsigned char idval:1;	/* rw Is the id field valid ? */
+  unsigned char dtval:1;	/* rw Is the data field valid ? */
+  unsigned char expra:1;	/* rw Is an Ack expected for this Req ? */
+  unsigned char id;		/* wo Send CCP ResetReq with this id */
+  unsigned short maxdlen;	/* ro Max bytes to be stored in data field */
+  unsigned short dlen;		/* rw Bytes stored in data field */
+  unsigned char *data;		/* wo Data for ResetReq info field */
+};
+
 /*
  * this is an 'old friend' from ppp-comp.h under a new name 
  * check the original include for more information
  */
 struct isdn_ppp_compressor {
-	struct isdn_ppp_compressor *next,*prev;
-	int num; /* CCP compression protocol number */
-	void *(*comp_alloc) (unsigned char *options, int opt_len);
-	void (*comp_free) (void *state);
-	int  (*comp_init) (void *state, unsigned char *options, int opt_len,
-		 int unit, int opthdr, int debug);
-	void (*comp_reset) (void *state);
-	int  (*compress) (void *state,struct sk_buff *in, struct sk_buff *skb_out,
-		 int proto);
-	void (*comp_stat) (void *state, struct compstat *stats);
-	void *(*decomp_alloc) (unsigned char *options, int opt_len);
-	void (*decomp_free) (void *state);
-	int  (*decomp_init) (void *state, unsigned char *options,
-			int opt_len, int unit, int opthdr, int mru, int debug);
-	void (*decomp_reset) (void *state);
-	int  (*decompress) (void *state, unsigned char *ibuf, int isize, unsigned char *obuf, int osize);
-	void (*incomp) (void *state, unsigned char *ibuf, int icnt);
-	void (*decomp_stat) (void *state, struct compstat *stats);
+  struct isdn_ppp_compressor *next, *prev;
+  int num; /* CCP compression protocol number */
+  
+  void *(*alloc) (struct isdn_ppp_comp_data *);
+  void (*free) (void *state);
+  int  (*init) (void *state, struct isdn_ppp_comp_data *,
+		int unit,int debug);
+  
+  /* The reset entry needs to get more exact information about the
+     ResetReq or ResetAck it was called with. The parameters are
+     obvious. If reset is called without a Req or Ack frame which
+     could be handed into it, code MUST be set to 0. Using rsparm,
+     the reset entry can control if and how a ResetAck is returned. */
+  
+  void (*reset) (void *state, unsigned char code, unsigned char id,
+		 unsigned char *data, unsigned len,
+		 struct isdn_ppp_resetparams *rsparm);
+  
+  int  (*compress) (void *state, struct sk_buff *in,
+		    struct sk_buff *skb_out, int proto);
+  
+	int  (*decompress) (void *state,struct sk_buff *in,
+			    struct sk_buff *skb_out,
+			    struct isdn_ppp_resetparams *rsparm);
+  
+  void (*incomp) (void *state, struct sk_buff *in,int proto);
+  void (*stat) (void *state, struct compstat *stats);
 };
 
 extern int isdn_ppp_register_compressor(struct isdn_ppp_compressor *);
 extern int isdn_ppp_unregister_compressor(struct isdn_ppp_compressor *);
+extern int isdn_ppp_dial_slave(char *);
+extern int isdn_ppp_hangup_slave(char *);
+
+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 ?                    */
+};
+
+#define NUM_RCV_BUFFS     64
+
+struct sqqueue {
+  struct sqqueue *next;
+  long sqno_start;
+  long sqno_end;
+  struct sk_buff *skb;
+  long timer;
+};
+
+struct mpqueue {
+  struct mpqueue *next;
+  struct mpqueue *last;
+  long 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;
+};
+
+/* The data structure for one CCP reset transaction */
+enum ippp_ccp_reset_states {
+  CCPResetIdle,
+  CCPResetSentReq,
+  CCPResetRcvdReq,
+  CCPResetSentAck,
+  CCPResetRcvdAck
+};
+
+struct ippp_ccp_reset_state {
+  enum ippp_ccp_reset_states state;	/* State of this transaction */
+  struct ippp_struct *is;		/* Backlink to device stuff */
+  unsigned char id;			/* Backlink id index */
+  unsigned char ta:1;			/* The timer is active (flag) */
+  unsigned char expra:1;		/* We expect a ResetAck at all */
+  int dlen;				/* Databytes stored in data */
+  struct timer_list timer;		/* For timeouts/retries */
+  /* This is a hack but seems sufficient for the moment. We do not want
+     to have this be yet another allocation for some bytes, it is more
+     memory management overhead than the whole mess is worth. */
+  unsigned char data[IPPP_RESET_MAXDATABYTES];
+};
+
+/* The data structure keeping track of the currently outstanding CCP Reset
+   transactions. */
+struct ippp_ccp_reset {
+  struct ippp_ccp_reset_state *rs[256];	/* One per possible id */
+  unsigned char lastid;			/* Last id allocated by the engine */
+};
+
+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 */
+#if LINUX_VERSION_CODE < 131841
+  struct wait_queue *wq;
+#else
+  wait_queue_head_t wq;
+#endif
+  struct task_struct *tk;
+  unsigned int mpppcfg;
+  unsigned int pppcfg;
+  unsigned int mru;
+  unsigned int mpmru;
+  unsigned int mpmtu;
+  unsigned int maxcid;
+  struct isdn_net_local_s *lp;
+  int unit;
+  int minor;
+  long last_link_seqno;
+  long mp_seqno;
+  long range;
+#ifdef CONFIG_ISDN_PPP_VJ
+  unsigned char *cbuf;
+  struct slcompress *slcomp;
+#endif
+  unsigned long debug;
+  struct isdn_ppp_compressor *compressor,*decompressor;
+  struct isdn_ppp_compressor *link_compressor,*link_decompressor;
+  void *decomp_stat,*comp_stat,*link_decomp_stat,*link_comp_stat;
+  struct ippp_ccp_reset *reset;	/* Allocated on demand, may never be needed */
+  unsigned long compflags;
+};
 
 #endif /* __KERNEL__ */
 
diff -u --recursive --new-file v2.3.3/linux/include/linux/isdn_timru.h linux/include/linux/isdn_timru.h
--- v2.3.3/linux/include/linux/isdn_timru.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/isdn_timru.h	Sun May 23 10:03:42 1999
@@ -0,0 +1,119 @@
+/* isdn_timru.h
+ *
+ * Linux ISDN subsystem, timeout-rules for network interfaces.
+ *
+ * Copyright 1997       by Christian Lademann <cal@zls.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. 
+ *
+ */
+
+/*
+02.06.97:cal:ISDN_TIMRU_PACKET_NONE def., ISDN_TIMRU_PACKET_* inkr.
+*/
+
+#ifndef __isdn_timru_h__
+#define __isdn_timru_h__
+
+#define	ISDN_TIMRU_PACKET_NONE		0
+#define	ISDN_TIMRU_PACKET_SKB		1
+#define	ISDN_TIMRU_PACKET_PPP		2
+#define	ISDN_TIMRU_PACKET_PPP_NO_HEADER	3
+
+#define	ISDN_TIMRU_BRINGUP		0
+#define	ISDN_TIMRU_KEEPUP_IN		1
+#define	ISDN_TIMRU_KEEPUP_OUT		2
+#define	ISDN_TIMRU_BRINGDOWN		3
+#define	ISDN_TIMRU_NUM_CHECK		4
+
+#define	ISDN_TIMRU_PROTFAM_WILDCARD	0
+#define	ISDN_TIMRU_PROTFAM_IP		1
+#define	ISDN_TIMRU_PROTFAM_PPP		2
+#define	ISDN_TIMRU_PROTFAM_IPX		3
+#define	ISDN_TIMRU_NUM_PROTFAM		4
+
+#define	ISDN_TIMRU_IP_WILDCARD		0
+#define	ISDN_TIMRU_IP_ICMP		1
+#define	ISDN_TIMRU_IP_TCP		2
+#define	ISDN_TIMRU_IP_UDP		3
+
+#define	ISDN_TIMRU_PPP_WILDCARD		0
+#define	ISDN_TIMRU_PPP_IPCP		1
+#define	ISDN_TIMRU_PPP_IPXCP		2
+#define	ISDN_TIMRU_PPP_CCP		3
+#define	ISDN_TIMRU_PPP_LCP		4
+#define	ISDN_TIMRU_PPP_PAP		5
+#define	ISDN_TIMRU_PPP_LQR		6
+#define	ISDN_TIMRU_PPP_CHAP		7
+
+typedef struct {
+	struct in_addr	saddr,		/* Source Address */
+			smask,		/* Source Subnetmask */
+			daddr,		/* Dest. Address */
+			dmask;		/* Dest. Subnetmask */
+	ushort		protocol;	/* TCP, UDP, ... */
+	union {
+		struct {
+			__u16	s_from,	/* Source Port */
+				s_to,
+				d_from,
+				d_to;
+		}		port;
+		struct {
+			__u8	from,	/* ICMP-Type */
+				to;
+		}		type;
+	}		pt;
+}	isdn_timeout_rule_ip;
+
+
+typedef struct {
+	ushort		protocol;	/* IPCP, LCP, ... */
+}	isdn_timeout_rule_ppp;
+
+
+typedef struct isdn_timeout_rule_s {
+	struct isdn_timeout_rule_s	*next,		/* Pointer to next rule */
+					*prev;		/* Pointer to previous rule */
+	ushort				type,		/* BRINGUP, KEEPUP_*, ... */
+					neg;
+	int				timeout;	/* Timeout value */
+	ushort				protfam;	/* IP, IPX, PPP, ... */
+	union {
+		isdn_timeout_rule_ip	ip;	/* IP-Rule */
+		isdn_timeout_rule_ppp	ppp;	/* PPP-Rule */
+	}				rule;	/* Prot.-specific rule */
+}	isdn_timeout_rule;
+
+
+typedef struct {
+	char			name [9];	/* Interface */
+	int			where,		/* 0/1: add to start/end of list, -1: handle default */
+				type,
+				protfam,
+				index,
+				defval;
+	isdn_timeout_rule	rule;		/* Rule */
+}	isdn_ioctl_timeout_rule;
+
+#ifdef __KERNEL__
+extern int	isdn_net_recalc_timeout(int, int, struct device *, void *, ulong);
+extern int	isdn_timru_alloc_timeout_rules(struct device *);
+extern int	isdn_timru_ioctl_add_rule(isdn_ioctl_timeout_rule *);
+extern int	isdn_timru_ioctl_del_rule(isdn_ioctl_timeout_rule *);
+extern int	isdn_timru_ioctl_get_rule(isdn_ioctl_timeout_rule *);
+#endif /* __KERNEL__ */
+
+#endif /* __isdn_timru_h__ */
diff -u --recursive --new-file v2.3.3/linux/include/linux/isdnif.h linux/include/linux/isdnif.h
--- v2.3.3/linux/include/linux/isdnif.h	Wed Apr  1 16:20:56 1998
+++ linux/include/linux/isdnif.h	Sun May 23 10:03:42 1999
@@ -1,8 +1,4 @@
-/* X25 changes:
-   Added constants ISDN_PROTO_L2_X25DTE/DCE and corresponding ISDN_FEATURE_..
-   */
-
-/* $Id: isdnif.h,v 1.23 1998/02/20 17:36:52 fritz Exp $
+/* $Id: isdnif.h,v 1.25 1998/06/17 19:51:55 he Exp $
  *
  * Linux ISDN subsystem
  *
@@ -26,6 +22,15 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: isdnif.h,v $
+ * Revision 1.25  1998/06/17 19:51:55  he
+ * merged with 2.1.10[34] (cosmetics and udelay() -> mdelay())
+ * brute force fix to avoid Ugh's in isdn_tty_write()
+ * cleaned up some dead code
+ *
+ * Revision 1.24  1998/03/19 13:18:57  keil
+ * Start of a CAPI like interface for supplementary Service
+ * first service: SUSPEND
+ *
  * Revision 1.23  1998/02/20 17:36:52  fritz
  * Added L2-protocols for V.110, changed FEATURE-Flag-constants.
  *
@@ -139,6 +144,7 @@
 #define ISDN_PROTO_L2_V11096 7   /* V.110 bitrate adaption 9600 Baud  */
 #define ISDN_PROTO_L2_V11019 8   /* V.110 bitrate adaption 19200 Baud */
 #define ISDN_PROTO_L2_V11038 9   /* V.110 bitrate adaption 38400 Baud */
+#define ISDN_PROTO_L2_MODEM  10  /* Analog Modem on Board */
 #define ISDN_PROTO_L2_MAX    15  /* Max. 16 Protocols                 */
 
 /*
@@ -173,6 +179,7 @@
 #define ISDN_CMD_UNLOCK  15       /* Release usage-lock                    */
 #define ISDN_CMD_SUSPEND 16       /* Suspend connection                    */
 #define ISDN_CMD_RESUME  17       /* Resume connection                     */
+#define CAPI_PUT_MESSAGE 18       /* CAPI message send down or up          */
 
 /*
  * Status-Values delivered from lowlevel to linklevel via
@@ -216,6 +223,7 @@
 #define ISDN_FEATURE_L2_V11096  (0x0001 << ISDN_PROTO_L2_V11096)
 #define ISDN_FEATURE_L2_V11019  (0x0001 << ISDN_PROTO_L2_V11019)
 #define ISDN_FEATURE_L2_V11038  (0x0001 << ISDN_PROTO_L2_V11038)
+#define ISDN_FEATURE_L2_MODEM   (0x0001 << ISDN_PROTO_L2_MODEM)
 
 #define ISDN_FEATURE_L2_MASK    (0x0FFFF) /* Max. 16 protocols */
 #define ISDN_FEATURE_L2_SHIFT   (0)
@@ -236,14 +244,37 @@
 #define ISDN_FEATURE_P_SHIFT    (24)
 
 typedef struct setup_parm {
-    char phone[32];         /* Remote Phone-Number */
-    char eazmsn[32];        /* Local EAZ or MSN    */
+    unsigned char phone[32];	/* Remote Phone-Number */
+    unsigned char eazmsn[32];	/* Local EAZ or MSN    */
     unsigned char si1;      /* Service Indicator 1 */
     unsigned char si2;      /* Service Indicator 2 */
     unsigned char plan;     /* Numbering plan      */
     unsigned char screen;   /* Screening info      */
 } setup_parm;
 
+/* CAPI structs */
+
+/* this is compatible to the old union size */
+#define MAX_CAPI_PARA_LEN 50
+
+typedef struct {
+	/* Header */
+	__u16 Length;
+	__u16 ApplId;
+	__u8 Command;
+	__u8 Subcommand;
+	__u16 Messagenumber;
+
+	/* Parameter */
+	union {
+		__u32 Controller;
+		__u32 PLCI;
+		__u32 NCCI;
+	} adr;
+	__u8 para[MAX_CAPI_PARA_LEN];
+} capi_msg;
+
+
 /*
  * Structure for exchanging above infos
  *
@@ -255,8 +286,9 @@
   union {
 	ulong errcode;               /* Type of error with STAT_L1ERR         */
 	int   length;                /* Amount of bytes sent with STAT_BSENT  */
-	char  num[50];               /* Additional Data                       */
-	setup_parm setup;
+	u_char	num[50];	/* Additional Data			*/
+	setup_parm 	setup;	/* For SETUP msg			*/
+	capi_msg  	cmsg;	/* For CAPI like messages		*/
   } parm;
 } isdn_ctrl;
 
diff -u --recursive --new-file v2.3.3/linux/include/linux/lp.h linux/include/linux/lp.h
--- v2.3.3/linux/include/linux/lp.h	Fri May 14 18:55:29 1999
+++ linux/include/linux/lp.h	Sat May 22 12:42:27 1999
@@ -7,12 +7,6 @@
  * Interrupt support added 1993 Nigel Gamble
  */
 
-/* Magic numbers for defining port-device mappings */
-#define LP_PARPORT_UNSPEC -4
-#define LP_PARPORT_AUTO -3
-#define LP_PARPORT_OFF -2
-#define LP_PARPORT_NONE -1
-
 /*
  * Per POSIX guidelines, this module reserves the LP and lp prefixes
  * These are the lp_table[minor].flags flags...
@@ -87,6 +81,14 @@
 #define LP_TIMEOUT_INTERRUPT	(60 * HZ)
 #define LP_TIMEOUT_POLLED	(10 * HZ)
 
+#ifdef __KERNEL__
+
+/* Magic numbers for defining port-device mappings */
+#define LP_PARPORT_UNSPEC -4
+#define LP_PARPORT_AUTO -3
+#define LP_PARPORT_OFF -2
+#define LP_PARPORT_NONE -1
+
 #define LP_F(minor)	lp_table[(minor)].flags		/* flags for busy, etc. */
 #define LP_CHAR(minor)	lp_table[(minor)].chars		/* busy timeout */
 #define LP_TIME(minor)	lp_table[(minor)].time		/* wait time */
@@ -180,5 +182,7 @@
  */
 
 extern int lp_init(void);
+
+#endif
 
 #endif
diff -u --recursive --new-file v2.3.3/linux/include/linux/major.h linux/include/linux/major.h
--- v2.3.3/linux/include/linux/major.h	Sat May 15 23:46:05 1999
+++ linux/include/linux/major.h	Mon May 31 22:08:10 1999
@@ -98,6 +98,8 @@
 #define IDE6_MAJOR	88
 #define IDE7_MAJOR	89
 
+#define AURORA_MAJOR 79
+
 #define UNIX98_PTY_MASTER_MAJOR	128
 #define UNIX98_PTY_MAJOR_COUNT	8
 #define UNIX98_PTY_SLAVE_MAJOR	(UNIX98_PTY_MASTER_MAJOR+UNIX98_PTY_MAJOR_COUNT)
diff -u --recursive --new-file v2.3.3/linux/include/linux/netdevice.h linux/include/linux/netdevice.h
--- v2.3.3/linux/include/linux/netdevice.h	Mon May 17 09:55:23 1999
+++ linux/include/linux/netdevice.h	Wed May 26 18:14:37 1999
@@ -266,6 +266,9 @@
 	struct Qdisc		*qdisc_list;
 	unsigned long		tx_queue_len;	/* Max frames per queue allowed */
 
+	/* hard_start_xmit synchronizer */
+	spinlock_t		xmit_lock;
+
 	/* Pointers to interface service routines.	*/
 	int			(*open)(struct device *dev);
 	int			(*stop)(struct device *dev);
@@ -331,7 +334,7 @@
 
 extern struct device		loopback_dev;		/* The loopback */
 extern struct device		*dev_base;		/* All devices */
-extern struct packet_type 	*ptype_base[16];	/* Hashed types */
+extern rwlock_t			dev_base_lock;		/* Device list lock */
 extern int			netdev_dropping;
 extern int			net_cpu_congestion;
 
diff -u --recursive --new-file v2.3.3/linux/include/linux/pagemap.h linux/include/linux/pagemap.h
--- v2.3.3/linux/include/linux/pagemap.h	Tue May 11 13:04:23 1999
+++ linux/include/linux/pagemap.h	Tue May 25 14:58:57 1999
@@ -14,7 +14,7 @@
 
 static inline unsigned long page_address(struct page * page)
 {
-	return PAGE_OFFSET + PAGE_SIZE * (page - mem_map);
+	return PAGE_OFFSET + ((page - mem_map) << PAGE_SHIFT);
 }
 
 /*
diff -u --recursive --new-file v2.3.3/linux/include/linux/parport.h linux/include/linux/parport.h
--- v2.3.3/linux/include/linux/parport.h	Mon May 17 09:55:23 1999
+++ linux/include/linux/parport.h	Tue May 25 14:57:59 1999
@@ -212,6 +212,13 @@
 	rwlock_t cad_lock;
 };
 
+struct parport_driver {
+	const char *name;
+	void (*attach) (struct parport *);
+	void (*detach) (struct parport *);
+	struct parport_driver *next;
+};
+
 /* parport_register_port registers a new parallel port at the given address (if
  * one does not already exist) and returns a pointer to it.  This entails
  * claiming the I/O region, IRQ and DMA.
@@ -220,6 +227,13 @@
 struct parport *parport_register_port(unsigned long base, int irq, int dma,
 				      struct parport_operations *ops);
 
+/* Once a registered port is ready for high-level drivers to use, the
+   low-level driver that registered it should announce it.  This will
+   call the high-level drivers' attach() functions (after things like
+   determining the IEEE 1284.3 topology of the port and collecting
+   DeviceIDs). */
+void parport_announce_port (struct parport *port);
+
 /* Unregister a port. */
 extern void parport_unregister_port(struct parport *port);
 
@@ -235,6 +249,12 @@
  */
 struct parport *parport_enumerate(void);
 
+/* Register a new high-level driver. */
+extern int parport_register_driver (struct parport_driver *);
+
+/* Unregister a high-level driver. */
+extern void parport_unregister_driver (struct parport_driver *);
+
 /* parport_register_device declares that a device is connected to a port, and 
  * tells the kernel all it needs to know.  
  * pf is the preemption function (may be NULL for no callback)
@@ -322,7 +342,8 @@
 #define PARPORT_FLAG_COMA		(1<<0)
 #define PARPORT_FLAG_EXCL		(1<<1)	/* EXCL driver registered. */
 
-extern void parport_parse_irqs(int, const char *[], int irqval[]);
+extern int parport_parse_irqs(int, const char *[], int irqval[]);
+extern int parport_parse_dmas(int, const char *[], int irqval[]);
 extern int parport_ieee1284_nibble_mode_ok(struct parport *, unsigned char);
 extern int parport_wait_peripheral(struct parport *, unsigned char, unsigned
 				   char);
diff -u --recursive --new-file v2.3.3/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- v2.3.3/linux/include/linux/proc_fs.h	Sat May 15 23:46:05 1999
+++ linux/include/linux/proc_fs.h	Wed May 26 09:35:29 1999
@@ -135,11 +135,11 @@
 	PROC_NET_TR_RIF,
 	PROC_NET_DN_DEV,
 	PROC_NET_DN_ADJ,
-	PROC_NET_DN_L1,
-	PROC_NET_DN_L2,
+	PROC_NET_DN_ROUTE,
 	PROC_NET_DN_CACHE,
 	PROC_NET_DN_SKT,
-	PROC_NET_DN_FW,
+	PROC_NET_DN_FW_CHAINS,
+	PROC_NET_DN_FW_CHAIN_NAMES,
 	PROC_NET_DN_RAW,
 	PROC_NET_NETSTAT,
 	PROC_NET_IPFW_CHAINS,
diff -u --recursive --new-file v2.3.3/linux/include/linux/rtnetlink.h linux/include/linux/rtnetlink.h
--- v2.3.3/linux/include/linux/rtnetlink.h	Fri May 14 18:55:30 1999
+++ linux/include/linux/rtnetlink.h	Wed May 26 09:36:35 1999
@@ -418,7 +418,9 @@
 	IFLA_MTU,
 	IFLA_LINK,
 	IFLA_QDISC,
-	IFLA_STATS
+	IFLA_STATS,
+	IFLA_COST,
+	IFLA_PRIORITY
 };
 
 
@@ -505,6 +507,9 @@
 #define RTMGRP_IPV6_IFADDR	0x100
 #define RTMGRP_IPV6_MROUTE	0x200
 #define RTMGRP_IPV6_ROUTE	0x400
+
+#define RTMGRP_DECnet_IFADDR    0x1000
+#define RTMGRP_DECnet_ROUTE     0x4000
 
 /* End of information exported to user level */
 
diff -u --recursive --new-file v2.3.3/linux/include/linux/sched.h linux/include/linux/sched.h
--- v2.3.3/linux/include/linux/sched.h	Fri May 14 18:55:30 1999
+++ linux/include/linux/sched.h	Tue May 25 14:58:57 1999
@@ -16,6 +16,7 @@
 #include <asm/system.h>
 #include <asm/semaphore.h>
 #include <asm/page.h>
+#include <asm/ptrace.h>
 
 #include <linux/smp.h>
 #include <linux/tty.h>
@@ -381,9 +382,13 @@
 /* signals */	SPIN_LOCK_UNLOCKED, &init_signals, {{0}}, {{0}}, NULL, &init_task.sigqueue, 0, 0, \
 }
 
+#ifndef INIT_TASK_SIZE
+# define INIT_TASK_SIZE	2048*sizeof(long)
+#endif
+
 union task_union {
 	struct task_struct task;
-	unsigned long stack[2048];
+	unsigned long stack[INIT_TASK_SIZE/sizeof(long)];
 };
 
 extern union task_union init_task_union;
@@ -538,8 +543,7 @@
 
 static inline int on_sig_stack(unsigned long sp)
 {
-	return (sp >= current->sas_ss_sp
-		&& sp < current->sas_ss_sp + current->sas_ss_size);
+	return (sp - current->sas_ss_sp < current->sas_ss_size);
 }
 
 static inline int sas_ss_flags(unsigned long sp)
diff -u --recursive --new-file v2.3.3/linux/include/linux/skbuff.h linux/include/linux/skbuff.h
--- v2.3.3/linux/include/linux/skbuff.h	Fri May 14 18:55:30 1999
+++ linux/include/linux/skbuff.h	Mon May 31 22:07:43 1999
@@ -15,6 +15,8 @@
 #define _LINUX_SKBUFF_H
 
 #include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
 #include <linux/time.h>
 
 #include <asm/atomic.h>
@@ -30,15 +32,19 @@
 #define CHECKSUM_UNNECESSARY 2
 
 struct sk_buff_head {
+	/* These two members must be first. */
 	struct sk_buff	* next;
 	struct sk_buff	* prev;
-	__u32		qlen;		/* Must be same length as a pointer
-					   for using debugging */
+
+	__u32		qlen;
+	spinlock_t	lock;
 };
 
 struct sk_buff {
+	/* These two members must be first. */
 	struct sk_buff	* next;			/* Next buffer in list 				*/
 	struct sk_buff	* prev;			/* Previous buffer in list 			*/
+
 	struct sk_buff_head * list;		/* List we are on				*/
 	struct sock	*sk;			/* Socket we are owned by 			*/
 	struct timeval	stamp;			/* Time we arrived				*/
@@ -245,6 +251,7 @@
 
 extern __inline__ void skb_queue_head_init(struct sk_buff_head *list)
 {
+	spin_lock_init(&list->lock);
 	list->prev = (struct sk_buff *)list;
 	list->next = (struct sk_buff *)list;
 	list->qlen = 0;
@@ -271,15 +278,13 @@
 	prev->next = newsk;
 }
 
-extern spinlock_t skb_queue_lock;
-
 extern __inline__ void skb_queue_head(struct sk_buff_head *list, struct sk_buff *newsk)
 {
 	unsigned long flags;
 
-	spin_lock_irqsave(&skb_queue_lock, flags);
+	spin_lock_irqsave(&list->lock, flags);
 	__skb_queue_head(list, newsk);
-	spin_unlock_irqrestore(&skb_queue_lock, flags);
+	spin_unlock_irqrestore(&list->lock, flags);
 }
 
 /*
@@ -304,9 +309,9 @@
 {
 	unsigned long flags;
 
-	spin_lock_irqsave(&skb_queue_lock, flags);
+	spin_lock_irqsave(&list->lock, flags);
 	__skb_queue_tail(list, newsk);
-	spin_unlock_irqrestore(&skb_queue_lock, flags);
+	spin_unlock_irqrestore(&list->lock, flags);
 }
 
 /*
@@ -338,9 +343,9 @@
 	long flags;
 	struct sk_buff *result;
 
-	spin_lock_irqsave(&skb_queue_lock, flags);
+	spin_lock_irqsave(&list->lock, flags);
 	result = __skb_dequeue(list);
-	spin_unlock_irqrestore(&skb_queue_lock, flags);
+	spin_unlock_irqrestore(&list->lock, flags);
 	return result;
 }
 
@@ -367,9 +372,9 @@
 {
 	unsigned long flags;
 
-	spin_lock_irqsave(&skb_queue_lock, flags);
+	spin_lock_irqsave(&old->list->lock, flags);
 	__skb_insert(newsk, old->prev, old, old->list);
-	spin_unlock_irqrestore(&skb_queue_lock, flags);
+	spin_unlock_irqrestore(&old->list->lock, flags);
 }
 
 /*
@@ -385,9 +390,9 @@
 {
 	unsigned long flags;
 
-	spin_lock_irqsave(&skb_queue_lock, flags);
+	spin_lock_irqsave(&old->list->lock, flags);
 	__skb_append(old, newsk);
-	spin_unlock_irqrestore(&skb_queue_lock, flags);
+	spin_unlock_irqrestore(&old->list->lock, flags);
 }
 
 /*
@@ -417,12 +422,16 @@
 
 extern __inline__ void skb_unlink(struct sk_buff *skb)
 {
-	unsigned long flags;
+	struct sk_buff_head *list = skb->list;
 
-	spin_lock_irqsave(&skb_queue_lock, flags);
-	if(skb->list)
-		__skb_unlink(skb, skb->list);
-	spin_unlock_irqrestore(&skb_queue_lock, flags);
+	if(list) {
+		unsigned long flags;
+
+		spin_lock_irqsave(&list->lock, flags);
+		if(skb->list == list)
+			__skb_unlink(skb, skb->list);
+		spin_unlock_irqrestore(&list->lock, flags);
+	}
 }
 
 /* XXX: more streamlined implementation */
@@ -439,9 +448,9 @@
 	long flags;
 	struct sk_buff *result;
 
-	spin_lock_irqsave(&skb_queue_lock, flags);
+	spin_lock_irqsave(&list->lock, flags);
 	result = __skb_dequeue_tail(list);
-	spin_unlock_irqrestore(&skb_queue_lock, flags);
+	spin_unlock_irqrestore(&list->lock, flags);
 	return result;
 }
 
@@ -462,11 +471,8 @@
 	unsigned char *tmp=skb->tail;
 	skb->tail+=len;
 	skb->len+=len;
-	if(skb->tail>skb->end)
-	{
-		__label__ here; 
-		skb_over_panic(skb, len, &&here); 
-here:		;
+	if(skb->tail>skb->end) {
+		skb_over_panic(skb, len, current_text_addr());
 	}
 	return tmp;
 }
@@ -482,11 +488,8 @@
 {
 	skb->data-=len;
 	skb->len+=len;
-	if(skb->data<skb->head)
-	{
-		__label__ here;
-		skb_under_panic(skb, len, &&here);
-here: 		;
+	if(skb->data<skb->head) {
+		skb_under_panic(skb, len, current_text_addr());
 	}
 	return skb->data;
 }
diff -u --recursive --new-file v2.3.3/linux/include/linux/sysctl.h linux/include/linux/sysctl.h
--- v2.3.3/linux/include/linux/sysctl.h	Sat Apr 24 12:47:47 1999
+++ linux/include/linux/sysctl.h	Wed May 26 09:36:35 1999
@@ -392,14 +392,42 @@
 	NET_TR_RIF_TIMEOUT=1
 };
 
-/* /proc/sys/net/decnet */
+/* /proc/sys/net/decnet/ */
 enum {
-	NET_DECNET_DEF_T3_BROADCAST=1,
-	NET_DECNET_DEF_T3_POINTTOPOINT=2,
-	NET_DECNET_DEF_T1=3,
-	NET_DECNET_DEF_BCT1=4,
-	NET_DECNET_CACHETIMEOUT=5,
-	NET_DECNET_DEBUG_LEVEL=6
+	NET_DECNET_NODE_TYPE = 1,
+	NET_DECNET_NODE_ADDRESS = 2,
+	NET_DECNET_NODE_NAME = 3,
+	NET_DECNET_DEFAULT_DEVICE = 4,
+	NET_DECNET_TIME_WAIT = 5,
+	NET_DECNET_DN_COUNT = 6,
+	NET_DECNET_DI_COUNT = 7,
+	NET_DECNET_DR_COUNT = 8,
+	NET_DECNET_DST_GC_INTERVAL = 9,
+	NET_DECNET_CONF = 10,
+	NET_DECNET_DEBUG_LEVEL = 255
+};
+
+/* /proc/sys/net/decnet/conf/<dev> */
+enum {
+	NET_DECNET_CONF_LOOPBACK = -2,
+	NET_DECNET_CONF_DDCMP = -3,
+	NET_DECNET_CONF_PPP = -4,
+	NET_DECNET_CONF_X25 = -5,
+	NET_DECNET_CONF_GRE = -6,
+	NET_DECNET_CONF_ETHER = -7
+
+	/* ... and ifindex of devices */
+};
+
+/* /proc/sys/net/decnet/conf/<dev>/ */
+enum {
+	NET_DECNET_CONF_DEV_PRIORITY = 1,
+	NET_DECNET_CONF_DEV_T1 = 2,
+	NET_DECNET_CONF_DEV_T2 = 3,
+	NET_DECNET_CONF_DEV_T3 = 4,
+	NET_DECNET_CONF_DEV_COST = 5,
+	NET_DECNET_CONF_DEV_BLKSIZE = 6,
+	NET_DECNET_CONF_DEV_STATE = 7
 };
 
 /* CTL_PROC names: */
diff -u --recursive --new-file v2.3.3/linux/include/linux/wait.h linux/include/linux/wait.h
--- v2.3.3/linux/include/linux/wait.h	Fri May 14 18:55:30 1999
+++ linux/include/linux/wait.h	Tue May 25 14:58:55 1999
@@ -8,11 +8,14 @@
 
 #ifdef __KERNEL__
 
-#include <asm/page.h>
-#include <asm/spinlock.h>
+#include <linux/kernel.h>
 #include <linux/list.h>
 #include <linux/stddef.h>
 
+#include <asm/page.h>
+#include <asm/spinlock.h>
+#include <asm/processor.h>
+
 /*
  * Temporary debugging help until all code is converted to the new
  * waitqueue usage.
@@ -118,7 +121,6 @@
 static inline void init_waitqueue_head(wait_queue_head_t *q)
 {
 #if WAITQUEUE_DEBUG
-	__label__ __x;
 	if (!q)
 		WQ_BUG();
 #endif
@@ -126,7 +128,7 @@
 	INIT_LIST_HEAD(&q->task_list);
 #if WAITQUEUE_DEBUG
 	q->__magic = (long)&q->__magic;
-	__x: q->__creator = (long)&&__x;
+	q->__creator = (long)current_text_addr();
 #endif
 }
 
diff -u --recursive --new-file v2.3.3/linux/include/net/decnet_call.h linux/include/net/decnet_call.h
--- v2.3.3/linux/include/net/decnet_call.h	Wed Dec 31 16:00:00 1969
+++ linux/include/net/decnet_call.h	Wed May 26 09:36:35 1999
@@ -0,0 +1,2 @@
+/* Separate to keep compilation of protocols.c simpler */
+extern void decnet_proto_init(struct net_proto *pro);
diff -u --recursive --new-file v2.3.3/linux/include/net/dn.h linux/include/net/dn.h
--- v2.3.3/linux/include/net/dn.h	Wed Dec 31 16:00:00 1969
+++ linux/include/net/dn.h	Wed May 26 09:36:35 1999
@@ -0,0 +1,206 @@
+#ifndef _NET_DN_H
+#define _NET_DN_H
+
+#include <linux/dn.h>
+#include <asm/byteorder.h>
+
+typedef unsigned short dn_address;
+
+#define dn_ntohs(x) le16_to_cpu(x)
+#define dn_htons(x) cpu_to_le16(x)
+
+struct dn_scp                                   /* Session Control Port */
+{
+        unsigned char           state;
+#define DN_O     1                      /* Open                 */
+#define DN_CR    2                      /* Connect Receive      */
+#define DN_DR    3                      /* Disconnect Reject    */
+#define DN_DRC   4                      /* Discon. Rej. Complete*/
+#define DN_CC    5                      /* Connect Confirm      */
+#define DN_CI    6                      /* Connect Initiate     */
+#define DN_NR    7                      /* No resources         */
+#define DN_NC    8                      /* No communication     */
+#define DN_CD    9                      /* Connect Delivery     */
+#define DN_RJ    10                     /* Rejected             */
+#define DN_RUN   11                     /* Running              */
+#define DN_DI    12                     /* Disconnect Initiate  */
+#define DN_DIC   13                     /* Disconnect Complete  */
+#define DN_DN    14                     /* Disconnect Notificat */
+#define DN_CL    15                     /* Closed               */
+#define DN_CN    16                     /* Closed Notification  */
+
+        unsigned short          addrloc;
+        unsigned short          addrrem;
+        unsigned short          numdat;
+        unsigned short          numoth;
+        unsigned short          numoth_rcv;
+        unsigned short          numdat_rcv;
+        unsigned short          ackxmt_dat;
+        unsigned short          ackxmt_oth;
+        unsigned short          ackrcv_dat;
+        unsigned short          ackrcv_oth;
+        unsigned char           flowrem_sw;
+	unsigned char		flowloc_sw;
+#define DN_SEND         2
+#define DN_DONTSEND     1
+#define DN_NOCHANGE     0
+	unsigned char		accept_mode;
+	unsigned short          mss;
+
+	struct optdata_dn     conndata_in;
+	struct optdata_dn     conndata_out;
+	struct optdata_dn     discdata_in;
+	struct optdata_dn     discdata_out;
+        struct accessdata_dn  accessdata;
+
+        struct sockaddr_dn addr; /* Local address  */
+	struct sockaddr_dn peer; /* Remote address */
+
+	/*
+	 * In this case the RTT estimation is not specified in the
+	 * docs, nor is any back off algorithm. Here we follow well
+	 * known tcp algorithms with a few small variations.
+	 *
+	 * snd_window: Max number of packets we send before we wait for
+	 *             an ack to come back. This will become part of a
+	 *             more complicated scheme when we support flow
+	 *             control.
+	 *
+	 * nsp_srtt:   Round-Trip-Time (x8) in jiffies. This is a rolling
+	 *             average.
+	 * nsp_rttvar: Round-Trip-Time-Varience (x4) in jiffies. This is the
+	 *             varience of the smoothed average (but calculated in
+	 *             a simpler way than for normal statistical varience
+	 *             calculations).
+	 *
+	 * nsp_rxtshift: Backoff counter. Value is zero normally, each time
+	 *               a packet is lost is increases by one until an ack
+	 *               is received. Its used to index an array of backoff
+	 *               multipliers.
+	 */
+#define NSP_MIN_WINDOW 1
+#define NSP_MAX_WINDOW 512
+	unsigned long snd_window;
+#define NSP_INITIAL_SRTT (HZ)
+	unsigned long nsp_srtt;
+#define NSP_INITIAL_RTTVAR (HZ*3)
+	unsigned long nsp_rttvar;
+#define NSP_MAXRXTSHIFT 12
+	unsigned long nsp_rxtshift;
+
+	/*
+	 * Output queues, one for data, one for otherdata/linkservice
+	 */
+	struct sk_buff_head data_xmit_queue;
+	struct sk_buff_head other_xmit_queue;
+
+	/*
+	 * Input queue for other data
+	 */
+	struct sk_buff_head other_receive_queue;
+	int other_report;
+
+	/*
+	 * Stuff to do with the slow timer
+	 */
+	unsigned long stamp;          /* time of last transmit */
+	unsigned long persist;
+	int (*persist_fxn)(struct sock *sk);
+	unsigned long keepalive;
+	void (*keepalive_fxn)(struct sock *sk);
+
+	/*
+	 * This stuff is for the fast timer for delayed acks
+	 */
+	struct timer_list delack_timer;
+	int delack_pending;
+	void (*delack_fxn)(struct sock *sk);
+};
+
+/*
+ * src,dst : Source and Destination DECnet addresses
+ * neigh: Address from which we've just got this skb.
+ * hops : Number of hops through the network
+ * dst_port, src_port : NSP port numbers
+ * services, info : Useful data extracted from conninit messages
+ * rt_flags : Routing flags byte
+ * nsp_flags : NSP layer flags byte
+ * segsize : Size of segment
+ * segnum : Number, for data, otherdata and linkservice
+ * xmit_count : Number of times we've transmitted this skb
+ * stamp : Time stamp of first transmission, used in RTT calculations
+ * iif: Input interface number
+ *
+ * As a general policy, this structure keeps all addresses in network
+ * byte order, and all else in host byte order. Thus dst, src, dst_port
+ * src_port and neigh are in network order. All else is in host order.
+ * 
+ */
+struct dn_skb_cb {
+	unsigned short dst;
+	unsigned short src;
+	unsigned short neigh;
+	unsigned short hops;
+	unsigned short dst_port;
+	unsigned short src_port;
+	unsigned char services;
+	unsigned char info;
+	unsigned char rt_flags;
+	unsigned char nsp_flags;
+	unsigned short segsize;
+	unsigned short segnum;
+	unsigned short xmit_count;
+	unsigned long stamp;
+	int iif;
+};
+
+static __inline__ dn_address dn_eth2dn(unsigned char *ethaddr)
+{
+	return ethaddr[4] | (ethaddr[5] << 8);
+}
+
+static __inline__ dn_address dn_saddr2dn(struct sockaddr_dn *saddr)
+{
+	return *(dn_address *)saddr->sdn_nodeaddr;
+}
+
+static __inline__ void dn_dn2eth(unsigned char *ethaddr, dn_address addr)
+{
+	ethaddr[0] = 0xAA;
+	ethaddr[1] = 0x00;
+	ethaddr[2] = 0x04;
+	ethaddr[3] = 0x00;
+	ethaddr[4] = (unsigned char)(addr & 0xff);
+	ethaddr[5] = (unsigned char)(addr >> 8);
+}
+
+#define DN_MENUVER_ACC 0x01
+#define DN_MENUVER_USR 0x02
+#define DN_MENUVER_PRX 0x04
+#define DN_MENUVER_UIC 0x08
+
+extern struct sock *dn_sklist_find_listener(struct sockaddr_dn *addr);
+extern struct sock *dn_find_by_skb(struct sk_buff *skb);
+extern unsigned short dn_alloc_port(void);
+#define DN_ASCBUF_LEN 7
+extern char *dn_addr2asc(dn_address, char *);
+extern void dn_destroy_sock(struct sock *sk);
+
+extern int dn_sockaddr2username(struct sockaddr_dn *addr, unsigned char *buf, unsigned char type);
+extern int dn_username2sockaddr(unsigned char *data, int len, struct sockaddr_dn *addr, unsigned char *type);
+
+extern void dn_start_slow_timer(struct sock *sk);
+extern void dn_stop_slow_timer(struct sock *sk);
+extern void dn_start_fast_timer(struct sock *sk);
+extern void dn_stop_fast_timer(struct sock *sk);
+
+extern dn_address decnet_address;
+extern unsigned char decnet_ether_address[6];
+extern int decnet_node_type;
+extern int decnet_debug_level;
+extern int decnet_time_wait;
+extern int decnet_dn_count;
+extern int decnet_di_count;
+extern int decnet_dr_count;
+
+#endif /* _NET_DN_H */
diff -u --recursive --new-file v2.3.3/linux/include/net/dn_dev.h linux/include/net/dn_dev.h
--- v2.3.3/linux/include/net/dn_dev.h	Wed Dec 31 16:00:00 1969
+++ linux/include/net/dn_dev.h	Wed May 26 09:36:35 1999
@@ -0,0 +1,194 @@
+#ifndef _NET_DN_DEV_H
+#define _NET_DN_DEV_H
+
+
+struct dn_dev;
+
+struct dn_ifaddr {
+	struct dn_ifaddr *ifa_next;
+	struct dn_dev    *ifa_dev;
+	dn_address       ifa_local;
+	unsigned char    ifa_flags;
+	unsigned char    ifa_scope;
+	char             ifa_label[IFNAMSIZ];
+};
+
+#define DN_DEV_S_RU  0 /* Run - working normally   */
+#define DN_DEV_S_CR  1 /* Circuit Rejected         */
+#define DN_DEV_S_DS  2 /* Data Link Start          */
+#define DN_DEV_S_RI  3 /* Routing Layer Initialize */
+#define DN_DEV_S_RV  4 /* Routing Layer Verify     */
+#define DN_DEV_S_RC  5 /* Routing Layer Complete   */
+#define DN_DEV_S_OF  6 /* Off                      */
+#define DN_DEV_S_HA  7 /* Halt                     */
+
+
+/*
+ * The dn_dev_parms structure contains the set of parameters
+ * for each device (hence inclusion in the dn_dev structure)
+ * and an array is used to store the default types of supported
+ * device (in dn_dev.c).
+ *
+ * The type field matches the ARPHRD_ constants and is used in
+ * searching the list for supported devices when new devices
+ * come up.
+ *
+ * The mode field is used to find out if a device is broadcast,
+ * multipoint, or pointopoint. Please note that DECnet thinks
+ * different ways about devices to the rest of the kernel
+ * so the normal IFF_xxx flags are invalid here. For devices
+ * which can be any combination of the previously mentioned
+ * attributes, you can set this on a per device basis by
+ * installing an up() routine.
+ *
+ * The device state field, defines the initial state in which the
+ * device will come up. In the dn_dev structure, it is the actual
+ * state.
+ *
+ * The cost field is used in the routing algorithm.
+ *
+ * Timers:
+ * t1 - Routing timer, send routing messages when it expires
+ * t2 - Rate limit timer, min time between routing and hello messages
+ * t3 - Hello timer, send hello messages when it expires
+ *
+ * Callbacks:
+ * up() - Called to initialize device, return value can veto use of
+ *        device with DECnet.
+ * down() - Called to turn device off when it goes down
+ * timer1() - Called when timer 1 goes off
+ * timer3() - Called when timer 3 goes off
+ * setsrc() - Called for each incomming frame to set previous hop info
+ * neigh_setup() - Called to do device specific setup of neighbours
+ * 
+ * sysctl - Hook for sysctl things
+ *
+ */
+struct dn_dev_parms {
+	int type;	          /* ARPHRD_xxx                     */
+	int mode;	          /* Broadcast, Unicast, Mulitpoint */
+#define DN_DEV_BCAST  1
+#define DN_DEV_UCAST  2
+#define DN_DEV_MPOINT 4
+	int state;                /* Initial state                  */
+	int cost;	          /* Default cost of device         */
+	unsigned short blksize;   /* Block Size                     */
+	unsigned long t1;         /* Default value of t1            */
+	unsigned long t2;         /* Default value of t2            */
+	unsigned long t3;         /* Default value of t3            */
+	int priority;             /* Priority to be a router        */
+	char *name;               /* Name for sysctl                */
+	int ctl_name;             /* Index for sysctl               */
+	int  (*up)(struct device *);
+	void (*down)(struct device *);
+	void (*timer1)(struct device *);
+	void (*timer3)(struct device *);
+	int (*setsrc)(struct sk_buff *skb);
+	int (*neigh_setup)(struct neighbour *);
+	void *sysctl;
+};
+
+
+struct dn_dev {
+	struct dn_ifaddr *ifa_list;
+	struct device *dev;
+	struct dn_dev_parms parms;
+	char use_long;
+        struct timer_list timer;
+        unsigned long t3, t1;
+	struct neigh_parms *neigh_parms;
+	unsigned char addr[ETH_ALEN];
+	struct neighbour *router; /* Default router on circuit */
+	struct neighbour *peer;   /* Peer on pointopoint links */
+	unsigned long uptime;     /* Time device went up in jiffies */
+};
+
+struct dn_short_packet
+{
+	unsigned char   msgflg          __attribute__((packed));
+        unsigned short  dstnode         __attribute__((packed));
+        unsigned short  srcnode         __attribute__((packed));
+        unsigned char   forward         __attribute__((packed));
+};
+
+struct dn_long_packet
+{
+	unsigned char   msgflg          __attribute__((packed));
+        unsigned char   d_area          __attribute__((packed));
+        unsigned char   d_subarea       __attribute__((packed));
+        unsigned char   d_id[6]         __attribute__((packed));
+        unsigned char   s_area          __attribute__((packed));
+        unsigned char   s_subarea       __attribute__((packed));
+        unsigned char   s_id[6]         __attribute__((packed));
+        unsigned char   nl2             __attribute__((packed));
+        unsigned char   visit_ct        __attribute__((packed));
+        unsigned char   s_class         __attribute__((packed));
+        unsigned char   pt              __attribute__((packed));
+};
+
+/*------------------------- DRP - Routing messages ---------------------*/
+
+        struct endnode_hello_message
+        {
+                unsigned char   msgflg          __attribute__((packed));
+                unsigned char   tiver[3]        __attribute__((packed));
+                unsigned char   id[6]           __attribute__((packed));
+                unsigned char   iinfo           __attribute__((packed));
+                unsigned short  blksize         __attribute__((packed));
+                unsigned char   area            __attribute__((packed));
+                unsigned char   seed[8]         __attribute__((packed));
+                unsigned char   neighbor[6]     __attribute__((packed));
+                unsigned short  timer           __attribute__((packed));
+                unsigned char   mpd             __attribute__((packed));
+                unsigned char   datalen         __attribute__((packed));
+                unsigned char   data[2]         __attribute__((packed));
+        };
+        struct rtnode_hello_message
+        {
+                unsigned char   msgflg          __attribute__((packed));
+                unsigned char   tiver[3]        __attribute__((packed));
+                unsigned char   id[6]           __attribute__((packed));
+                unsigned char   iinfo           __attribute__((packed));
+                unsigned short  blksize         __attribute__((packed));
+                unsigned char   priority        __attribute__((packed));
+                unsigned char   area            __attribute__((packed));
+                unsigned short  timer           __attribute__((packed));
+                unsigned char   mpd             __attribute__((packed));
+        };
+
+
+extern void dn_dev_init(void);
+extern void dn_dev_cleanup(void);
+
+extern int dn_dev_ioctl(unsigned int cmd, void *arg);
+
+extern void dn_dev_devices_off(void);
+extern void dn_dev_devices_on(void);
+
+extern void dn_dev_init_pkt(struct sk_buff *skb);
+extern void dn_dev_veri_pkt(struct sk_buff *skb);
+extern void dn_dev_hello(struct sk_buff *skb);
+
+extern void dn_dev_up(struct device *);
+extern void dn_dev_down(struct device *);
+
+extern struct device *decnet_default_device;
+
+static __inline__ int dn_dev_islocal(struct device *dev, dn_address addr)
+{
+	struct dn_dev *dn_db = dev->dn_ptr;
+	struct dn_ifaddr *ifa;
+
+	if (dn_db == NULL) {
+		printk(KERN_DEBUG "dn_dev_islocal: Called for non DECnet device\n");
+		return 0;
+	}
+
+	for(ifa = dn_db->ifa_list; ifa; ifa = ifa->ifa_next)
+		if ((addr ^ ifa->ifa_local) == 0)
+			return 1;
+
+	return 0;
+}
+
+#endif /* _NET_DN_DEV_H */
diff -u --recursive --new-file v2.3.3/linux/include/net/dn_fib.h linux/include/net/dn_fib.h
--- v2.3.3/linux/include/net/dn_fib.h	Wed Dec 31 16:00:00 1969
+++ linux/include/net/dn_fib.h	Wed May 26 09:36:35 1999
@@ -0,0 +1,83 @@
+#ifndef _NET_DN_FIB_H
+#define _NET_DN_FIB_H
+
+#ifdef CONFIG_DECNET_ROUTER
+
+
+struct dn_fib_res {
+	dn_address res_addr;
+	dn_address res_mask;
+	int res_ifindex;
+	int res_proto;
+	int res_cost;
+	int res_type;
+	struct dn_fib_node *res_fn;
+	struct dn_fib_action *res_fa;
+};
+
+struct dn_fib_action {
+	struct dn_fib_action *fa_next;
+	dn_address fa_key;
+	dn_address fa_mask;
+	int fa_ifindex;
+	int fa_proto;
+	int fa_cost;
+	int fa_type;
+	union {
+		struct neighbour *fau_neigh;	/* Normal route */
+		int fau_error;			/* Reject       */
+		int fau_table;			/* Throw        */
+	} fa_u;
+#define fa_neigh fa_u.fau_neigh
+#define fa_error fa_u.fau_error
+#define fa_table fa_u.fau_table
+};
+
+struct dn_fib_node {
+	struct dn_fib_node *fn_up;
+	dn_address fn_cmpmask;
+	dn_address fn_key;
+	int fn_shift;
+	struct dn_fib_action *fn_action;
+	struct dn_fib_node *fn_children[2];
+};
+
+#define DN_FIB_NEXT(fibnode, key) ((fibnode)->fn_children[((key) ^ (fibnode)->fn_cmpmask) >> (fibnode)->fn_shift]) 
+
+struct dn_fib_walker_t;
+
+struct dn_fib_table {
+	int n;
+	unsigned long count;
+	struct dn_fib_node *root;
+
+	int (*insert)(struct dn_fib_table *t, struct dn_fib_action *fa);
+	int (*delete)(struct dn_fib_table *t, struct dn_fib_action *fa);
+	int (*lookup)(struct dn_fib_table *t, struct dn_fib_res *res);
+	int (*walk)(struct dn_fib_walker_t *fwt);
+#ifdef CONFIG_RTNETLINK
+	int (*dump)(struct dn_fib_table *t, struct sk_buff *skb, struct netlink_callback *cb);
+#endif /* CONFIG_RTNETLINK */
+};
+
+struct dn_fib_walker_t {
+	struct dn_fib_table *table;
+	void *arg;
+	int (*fxn)(struct dn_fib_walker_t *fwt, struct dn_fib_node *n);
+};
+
+extern void dn_fib_init(void);
+extern void dn_fib_cleanup(void);
+
+extern int dn_fib_rt_message(struct sk_buff *skb);
+extern int dn_fib_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg);
+
+#ifdef CONFIG_RTNETLINK
+extern int dn_fib_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg);
+extern int dn_fib_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg);
+extern int dn_fib_dump(struct sk_buff *skb, struct netlink_callback *cb);
+extern int dn_fib_rtm_getroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg);
+#endif /* CONFIG_RTNETLINK */
+#endif /* CONFIG_DECNET_ROUTER */
+
+#endif /* _NET_DN_FIB_H */
diff -u --recursive --new-file v2.3.3/linux/include/net/dn_neigh.h linux/include/net/dn_neigh.h
--- v2.3.3/linux/include/net/dn_neigh.h	Wed Dec 31 16:00:00 1969
+++ linux/include/net/dn_neigh.h	Wed May 26 09:36:35 1999
@@ -0,0 +1,29 @@
+#ifndef _NET_DN_NEIGH_H
+#define _NET_DN_NEIGH_H
+
+/*
+ * The position of the first two fields of
+ * this structure are critical - SJW
+ */
+struct dn_neigh {
+        struct neighbour n;
+        unsigned char addr[ETH_ALEN];
+        unsigned long flags;
+#define DN_NDFLAG_R1    0x0001 /* Router L1      */
+#define DN_NDFLAG_R2    0x0002 /* Router L2      */
+#define DN_NDFLAG_P3    0x0004 /* Phase III Node */
+        unsigned long blksize;
+	unsigned char priority;
+};
+
+extern void dn_neigh_init(void);
+extern void dn_neigh_cleanup(void);
+extern struct neighbour *dn_neigh_lookup(struct neigh_table *tbl, void *ptr);
+extern void dn_neigh_router_hello(struct sk_buff *skb);
+extern void dn_neigh_endnode_hello(struct sk_buff *skb);
+extern void dn_neigh_pointopoint_hello(struct sk_buff *skb);
+extern int dn_neigh_elist(struct device *dev, unsigned char *ptr, int n);
+
+extern struct neigh_table dn_neigh_table;
+
+#endif /* _NET_DN_NEIGH_H */
diff -u --recursive --new-file v2.3.3/linux/include/net/dn_nsp.h linux/include/net/dn_nsp.h
--- v2.3.3/linux/include/net/dn_nsp.h	Wed Dec 31 16:00:00 1969
+++ linux/include/net/dn_nsp.h	Wed May 26 09:36:35 1999
@@ -0,0 +1,194 @@
+#ifndef _NET_DN_NSP_H
+#define _NET_DN_NSP_H
+/******************************************************************************
+    (c) 1995-1998 E.M. Serrat		emserrat@geocities.com
+    
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    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.
+*******************************************************************************/
+/* dn_nsp.c functions prototyping */
+
+extern void dn_nsp_send_data_ack(struct sock *sk);
+extern void dn_nsp_send_oth_ack(struct sock *sk);
+extern void dn_nsp_delayed_ack(struct sock *sk);
+extern void dn_send_conn_ack(struct sock *sk);
+extern void dn_send_conn_conf(struct sock *sk);
+extern void dn_send_disc(struct sock *sk, unsigned char type, unsigned short reason);
+extern void dn_nsp_send_lnk(struct sock *sk, unsigned short flags);
+extern void dn_nsp_send_conninit(struct sock *sk, unsigned char flags);
+
+extern void dn_nsp_output(struct sock *sk);
+extern int dn_nsp_check_xmit_queue(struct sock *sk, struct sk_buff *skb, struct sk_buff_head *q, unsigned short acknum);
+extern void dn_nsp_queue_xmit(struct sock *sk, struct sk_buff *skb, int oob);
+extern unsigned long dn_nsp_persist(struct sock *sk);
+extern int dn_nsp_xmit_timeout(struct sock *sk);
+
+extern int dn_nsp_rx(struct sk_buff *);
+extern int dn_nsp_backlog_rcv(struct sock *sk, struct sk_buff *skb);
+
+extern struct sk_buff *dn_alloc_skb(struct sock *sk, int size, int pri);
+extern struct sk_buff *dn_alloc_send_skb(struct sock *sk, int *size, int noblock, int *err);
+
+#define NSP_REASON_NR 1
+#define NSP_REASON_DC 42
+#define NSP_REASON_NL 41
+
+#define NSP_DISCINIT 0x38
+#define NSP_DISCCONF 0x48
+
+/*------------------------- NSP - messages ------------------------------*/
+/* Data Messages */
+/*---------------*/
+
+/* Data Messages    (data segment/interrupt/link service)               */
+
+        struct nsp_data_seg_msg
+        {
+                unsigned char   msgflg          __attribute__((packed));
+                unsigned short  dstaddr         __attribute__((packed));
+                unsigned short  srcaddr         __attribute__((packed));
+        };
+
+        struct nsp_data_opt_msg
+        {
+                unsigned short  acknum          __attribute__((packed));
+                unsigned short  segnum          __attribute__((packed));
+                unsigned short  lsflgs          __attribute__((packed));
+        };
+
+        struct nsp_data_opt_msg1
+        {
+                unsigned short  acknum          __attribute__((packed));
+                unsigned short  segnum          __attribute__((packed));
+        };
+
+/* Acknowledgment Messages */
+/*-------------------------*/
+
+/* Acknowledgment Messages (data/other data)                             */
+
+        struct  nsp_data_ack_msg
+        {
+                unsigned char   msgflg          __attribute__((packed));
+                unsigned short  dstaddr         __attribute__((packed));
+                unsigned short  srcaddr         __attribute__((packed));
+                unsigned short  acknum          __attribute__((packed));
+        };
+
+/* Connect Acknowledgment Message */
+
+        struct  nsp_conn_ack_msg
+        {
+                unsigned char   msgflg          __attribute__((packed));
+                unsigned short  dstaddr         __attribute__((packed));
+        };
+
+/* Control Messages */
+/*------------------*/
+
+/* Connect Initiate/Retransmit Initiate/Connect Confirm */
+
+        struct  nsp_conn_init_msg
+        {
+                unsigned char   msgflg          __attribute__((packed));
+#define         NSP_CI          0x18            /* Connect Initiate     */
+#define         NSP_RCI         0x68            /* Retrans. Conn Init   */
+                unsigned short  dstaddr         __attribute__((packed));
+                unsigned short  srcaddr         __attribute__((packed));
+                unsigned char   services        __attribute__((packed));
+#define         NSP_FC_NONE     0x00            /* Flow Control None    */
+#define         NSP_FC_SRC      0x04            /* Seg Req. Count       */
+#define         NSP_FC_SCMC     0x08            /* Sess. Control Mess   */
+                unsigned char   info            __attribute__((packed));
+                unsigned short  segsize         __attribute__((packed));
+        };
+
+/* Disconnect Initiate/Disconnect Confirm */
+
+        struct  nsp_disconn_init_msg
+        {
+                unsigned char   msgflg          __attribute__((packed));
+                unsigned short  dstaddr         __attribute__((packed));
+                unsigned short  srcaddr         __attribute__((packed));
+                unsigned short  reason          __attribute__((packed));
+        };
+
+
+/*------------------------- SCP - messages ------------------------------*/
+
+        struct  srcobj_fmt
+        {
+                char            format          __attribute__((packed));
+                unsigned char   task            __attribute__((packed));
+                unsigned short  grpcode         __attribute__((packed));
+                unsigned short  usrcode         __attribute__((packed));
+                char            dlen            __attribute__((packed));
+        };
+
+/*
+ * A collection of functions for manipulating the sequence
+ * numbers used in NSP. Similar in operation to the functions
+ * of the same name in TCP.
+ */
+static __inline__ int before(unsigned short seq1, unsigned short seq2)
+{
+        seq1 &= 0x0fff;
+        seq2 &= 0x0fff;
+
+        return (int)((seq1 - seq2) & 0x0fff) > 2048;
+}
+
+
+static __inline__ int after(unsigned short seq1, unsigned short seq2)
+{
+        seq1 &= 0x0fff;
+        seq2 &= 0x0fff;
+
+        return (int)((seq2 - seq1) & 0x0fff) > 2048;
+}
+
+static __inline__ int equal(unsigned short seq1, unsigned short seq2)
+{
+        return ((seq1 ^ seq2) & 0x0fff) == 0;
+}
+
+static __inline__ int before_or_equal(unsigned short seq1, unsigned short seq2)
+{
+	return (before(seq1, seq2) || equal(seq1, seq2));
+}
+
+static __inline__ void seq_add(unsigned short *seq, unsigned short off)
+{
+        *seq += off;
+        *seq &= 0x0fff;
+}
+
+static __inline__ int seq_next(unsigned short seq1, unsigned short seq2)
+{
+	return (((seq2&0x0fff) - (seq1&0x0fff)) == 1);
+}
+
+/*
+ * Can we delay the ack ?
+ */
+static __inline__ int sendack(unsigned short seq)
+{
+        return (int)((seq & 0x1000) ? 0 : 1);
+}
+
+/*
+ * Is socket congested ?
+ */
+static __inline__ int dn_congested(struct sock *sk)
+{
+        return atomic_read(&sk->rmem_alloc) > (sk->rcvbuf >> 1);
+}
+
+#endif /* _NET_DN_NSP_H */
diff -u --recursive --new-file v2.3.3/linux/include/net/dn_raw.h linux/include/net/dn_raw.h
--- v2.3.3/linux/include/net/dn_raw.h	Wed Dec 31 16:00:00 1969
+++ linux/include/net/dn_raw.h	Wed May 26 09:36:35 1999
@@ -0,0 +1,17 @@
+#ifndef _NET_DN_RAW_H
+#define _NET_DN_RAW_H
+
+#ifdef CONFIG_DECNET_RAW
+
+extern struct proto_ops dn_raw_proto_ops;
+
+extern void dn_raw_rx_nsp(struct sk_buff *skb);
+extern void dn_raw_rx_routing(struct sk_buff *skb);
+
+#ifdef CONFIG_DECNET_MOP
+extern void dn_raw_rx_mop(struct sk_buff *skb);
+#endif /* CONFIG_DECNET_MOP */
+
+#endif /* CONFIG_DECNET_RAW */
+
+#endif /* _NET_DN_RAW_H */
diff -u --recursive --new-file v2.3.3/linux/include/net/dn_route.h linux/include/net/dn_route.h
--- v2.3.3/linux/include/net/dn_route.h	Wed Dec 31 16:00:00 1969
+++ linux/include/net/dn_route.h	Wed May 26 09:36:35 1999
@@ -0,0 +1,74 @@
+#ifndef _NET_DN_ROUTE_H
+#define _NET_DN_ROUTE_H
+
+/******************************************************************************
+    (c) 1995-1998 E.M. Serrat		emserrat@geocities.com
+    
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    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.
+*******************************************************************************/
+/* dn_route.c functions prototyping */
+extern void dn_send_skb(struct sk_buff *);
+extern struct sk_buff *dn_alloc_skb(struct sock *sk, int size, int pri);
+extern int dn_route_output(struct sock *sk);
+
+/* Masks for flags field */
+#define DN_RT_F_PID 0x07 /* Mask for packet type                      */
+#define DN_RT_F_PF  0x80 /* Padding Follows                           */
+#define DN_RT_F_VER 0x40 /* Version =0 discard packet if ==1          */
+#define DN_RT_F_IE  0x20 /* Intra Ethernet, Reserved in short pkt     */
+#define DN_RT_F_RTS 0x10 /* Packet is being returned to sender        */
+#define DN_RT_F_RQR 0x08 /* Return packet to sender upon non-delivery */
+
+/* Mask for types of routing packets */
+#define DN_RT_PKT_MSK   0x06
+/* Types of routing packets */
+#define DN_RT_PKT_SHORT 0x02 /* Short routing packet */
+#define DN_RT_PKT_LONG  0x06 /* Long routing packet  */
+
+/* Mask for control/routing selection */
+#define DN_RT_PKT_CNTL  0x01 /* Set to 1 if a control packet  */
+/* Types of control packets */
+#define DN_RT_CNTL_MSK  0x0f /* Mask for control packets      */
+#define DN_RT_PKT_INIT  0x01 /* Initialisation packet         */
+#define DN_RT_PKT_VERI  0x03 /* Verification Message          */
+#define DN_RT_PKT_HELO  0x05 /* Hello and Test Message        */
+#define DN_RT_PKT_L1RT  0x07 /* Level 1 Routing Message       */
+#define DN_RT_PKT_L2RT  0x09 /* Level 2 Routing Message       */
+#define DN_RT_PKT_ERTH  0x0b /* Ethernet Router Hello         */
+#define DN_RT_PKT_EEDH  0x0d /* Ethernet EndNode Hello        */
+
+/* Values for info field in hello message */
+#define DN_RT_INFO_TYPE 0x03 /* Type mask                     */
+#define DN_RT_INFO_L1RT 0x02 /* L1 Router                     */
+#define DN_RT_INFO_L2RT 0x01 /* L2 Router                     */
+#define DN_RT_INFO_ENDN 0x03 /* EndNode                       */
+#define DN_RT_INFO_VERI 0x04 /* Verification Reqd.            */
+#define DN_RT_INFO_RJCT 0x08 /* Reject Flag, Reserved         */
+#define DN_RT_INFO_VFLD 0x10 /* Verification Failed, Reserved */
+#define DN_RT_INFO_NOML 0x20 /* No Multicast traffic accepted */
+#define DN_RT_INFO_BLKR 0x40 /* Blocking Requested            */
+
+
+struct dn_route {
+	union {
+		struct dst_entry dst;
+		struct dn_route *rt_next;
+	} u;
+	unsigned short rt_saddr;
+	unsigned short rt_daddr;
+	int rt_iif;
+	int rt_oif;
+};
+
+extern void dn_route_init(void);
+extern void dn_route_cleanup(void);
+
+#endif /* _NET_DN_ROUTE_H */
diff -u --recursive --new-file v2.3.3/linux/include/net/dst.h linux/include/net/dst.h
--- v2.3.3/linux/include/net/dst.h	Tue May 11 13:05:02 1999
+++ linux/include/net/dst.h	Wed May 26 18:14:37 1999
@@ -77,9 +77,6 @@
 
 #ifdef __KERNEL__
 
-extern struct dst_entry * dst_garbage_list;
-extern atomic_t	dst_total;
-
 extern __inline__
 struct dst_entry * dst_clone(struct dst_entry * dst)
 {
diff -u --recursive --new-file v2.3.3/linux/include/net/ip.h linux/include/net/ip.h
--- v2.3.3/linux/include/net/ip.h	Tue May 11 13:05:15 1999
+++ linux/include/net/ip.h	Mon May 31 22:07:43 1999
@@ -83,7 +83,6 @@
  *	Functions provided by ip.c
  */
 
-extern int		ip_ioctl(struct sock *sk, int cmd, unsigned long arg);
 extern void		ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk,
 					      u32 saddr, u32 daddr,
 					      struct ip_options *opt);
diff -u --recursive --new-file v2.3.3/linux/include/net/ip_masq.h linux/include/net/ip_masq.h
--- v2.3.3/linux/include/net/ip_masq.h	Tue May 11 13:05:13 1999
+++ linux/include/net/ip_masq.h	Sat May 29 11:06:06 1999
@@ -231,24 +231,6 @@
 
 extern rwlock_t __ip_masq_lock;
 
-#ifdef __SMP__
-#define read_lock_bh(lock) 	do { start_bh_atomic(); read_lock(lock); \
-					} while (0)
-#define read_unlock_bh(lock)	do { read_unlock(lock); end_bh_atomic(); \
-					} while (0)
-#define write_lock_bh(lock)	do { start_bh_atomic(); write_lock(lock); \
-					} while (0)
-#define write_unlock_bh(lock)	do { write_unlock(lock); end_bh_atomic(); \
-					} while (0)
-#else
-#define read_lock_bh(lock)	start_bh_atomic()
-#define read_unlock_bh(lock)	end_bh_atomic()
-#define write_lock_bh(lock)	start_bh_atomic()
-#define write_unlock_bh(lock)	end_bh_atomic()
-#endif 
-/*
- *
- */
 
 /*
  *	Debugging stuff
diff -u --recursive --new-file v2.3.3/linux/include/net/irda/crc.h linux/include/net/irda/crc.h
--- v2.3.3/linux/include/net/irda/crc.h	Wed Jan 20 11:05:33 1999
+++ linux/include/net/irda/crc.h	Sun May 30 10:27:04 1999
@@ -6,25 +6,28 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Mon Aug  4 20:40:53 1997
- * Modified at:   Tue Dec 15 22:18:53 1998
+ * Modified at:   Sun May  2 20:25:23 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
  ********************************************************************/
 
-#ifndef IR_CRC_H
-#define IR_CRC_H
+#ifndef IRDA_CRC_H
+#define IRDA_CRC_H
 
 #include <linux/types.h>
 
 #define INIT_FCS  0xffff   /* Initial FCS value */
 #define GOOD_FCS  0xf0b8   /* Good final FCS value */
 
+extern __u16 const irda_crc16_table[];
+
 /* Recompute the FCS with one more character appended. */
-#define IR_FCS(fcs, c) (((fcs) >> 8) ^ irda_crc16_table[((fcs) ^ (c)) & 0xff])
+static inline __u16 irda_fcs(__u16 fcs, __u8 c)
+{
+	return (((fcs) >> 8) ^ irda_crc16_table[((fcs) ^ (c)) & 0xff]);
+}
 
 /* Recompute the FCS with len bytes appended. */
 unsigned short crc_calc( __u16 fcs, __u8 const *buf, size_t len);
-
-extern __u16 const irda_crc16_table[];
 
 #endif
diff -u --recursive --new-file v2.3.3/linux/include/net/irda/dongle.h linux/include/net/irda/dongle.h
--- v2.3.3/linux/include/net/irda/dongle.h	Sun Mar  7 15:26:43 1999
+++ linux/include/net/irda/dongle.h	Sun May 30 10:27:04 1999
@@ -6,10 +6,10 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Wed Oct 21 22:47:12 1998
- * Modified at:   Sat Feb  6 07:37:49 1999
+ * Modified at:   Mon May 10 14:51:06 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1998 Dag Brattli, All Rights Reserved.
+ *     Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
  *      
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
@@ -26,7 +26,6 @@
 #define DONGLE_H
 
 #include <net/irda/qos.h>
-#include <net/irda/irda_device.h>
 
 /* These are the currently known dongles */
 typedef enum {
@@ -35,7 +34,10 @@
 	ACTISYS_DONGLE,
 	ACTISYS_PLUS_DONGLE,
 	GIRBIL_DONGLE,
+	LITELINK_DONGLE,
 } DONGLE_T;
+
+struct irda_device;
 
 struct dongle {
 	DONGLE_T type;
diff -u --recursive --new-file v2.3.3/linux/include/net/irda/ircomm_common.h linux/include/net/irda/ircomm_common.h
--- v2.3.3/linux/include/net/irda/ircomm_common.h	Fri May 14 18:55:30 1999
+++ linux/include/net/irda/ircomm_common.h	Sun May 30 10:27:04 1999
@@ -6,7 +6,7 @@
  * Status:        Experimental.
  * Author:        Takahide Higuchi <thiguchi@pluto.dti.ne.jp>
  *
- *     Copyright (c) 1998, Takahide Higuchi, <thiguchi@pluto.dti.ne.jp>,
+ *     Copyright (c) 1998-1999, Takahide Higuchi, <thiguchi@pluto.dti.ne.jp>,
  *     All Rights Reserved.
  *
  *     This program is free software; you can redistribute it and/or
@@ -78,7 +78,7 @@
 #define IRCOMM_MAGIC            0x434f4d4d
 #define COMM_INIT_CTRL_PARAM    3          /* length of initial control parameters */
 #define COMM_HEADER             1          /* length of clen field */
-#define COMM_HEADER_SIZE        (LAP_HEADER+LMP_HEADER+TTP_HEADER+COMM_HEADER)
+#define COMM_HEADER_SIZE        (TTP_MAX_HEADER+COMM_HEADER)
 #define COMM_DEFAULT_DATA_SIZE  64
 #define IRCOMM_MAX_CONNECTION   1          /* Don't change for now */
 
@@ -167,7 +167,7 @@
 #define LSR_BI     0x01    /* Break interrupt indicator */
 
 
-struct ircomm_cb{
+struct ircomm_cb {
 	int magic;
 	int state;          /* Current state of IrCOMM layer: 
 			     *  DISCOVERY,COMM_IDLE, COMM_WAITR,
@@ -178,7 +178,8 @@
 	int ttp_stop;
 
 	int max_txbuff_size;          
-	__u32 maxsdusize;
+	__u32 max_sdu_size;
+	__u8 max_header_size;
 
  	__u32 daddr;        /* Device address of the peer device */ 
 	__u32 saddr;
@@ -211,8 +212,6 @@
 	int                 pending_control_tuples;
 	int                 ignored_control_tuples;
 
-
-
 	__u8 pi ;            /* instruction of control channel*/ 
 
 	__u8 port_type;
@@ -252,8 +251,6 @@
 	char port_name[33];
 	int port_name_critical;
 };
-
-
 
 void ircomm_connect_request(struct ircomm_cb *self, __u8 servicetype);
 void ircomm_connect_response(struct ircomm_cb *self, struct sk_buff *userdata,
diff -u --recursive --new-file v2.3.3/linux/include/net/irda/irda.h linux/include/net/irda/irda.h
--- v2.3.3/linux/include/net/irda/irda.h	Fri May 14 18:55:30 1999
+++ linux/include/net/irda/irda.h	Sun May 30 10:27:04 1999
@@ -6,10 +6,10 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Tue Dec  9 21:13:12 1997
- * Modified at:   Wed Apr 21 17:49:00 1999
+ * Modified at:   Mon May 10 09:51:13 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1998 Dag Brattli, All Rights Reserved.
+ *     Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
  *      
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
@@ -109,6 +109,8 @@
 	
 	__u32 max_sdu_size_rx;
 	__u32 max_sdu_size_tx;
+	__u32 max_data_size;
+	__u8  max_header_size;
 	struct qos_info qos_tx;
 
 	__u16 mask;           /* Hint bits mask */
@@ -225,10 +227,10 @@
 	int (*udata_indication)(void *priv, void *sap, struct sk_buff *skb);
 	void (*connect_confirm)(void *instance, void *sap, 
 				struct qos_info *qos, __u32 max_sdu_size,
-				struct sk_buff *skb);
+				__u8 max_header_size, struct sk_buff *skb);
 	void (*connect_indication)(void *instance, void *sap, 
 				   struct qos_info *qos, __u32 max_sdu_size,
-				   struct sk_buff *skb);
+				   __u8 max_header_size, struct sk_buff *skb);
 	void (*disconnect_indication)(void *instance, void *sap, 
 				      LM_REASON reason, struct sk_buff *);
 	void (*flow_indication)(void *instance, void *sap, LOCAL_FLOW flow);
diff -u --recursive --new-file v2.3.3/linux/include/net/irda/irda_device.h linux/include/net/irda/irda_device.h
--- v2.3.3/linux/include/net/irda/irda_device.h	Sat Apr 24 17:50:05 1999
+++ linux/include/net/irda/irda_device.h	Sun May 30 10:27:04 1999
@@ -4,24 +4,29 @@
  * Version:       
  * Description:   
  * Status:        Experimental.
- * Author:        Haris Zukanovic <haris@stud.cs.uit.no>
+ * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Tue Apr 14 12:41:42 1998
- * Modified at:   Tue Apr 20 11:06:28 1999
+ * Modified at:   Mon May 10 15:46:02 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1998 Haris Zukanovic, <haris@stud.cs.uit.no>
- *     Copyright (c) 1998 Dag Brattli, <dagb@cs.uit.no>
+ *     Copyright (c) 1999 Dag Brattli, All Rights Reserved.
  *     Copyright (c) 1998 Thomas Davis, <ratbert@radiks.net>,
- *     All Rights Reserved.
- *      
+ *     Copyright (c) 1998 Haris Zukanovic, <haris@stud.cs.uit.no>
+ *
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
  *     published by the Free Software Foundation; either version 2 of 
  *     the License, or (at your option) any later version.
- *  
- *     Neither Haris Zukanovic nor University of Tromsř admit liability nor
- *     provide warranty for any of this software. This material is 
- *     provided "AS-IS" and at no charge.
+ * 
+ *     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., 59 Temple Place, Suite 330, Boston, 
+ *     MA 02111-1307 USA
  *     
  ********************************************************************/
 
@@ -35,6 +40,7 @@
 
 #include <net/irda/irda.h>
 #include <net/irda/qos.h>
+#include <net/irda/dongle.h>
 #include <net/irda/irqueue.h>
 #include <net/irda/irlap_frame.h>
 
@@ -52,6 +58,11 @@
 #define IO_XMIT 0x01
 #define IO_RECV 0x02
 
+struct dongle_q {
+	QUEUE q;
+	struct dongle *dongle;
+};
+
 /* Chip specific info */
 struct chipio_t {
         int iobase, iobase2;  /* IO base */
@@ -111,6 +122,8 @@
 	struct iobuff_t tx_buff;
 	struct iobuff_t rx_buff;
 
+	struct dongle *dongle; /* Dongle driver */
+
 	/* spinlock_t lock; */ /* For serializing operations */
 	
 	/* Media busy stuff */
@@ -120,7 +133,8 @@
 	/* Callbacks for driver specific implementation */
         void (*change_speed)(struct irda_device *driver, int baud);
  	int  (*is_receiving)(struct irda_device *);    /* receiving? */
-	/* int (*is_tbusy)(struct irda_device *); */   /* transmitting? */
+	void (*set_dtr_rts)(struct irda_device *idev, int dtr, int rts);
+	int  (*raw_write)(struct irda_device *idev, __u8 *buf, int len);
 	void (*wait_until_sent)(struct irda_device *);
 	void (*set_caddr)(struct irda_device *);      /* Set connection addr */
 };
@@ -142,6 +156,9 @@
 
 inline struct qos_info *irda_device_get_qos(struct irda_device *self);
 int irda_device_txqueue_empty(struct irda_device *self);
+void irda_device_init_dongle(struct irda_device *self, int type);
+void irda_device_unregister_dongle(struct dongle *dongle);
+int irda_device_register_dongle(struct dongle *dongle);
 
 int irda_device_setup(struct device *dev);
 
@@ -153,7 +170,7 @@
  *    Utility function for getting the minimum turnaround time out of 
  *    the skb, where it has been hidden in the cb field.
  */
-inline static __u16 irda_get_mtt(struct sk_buff *skb)
+extern inline __u16 irda_get_mtt(struct sk_buff *skb)
 {
 	__u16 mtt;
 
@@ -165,6 +182,23 @@
 	ASSERT(mtt <= 10000, return 10000;);
 	
 	return mtt;
+}
+
+extern inline void irda_device_set_dtr_rts(struct irda_device *self, int dtr,
+					   int rts)
+{
+	if (self->set_dtr_rts)
+		self->set_dtr_rts(self, dtr, rts);
+}
+
+extern inline int irda_device_raw_write(struct irda_device *self, __u8 *buf,
+					int len)
+{
+	int ret = -1;
+
+	if (self->raw_write)
+		ret = self->raw_write(self, buf, len);
+	return ret;
 }
 
 #endif
diff -u --recursive --new-file v2.3.3/linux/include/net/irda/iriap.h linux/include/net/irda/iriap.h
--- v2.3.3/linux/include/net/irda/iriap.h	Sat Apr 24 17:50:05 1999
+++ linux/include/net/irda/iriap.h	Sun May 30 10:27:04 1999
@@ -6,10 +6,10 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Thu Aug 21 00:02:07 1997
- * Modified at:   Wed Apr 21 16:37:21 1999
+ * Modified at:   Sun May  9 10:56:57 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1997 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved.
+ *     Copyright (c) 1997, 1999 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved.
  *     
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
@@ -81,6 +81,8 @@
 	CONFIRM_CALLBACK confirm;
 	void *priv;
 
+	__u8 max_header_size;
+	
 	struct timer_list watchdog_timer;
 };
 
@@ -92,8 +94,6 @@
 void iriap_getvaluebyclass_confirm(struct iriap_cb *self, struct sk_buff *skb);
 
 void iriap_send_ack( struct iriap_cb *self);
-void iriap_connect_confirm(void *instance, void *sap, struct qos_info *qos, 
-			   __u32 max_sdu_size, struct sk_buff *skb);
 void iriap_call_indication(struct iriap_cb *self, struct sk_buff *skb);
 
 void iriap_register_server(void);
diff -u --recursive --new-file v2.3.3/linux/include/net/irda/irlan_common.h linux/include/net/irda/irlan_common.h
--- v2.3.3/linux/include/net/irda/irlan_common.h	Sat Apr 24 17:50:05 1999
+++ linux/include/net/irda/irlan_common.h	Sun May 30 10:27:04 1999
@@ -6,10 +6,11 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Sun Aug 31 20:14:37 1997
- * Modified at:   Thu Apr 22 14:30:37 1999
+ * Modified at:   Sun May  9 11:45:33 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved.
+ *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, 
+ *     All Rights Reserved.
  *     
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
@@ -98,7 +99,7 @@
 #define IRLAN_SHORT  1
 #define IRLAN_ARRAY  2
 
-#define IRLAN_MAX_HEADER (TTP_HEADER+LMP_HEADER+LAP_HEADER)
+#define IRLAN_MAX_HEADER (TTP_HEADER+LMP_HEADER+LAP_MAX_HEADER)
 
 /*
  *  IrLAN client
@@ -109,7 +110,10 @@
 	int open_retries;
 
 	struct tsap_cb *tsap_ctrl;
+	__u32 max_sdu_size;
+	__u8  max_header_size;
 	
+	int access_type;         /* Access type of provider */
 	__u8 reconnect_key[255];
 	__u8 key_len;
 	
@@ -130,6 +134,8 @@
 	int state;
 	
 	struct tsap_cb *tsap_ctrl;
+	__u32 max_sdu_size;
+	__u8  max_header_size;
 
 	/*
 	 *  Store some values here which are used by the provider to parse
@@ -140,42 +146,45 @@
 	int filter_mode;
 	int filter_operation;
 	int filter_entry;
-
+	int access_type;     /* Access type */
 	__u16 send_arb_val;
 
 	__u8 mac_address[6]; /* Generated MAC address for peer device */
 };
 
 /*
- *  IrLAN
+ *  IrLAN control block
  */
 struct irlan_cb {
 	QUEUE queue; /* Must be first */
 
 	int    magic;
 	char   ifname[9];
-	struct device dev;  /* Ethernet device structure*/
+	struct device dev;        /* Ethernet device structure*/
 	struct enet_statistics stats;
 
-	__u32 saddr;        /* Source devcie address */
-	__u32 daddr;        /* Destination device address */
+	__u32 saddr;              /* Source devcie address */
+	__u32 daddr;              /* Destination device address */
 	int   netdev_registered;
 	int   notify_irmanager;
 	
-	int media;          /* Media type */
-	int access_type;    /* Currently used access type */
-	__u8 version[2];    /* IrLAN version */
+	int media;                /* Media type */
+	__u8 version[2];          /* IrLAN version */
 	
 	struct tsap_cb *tsap_data;
 
-	int  use_udata;  /* Use Unit Data transfers */
+	int  master;              /* Master instance? */
+	int  use_udata;           /* Use Unit Data transfers */
 
-	__u8 stsap_sel_data; /* Source data TSAP selector */
-	__u8 dtsap_sel_data; /* Destination data TSAP selector */
-	__u8 dtsap_sel_ctrl; /* Destination ctrl TSAP selector */
+	__u8 stsap_sel_data;      /* Source data TSAP selector */
+	__u8 dtsap_sel_data;      /* Destination data TSAP selector */
+	__u8 dtsap_sel_ctrl;      /* Destination ctrl TSAP selector */
 
-	struct irlan_client_cb client;     /* Client specific fields */
+	struct irlan_client_cb   client;   /* Client specific fields */
 	struct irlan_provider_cb provider; /* Provider specific fields */
+
+	__u32 max_sdu_size;
+	__u8  max_header_size;
 	
 	struct timer_list watchdog_timer;
 };
diff -u --recursive --new-file v2.3.3/linux/include/net/irda/irlan_provider.h linux/include/net/irda/irlan_provider.h
--- v2.3.3/linux/include/net/irda/irlan_provider.h	Sat Apr 24 17:50:05 1999
+++ linux/include/net/irda/irlan_provider.h	Sun May 30 10:27:04 1999
@@ -6,10 +6,10 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Sun Aug 31 20:14:37 1997
- * Modified at:   Thu Apr 22 14:29:16 1999
+ * Modified at:   Sun May  9 12:26:11 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved.
+ *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved.
  *     
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
@@ -36,13 +36,7 @@
 					       LM_REASON reason, 
 					       struct sk_buff *skb);
 
-void irlan_provider_ctrl_data_indication(void *instance, void *sap,
-					 struct sk_buff *skb);
 
-void irlan_provider_connect_indication(void *instance, void *sap, 
-				       struct qos_info *qos, 
-				       __u32 max_sdu_size,
-				       struct sk_buff *skb);
 void irlan_provider_connect_response(struct irlan_cb *, struct tsap_cb *);
 
 int irlan_parse_open_data_cmd(struct irlan_cb *self, struct sk_buff *skb);
diff -u --recursive --new-file v2.3.3/linux/include/net/irda/irlap.h linux/include/net/irda/irlap.h
--- v2.3.3/linux/include/net/irda/irlap.h	Sat Apr 24 17:50:05 1999
+++ linux/include/net/irda/irlap.h	Sun May 30 10:27:04 1999
@@ -6,10 +6,10 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Mon Aug  4 20:40:53 1997
- * Modified at:   Fri Apr 23 09:51:15 1999
+ * Modified at:   Sun May  9 11:38:18 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved.
+ *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved.
  *     
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
@@ -42,11 +42,11 @@
 #define LAP_COMP_HEADER 1  /* IrLAP Compression Header */
 
 #ifdef CONFIG_IRDA_COMPRESSION
-#  define LAP_HEADER  (LAP_ADDR_HEADER + LAP_CTRL_HEADER + LAP_COMP_HEADER)
+#  define LAP_MAX_HEADER  (LAP_ADDR_HEADER + LAP_CTRL_HEADER + LAP_COMP_HEADER)
 #  define IRDA_COMPRESSED 1
 #  define IRDA_NORMAL     0
 #else
-#define LAP_HEADER (LAP_ADDR_HEADER + LAP_CTRL_HEADER)
+#define LAP_MAX_HEADER (LAP_ADDR_HEADER + LAP_CTRL_HEADER)
 #endif
 
 #define BROADCAST  0xffffffff /* Broadcast device address */
@@ -138,7 +138,7 @@
 
 	__u8    vs;           /* Next frame to be sent */
 	__u8    vr;           /* Next frame to be received */
-	int     tmp;
+/* 	int     tmp; */
 	__u8    va;           /* Last frame acked */
  	int     window;       /* Nr of I-frames allowed to send */
 	int     window_size;  /* Current negotiated window size */
@@ -155,8 +155,7 @@
  	__u8    s;           /* Current slot */
 	int     frame_sent;  /* Have we sent reply? */
 
-	int discovery_count;
-	hashbin_t *discovery_log;
+	hashbin_t   *discovery_log;
  	discovery_t *discovery_cmd;
 
 	struct qos_info qos_tx;    /* QoS requested by peer */
@@ -226,5 +225,10 @@
 void irlap_init_qos_capabilities(struct irlap_cb *, struct qos_info *);
 void irlap_apply_default_connection_parameters(struct irlap_cb *self);
 void irlap_apply_connection_parameters(struct irlap_cb *, struct qos_info *);
+
+extern inline __u8 irlap_get_header_size(struct irlap_cb *self)
+{
+	return 2;
+}
 
 #endif
diff -u --recursive --new-file v2.3.3/linux/include/net/irda/irlmp.h linux/include/net/irda/irlmp.h
--- v2.3.3/linux/include/net/irda/irlmp.h	Sat Apr 24 17:50:06 1999
+++ linux/include/net/irda/irlmp.h	Sun May 30 10:27:04 1999
@@ -6,10 +6,10 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Sun Aug 17 20:54:32 1997
- * Modified at:   Fri Apr 23 09:15:07 1999
+ * Modified at:   Sun May  9 11:01:34 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved.
+ *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved.
  *     
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
@@ -52,7 +52,7 @@
 
 #define LMP_HEADER          2    /* Dest LSAP + Source LSAP */
 #define LMP_CONTROL_HEADER  4
-#define LMP_MAX_HEADER      (LAP_HEADER+LMP_HEADER)
+#define LMP_MAX_HEADER      (LMP_CONTROL_HEADER+LAP_MAX_HEADER)
 
 #define LM_MAX_CONNECTIONS  10
 
diff -u --recursive --new-file v2.3.3/linux/include/net/irda/irlpt_common.h linux/include/net/irda/irlpt_common.h
--- v2.3.3/linux/include/net/irda/irlpt_common.h	Fri May 14 18:55:30 1999
+++ linux/include/net/irda/irlpt_common.h	Sun May 30 10:27:04 1999
@@ -158,7 +158,8 @@
 	struct miscdevice ir_dev; /* used to register the misc device. */
 
 	int count;                /* open count */
-	int irlap_data_size;	/* max frame size we can send */
+	int max_data_size;	/* max frame size we can send */
+	int max_header_size;    /* how much header space is needed */
 	int pkt_count;		/* how many packets are queued up */
 
 	wait_queue_head_t read_wait;	/* wait queues */
diff -u --recursive --new-file v2.3.3/linux/include/net/irda/irport.h linux/include/net/irda/irport.h
--- v2.3.3/linux/include/net/irda/irport.h	Wed Jan 20 11:05:33 1999
+++ linux/include/net/irda/irport.h	Sun May 30 10:27:04 1999
@@ -6,10 +6,10 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Sun Aug  3 13:49:59 1997
- * Modified at:   Thu Jan  7 14:17:31 1999
+ * Modified at:   Mon May 10 22:12:56 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1997, 1998 Dag Brattli <dagb@cs.uit.no>
+ *     Copyright (c) 1997, 1998-1999 Dag Brattli <dagb@cs.uit.no>
  *     All Rights Reserved.
  *     
  *     This program is free software; you can redistribute it and/or 
@@ -49,13 +49,13 @@
 
 #define FRAME_MAX_SIZE 2048
 
-void irport_close( int iobase);
-int  irport_open( int iobase);
-int  irport_detect(struct irda_device *idev);
+void irport_start(int iobase);
+void irport_stop(int iobase);
+int  irport_probe(int iobase);
 
-void irport_change_speed( int iobase, int speed);
+void irport_change_speed(struct irda_device *idev, int speed);
 void irport_interrupt(int irq, void *dev_id, struct pt_regs *regs);
 
-int  irport_hard_xmit( struct sk_buff *skb, struct device *dev);
+int  irport_hard_xmit(struct sk_buff *skb, struct device *dev);
 
 #endif
diff -u --recursive --new-file v2.3.3/linux/include/net/irda/irttp.h linux/include/net/irda/irttp.h
--- v2.3.3/linux/include/net/irda/irttp.h	Sat Apr 24 17:50:06 1999
+++ linux/include/net/irda/irttp.h	Sun May 30 10:27:04 1999
@@ -6,10 +6,10 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Sun Aug 31 20:14:31 1997
- * Modified at:   Sat Apr 10 10:19:56 1999
+ * Modified at:   Mon May 10 19:14:51 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved.
+ *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved.
  *     
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
@@ -36,7 +36,8 @@
 
 #define TTP_MAX_CONNECTIONS    LM_MAX_CONNECTIONS
 #define TTP_HEADER             1
-#define TTP_HEADER_WITH_SAR    6
+#define TTP_MAX_HEADER         (TTP_HEADER + LMP_MAX_HEADER)
+#define TTP_SAR_HEADER         5
 #define TTP_PARAMETERS         0x80
 #define TTP_MORE               0x80
 
@@ -61,8 +62,6 @@
 	QUEUE queue;          /* For linking it into the hashbin */
 	int  magic;           /* Just in case */
 
-	int max_seg_size;     /* Max data that fit into an IrLAP frame */
-
 	__u8 stsap_sel;       /* Source TSAP */
 	__u8 dtsap_sel;       /* Destination TSAP */
 
@@ -88,6 +87,9 @@
 	struct irda_statistics stats;
 	struct timer_list todo_timer; 
 	
+	__u32 max_seg_size;     /* Max data that fit into an IrLAP frame */
+	__u8  max_header_size;
+
 	int   rx_sdu_busy;     /* RxSdu.busy */
 	__u32 rx_sdu_size;     /* Current size of a partially received frame */
 	__u32 rx_max_sdu_size; /* Max receive user data size */
@@ -120,8 +122,6 @@
 			  __u32 saddr, __u32 daddr,
 			  struct qos_info *qos, __u32 max_sdu_size, 
 			  struct sk_buff *userdata);
-void irttp_connect_confirm(void *instance, void *sap, struct qos_info *qos, 
-			   __u32 max_sdu_size, struct sk_buff *skb);
 void irttp_connect_response(struct tsap_cb *self, __u32 max_sdu_size, 
 			    struct sk_buff *userdata);
 struct tsap_cb *irttp_dup(struct tsap_cb *self, void *instance);
diff -u --recursive --new-file v2.3.3/linux/include/net/irda/irtty.h linux/include/net/irda/irtty.h
--- v2.3.3/linux/include/net/irda/irtty.h	Sun Mar  7 15:26:44 1999
+++ linux/include/net/irda/irtty.h	Sun May 30 10:27:04 1999
@@ -6,10 +6,10 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Tue Dec  9 21:13:12 1997
- * Modified at:   Sun Feb  7 01:57:33 1999
+ * Modified at:   Mon May 10 13:22:23 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  *  
- *     Copyright (c) 1997 Dag Brattli, All Rights Reserved.
+ *     Copyright (c) 1997, 1999 Dag Brattli, All Rights Reserved.
  *      
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
@@ -33,8 +33,6 @@
 #include <net/irda/irqueue.h>
 #include <net/irda/irda_device.h>
 
-#include <net/irda/dongle.h>
-
 #define IRTTY_IOC_MAGIC 'e'
 #define IRTTY_IOCTDONGLE  _IO(IRTTY_IOC_MAGIC, 1)
 #define IRTTY_IOC_MAXNR   1
@@ -43,28 +41,21 @@
 #define N_IRDA         11   /* This one should go in </asm/termio.h> */
 #endif
 
-struct dongle_q {
-	QUEUE q;
-
-	struct dongle *dongle;
-};
-
 struct irtty_cb {
 	QUEUE q; /* Must be first */
 
-/* 	char name[16]; */
-
 	int	magic;
 	
 	struct  tty_struct  *tty;  /* Ptr to TTY structure */
 	struct  irda_device idev;
-
-	struct dongle_q *dongle_q; /* Has this tty got a dongle attached? */
 };
  
-int irtty_register_dongle( struct dongle *dongle);
-void irtty_unregister_dongle( struct dongle *dongle);
-
-void irtty_set_dtr_rts(struct tty_struct *tty, int dtr, int rts);
+int irtty_register_dongle(struct dongle *dongle);
+void irtty_unregister_dongle(struct dongle *dongle);
 
 #endif
+
+
+
+
+
diff -u --recursive --new-file v2.3.3/linux/include/net/irda/toshoboe.h linux/include/net/irda/toshoboe.h
--- v2.3.3/linux/include/net/irda/toshoboe.h	Wed Dec 31 16:00:00 1969
+++ linux/include/net/irda/toshoboe.h	Sun May 30 10:27:04 1999
@@ -0,0 +1,165 @@
+/*********************************************************************
+ *                
+ * Filename:      toshoboe.h
+ * Version:       0.1
+ * Description:   Driver for the Toshiba OBOE (or type-O)
+ *                FIR Chipset. 
+ * Status:        Experimental.
+ * Author:        James McKenzie <james@fishsoup.dhs.org>
+ * Created at:    Sat May 8  12:35:27 1999
+ * 
+ *     Copyright (c) 1999 James McKenzie, All Rights Reserved.
+ *      
+ *     This program is free software; you can redistribute it and/or 
+ *     modify it under the terms of the GNU General Public License as 
+ *     published by the Free Software Foundation; either version 2 of 
+ *     the License, or (at your option) any later version.
+ *  
+ *     Neither James McKenzie nor Cambridge University admit liability nor
+ *     provide warranty for any of this software. This material is 
+ *     provided "AS-IS" and at no charge.
+ *
+ *     Applicable Models : Libretto 100CT. and many more
+ *
+ ********************************************************************/
+
+/*
+ * $Log: toshoboe.h,v $
+ * Revision 1.2  1999/05/09 01:43:08  root
+ * *** empty log message ***
+ *
+ * Revision 1.1  1999/05/09 01:25:58  root
+ * Initial revision
+ *
+ */
+
+#ifndef TOSHOBOE_H
+#define TOSHOBOE_H
+
+/* Registers */
+/*Receive and transmit task registers (read only) */
+#define OBOE_RCVT	(0x00+(self->base))
+#define OBOE_XMTT	(0x01+(self->base))
+#define OBOE_XMTT_OFFSET	0x40
+
+/*Page pointers to the TaskFile structure */
+#define OBOE_TFP2	(0x02+(self->base))
+#define OBOE_TFP0	(0x04+(self->base))
+#define OBOE_TFP1	(0x05+(self->base))
+
+/*Dunno */
+#define OBOE_REG_3	(0x03+(self->base))
+
+/*Number of tasks to use in Xmit and Recv queues */
+#define OBOE_NTR	(0x07+(self->base))
+#define OBOE_NTR_XMIT4	0x00
+#define OBOE_NTR_XMIT8	0x10
+#define OBOE_NTR_XMIT16	0x30
+#define OBOE_NTR_XMIT32	0x70
+#define OBOE_NTR_XMIT64	0xf0
+#define OBOE_NTR_RECV4	0x00
+#define OBOE_NTR_RECV8	0x01
+#define OBOE_NTR_RECV6	0x03
+#define OBOE_NTR_RECV32	0x07
+#define OBOE_NTR_RECV64	0x0f
+
+/* Dunno */
+#define OBOE_REG_9	(0x09+(self->base))
+
+/* Interrupt Status Register */
+#define OBOE_ISR	(0x0c+(self->base))
+#define OBOE_ISR_TXDONE	0x80
+#define OBOE_ISR_RXDONE	0x40
+#define OBOE_ISR_20	0x20
+#define OBOE_ISR_10	0x10
+#define OBOE_ISR_8	0x08         /*This is collision or parity or something */
+#define OBOE_ISR_4	0x08
+#define OBOE_ISR_2	0x08
+#define OBOE_ISR_1	0x08
+
+/*Dunno */
+#define OBOE_REG_D	(0x0d+(self->base))
+
+/*Register Lock Register */
+#define OBOE_LOCK	((self->base)+0x0e)
+
+
+
+/*Speed control registers */
+#define OBOE_PMDL	(0x10+(self->base))
+#define OBOE_PMDL_SIR	0x18
+#define OBOE_PMDL_MIR	0xa0
+#define OBOE_PMDL_FIR	0x40
+
+#define OBOE_SMDL	(0x18+(self->base))
+#define OBOE_SMDL_SIR	0x20
+#define OBOE_SMDL_MIR	0x01
+#define OBOE_SMDL_FIR	0x0f
+
+#define OBOE_UDIV	(0x19+(self->base))
+
+/*Dunno */
+#define OBOE_REG_11	(0x11+(self->base))
+
+/*Chip Reset Register */
+#define OBOE_RST	(0x15+(self->base))
+#define OBOE_RST_WRAP	0x8
+
+/*Dunno */
+#define OBOE_REG_1A	(0x1a+(self->base))
+#define OBOE_REG_1B	(0x1b+(self->base))
+
+/* The PCI ID of the OBOE chip */
+#ifndef PCI_DEVICE_ID_FIR701
+#define PCI_DEVICE_ID_FIR701 	0x0701
+#endif
+
+typedef unsigned int dword;
+typedef unsigned short int word;
+typedef unsigned char byte;
+typedef dword Paddr;
+
+struct OboeTask
+  {
+    __u16 len;
+    __u8 unused;
+    __u8 control;
+    __u32 buffer;
+  };
+
+#define OBOE_NTASKS 64
+
+struct OboeTaskFile
+  {
+    struct OboeTask recv[OBOE_NTASKS];
+    struct OboeTask xmit[OBOE_NTASKS];
+  };
+
+#define OBOE_TASK_BUF_LEN (sizeof(struct OboeTaskFile) << 1)
+
+/*These set the number of slots in use */
+#define TX_SLOTS	4
+#define RX_SLOTS	4
+
+/* You need also to change this, toshiba uses 4,8 and 4,4 */
+/* It makes no difference if you are only going to use ONETASK mode */
+/* remember each buffer use XX_BUF_SZ more _PHYSICAL_ memory */
+#define OBOE_NTR_VAL 	(OBOE_NTR_XMIT4 | OBOE_NTR_RECV4)
+
+struct toshoboe_cb
+  {
+    struct irda_device idev;    /*IRDA device */
+    struct pci_dev *pdev;       /*PCI device */
+    int base;                   /*IO base */
+    int txpending;              /*how many tx's are pending */
+    int txs, rxs;               /*Which slots are we at  */
+    void *taskfilebuf;          /*The unaligned taskfile buffer */
+    struct OboeTaskFile *taskfile;  /*The taskfile   */
+    void *xmit_bufs[TX_SLOTS];  /*The buffers   */
+    void *recv_bufs[RX_SLOTS];
+  };
+
+
+#endif
+
+
diff -u --recursive --new-file v2.3.3/linux/include/net/irda/w83977af_ir.h linux/include/net/irda/w83977af_ir.h
--- v2.3.3/linux/include/net/irda/w83977af_ir.h	Thu Dec 17 09:01:03 1998
+++ linux/include/net/irda/w83977af_ir.h	Sun May 30 10:27:04 1999
@@ -6,10 +6,10 @@
  * Status:        Experimental.
  * Author:        Paul VanderSpek
  * Created at:    Thu Nov 19 13:55:34 1998
- * Modified at:   Thu Dec 10 14:06:18 1998
+ * Modified at:   Mon May  3 12:07:25 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1998 Dag Brattli, All Rights Reserved.
+ *     Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
  *      
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
@@ -149,6 +149,29 @@
 #define IRM_CR		0x07 /* Infrared module control register */
 #define IRM_CR_IRX_MSL	0x40
 #define IRM_CR_AF_MNT   0x80 /* Automatic format */
+
+/* For storing entries in the status FIFO */
+struct st_fifo_entry {
+	int status;
+	int len;
+};
+
+struct st_fifo {
+	struct st_fifo_entry entries[10];
+	int head;
+	int tail;
+	int len;
+};
+
+/* Private data for each instance */
+struct w83977af_ir {
+	struct st_fifo st_fifo;
+
+	int tx_buff_offsets[10]; /* Offsets between frames in tx_buff */
+	int tx_len;          /* Number of frames in tx_buff */
+
+	struct irda_device idev;
+};
 
 static inline void switch_bank( int iobase, int set)
 {
diff -u --recursive --new-file v2.3.3/linux/include/net/irda/wrapper.h linux/include/net/irda/wrapper.h
--- v2.3.3/linux/include/net/irda/wrapper.h	Sun Mar  7 15:26:44 1999
+++ linux/include/net/irda/wrapper.h	Sun May 30 10:27:04 1999
@@ -1,15 +1,16 @@
 /*********************************************************************
  *                
  * Filename:      wrapper.h
- * Version:       1.0
- * Description:   IrDA Wrapper layer
+ * Version:       1.2
+ * Description:   IrDA SIR async wrapper layer
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Mon Aug  4 20:40:53 1997
- * Modified at:   Fri Jan 29 10:15:46 1999
+ * Modified at:   Mon May  3 09:02:36 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved.
+ *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, 
+ *     All Rights Reserved.
  *     
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
@@ -38,17 +39,18 @@
 #define STA BOF  /* Start flag */
 #define STO EOF  /* End flag */
 
-#define IR_TRANS 0x20    /* Asynchronous transparency modifier */       
+#define IRDA_TRANS 0x20    /* Asynchronous transparency modifier */       
 
+/* States for receving a frame in async mode */
 enum {
-	OUTSIDE_FRAME = 1, 
+	OUTSIDE_FRAME, 
 	BEGIN_FRAME, 
 	LINK_ESCAPE, 
 	INSIDE_FRAME
 };
 
 /* Proto definitions */
-int  async_wrap_skb( struct sk_buff *skb, __u8 *tx_buff, int buffsize);
-void async_unwrap_char( struct irda_device *, __u8 byte);
+int async_wrap_skb(struct sk_buff *skb, __u8 *tx_buff, int buffsize);
+inline void async_unwrap_char(struct irda_device *idev, __u8 byte);
 
 #endif
diff -u --recursive --new-file v2.3.3/linux/include/net/route.h linux/include/net/route.h
--- v2.3.3/linux/include/net/route.h	Tue May 11 13:05:03 1999
+++ linux/include/net/route.h	Tue May 25 14:58:59 1999
@@ -94,8 +94,6 @@
 #endif
 };
 
-extern struct rtable 	*rt_hash_table[RT_HASH_DIVISOR];
-
 struct ip_rt_acct
 {
 	__u32 	o_bytes;
diff -u --recursive --new-file v2.3.3/linux/include/net/sock.h linux/include/net/sock.h
--- v2.3.3/linux/include/net/sock.h	Fri May 14 18:55:30 1999
+++ linux/include/net/sock.h	Wed May 26 09:36:35 1999
@@ -105,6 +105,7 @@
 	struct sock **		list;
 	struct sock *		gc_tree;
 	int			inflight;
+	atomic_t		user_count;
 };
 
 #ifdef CONFIG_NETLINK
@@ -353,6 +354,22 @@
 #define SOCK_DEBUG(sk, msg...) do { } while (0)
 #endif
 
+/* This is the per-socket lock.  The spinlock provides a synchronization
+ * between user contexts and software interrupt processing, whereas the
+ * mini-semaphore synchronizes multiple users amongst themselves.
+ */
+typedef struct {
+	spinlock_t		slock;
+	unsigned int		users;
+	wait_queue_head_t	wq;
+} socket_lock_t;
+
+#define sock_lock_init(__sk) \
+do {	spin_lock_init(&((__sk)->lock.slock)); \
+	(__sk)->lock.users = 0; \
+	init_waitqueue_head(&((__sk)->lock.wq)); \
+} while(0);
+
 struct sock {
 	/* This must be first. */
 	struct sock		*sklist_next;
@@ -381,7 +398,7 @@
 	unsigned char		reuse,		/* SO_REUSEADDR setting			*/
 				nonagle;	/* Disable Nagle algorithm?		*/
 
-	atomic_t		sock_readers;	/* User count				*/
+	socket_lock_t		lock;		/* Synchronizer...			*/
 	int			rcvbuf;		/* Size of receive buffer in bytes	*/
 
 	wait_queue_head_t	*sleep;	/* Sock wait queue			*/
@@ -415,9 +432,17 @@
 	int			hashent;
 	struct sock		*pair;
 
-	/* Error and backlog packet queues, rarely used. */
-	struct sk_buff_head	back_log,
-	                        error_queue;
+	/* The backlog queue is special, it is always used with
+	 * the per-socket spinlock held and requires low latency
+	 * access.  Therefore we special case it's implementation.
+	 */
+	struct {
+		struct sk_buff *head;
+		struct sk_buff *tail;
+	} backlog;
+
+	/* Error queue, rarely used. */
+	struct sk_buff_head	error_queue;
 
 	struct proto		*prot;
 
@@ -537,6 +562,18 @@
 	void                    (*destruct)(struct sock *sk);
 };
 
+/* The per-socket spinlock must be held here. */
+#define sk_add_backlog(__sk, __skb)			\
+do {	if((__sk)->backlog.tail == NULL) {		\
+		(__sk)->backlog.head =			\
+		     (__sk)->backlog.tail = (__skb);	\
+	} else {					\
+		((__sk)->backlog.tail)->next = (__skb);	\
+		(__sk)->backlog.tail = (__skb);		\
+	}						\
+	(__skb)->next = NULL;				\
+} while(0)
+
 /* IP protocol blocks we attach to sockets.
  * socket layer -> transport layer interface
  * transport -> network interface is defined by struct inet_proto
@@ -616,15 +653,26 @@
 /* Per-protocol hash table implementations use this to make sure
  * nothing changes.
  */
-#define SOCKHASH_LOCK()		start_bh_atomic()
-#define SOCKHASH_UNLOCK()	end_bh_atomic()
+extern rwlock_t sockhash_lock;
+#define SOCKHASH_LOCK_READ()		read_lock_bh(&sockhash_lock)
+#define SOCKHASH_UNLOCK_READ()		read_unlock_bh(&sockhash_lock)
+#define SOCKHASH_LOCK_WRITE()		write_lock_bh(&sockhash_lock)
+#define SOCKHASH_UNLOCK_WRITE()		write_unlock_bh(&sockhash_lock)
+
+/* The following variants must _only_ be used when you know you
+ * can only be executing in a BH context.
+ */
+#define SOCKHASH_LOCK_READ_BH()		read_lock(&sockhash_lock)
+#define SOCKHASH_UNLOCK_READ_BH()	read_unlock(&sockhash_lock)
+#define SOCKHASH_LOCK_WRITE_BH()	write_lock(&sockhash_lock)
+#define SOCKHASH_UNLOCK_WRITE_BH()	write_unlock(&sockhash_lock)
 
 /* Some things in the kernel just want to get at a protocols
  * entire socket list commensurate, thus...
  */
 static __inline__ void add_to_prot_sklist(struct sock *sk)
 {
-	SOCKHASH_LOCK();
+	SOCKHASH_LOCK_WRITE();
 	if(!sk->sklist_next) {
 		struct proto *p = sk->prot;
 
@@ -638,54 +686,37 @@
 		if(sk->prot->highestinuse < sk->prot->inuse)
 			sk->prot->highestinuse = sk->prot->inuse;
 	}
-	SOCKHASH_UNLOCK();
+	SOCKHASH_UNLOCK_WRITE();
 }
 
 static __inline__ void del_from_prot_sklist(struct sock *sk)
 {
-	SOCKHASH_LOCK();
+	SOCKHASH_LOCK_WRITE();
 	if(sk->sklist_next) {
 		sk->sklist_next->sklist_prev = sk->sklist_prev;
 		sk->sklist_prev->sklist_next = sk->sklist_next;
 		sk->sklist_next = NULL;
 		sk->prot->inuse--;
 	}
-	SOCKHASH_UNLOCK();
+	SOCKHASH_UNLOCK_WRITE();
 }
 
-/*
- * Used by processes to "lock" a socket state, so that
+/* Used by processes to "lock" a socket state, so that
  * interrupts and bottom half handlers won't change it
  * from under us. It essentially blocks any incoming
  * packets, so that we won't get any new data or any
  * packets that change the state of the socket.
  *
- * Note the 'barrier()' calls: gcc may not move a lock
- * "downwards" or a unlock "upwards" when optimizing.
- */
-extern void __release_sock(struct sock *sk);
-
-static inline void lock_sock(struct sock *sk)
-{
-#if 0
-/* debugging code: the test isn't even 100% correct, but it can catch bugs */
-/* Note that a double lock is ok in theory - it's just _usually_ a bug */
-	if (atomic_read(&sk->sock_readers)) {
-		__label__ here;
-		printk("double lock on socket at %p\n", &&here);
-here:
-	}
-#endif
-	atomic_inc(&sk->sock_readers);
-	synchronize_bh();
-}
-
-static inline void release_sock(struct sock *sk)
-{
-	barrier();
-	if (atomic_dec_and_test(&sk->sock_readers))
-		__release_sock(sk);
-}
+ * While locked, BH processing will add new packets to
+ * the backlog queue.  This queue is processed by the
+ * owner of the socket lock right before it is released.
+ */
+extern void lock_sock(struct sock *sk);
+extern void release_sock(struct sock *sk);
+
+/* BH context may only use the following locking interface. */
+#define bh_lock_sock(__sk)	spin_lock(&((__sk)->lock.slock))
+#define bh_unlock_sock(__sk)	spin_unlock(&((__sk)->lock.slock))
 
 /*
  *	This might not be the most appropriate place for this two	 
@@ -943,5 +974,27 @@
 #else
 #define NETDEBUG(x)	do { x; } while (0)
 #endif
+
+/*
+ * Macros for sleeping on a socket. Use them like this:
+ *
+ * SOCK_SLEEP_PRE(sk)
+ * if (condition)
+ * 	schedule();
+ * SOCK_SLEEP_POST(sk)
+ *
+ */
+
+#define SOCK_SLEEP_PRE(sk) 	{ struct task_struct *tsk = current; \
+				DECLARE_WAITQUEUE(wait, tsk); \
+				tsk->state = TASK_INTERRUPTIBLE; \
+				add_wait_queue((sk)->sleep, &wait); \
+				release_sock(sk);
+
+#define SOCK_SLEEP_POST(sk)	tsk->state = TASK_RUNNING; \
+				remove_wait_queue((sk)->sleep, &wait); \
+				lock_sock(sk); \
+				}
+
 
 #endif	/* _SOCK_H */
diff -u --recursive --new-file v2.3.3/linux/include/net/tcp.h linux/include/net/tcp.h
--- v2.3.3/linux/include/net/tcp.h	Fri May 14 18:55:30 1999
+++ linux/include/net/tcp.h	Tue May 25 14:59:00 1999
@@ -27,19 +27,16 @@
  * New scheme, half the table is for TIME_WAIT, the other half is
  * for the rest.  I'll experiment with dynamic table growth later.
  */
-#define TCP_HTABLE_SIZE		512
+extern int tcp_ehash_size;
+extern struct sock **tcp_ehash;
 
 /* This is for listening sockets, thus all sockets which possess wildcards. */
 #define TCP_LHTABLE_SIZE	32	/* Yes, really, this is all you need. */
 
-/* This is for all sockets, to keep track of the local port allocations. */
-#define TCP_BHTABLE_SIZE	512
-
 /* tcp_ipv4.c: These need to be shared by v4 and v6 because the lookup
  *             and hashing code needs to work with different AF's yet
  *             the port space is shared.
  */
-extern struct sock *tcp_established_hash[TCP_HTABLE_SIZE];
 extern struct sock *tcp_listening_hash[TCP_LHTABLE_SIZE];
 
 /* There are a few simple rules, which allow for local port reuse by
@@ -85,7 +82,9 @@
 	struct tcp_bind_bucket	**pprev;
 };
 
-extern struct tcp_bind_bucket *tcp_bound_hash[TCP_BHTABLE_SIZE];
+extern struct tcp_bind_bucket **tcp_bhash;
+extern int tcp_bhash_size;
+
 extern kmem_cache_t *tcp_bucket_cachep;
 extern struct tcp_bind_bucket *tcp_bucket_create(unsigned short snum);
 extern void tcp_bucket_unlock(struct sock *sk);
@@ -113,7 +112,7 @@
 /* These are AF independent. */
 static __inline__ int tcp_bhashfn(__u16 lport)
 {
-	return (lport & (TCP_BHTABLE_SIZE - 1));
+	return (lport & (tcp_bhash_size - 1));
 }
 
 static __inline__ void tcp_sk_bindify(struct sock *sk)
@@ -121,7 +120,7 @@
 	struct tcp_bind_bucket *tb;
 	unsigned short snum = sk->num;
 
-	for(tb = tcp_bound_hash[tcp_bhashfn(snum)]; tb->port != snum; tb = tb->next)
+	for(tb = tcp_bhash[tcp_bhashfn(snum)]; tb->port != snum; tb = tb->next)
 		;
 	/* Update bucket flags. */
 	if(tb->owners == NULL) {
diff -u --recursive --new-file v2.3.3/linux/kernel/fork.c linux/kernel/fork.c
--- v2.3.3/linux/kernel/fork.c	Fri May 14 18:55:30 1999
+++ linux/kernel/fork.c	Wed May 26 11:15:36 1999
@@ -390,7 +390,10 @@
 	return 0;
 
 free_mm:
-	mm->pgd = NULL;
+	tsk->mm = NULL;
+	release_segments(mm);
+	kmem_cache_free(mm_cachep, mm);
+	return retval;
 free_pt:
 	tsk->mm = NULL;
 	mmput(mm);
diff -u --recursive --new-file v2.3.3/linux/kernel/signal.c linux/kernel/signal.c
--- v2.3.3/linux/kernel/signal.c	Fri May 14 18:55:30 1999
+++ linux/kernel/signal.c	Mon May 24 22:47:44 1999
@@ -1039,7 +1039,7 @@
 #endif /* __sparc__ */
 #endif
 
-#if !defined(__alpha__)
+#if !defined(__alpha__) && !defined(__ia64__)
 /*
  * For backwards compatibility.  Functionality superseded by sigprocmask.
  */
@@ -1082,4 +1082,4 @@
 
 	return ret ? ret : (unsigned long)old_sa.sa.sa_handler;
 }
-#endif /* !alpha */
+#endif /* !alpha && !__ia64__ */
diff -u --recursive --new-file v2.3.3/linux/kernel/time.c linux/kernel/time.c
--- v2.3.3/linux/kernel/time.c	Thu Mar 11 23:26:03 1999
+++ linux/kernel/time.c	Mon May 24 22:47:44 1999
@@ -54,13 +54,15 @@
 	do_get_fast_time(t);
 }
 
-#ifndef __alpha__
+#if !defined(__alpha__) && !defined(__ia64__)
 
 /*
  * sys_time() can be implemented in user-level using
  * sys_gettimeofday().  Is this for backwards compatibility?  If so,
  * why not move it into the appropriate arch directory (for those
  * architectures that need it).
+ *
+ * XXX This function is NOT 64-bit clean!
  */
 asmlinkage int sys_time(int * tloc)
 {
diff -u --recursive --new-file v2.3.3/linux/mm/filemap.c linux/mm/filemap.c
--- v2.3.3/linux/mm/filemap.c	Fri May 14 18:55:30 1999
+++ linux/mm/filemap.c	Mon May 24 08:56:06 1999
@@ -1311,18 +1311,9 @@
 	struct vm_operations_struct * ops;
 	struct inode *inode = file->f_dentry->d_inode;
 
-	if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE)) {
+	ops = &file_private_mmap;
+	if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE))
 		ops = &file_shared_mmap;
-		/* share_page() can only guarantee proper page sharing if
-		 * the offsets are all page aligned. */
-		if (vma->vm_offset & (PAGE_SIZE - 1))
-			return -EINVAL;
-	} else {
-		ops = &file_private_mmap;
-		if (inode->i_op && inode->i_op->bmap &&
-		    (vma->vm_offset & (inode->i_sb->s_blocksize - 1)))
-			return -EINVAL;
-	}
 	if (!inode->i_sb || !S_ISREG(inode->i_mode))
 		return -EACCES;
 	if (!inode->i_op || !inode->i_op->readpage)
diff -u --recursive --new-file v2.3.3/linux/mm/memory.c linux/mm/memory.c
--- v2.3.3/linux/mm/memory.c	Mon Apr 12 16:18:26 1999
+++ linux/mm/memory.c	Wed May 26 11:15:36 1999
@@ -130,16 +130,14 @@
 {
 	pgd_t * page_dir = mm->pgd;
 
-	if (page_dir && page_dir != swapper_pg_dir) {
-		page_dir += first;
-		do {
-			free_one_pgd(page_dir);
-			page_dir++;
-		} while (--nr);
+	page_dir += first;
+	do {
+		free_one_pgd(page_dir);
+		page_dir++;
+	} while (--nr);
 
-		/* keep the page table cache within bounds */
-		check_pgt_cache();
-	}
+	/* keep the page table cache within bounds */
+	check_pgt_cache();
 }
 
 /*
diff -u --recursive --new-file v2.3.3/linux/mm/mmap.c linux/mm/mmap.c
--- v2.3.3/linux/mm/mmap.c	Tue May  4 15:44:38 1999
+++ linux/mm/mmap.c	Mon May 24 08:56:53 1999
@@ -185,6 +185,9 @@
 	if (len > TASK_SIZE || addr > TASK_SIZE-len)
 		return -EINVAL;
 
+	if (off & ~PAGE_MASK)
+		return -EINVAL;
+
 	/* offset overflow? */
 	if (off + len < off)
 		return -EINVAL;
diff -u --recursive --new-file v2.3.3/linux/mm/slab.c linux/mm/slab.c
--- v2.3.3/linux/mm/slab.c	Fri May 14 18:55:30 1999
+++ linux/mm/slab.c	Fri May 28 10:13:37 1999
@@ -892,7 +892,7 @@
 		left_over -= slab_align_size;
 	}
 
-	/* Offset must be a factor of the alignment. */
+	/* Offset must be a multiple of the alignment. */
 	offset += (align-1);
 	offset &= ~(align-1);
 
diff -u --recursive --new-file v2.3.3/linux/mm/swapfile.c linux/mm/swapfile.c
--- v2.3.3/linux/mm/swapfile.c	Mon May 17 09:55:23 1999
+++ linux/mm/swapfile.c	Fri May 28 15:52:46 1999
@@ -503,7 +503,9 @@
 	int swap_header_version;
 	int lock_map_size = PAGE_SIZE;
 	int nr_good_pages = 0;
+	unsigned long maxpages;
 	unsigned long tmp_lock_map = 0;
+	int swapfilesize;
 	
 	lock_kernel();
 	if (!capable(CAP_SYS_ADMIN))
@@ -542,27 +544,32 @@
 	error = -EINVAL;
 
 	if (S_ISBLK(swap_dentry->d_inode->i_mode)) {
-		p->swap_device = swap_dentry->d_inode->i_rdev;
-		set_blocksize(p->swap_device, PAGE_SIZE);
+		kdev_t dev = swap_dentry->d_inode->i_rdev;
+
+		p->swap_device = dev;
+		set_blocksize(dev, PAGE_SIZE);
 		
 		filp.f_dentry = swap_dentry;
 		filp.f_mode = 3; /* read write */
 		error = blkdev_open(swap_dentry->d_inode, &filp);
 		if (error)
 			goto bad_swap_2;
-		set_blocksize(p->swap_device, PAGE_SIZE);
+		set_blocksize(dev, PAGE_SIZE);
 		error = -ENODEV;
-		if (!p->swap_device ||
-		    (blk_size[MAJOR(p->swap_device)] &&
-		     !blk_size[MAJOR(p->swap_device)][MINOR(p->swap_device)]))
+		if (!dev || (blk_size[MAJOR(dev)] &&
+		     !blk_size[MAJOR(dev)][MINOR(dev)]))
 			goto bad_swap;
 		error = -EBUSY;
 		for (i = 0 ; i < nr_swapfiles ; i++) {
 			if (i == type)
 				continue;
-			if (p->swap_device == swap_info[i].swap_device)
+			if (dev == swap_info[i].swap_device)
 				goto bad_swap;
 		}
+		swapfilesize = 0;
+		if (blk_size[MAJOR(dev)])
+			swapfilesize = blk_size[MAJOR(dev)][MINOR(dev)]
+				/ (PAGE_SIZE / 1024);
 	} else if (S_ISREG(swap_dentry->d_inode->i_mode)) {
 		error = -EBUSY;
 		for (i = 0 ; i < nr_swapfiles ; i++) {
@@ -571,6 +578,7 @@
 			if (swap_dentry->d_inode == swap_info[i].swap_file->d_inode)
 				goto bad_swap;
 		}
+		swapfilesize = swap_dentry->d_inode->i_size / PAGE_SIZE;
 	} else
 		goto bad_swap;
 
@@ -639,11 +647,13 @@
 		p->highest_bit = swap_header->info.last_page - 1;
 		p->max	       = swap_header->info.last_page;
 
+		maxpages = SWP_OFFSET(SWP_ENTRY(0,~0UL));
+		if (p->max >= maxpages)
+			p->max = maxpages-1;
+
 		error = -EINVAL;
 		if (swap_header->info.nr_badpages > MAX_SWAP_BADPAGES)
 			goto bad_swap;
-		if (p->max >= SWP_OFFSET(SWP_ENTRY(0,~0UL)))
-			goto bad_swap;
 		
 		/* OK, set up the swap map and apply the bad block list */
 		if (!(p->swap_map = vmalloc (p->max * sizeof(short)))) {
@@ -666,6 +676,12 @@
 			goto bad_swap;
 	}
 	
+	if (swapfilesize && p->max > swapfilesize) {
+		printk(KERN_WARNING
+		       "Swap area shorter than signature indicates\n");
+		error = -EINVAL;
+		goto bad_swap;
+	}
 	if (!nr_good_pages) {
 		printk(KERN_WARNING "Empty swap-file\n");
 		error = -EINVAL;
diff -u --recursive --new-file v2.3.3/linux/net/Config.in linux/net/Config.in
--- v2.3.3/linux/net/Config.in	Thu Feb 25 10:46:47 1999
+++ linux/net/Config.in	Wed May 26 09:36:36 1999
@@ -32,10 +32,10 @@
 fi
 tristate 'Appletalk DDP' CONFIG_ATALK
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-#  tristate 'DECnet Support (NOT YET FUNCTIONAL)' CONFIG_DECNET
-#  if [ "$CONFIG_DECNET" != "n" ]; then
-#    source net/decnet/Config.in
-#  fi
+  tristate 'DECnet Support (EXPERIMENTAL)' CONFIG_DECNET
+  if [ "$CONFIG_DECNET" != "n" ]; then
+    source net/decnet/Config.in
+  fi
   tristate 'CCITT X.25 Packet Layer (EXPERIMENTAL)' CONFIG_X25
   tristate 'LAPB Data Link Driver (EXPERIMENTAL)' CONFIG_LAPB
   bool 'Bridging (EXPERIMENTAL)' CONFIG_BRIDGE
diff -u --recursive --new-file v2.3.3/linux/net/Makefile linux/net/Makefile
--- v2.3.3/linux/net/Makefile	Mon Mar 22 11:18:17 1999
+++ linux/net/Makefile	Wed May 26 09:36:36 1999
@@ -10,7 +10,7 @@
 MOD_SUB_DIRS := ipv4
 ALL_SUB_DIRS := 802 ax25 bridge core ethernet ipv4 ipv6 ipx unix appletalk \
 		netrom rose lapb x25 wanrouter netlink sched packet sunrpc \
-		econet irda #decnet
+		econet irda decnet
 SUB_DIRS     := core ethernet sched
 MOD_LIST_NAME := NET_MISC_MODULES
 
diff -u --recursive --new-file v2.3.3/linux/net/core/datagram.c linux/net/core/datagram.c
--- v2.3.3/linux/net/core/datagram.c	Fri May 14 18:55:30 1999
+++ linux/net/core/datagram.c	Mon May 31 22:07:43 1999
@@ -151,15 +151,15 @@
 		   is reentearble (it is not) or this function
 		   is called by interrupts.
 
-		   Protect it with global skb spinlock,
+		   Protect it with skb queue spinlock,
 		   though for now even this is overkill.
 		                                --ANK (980728)
 		 */
-		spin_lock_irqsave(&skb_queue_lock, cpu_flags);
+		spin_lock_irqsave(&sk->receive_queue.lock, cpu_flags);
 		skb = skb_peek(&sk->receive_queue);
 		if(skb!=NULL)
 			atomic_inc(&skb->users);
-		spin_unlock_irqrestore(&skb_queue_lock, cpu_flags);
+		spin_unlock_irqrestore(&sk->receive_queue.lock, cpu_flags);
 	} else
 		skb = skb_dequeue(&sk->receive_queue);
 
diff -u --recursive --new-file v2.3.3/linux/net/core/dev.c linux/net/core/dev.c
--- v2.3.3/linux/net/core/dev.c	Thu Mar 25 09:23:34 1999
+++ linux/net/core/dev.c	Mon May 31 22:07:43 1999
@@ -129,8 +129,9 @@
  *		86DD	IPv6
  */
 
-struct packet_type *ptype_base[16];		/* 16 way hashed list */
-struct packet_type *ptype_all = NULL;		/* Taps */
+static struct packet_type *ptype_base[16];		/* 16 way hashed list */
+static struct packet_type *ptype_all = NULL;		/* Taps */
+static rwlock_t ptype_lock = RW_LOCK_UNLOCKED;
 
 /*
  *	Device list lock. Setting it provides that interface
@@ -199,6 +200,7 @@
 		dev_clear_fastroute(pt->dev);
 	}
 #endif
+	write_lock_bh(&ptype_lock);
 	if(pt->type==htons(ETH_P_ALL))
 	{
 		netdev_nit++;
@@ -211,6 +213,7 @@
 		pt->next = ptype_base[hash];
 		ptype_base[hash] = pt;
 	}
+	write_unlock_bh(&ptype_lock);
 }
 
 
@@ -228,19 +231,21 @@
 	}
 	else
 		pt1=&ptype_base[ntohs(pt->type)&15];
+	write_lock_bh(&ptype_lock);
 	for(; (*pt1)!=NULL; pt1=&((*pt1)->next))
 	{
 		if(pt==(*pt1))
 		{
 			*pt1=pt->next;
-			synchronize_bh();
 #ifdef CONFIG_NET_FASTROUTE
 			if (pt->data)
 				netdev_fastroute_obstacles--;
 #endif
+			write_unlock_bh(&ptype_lock);
 			return;
 		}
 	}
+	write_unlock_bh(&ptype_lock);
 	printk(KERN_WARNING "dev_remove_pack: %p not found.\n", pt);
 }
 
@@ -258,37 +263,43 @@
 {
 	struct device *dev;
 
-	for (dev = dev_base; dev != NULL; dev = dev->next) 
-	{
+	read_lock_bh(&dev_base_lock);
+	for (dev = dev_base; dev != NULL; dev = dev->next) {
 		if (strcmp(dev->name, name) == 0)
-			return(dev);
+			goto out;
 	}
-	return NULL;
+out:
+	read_unlock_bh(&dev_base_lock);
+	return dev;
 }
 
 struct device * dev_get_by_index(int ifindex)
 {
 	struct device *dev;
 
-	for (dev = dev_base; dev != NULL; dev = dev->next) 
-	{
+	read_lock_bh(&dev_base_lock);
+	for (dev = dev_base; dev != NULL; dev = dev->next) {
 		if (dev->ifindex == ifindex)
-			return(dev);
+			goto out;
 	}
-	return NULL;
+out:
+	read_unlock_bh(&dev_base_lock);
+	return dev;
 }
 
 struct device *dev_getbyhwaddr(unsigned short type, char *ha)
 {
 	struct device *dev;
 
-	for (dev = dev_base; dev != NULL; dev = dev->next) 
-	{
+	read_lock_bh(&dev_base_lock);
+	for (dev = dev_base; dev != NULL; dev = dev->next) {
 		if (dev->type == type &&
 		    memcmp(dev->dev_addr, ha, dev->addr_len) == 0)
-			return(dev);
+			goto out;
 	}
-	return(NULL);
+out:
+	read_unlock_bh(&dev_base_lock);
+	return dev;
 }
 
 /*
@@ -376,6 +387,9 @@
 	if (dev->flags&IFF_UP)
 		return 0;
 
+	/* Setup the lock before we open the faucet. */
+	spin_lock_init(&dev->xmit_lock);
+
 	/*
 	 *	Call device private open method
 	 */
@@ -438,8 +452,13 @@
 	if (dev) {
 		dev_do_clear_fastroute(dev);
 	} else {
-		for (dev = dev_base; dev; dev = dev->next)
+		read_lock_bh(&dev_base_lock);
+		for (dev = dev_base; dev; dev = dev->next) {
+			read_unlock_bh(&dev_base_lock);
 			dev_do_clear_fastroute(dev);
+			read_lock_bh(&dev_base_lock);
+		}
+		read_unlock_bh(&dev_base_lock);
 	}
 }
 #endif
@@ -512,6 +531,7 @@
 	struct packet_type *ptype;
 	get_fast_time(&skb->stamp);
 
+	read_lock(&ptype_lock);
 	for (ptype = ptype_all; ptype!=NULL; ptype = ptype->next) 
 	{
 		/* Never send packets back to the socket
@@ -552,6 +572,7 @@
 			ptype->func(skb2, skb->dev, ptype);
 		}
 	}
+	read_unlock(&ptype_lock);
 }
 
 /*
@@ -583,12 +604,12 @@
 	NET_PROFILE_ENTER(dev_queue_xmit);
 #endif
 
-	start_bh_atomic();
+	spin_lock_bh(&dev->xmit_lock);
 	q = dev->qdisc;
 	if (q->enqueue) {
 		q->enqueue(skb, q);
 		qdisc_wakeup(dev);
-		end_bh_atomic();
+		spin_unlock_bh(&dev->xmit_lock);
 
 #ifdef CONFIG_NET_PROFILE
 	        NET_PROFILE_LEAVE(dev_queue_xmit);
@@ -610,7 +631,7 @@
 		if (netdev_nit) 
 			dev_queue_xmit_nit(skb,dev);
 		if (dev->hard_start_xmit(skb, dev) == 0) {
-			end_bh_atomic();
+			spin_unlock_bh(&dev->xmit_lock);
 
 #ifdef CONFIG_NET_PROFILE
 			NET_PROFILE_LEAVE(dev_queue_xmit);
@@ -622,7 +643,7 @@
 		if (net_ratelimit())
 			printk(KERN_DEBUG "Virtual device %s asks to queue packet!\n", dev->name);
 	}
-	end_bh_atomic();
+	spin_unlock_bh(&dev->xmit_lock);
 
 	kfree_skb(skb);
 
@@ -732,9 +753,9 @@
 			curr=curr->next;
 			if ( curr->prev->dev == dev ) {
 				prev = curr->prev;
-				spin_lock_irqsave(&skb_queue_lock, flags);
+				spin_lock_irqsave(&backlog.lock, flags);
 				__skb_unlink(prev, &backlog);
-				spin_unlock_irqrestore(&skb_queue_lock, flags);
+				spin_unlock_irqrestore(&backlog.lock, flags);
 				kfree_skb(prev);
 			}
 		}
@@ -939,6 +960,7 @@
 		 */
 
 		pt_prev = NULL;
+		read_lock(&ptype_lock);
 		for (ptype = ptype_all; ptype!=NULL; ptype=ptype->next)
 		{
 			if (!ptype->dev || ptype->dev == skb->dev) {
@@ -992,6 +1014,7 @@
 		else {
 			kfree_skb(skb);
 		}
+		read_unlock(&ptype_lock);
   	}	/* End of queue loop */
   	
   	/*
@@ -1113,7 +1136,9 @@
 	 */
 
 	total = 0;
+	read_lock_bh(&dev_base_lock);
 	for (dev = dev_base; dev != NULL; dev = dev->next) {
+		read_unlock_bh(&dev_base_lock);
 		for (i=0; i<NPROTO; i++) {
 			if (gifconf_list[i]) {
 				int done;
@@ -1122,12 +1147,15 @@
 				} else {
 					done = gifconf_list[i](dev, pos+total, len-total);
 				}
-				if (done<0)
+				if (done<0) {
 					return -EFAULT;
+				}
 				total += done;
 			}
 		}
+		read_lock_bh(&dev_base_lock);
   	}
+	read_unlock_bh(&dev_base_lock);
 
 	/*
 	 *	All done.  Write the updated control block back to the caller. 
@@ -1199,20 +1227,20 @@
 	len+=size;
 	
 
-	for (dev = dev_base; dev != NULL; dev = dev->next) 
-	{
+	read_lock_bh(&dev_base_lock);
+	for (dev = dev_base; dev != NULL; dev = dev->next) {
 		size = sprintf_stats(buffer+len, dev);
 		len+=size;
 		pos=begin+len;
 				
-		if(pos<offset)
-		{
+		if(pos<offset) {
 			len=0;
 			begin=pos;
 		}
 		if(pos>offset+length)
 			break;
 	}
+	read_unlock_bh(&dev_base_lock);
 	
 	*start=buffer+(offset-begin);	/* Start of wanted data */
 	len-=(offset-begin);		/* Start slop */
@@ -1314,20 +1342,20 @@
 	pos+=size;
 	len+=size;
 
-	for(dev = dev_base; dev != NULL; dev = dev->next) 
-	{
+	read_lock_bh(&dev_base_lock);
+	for(dev = dev_base; dev != NULL; dev = dev->next) {
 		size = sprintf_wireless_stats(buffer+len, dev);
 		len+=size;
 		pos=begin+len;
 
-		if(pos < offset)
-		{
+		if(pos < offset) {
 			len=0;
 			begin=pos;
 		}
 		if(pos > offset + length)
 			break;
 	}
+	read_unlock_bh(&dev_base_lock);
 
 	*start = buffer + (offset - begin);	/* Start of wanted data */
 	len -= (offset - begin);		/* Start slop */
@@ -1751,12 +1779,16 @@
 		printk(KERN_INFO "early initialization of device %s is deferred\n", dev->name);
 
 		/* Check for existence, and append to tail of chain */
+		write_lock_bh(&dev_base_lock);
 		for (dp=&dev_base; (d=*dp) != NULL; dp=&d->next) {
-			if (d == dev || strcmp(d->name, dev->name) == 0)
+			if (d == dev || strcmp(d->name, dev->name) == 0) {
+				write_unlock_bh(&dev_base_lock);
 				return -EEXIST;
+			}
 		}
 		dev->next = NULL;
 		*dp = dev;
+		write_unlock_bh(&dev_base_lock);
 		return 0;
 	}
 
@@ -1767,16 +1799,22 @@
 		return -EIO;
 
 	/* Check for existence, and append to tail of chain */
+	write_lock_bh(&dev_base_lock);
 	for (dp=&dev_base; (d=*dp) != NULL; dp=&d->next) {
-		if (d == dev || strcmp(d->name, dev->name) == 0)
+		if (d == dev || strcmp(d->name, dev->name) == 0) {
+			write_unlock_bh(&dev_base_lock);
 			return -EEXIST;
+		}
 	}
 	dev->next = NULL;
 	dev_init_scheduler(dev);
+	*dp = dev;
+	write_unlock_bh(&dev_base_lock);
+
+	dev->ifindex = -1;
 	dev->ifindex = dev_new_index();
 	if (dev->iflink == -1)
 		dev->iflink = dev->ifindex;
-	*dp = dev;
 
 	/* Notify protocols, that a new device appeared. */
 	notifier_call_chain(&netdev_chain, NETDEV_REGISTER, dev);
@@ -1820,17 +1858,19 @@
 	}
 
 	/* And unlink it from device chain. */
+	write_lock_bh(&dev_base_lock);
 	for (dp = &dev_base; (d=*dp) != NULL; dp=&d->next) {
 		if (d == dev) {
 			*dp = d->next;
-			synchronize_bh();
 			d->next = NULL;
+			write_unlock_bh(&dev_base_lock);
 
 			if (dev->destructor)
 				dev->destructor(dev);
 			return 0;
 		}
 	}
+	write_unlock_bh(&dev_base_lock);
 	return -ENODEV;
 }
 
@@ -1976,26 +2016,25 @@
 	 */
 
 	dp = &dev_base;
-	while ((dev = *dp) != NULL)
-	{
+	write_lock_bh(&dev_base_lock);
+	while ((dev = *dp) != NULL) {
 		dev->iflink = -1;
-		if (dev->init && dev->init(dev)) 
-		{
+		if (dev->init && dev->init(dev)) {
 			/*
 			 *	It failed to come up. Unhook it.
 			 */
 			*dp = dev->next;
-			synchronize_bh();
-		} 
-		else
-		{
+		} else {
 			dp = &dev->next;
+			write_unlock_bh(&dev_base_lock);
 			dev->ifindex = dev_new_index();
+			write_lock_bh(&dev_base_lock);
 			if (dev->iflink == -1)
 				dev->iflink = dev->ifindex;
 			dev_init_scheduler(dev);
 		}
 	}
+	write_unlock_bh(&dev_base_lock);
 
 #ifdef CONFIG_PROC_FS
 	proc_net_register(&proc_net_dev);
diff -u --recursive --new-file v2.3.3/linux/net/core/dev_mcast.c linux/net/core/dev_mcast.c
--- v2.3.3/linux/net/core/dev_mcast.c	Sun Mar 21 07:22:00 1999
+++ linux/net/core/dev_mcast.c	Tue May 25 13:06:34 1999
@@ -203,8 +203,7 @@
 	int len=0;
 	struct device *dev;
 
-	start_bh_atomic();
-
+	read_lock_bh(&dev_base_lock);
 	for (dev = dev_base; dev; dev = dev->next) {
 		for (m = dev->mc_list; m; m = m->next) {
 			int i;
@@ -229,7 +228,7 @@
 	*eof = 1;
 
 done:
-	end_bh_atomic();
+	read_unlock_bh(&dev_base_lock);
 	*start=buffer+(offset-begin);
 	len-=(offset-begin);
 	if(len>length)
diff -u --recursive --new-file v2.3.3/linux/net/core/dst.c linux/net/core/dst.c
--- v2.3.3/linux/net/core/dst.c	Mon Mar 23 16:48:25 1998
+++ linux/net/core/dst.c	Wed May 26 18:14:37 1999
@@ -19,8 +19,18 @@
 
 #include <net/dst.h>
 
-struct dst_entry * dst_garbage_list;
-atomic_t	dst_total = ATOMIC_INIT(0);
+/* Locking strategy:
+ * 1) Garbage collection state of dead destination cache
+ *    entries is protected by dst_lock.
+ * 2) GC is run only from BH context, and is the only remover
+ *    of entries.
+ * 3) Entries are added to the garbage list from both BH
+ *    and non-BH context, so local BH disabling is needed.
+ * 4) All operations modify state, so a spinlock is used.
+ */
+static struct dst_entry 	*dst_garbage_list;
+static atomic_t			 dst_total = ATOMIC_INIT(0);
+static spinlock_t		 dst_lock = SPIN_LOCK_UNLOCKED;
 
 static unsigned long dst_gc_timer_expires;
 static unsigned long dst_gc_timer_inc = DST_GC_MAX;
@@ -38,6 +48,8 @@
 	int    delayed = 0;
 	struct dst_entry * dst, **dstp;
 
+	spin_lock(&dst_lock);
+
 	del_timer(&dst_gc_timer);
 	dstp = &dst_garbage_list;
 	while ((dst = *dstp) != NULL) {
@@ -51,7 +63,7 @@
 	}
 	if (!dst_garbage_list) {
 		dst_gc_timer_inc = DST_GC_MAX;
-		return;
+		goto out;
 	}
 	if ((dst_gc_timer_expires += dst_gc_timer_inc) > DST_GC_MAX)
 		dst_gc_timer_expires = DST_GC_MAX;
@@ -62,6 +74,9 @@
 	       atomic_read(&dst_total), delayed,  dst_gc_timer_expires);
 #endif
 	add_timer(&dst_gc_timer);
+
+out:
+	spin_unlock(&dst_lock);
 }
 
 static int dst_discard(struct sk_buff *skb)
@@ -100,7 +115,8 @@
 
 void __dst_free(struct dst_entry * dst)
 {
-	start_bh_atomic();
+	spin_lock_bh(&dst_lock);
+
 	/* The first case (dev==NULL) is required, when
 	   protocol module is unloaded.
 	 */
@@ -119,7 +135,8 @@
 		dst_gc_timer.expires = jiffies + dst_gc_timer_expires;
 		add_timer(&dst_gc_timer);
 	}
-	end_bh_atomic();
+
+	spin_unlock_bh(&dst_lock);
 }
 
 void dst_destroy(struct dst_entry * dst)
diff -u --recursive --new-file v2.3.3/linux/net/core/rtnetlink.c linux/net/core/rtnetlink.c
--- v2.3.3/linux/net/core/rtnetlink.c	Fri May 14 18:55:30 1999
+++ linux/net/core/rtnetlink.c	Tue May 25 13:06:34 1999
@@ -189,12 +189,14 @@
 	int s_idx = cb->args[0];
 	struct device *dev;
 
+	read_lock_bh(&dev_base_lock);
 	for (dev=dev_base, idx=0; dev; dev = dev->next, idx++) {
 		if (idx < s_idx)
 			continue;
 		if (rtnetlink_fill_ifinfo(skb, dev, RTM_NEWLINK, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq) <= 0)
 			break;
 	}
+	read_unlock_bh(&dev_base_lock);
 	cb->args[0] = idx;
 
 	return skb->len;
diff -u --recursive --new-file v2.3.3/linux/net/core/skbuff.c linux/net/core/skbuff.c
--- v2.3.3/linux/net/core/skbuff.c	Sun Mar  7 10:12:18 1999
+++ linux/net/core/skbuff.c	Mon May 31 22:07:43 1999
@@ -4,7 +4,7 @@
  *	Authors:	Alan Cox <iiitac@pyr.swan.ac.uk>
  *			Florian La Roche <rzsfl@rz.uni-sb.de>
  *
- *	Version:	$Id: skbuff.c,v 1.55 1999/02/23 08:12:27 davem Exp $
+ *	Version:	$Id: skbuff.c,v 1.56 1999/05/29 23:20:42 davem Exp $
  *
  *	Fixes:	
  *		Alan Cox	:	Fixed the worst of the load balancer bugs.
@@ -60,11 +60,6 @@
 
 #include <asm/uaccess.h>
 #include <asm/system.h>
-
-/*
- * Skb list spinlock
- */
-spinlock_t skb_queue_lock = SPIN_LOCK_UNLOCKED;
 
 /*
  *	Resource tracking variables
diff -u --recursive --new-file v2.3.3/linux/net/core/sock.c linux/net/core/sock.c
--- v2.3.3/linux/net/core/sock.c	Fri May 14 18:55:30 1999
+++ linux/net/core/sock.c	Wed May 26 18:14:37 1999
@@ -7,7 +7,7 @@
  *		handler for protocols to use and generic option handler.
  *
  *
- * Version:	$Id: sock.c,v 1.81 1999/05/12 11:24:19 davem Exp $
+ * Version:	$Id: sock.c,v 1.82 1999/05/27 00:37:03 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -487,10 +487,10 @@
 {
 	struct sock *sk = kmem_cache_alloc(sk_cachep, priority);
 
-	if(sk) {
-		if (zero_it) 
-			memset(sk, 0, sizeof(struct sock));
+	if(sk && zero_it) {
+		memset(sk, 0, sizeof(struct sock));
 		sk->family = family;
+		sock_lock_init(sk);
 	}
 
 	return sk;
@@ -736,24 +736,44 @@
 	return NULL;
 }
 
-
-void __release_sock(struct sock *sk)
+void lock_sock(struct sock *sk)
 {
-#ifdef CONFIG_INET
-	if (!sk->prot || !sk->backlog_rcv)
-		return;
-		
-	/* See if we have any packets built up. */
-	start_bh_atomic();
-	while (!skb_queue_empty(&sk->back_log)) {
-		struct sk_buff * skb = sk->back_log.next;
-		__skb_unlink(skb, &sk->back_log);
-		sk->backlog_rcv(sk, skb);
+	spin_lock_bh(&sk->lock.slock);
+	if(sk->lock.users != 0) {
+		DECLARE_WAITQUEUE(wait, current);
+
+		add_wait_queue_exclusive(&sk->lock.wq, &wait);
+		for(;;) {
+			current->state = TASK_EXCLUSIVE | TASK_UNINTERRUPTIBLE;
+			spin_unlock_bh(&sk->lock.slock);
+			schedule();
+			spin_lock_bh(&sk->lock.slock);
+			if(!sk->lock.users)
+				break;
+		}
+		current->state = TASK_RUNNING;
+		remove_wait_queue(&sk->lock.wq, &wait);
 	}
-	end_bh_atomic();
-#endif  
+	sk->lock.users = 1;
+	spin_unlock_bh(&sk->lock.slock);
 }
 
+void release_sock(struct sock *sk)
+{
+	spin_lock_bh(&sk->lock.slock);
+	sk->lock.users = 0;
+	if(sk->backlog.tail != NULL) {
+		struct sk_buff *skb = sk->backlog.head;
+		do {	struct sk_buff *next = skb->next;
+			skb->next = NULL;
+			sk->backlog_rcv(sk, skb);
+			skb = next;
+		} while(skb != NULL);
+		sk->backlog.head = sk->backlog.tail = NULL;
+	}
+	wake_up(&sk->lock.wq);
+	spin_unlock_bh(&sk->lock.slock);
+}
 
 /*
  *	Generic socket manager library. Most simpler socket families
@@ -1019,7 +1039,6 @@
 {
 	skb_queue_head_init(&sk->receive_queue);
 	skb_queue_head_init(&sk->write_queue);
-	skb_queue_head_init(&sk->back_log);
 	skb_queue_head_init(&sk->error_queue);
 	
 	init_timer(&sk->timer);
diff -u --recursive --new-file v2.3.3/linux/net/decnet/Config.in linux/net/decnet/Config.in
--- v2.3.3/linux/net/decnet/Config.in	Wed Dec 31 16:00:00 1969
+++ linux/net/decnet/Config.in	Sat May 29 11:09:54 1999
@@ -0,0 +1,13 @@
+#
+# DECnet configuration
+#
+bool 'DECnet: SIOCGIFCONF support' CONFIG_DECNET_SIOCGIFCONF 
+bool 'DECnet: router support (VERY VERY EXPERIMENTAL)' CONFIG_DECNET_ROUTER
+bool 'DECnet: raw socket support' CONFIG_DECNET_RAW
+#bool 'DECnet: MOP support' CONFIG_DECNET_MOP
+#if [ "$CONFIG_FIREWALL" = "y" ]; then
+#	bool 'DECnet: firewall support' CONFIG_DECNET_FW
+#	if [ "$CONFIG_DECNET_FW" = "y" ]; then
+#		bool 'DECnet: firewall netlink support' CONFIG_DECNET_FIREWALL_NETLINK
+#	fi
+#fi
diff -u --recursive --new-file v2.3.3/linux/net/decnet/Makefile linux/net/decnet/Makefile
--- v2.3.3/linux/net/decnet/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/net/decnet/Makefile	Wed May 26 09:36:36 1999
@@ -0,0 +1,30 @@
+# Note 2! The CFLAGS definition is now in the main makefile...
+
+O_TARGET := decnet.o
+O_OBJS   := af_decnet.o dn_nsp_in.o dn_nsp_out.o dn_route.o dn_dev.o dn_neigh.o dn_timer.o
+M_OBJS	 := $(O_TARGET)
+
+ifeq ($(CONFIG_DECNET_ROUTER),y)
+O_OBJS += dn_fib.o
+endif
+
+ifeq ($(CONFIG_DECNET_RAW),y)
+O_OBJS += dn_raw.o
+endif
+
+#ifeq ($(CONFIG_DECNET_MOP),y)
+#O_OBJS += dn_mop.o
+#endif
+
+ifeq ($(CONFIG_DECNET_FW),y)
+O_OBJS += dn_fw.o
+endif
+
+ifeq ($(CONFIG_SYSCTL),y)
+O_OBJS += sysctl_net_decnet.o
+endif
+
+include $(TOPDIR)/Rules.make
+
+tar:
+		tar -cvf /dev/f1 .
diff -u --recursive --new-file v2.3.3/linux/net/decnet/README linux/net/decnet/README
--- v2.3.3/linux/net/decnet/README	Sun Jan 24 22:05:07 1999
+++ linux/net/decnet/README	Wed May 26 09:36:36 1999
@@ -1,15 +1,8 @@
                        Linux DECnet Project
                       ======================
 
-For information on the Linux DECnet Project and the latest progress,
-look at the project home page:
-
-http://www.sucs.swan.ac.uk/~rohan/DECnet/
-
-To contribute either mail <SteveW@ACM.org> or post on one of the Linux
-mailing lists (either linux-net or netdev). DECnet for Linux will not
-be distributed as part of the 2.2.xx kernel series. It is available as a
-patch from the above site. Expect DECnet to arrive as part of the standard 
-kernel distribution early in the 2.3.xx series.
+The documentation for this kernel subsystem is available in the
+Documentation/networking subdirctory of this distribution and also
+on line at http://www.sucs.swan.ac.uk/~rohan/DECnet/index.html.
 
 Steve Whitehouse <SteveW@ACM.org>
diff -u --recursive --new-file v2.3.3/linux/net/decnet/TODO linux/net/decnet/TODO
--- v2.3.3/linux/net/decnet/TODO	Wed Dec 31 16:00:00 1969
+++ linux/net/decnet/TODO	Wed May 26 09:36:36 1999
@@ -0,0 +1,59 @@
+Steve's quick list of things that need finishing off:
+[they are in no particular order and range from the trivial to the long winded]
+
+ o Proper timeouts on each neighbour (in routing mode) rather than
+   just the 60 second On-Ethernet cache value.
+
+ o MOP support (probably as part of Raw sockets) [hooks in]
+
+ o Routing stuff in dn_fib.c
+
+ o Misc. get/set_sockopt() functions [done for the time being, more later]
+
+ o Support for X.25 linklayer
+
+ o Support for DDCMP link layer
+
+ o The DDCMP device itself
+
+ o PPP support (rfc1762)
+
+ o sendmsg() in the raw socket layer
+
+ o Better filtering of traffic in raw sockets
+
+ o Fix /proc for raw sockets
+
+ o Lots of testing with real applications
+
+ o Verify errors etc. against POSIX 1003.1g (draft)
+
+ o Using send/recvmsg() to get at connect/disconnect data (POSIX 1003.1g) 
+   [maybe this should be done at socket level... the control data in the
+    send/recvmsg() calls should simply be a vector of set/getsockopt()
+    calls]
+
+ o recvmsg() to optionally report remote address.
+
+ o check MSG_TRUNC, MSG_CTRUNC are set where they should be.
+
+ o Work out if I really need support for rtnetlink "link" messages and if
+   so how they should be handled.
+
+ o More rtnetlink "route" message support & testing of this code
+
+ o Routing ioctl() support
+
+ o Start to hack together user level software and add more DECnet support
+   in ifconfig for example. Also a DECnet equivalent to Alexey's ip config
+   tool is required. Hopefully I can steal some code from that.
+
+ o Sort out MSG_EOR in sendmsg.... should it be used, or is each transmission
+   a seperate message ? What about when you get interrupted by a signal ?
+
+ o Fix conninit_rx to check out each CI before queuing it
+
+ o Work out which errors we can return from conninit_rx, and how to do it
+
+ o Check out receiving of errors in the light of what conninit_rx can return
+
diff -u --recursive --new-file v2.3.3/linux/net/decnet/af_decnet.c linux/net/decnet/af_decnet.c
--- v2.3.3/linux/net/decnet/af_decnet.c	Wed Dec 31 16:00:00 1969
+++ linux/net/decnet/af_decnet.c	Sat May 29 11:09:54 1999
@@ -0,0 +1,2192 @@
+
+/*
+ * DECnet       An implementation of the DECnet protocol suite for the LINUX
+ *              operating system.  DECnet is implemented using the  BSD Socket
+ *              interface as the means of communication with the user level.
+ *
+ *              DECnet Socket Layer Interface
+ *
+ * Authors:     Eduardo Marcelo Serrat <emserrat@geocities.com>
+ *              Patrick Caulfield <patrick@pandh.demon.co.uk>
+ *
+ * Changes:
+ *        Steve Whitehouse: Copied from Eduardo Serrat and Patrick Caulfield's
+ *                          version of the code. Original copyright preserved
+ *                          below.
+ *        Steve Whitehouse: Some bug fixes, cleaning up some code to make it
+ *                          compatible with my routing layer.
+ *        Steve Whitehouse: Merging changes from Eduardo Serrat and Patrick
+ *                          Caulfield.
+ *        Steve Whitehouse: Further bug fixes, checking module code still works
+ *                          with new routing layer.
+ *        Steve Whitehouse: Additional set/get_sockopt() calls.
+ *        Steve Whitehouse: Fixed TIOCINQ ioctl to be same as Eduardo's new
+ *                          code.
+ *        Steve Whitehouse: recvmsg() changed to try and behave in a POSIX like
+ *                          way. Didn't manage it entirely, but its better.
+ *        Steve Whitehouse: ditto for sendmsg().
+ *        Steve Whitehouse: A selection of bug fixes to various things.
+ *        Steve Whitehouse: Added TIOCOUTQ ioctl.
+ *        Steve Whitehouse: Fixes to username2sockaddr & sockaddr2username.
+ *        Steve Whitehouse: Fixes to connect() error returns.
+ *       Patrick Caulfield: Fixes to delayed acceptance logic.
+ */
+
+
+/******************************************************************************
+    (c) 1995-1998 E.M. Serrat		emserrat@geocities.com
+    
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    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.
+
+HISTORY:
+
+Version           Kernel     Date       Author/Comments
+-------           ------     ----       ---------------
+Version 0.0.1     2.0.30    01-dic-97	Eduardo Marcelo Serrat
+					(emserrat@geocities.com)
+
+                                        First Development of DECnet Socket La-
+					yer for Linux. Only supports outgoing
+					connections.
+
+Version 0.0.2	  2.1.105   20-jun-98   Patrick J. Caulfield
+					(patrick@pandh.demon.co.uk)
+
+					Port to new kernel development version.
+
+Version 0.0.3     2.1.106   25-jun-98   Eduardo Marcelo Serrat
+					(emserrat@geocities.com)
+					_
+                                        Added support for incoming connections
+                                        so we can start developing server apps
+                                        on Linux.
+					-
+					Module Support
+Version 0.0.4     2.1.109   21-jul-98   Eduardo Marcelo Serrat
+                                       (emserrat@geocities.com)
+                                       _
+                                        Added support for X11R6.4. Now we can 
+                                        use DECnet transport for X on Linux!!!
+                                       -
+Version 0.0.5    2.1.110   01-aug-98   Eduardo Marcelo Serrat
+                                       (emserrat@geocities.com)
+                                       Removed bugs on flow control
+                                       Removed bugs on incoming accessdata
+                                       order
+                                       -
+Version 0.0.6    2.1.110   07-aug-98   Eduardo Marcelo Serrat
+                                       dn_recvmsg fixes
+
+                                        Patrick J. Caulfield
+                                       dn_bind fixes
+*******************************************************************************/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/inet.h>
+#include <linux/route.h>
+#include <net/sock.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <net/neighbour.h>
+#include <net/dst.h>
+#include <net/dn.h>
+#include <net/dn_nsp.h>
+#include <net/dn_dev.h>
+#include <net/dn_route.h>
+#include <net/dn_fib.h>
+#include <net/dn_raw.h>
+#include <net/dn_neigh.h>
+
+#define MAX(a,b) ((a)>(b)?(a):(b))
+
+static void dn_keepalive(struct sock *sk);
+
+/*
+ * decnet_address is kept in network order, decnet_ether_address is kept
+ * as a string of bytes.
+ */
+dn_address decnet_address = 0;
+unsigned char decnet_ether_address[ETH_ALEN] = { 0xAA, 0x00, 0x04, 0x00, 0x00, 0x00 };
+int decnet_node_type = DN_RT_INFO_ENDN;
+
+static struct proto_ops dn_proto_ops;
+static struct sock *dn_sklist = NULL;
+static struct sock *dn_wild_sk = NULL;
+
+static int _dn_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen, int flags);
+static int _dn_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen, int flags);
+
+int dn_sockaddr2username(struct sockaddr_dn *sdn, unsigned char *buf, unsigned char type)
+{
+	int len = 2;
+
+	*buf++ = type;
+
+	switch(type) {
+		case 0:
+			*buf++ = sdn->sdn_objnum;
+			break;
+		case 1:
+			*buf++ = 0;
+			*buf++ = sdn->sdn_objnamel;
+			memcpy(buf, sdn->sdn_objname, sdn->sdn_objnamel);
+			len = 3 + sdn->sdn_objnamel;
+			break;
+		case 2:
+			memset(buf, 0, 5);
+			buf += 5;
+			*buf++ = sdn->sdn_objnamel;
+			memcpy(buf, sdn->sdn_objname, sdn->sdn_objnamel);
+			len = 7 + sdn->sdn_objnamel;
+			break;
+	}
+
+	return len;
+}
+
+/*
+ * On reception of usernames, we handle types 1 and 0 for destination
+ * addresses only. Types 2 and 4 are used for source addresses, but the
+ * UIC, GIC are ignored and they are both treated the same way. Type 3
+ * is never used as I've no idea what its purpose might be or what its
+ * format is.
+ */
+int dn_username2sockaddr(unsigned char *data, int len, struct sockaddr_dn *sdn, unsigned char *fmt)
+{
+	unsigned char type;
+	int size = len;
+	int namel = 12;
+
+	sdn->sdn_objnum = 0;
+	sdn->sdn_objnamel = 0;
+	memset(sdn->sdn_objname, 0, DN_MAXOBJL);
+
+	if (len < 2)
+		return -1;
+
+	len -= 2;
+	*fmt = *data++;
+	type = *data++;
+
+	switch(*fmt) {
+		case 0:
+			sdn->sdn_objnum = type;
+			return 2;
+		case 1:
+			namel = 16;
+			break;
+		case 2:
+			len  -= 4;
+			data += 4;
+			break;
+		case 4:
+			len  -= 8;
+			data += 8;
+			break;
+		default:
+			return -1;
+	}
+
+	len -= 1;
+
+	if (len < 0)
+		return -1;
+
+	sdn->sdn_objnamel = *data++;
+	len -= sdn->sdn_objnamel;
+
+	if ((len < 0) || (sdn->sdn_objnamel > namel))
+		return -1;
+
+	memcpy(sdn->sdn_objname, data, sdn->sdn_objnamel);
+
+	return size - len;
+}
+
+struct sock *dn_sklist_find_listener(struct sockaddr_dn *addr)
+{
+	struct sock *sk;
+
+	for(sk = dn_sklist; sk != NULL; sk = sk->next) {
+		struct dn_scp *scp = &sk->protinfo.dn;
+		if (sk->state != TCP_LISTEN)
+			continue;
+		if (scp->addr.sdn_objnum) {
+			if (scp->addr.sdn_objnum != addr->sdn_objnum)
+				continue;
+		} else {
+			if (addr->sdn_objnum)
+				continue;
+			if (scp->addr.sdn_objnamel != addr->sdn_objnamel)
+				continue;
+			if (memcmp(scp->addr.sdn_objname, addr->sdn_objname, addr->sdn_objnamel) != 0)
+				continue;
+		}
+		return sk;
+	}
+
+	return (dn_wild_sk && (dn_wild_sk->state == TCP_LISTEN)) ? dn_wild_sk : NULL;
+}
+
+struct sock *dn_sklist_find(unsigned short port)
+{
+        struct sock *s;
+
+        for (s = dn_sklist; s != NULL; s = s->next) {
+                if (s->protinfo.dn.addrloc == port) {
+                        return s;
+                }
+        }
+
+        return NULL;
+}
+
+static struct sock *dn_sklist_find_by_objnum(unsigned char objnum)
+{
+        struct sock *s;
+
+        for (s = dn_sklist; s != NULL; s = s->next) {
+                if ((s->protinfo.dn.addr.sdn_objnum == objnum) &&
+                     (s->state == TCP_LISTEN)) {
+                        return s;
+                }
+        }
+        return NULL;
+}
+
+static struct sock *dn_sklist_find_by_name(char *name)
+{
+       struct sock *s;
+
+       for (s = dn_sklist; s != NULL; s = s->next) {
+		if (s->protinfo.dn.addr.sdn_objnum)
+			continue;
+               if ((memcmp(s->protinfo.dn.addr.sdn_objname,name,
+                           s->protinfo.dn.addr.sdn_objnamel) == 0) 
+                     && (s->state == TCP_LISTEN)) {
+                       return s;
+               }
+       }
+       return NULL;
+}
+
+
+struct sock *dn_find_by_skb(struct sk_buff *skb)
+{
+	struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+	struct sock *sk;
+	struct dn_scp *scp;
+
+	for(sk = dn_sklist; sk != NULL; sk = sk->next) {
+		scp = &sk->protinfo.dn;
+		if (cb->src != dn_saddr2dn(&scp->peer))
+			continue;
+		if (cb->dst_port != scp->addrloc)
+			continue;
+		if (scp->addrrem && (cb->src_port != scp->addrrem))
+			continue;
+		break;
+	}
+
+	return sk;
+}
+
+
+unsigned short dn_alloc_port(void)
+{
+	struct sock *sk;
+	static unsigned short dn_port = 0x2000;
+	short port;
+
+	start_bh_atomic();
+
+	do {
+		port = dn_port++;
+		sk = dn_sklist_find(port);
+	} while((sk != NULL) || (port == 0));
+
+	end_bh_atomic();
+
+        return dn_htons(port);
+};
+
+
+static void dn_destruct(struct sock *sk)
+{
+	struct dn_scp *scp = &sk->protinfo.dn;
+
+	skb_queue_purge(&scp->data_xmit_queue);
+	skb_queue_purge(&scp->other_xmit_queue);
+	skb_queue_purge(&scp->other_receive_queue);
+
+	dst_release(xchg(&sk->dst_cache, NULL));
+
+	MOD_DEC_USE_COUNT;
+}
+
+struct sock *dn_alloc_sock(struct socket *sock, int flags)
+{
+	struct sock *sk;
+	struct dn_scp *scp;
+
+	if  ((sk = sk_alloc(PF_DECnet, flags, 1)) == NULL) 
+		goto no_sock;
+
+	if (sock) {
+#ifdef CONFIG_DECNET_RAW
+		if (sock->type == SOCK_RAW)
+			sock->ops = &dn_raw_proto_ops;
+		else
+#endif /* CONFIG_DECNET_RAW */
+			sock->ops = &dn_proto_ops;
+	}
+	sock_init_data(sock,sk);
+	scp = &sk->protinfo.dn;
+
+	sk->backlog_rcv = dn_nsp_backlog_rcv;
+	sk->destruct    = dn_destruct;
+	sk->no_check    = 1;
+	sk->family      = PF_DECnet;
+	sk->protocol    = 0;
+
+	/* Initialization of DECnet Session Control Port		*/
+	scp->state	= DN_O;		/* Open			*/
+	scp->numdat	= 1;		/* Next data seg to tx	*/
+	scp->numoth	= 1;		/* Next oth data to tx  */
+	scp->ackxmt_dat = 0;		/* Last data seg ack'ed */
+	scp->ackxmt_oth = 0;		/* Last oth data ack'ed */
+	scp->ackrcv_dat = 0;		/* Highest data ack recv*/
+	scp->ackrcv_oth = 0;		/* Last oth data ack rec*/
+        scp->flowrem_sw = DN_SEND;
+	scp->flowloc_sw = DN_SEND;
+	scp->accept_mode = ACC_IMMED;
+	scp->addr.sdn_family    = AF_DECnet;
+	scp->peer.sdn_family    = AF_DECnet;
+	scp->accessdata.acc_accl = 5;
+	memcpy(scp->accessdata.acc_acc, "LINUX", 5);
+	scp->mss = 1460;
+
+	scp->snd_window   = NSP_MIN_WINDOW;
+	scp->nsp_srtt     = NSP_INITIAL_SRTT;
+	scp->nsp_rttvar   = NSP_INITIAL_RTTVAR;
+	scp->nsp_rxtshift = 0;
+
+	skb_queue_head_init(&scp->data_xmit_queue);
+	skb_queue_head_init(&scp->other_xmit_queue);
+	skb_queue_head_init(&scp->other_receive_queue);
+
+	scp->persist = 0;
+	scp->persist_fxn = NULL;
+	scp->keepalive = 10 * HZ;
+	scp->keepalive_fxn = dn_keepalive;
+
+	init_timer(&scp->delack_timer);
+	scp->delack_pending = 0;
+	scp->delack_fxn = dn_nsp_delayed_ack;
+
+	dn_start_slow_timer(sk);
+
+	MOD_INC_USE_COUNT;
+
+	return sk;
+no_sock:
+	return NULL;
+}
+
+/*
+ * Keepalive timer.
+ * FIXME: Should respond to SO_KEEPALIVE etc.
+ */
+static void dn_keepalive(struct sock *sk)
+{
+	struct dn_scp *scp = &sk->protinfo.dn;
+
+	/*
+	 * By checking the other_data transmit queue is empty
+	 * we are double checking that we are not sending too
+	 * many of these keepalive frames.
+	 */
+	if (skb_queue_len(&scp->other_xmit_queue) == 0)
+		dn_nsp_send_lnk(sk, DN_NOCHANGE);
+}
+
+
+/*
+ * Timer for shutdown/destroyed sockets.
+ * When socket is dead & no packets have been sent for a
+ * certain amount of time, they are removed by this
+ * routine. Also takes care of sending out DI & DC
+ * frames at correct times. This is called by both
+ * socket level and interrupt driven code.
+ */
+static int dn_destroy_timer(struct sock *sk)
+{
+	struct dn_scp *scp = &sk->protinfo.dn;
+
+	scp->persist = dn_nsp_persist(sk);
+
+	switch(scp->state) {
+		case DN_DI:
+			/* printk(KERN_DEBUG "dn_destroy_timer: DI\n"); */
+			dn_send_disc(sk, NSP_DISCINIT, 0);
+			if (scp->nsp_rxtshift >= decnet_di_count)
+				scp->state = DN_CN;
+			return 0;
+
+		case DN_DR:
+			/* printk(KERN_DEBUG "dn_destroy_timer: DR\n"); */
+			dn_send_disc(sk, NSP_DISCINIT, 0);
+			if (scp->nsp_rxtshift >= decnet_dr_count)
+				scp->state = DN_DRC;
+			return 0;
+
+		case DN_DN:
+			if (scp->nsp_rxtshift < decnet_dn_count) {
+				/* printk(KERN_DEBUG "dn_destroy_timer: DN\n"); */
+				dn_send_disc(sk, NSP_DISCCONF, NSP_REASON_DC);
+				return 0;
+			}
+	}
+
+	scp->persist = (HZ * decnet_time_wait);
+
+/*	printk(KERN_DEBUG "dn_destroy_timer: testing dead\n"); */
+	
+	if (sk->socket)
+		return 0;
+
+	dn_stop_fast_timer(sk); /* unlikely, but possible that this is runninng */
+	if ((jiffies - scp->stamp) >= (HZ * decnet_time_wait)) {
+		sklist_destroy_socket(&dn_sklist, sk);
+		return 1;
+	}
+
+	/*printk(KERN_DEBUG "dn_destroy_timer: dead 'n' waiting...\n"); */
+
+	return 0;
+}
+
+void dn_destroy_sock(struct sock *sk)
+{
+	struct dn_scp *scp = &sk->protinfo.dn;
+
+	if (sk->dead)
+		return;
+
+	sk->dead = 1;
+	scp->nsp_rxtshift = 0; /* reset back off */
+
+	if (sk->socket) {
+		if (sk->socket->state != SS_UNCONNECTED)
+			sk->socket->state = SS_DISCONNECTING;
+	}
+
+	sk->state = TCP_CLOSE;
+
+	switch(scp->state) {
+		case DN_DN:
+			dn_send_disc(sk, NSP_DISCCONF, NSP_REASON_DC);
+			scp->persist_fxn = dn_destroy_timer;
+			scp->persist = dn_nsp_persist(sk);
+			break;
+		case DN_CD:
+		case DN_CR:
+			scp->state = DN_DR;
+			goto disc_reject;
+		case DN_RUN:
+			scp->state = DN_DI;
+		case DN_DI:
+		case DN_DR:
+disc_reject:
+			dn_send_disc(sk, NSP_DISCINIT, 0);
+		case DN_NC:
+		case DN_NR:
+		case DN_RJ:
+		case DN_DIC:
+		case DN_CN:
+		case DN_DRC:
+		case DN_CI:
+			scp->persist_fxn = dn_destroy_timer;
+			scp->persist = dn_nsp_persist(sk);
+			break;
+		default:
+			printk(KERN_DEBUG "DECnet: dn_destroy_sock passed socket in invalid state\n");
+		case DN_O:
+			start_bh_atomic();
+			dn_stop_fast_timer(sk);
+			dn_stop_slow_timer(sk);
+
+	                if (sk == dn_wild_sk) {
+        	                dn_wild_sk = NULL;
+                	        sklist_destroy_socket(NULL, sk);
+			} else {
+                        	sklist_destroy_socket(&dn_sklist, sk);
+                	}
+
+			end_bh_atomic();
+			break;
+	}
+}
+
+char *dn_addr2asc(dn_address addr, char *buf)
+{
+	unsigned short node, area;
+
+	node = addr & 0x03ff;
+	area = addr >> 10;
+	sprintf(buf, "%hd.%hd", area, node);
+
+	return buf;
+}
+
+
+static char *dn_state2asc(unsigned char state)
+{
+	switch(state) {
+		case DN_O:
+			return "OPEN";
+		case DN_CR:
+			return "  CR";
+		case DN_DR:
+			return "  DR";
+		case DN_DRC:
+			return " DRC";
+		case DN_CC:
+			return "  CC";
+		case DN_CI:
+			return "  CI";
+		case DN_NR:
+			return "  NR";
+		case DN_NC:
+			return "  NC";
+		case DN_CD:
+			return "  CD";
+		case DN_RJ:
+			return "  RJ";
+		case DN_RUN:
+			return " RUN";
+		case DN_DI:
+			return "  DI";
+		case DN_DIC:
+			return " DIC";
+		case DN_DN:
+			return "  DN";
+		case DN_CL:
+			return "  CL";
+		case DN_CN:
+			return "  CN";
+	}
+
+	return "????";
+}
+
+static int dn_get_info(char *buffer, char **start, off_t offset,
+			    int length, int dummy)
+{
+	struct	sock	*sk;
+	int		len = 0;
+	off_t		pos = 0;
+	off_t		begin = 0;
+	char buf[DN_ASCBUF_LEN];
+
+	len += sprintf(buffer+len,"%-8s%-7s%-7s%-7s%-5s%-13s%-13s\n",
+			"Remote","Source","Remote","Object","Link ",
+			"   Data  Packets ","Link  Packets");
+	len += sprintf(buffer+len,"%-8s%-7s%-7s%-7s%-5s%-13s%-13s\n\n",
+			"Node  ","Port  ","Port  ","Number","State",
+			"   Out     In ","   Out     In");
+	start_bh_atomic();
+	for (sk = dn_sklist; sk != NULL; sk = sk->next) {
+		len += sprintf(buffer+len,
+		      "%6s  %04X   %04X   %6d %4s %6d %6d %6d %6d\n",
+					   
+		       dn_addr2asc(dn_ntohs(dn_saddr2dn(&sk->protinfo.dn.peer)), buf),
+		       sk->protinfo.dn.addrloc,sk->protinfo.dn.addrrem,
+		       sk->protinfo.dn.addr.sdn_objnum,
+		       dn_state2asc(sk->protinfo.dn.state),
+		       sk->protinfo.dn.numdat, sk->protinfo.dn.numdat_rcv,
+		       sk->protinfo.dn.numoth, sk->protinfo.dn.numoth_rcv);
+
+		pos = begin + len;
+		if (pos < offset) {
+                        len = 0;
+			begin = pos;
+		}
+		if (pos > offset+length) 
+			break;
+	}
+	end_bh_atomic();
+
+	*start = buffer + (offset - begin);
+	len -= (offset - begin);
+
+	if (len > length)
+		len = length;
+
+	return len;
+}
+
+static int dn_create(struct socket *sock, int protocol)
+{
+	struct	sock	*sk;
+
+	switch(sock->type) {
+		case SOCK_SEQPACKET:
+			if (protocol != DNPROTO_NSP)
+				return -EPROTONOSUPPORT;
+			break;
+		case SOCK_STREAM:
+			break;
+#ifdef CONFIG_DECNET_RAW
+		case SOCK_RAW:
+			if ((protocol != DNPROTO_NSP) &&
+#ifdef CONFIG_DECNET_MOP
+					(protocol != DNPROTO_MOP) &&
+#endif /* CONFIG_DECNET_MOP */
+					(protocol != DNPROTO_ROU))
+				return -EPROTONOSUPPORT;
+			break;
+#endif /* CONFIG_DECNET_RAW */
+		default:
+			return -ESOCKTNOSUPPORT;
+	}
+
+
+	if ((sk = dn_alloc_sock(sock, GFP_KERNEL)) == NULL) 
+		return -ENOBUFS;
+
+	sk->protocol = protocol;
+
+	return 0;
+}
+
+
+static int
+dn_release(struct socket *sock, struct socket *peer)
+{
+	struct sock *sk = sock->sk;
+
+	if (sk) {
+		lock_sock(sk);
+		sock->sk = NULL;
+		sk->socket = NULL;
+		dn_destroy_sock(sk);
+		release_sock(sk);
+	}
+
+        return 0;
+}
+
+static int dn_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
+{
+	struct	sock	*sk = sock->sk;
+	struct sockaddr_dn *saddr = (struct sockaddr_dn *)uaddr;
+
+	if (sk->zapped == 0)
+		return -EINVAL;
+
+	if (addr_len != sizeof(struct sockaddr_dn))
+		return -EINVAL;
+
+	if (saddr->sdn_family != AF_DECnet)
+		return -EINVAL;
+
+	if (saddr->sdn_objnum && !suser())
+		return -EPERM;
+
+	if (!saddr->sdn_objname && (saddr->sdn_objnamel > DN_MAXOBJL))
+		return -EINVAL;
+
+	if (saddr->sdn_flags & ~SDF_WILD)
+		return -EINVAL;
+
+	if ((saddr->sdn_flags & SDF_WILD) && !suser())
+		return -EPERM;
+
+	start_bh_atomic();
+
+	if (saddr->sdn_flags & SDF_WILD) {
+		if (dn_wild_sk) {
+			end_bh_atomic();
+			return -EADDRINUSE;
+		}
+		dn_wild_sk = sk;
+		sk->zapped = 0;
+		memcpy(&sk->protinfo.dn.addr, saddr, addr_len);
+		end_bh_atomic();
+		return 0;
+	}
+
+	if (saddr->sdn_objnum && dn_sklist_find_by_objnum(saddr->sdn_objnum)) {
+		end_bh_atomic();
+		return -EADDRINUSE;
+	}
+
+	if (!saddr->sdn_objnum) {
+		if (dn_sklist_find_by_name(saddr->sdn_objname)) {
+			end_bh_atomic();
+			return -EADDRINUSE;
+		}
+	}
+
+	memcpy(&sk->protinfo.dn.addr, saddr, addr_len);
+	sk->zapped = 0;
+	sklist_insert_socket(&dn_sklist, sk);
+	end_bh_atomic();
+
+        return 0;
+}
+
+
+static int dn_auto_bind(struct socket *sock)
+{
+	struct sock *sk = sock->sk;
+	struct dn_scp *scp = &sk->protinfo.dn;
+
+	sk->zapped = 0;
+
+	scp->addr.sdn_flags  = 0;
+	scp->addr.sdn_objnum = 0;
+
+	/*
+	 * This stuff is to keep compatibility with Eduardo's
+	 * patch. I hope I can dispense with it shortly...
+	 */
+	if ((scp->accessdata.acc_accl != 0) &&
+		(scp->accessdata.acc_accl <= 12)) {
+	
+		scp->addr.sdn_objnamel = scp->accessdata.acc_accl;
+		memcpy(scp->addr.sdn_objname, scp->accessdata.acc_acc, scp->addr.sdn_objnamel);
+
+		scp->accessdata.acc_accl = 0;
+		memset(scp->accessdata.acc_acc, 0, 40);
+	}
+
+	scp->addr.sdn_add.a_len = 2;
+	*(dn_address *)scp->addr.sdn_add.a_addr = decnet_address;
+
+	sklist_insert_socket(&dn_sklist, sk);
+
+	return 0;
+}
+
+
+static int dn_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags)
+{
+	struct sockaddr_dn *addr = (struct sockaddr_dn *)uaddr;
+	struct sock *sk = sock->sk;
+	int err = -EISCONN;
+
+	lock_sock(sk);
+
+	if (sock->state == SS_CONNECTED) 
+		goto out;
+
+	if (sock->state == SS_CONNECTING) {
+		err = 0;
+		if (sk->state == TCP_ESTABLISHED)
+			goto out;
+
+		err = -ECONNREFUSED;
+		if (sk->state == TCP_CLOSE)
+			goto out;
+	}
+
+	err = -EINVAL;
+	if (sk->protinfo.dn.state != DN_O)
+		goto out;
+
+	if (addr_len != sizeof(struct sockaddr_dn))
+		goto out;
+
+	if (addr->sdn_family != AF_DECnet)
+		goto out;
+
+	if (addr->sdn_flags & SDF_WILD)
+		goto out;
+
+	err = -EADDRNOTAVAIL;
+	if (sk->zapped && (err = dn_auto_bind(sock)))
+		goto out;
+
+	memcpy(&sk->protinfo.dn.peer, addr, addr_len);
+
+	err = -EHOSTUNREACH;
+	if (dn_route_output(sk) < 0)
+		goto out;
+
+	sk->state   = TCP_SYN_SENT;
+	sock->state = SS_CONNECTING;
+	sk->protinfo.dn.state = DN_CI;
+
+	dn_nsp_send_conninit(sk, NSP_CI);
+
+	err = -EINPROGRESS;
+	if ((sk->state == TCP_SYN_SENT) && (flags & O_NONBLOCK))
+		goto out;
+
+	while(sk->state == TCP_SYN_SENT) {
+
+		err = -ERESTARTSYS;
+		if (signal_pending(current))
+			goto out;
+
+		if ((err = sock_error(sk)) != 0) {
+			sock->state = SS_UNCONNECTED;
+			goto out;
+		}
+
+		SOCK_SLEEP_PRE(sk);
+
+		if (sk->state == TCP_SYN_SENT)
+			schedule();
+
+		SOCK_SLEEP_POST(sk);
+	}
+
+	if (sk->state != TCP_ESTABLISHED) {
+		sock->state = SS_UNCONNECTED;
+		err = sock_error(sk);
+		goto out;
+	}
+
+	err = 0;
+	sock->state = SS_CONNECTED;
+out:
+	release_sock(sk);
+
+        return err;
+}
+
+static int dn_access_copy(struct sk_buff *skb, struct accessdata_dn *acc)
+{
+        unsigned char *ptr = skb->data;
+
+        acc->acc_userl = *ptr++;
+        memcpy(&acc->acc_user, ptr, acc->acc_userl);
+        ptr += acc->acc_userl;
+
+        acc->acc_passl = *ptr++;
+        memcpy(&acc->acc_pass, ptr, acc->acc_passl);
+        ptr += acc->acc_passl;
+
+        acc->acc_accl = *ptr++;
+        memcpy(&acc->acc_acc, ptr, acc->acc_accl);
+
+        skb_pull(skb, acc->acc_accl + acc->acc_passl + acc->acc_userl + 3);
+
+	return 0;
+}
+
+static int dn_user_copy(struct sk_buff *skb, struct optdata_dn *opt)
+{
+        unsigned char *ptr = skb->data;
+        
+        opt->opt_optl   = *ptr++;
+        opt->opt_status = 0;
+        memcpy(opt->opt_data, ptr, opt->opt_optl);
+        skb_pull(skb, opt->opt_optl + 1);
+
+	return 0;
+}
+
+
+/*
+ * This is here for use in the sockopt() call as well as
+ * in accept(). Must be called with a locked socket.
+ */
+static int dn_wait_accept(struct socket *sock, int flags)
+{
+        struct sock *sk = sock->sk;
+
+	/* printk(KERN_DEBUG "dn_wait_accept: in\n"); */
+
+        while(sk->state == TCP_LISTEN) {
+                if (flags & O_NONBLOCK) {
+                        return -EAGAIN;
+                }
+
+		SOCK_SLEEP_PRE(sk)
+
+		if (sk->state == TCP_LISTEN)
+			schedule();
+
+		SOCK_SLEEP_POST(sk)
+
+                if (signal_pending(current)) {
+			/* printk(KERN_DEBUG "dn_wait_accept: signal\n"); */
+                        return -ERESTARTSYS; /* But of course you don't! */
+                }
+        }
+
+        if ((sk->protinfo.dn.state != DN_RUN) && (sk->protinfo.dn.state != DN_DRC)) {
+                sock->state = SS_UNCONNECTED;
+                return sock_error(sk);
+        }
+
+	sock->state = SS_CONNECTED;
+	/* printk(KERN_DEBUG "dn_wait_accept: out\n"); */
+
+        return 0;
+}
+
+
+static int dn_accept(struct socket *sock, struct socket *newsock,int flags)
+{
+	struct	sock	*sk=sock->sk, *newsk;
+	struct	sk_buff	*skb = NULL;
+	struct dn_skb_cb *cb;
+	unsigned char menuver;
+	int err = 0;
+	unsigned char type;
+
+	lock_sock(sk);
+
+        if (sk->state != TCP_LISTEN) {
+		release_sock(sk);
+		return -EINVAL;
+	}
+
+	if (sk->protinfo.dn.state != DN_O) {
+		release_sock(sk);
+		return -EINVAL;
+	}
+
+	if (newsock->sk != NULL) {
+		newsock->sk->socket = NULL;
+		dn_destroy_sock(newsock->sk);
+		newsock->sk = NULL;
+	}
+
+        do
+        {
+		/* printk(KERN_DEBUG "dn_accept: loop top\n"); */
+                if ((skb = skb_dequeue(&sk->receive_queue)) == NULL)
+                {
+                        if (flags & O_NONBLOCK)
+                        {
+                                release_sock(sk);
+                                return -EAGAIN;
+                        }
+
+			SOCK_SLEEP_PRE(sk);
+
+			if (!skb_peek(&sk->receive_queue))
+				schedule();
+
+			SOCK_SLEEP_POST(sk);
+
+                        if (signal_pending(current))
+                        {
+				release_sock(sk);
+                                return -ERESTARTSYS;
+                        }
+                }
+        } while (skb == NULL);
+
+	cb = (struct dn_skb_cb *)skb->cb;
+
+	if ((newsk = dn_alloc_sock(newsock, GFP_KERNEL)) == NULL) {
+		release_sock(sk);
+		kfree_skb(skb);
+		return -ENOBUFS;
+	}
+	sk->ack_backlog--;
+	release_sock(sk);
+
+	dst_release(xchg(&newsk->dst_cache, skb->dst));
+	skb->dst = NULL;
+
+
+        newsk->protinfo.dn.state      = DN_CR;
+        newsk->protinfo.dn.addrloc    = dn_alloc_port();
+	newsk->protinfo.dn.addrrem    = cb->src_port;
+	newsk->protinfo.dn.mss        = cb->segsize;
+	newsk->protinfo.dn.accept_mode = sk->protinfo.dn.accept_mode;
+	
+	if (newsk->protinfo.dn.mss < 230)
+		newsk->protinfo.dn.mss = 230;
+
+	newsk->state  = TCP_LISTEN;
+	newsk->zapped = 0;
+
+	memcpy(&newsk->protinfo.dn.addr, &sk->protinfo.dn.addr, sizeof(struct sockaddr_dn));
+
+	skb_pull(skb, dn_username2sockaddr(skb->data, skb->len, &newsk->protinfo.dn.addr, &type));
+	skb_pull(skb, dn_username2sockaddr(skb->data, skb->len, &newsk->protinfo.dn.peer, &type));
+	*(dn_address *)newsk->protinfo.dn.peer.sdn_add.a_addr = cb->src;
+
+	menuver = *skb->data;
+	skb_pull(skb, 1);
+
+	if (menuver & DN_MENUVER_ACC)
+		dn_access_copy(skb, &newsk->protinfo.dn.accessdata);
+
+	if (menuver & DN_MENUVER_USR)
+		dn_user_copy(skb, &newsk->protinfo.dn.conndata_in);
+
+	if (menuver & DN_MENUVER_PRX)
+		newsk->protinfo.dn.peer.sdn_flags |= SDF_PROXY;
+
+	if (menuver & DN_MENUVER_UIC)
+		newsk->protinfo.dn.peer.sdn_flags |= SDF_UICPROXY;
+
+	kfree_skb(skb);
+
+	memcpy(&newsk->protinfo.dn.conndata_out, &sk->protinfo.dn.conndata_out,
+		sizeof(struct optdata_dn));
+	memcpy(&newsk->protinfo.dn.discdata_out, &sk->protinfo.dn.discdata_out,
+		sizeof(struct optdata_dn));
+
+	lock_sock(newsk);
+        sklist_insert_socket(&dn_sklist, newsk);
+
+	dn_send_conn_ack(newsk);
+
+	if (newsk->protinfo.dn.accept_mode == ACC_IMMED) {
+		newsk->protinfo.dn.state = DN_CC;
+        	dn_send_conn_conf(newsk);
+		err = dn_wait_accept(newsock, flags);
+	}
+
+	release_sock(newsk);
+        return err;
+}
+
+
+static int dn_getname(struct socket *sock, struct sockaddr *uaddr,int *uaddr_len,int peer)
+{
+	struct sockaddr_dn *sa = (struct sockaddr_dn *)uaddr;
+	struct sock *sk = sock->sk;
+	struct dn_scp *scp = &sk->protinfo.dn;
+
+	*uaddr_len = sizeof(struct sockaddr_dn);
+
+	lock_sock(sk);
+
+	if (peer) {
+		if (sock->state != SS_CONNECTED && sk->protinfo.dn.accept_mode == ACC_IMMED)
+			return -ENOTCONN;
+
+		memcpy(sa, &scp->peer, sizeof(struct sockaddr_dn));
+	} else {
+		memcpy(sa, &scp->addr, sizeof(struct sockaddr_dn));
+	}
+
+	release_sock(sk);
+
+        return 0;
+}
+
+
+static unsigned int dn_poll(struct file *file, struct socket *sock, poll_table  *wait)
+{
+	struct sock *sk = sock->sk;
+	struct dn_scp *scp = &sk->protinfo.dn;
+	int mask = datagram_poll(file, sock, wait);
+
+	if (skb_queue_len(&scp->other_receive_queue))
+		mask |= POLLRDBAND;
+
+	return mask;
+}
+
+static int dn_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+	struct sock *sk = sock->sk;
+	int err = -EOPNOTSUPP;
+	unsigned long amount = 0;
+	struct sk_buff *skb;
+
+#if 0
+	struct dn_naddr dnaddr;
+#endif
+	switch(cmd)
+	{
+	case SIOCGIFADDR:
+	case SIOCSIFADDR:
+		return dn_dev_ioctl(cmd, (void *)arg);
+
+#ifdef CONFIG_DECNET_ROUTER
+	case SIOCADDRT:
+	case SIOCDELRT:
+		return dn_fib_ioctl(sock, cmd, arg);
+#endif /* CONFIG_DECNET_ROUTER */
+
+#if 0
+	case SIOCSIFADDR:
+		if (!suser())    return -EPERM;
+
+		if ((err = copy_from_user(devname, ioarg->devname, 5)) != 0)
+			break;
+		if ((err = copy_from_user(addr, ioarg->exec_addr, 6)) != 0)
+			break;
+		if ((dev = dev_get(devname)) == NULL) {
+			err = -ENODEV;
+			break;
+		}
+		if (dev->dn_ptr == NULL) {
+			err = -ENODEV;
+			break;
+		}
+
+		dn_dev_devices_off();
+
+		decnet_default_device = dev;
+		memcpy(decnet_ether_address, addr, ETH_ALEN);
+		decnet_address = dn_htons(dn_eth2dn(decnet_ether_address));
+
+		dn_dev_devices_on();
+
+		break;
+
+	case SIOCGIFADDR:
+		if (decnet_default_device)
+			strcpy(devname, decnet_default_device->name);
+		else
+			memset(devname, 0, 6);
+
+		if ((err = copy_to_user(ioarg->devname, devname, 5)) != 0)
+			break;
+
+		if ((err = copy_to_user(ioarg->exec_addr, decnet_ether_address, 6)) != 0)
+			break;
+
+		break;
+#endif
+
+#if 0
+	case SIOCSNETADDR:
+		if (!suser()) {
+			err = -EPERM;
+			break;
+		}
+
+		if ((err = copy_from_user(&dnaddr, (void *)arg, sizeof(struct dn_naddr))) != 0)
+			break;
+
+		if (dnaddr.a_len != ETH_ALEN) {
+			err = -EINVAL;
+			break;
+		}
+
+		dn_dev_devices_off();
+
+		memcpy(decnet_ether_address, dnaddr.a_addr, ETH_ALEN);
+		decnet_address = dn_htons(dn_eth2dn(decnet_ether_address));
+
+		dn_dev_devices_on();
+		break;
+
+	case SIOCGNETADDR:
+		dnaddr.a_len = ETH_ALEN;
+		memcpy(dnaddr.a_addr, decnet_ether_address, ETH_ALEN);
+
+		if ((err = copy_to_user((void *)arg, &dnaddr, sizeof(struct dn_naddr))) != 0)
+			break;
+
+		break;
+#endif
+	case OSIOCSNETADDR:
+		if (!suser()) {
+			err = -EPERM;
+			break;
+		}
+
+		dn_dev_devices_off();
+
+		decnet_address = (unsigned short)arg;
+		dn_dn2eth(decnet_ether_address, dn_ntohs(decnet_address));
+
+		dn_dev_devices_on();
+		err = 0;
+		break;
+
+	case OSIOCGNETADDR:
+		if ((err = put_user(decnet_address, (unsigned short *)arg)) != 0)
+			break;
+		break;
+        case SIOCGIFCONF:
+        case SIOCGIFFLAGS:
+        case SIOCGIFBRDADDR:
+                return dev_ioctl(cmd,(void *)arg);
+
+	case TIOCOUTQ:
+		amount = sk->sndbuf - atomic_read(&sk->wmem_alloc);
+		if (amount < 0)
+			amount = 0;
+		err = put_user(amount, (int *)arg);
+		break;
+
+	case TIOCINQ:
+		lock_sock(sk);
+		if ((skb = skb_peek(&sk->receive_queue)) != NULL)
+			amount = skb->len;
+		release_sock(sk);
+		err = put_user(amount, (int *)arg);
+		break;
+	}
+
+	return err;
+}
+
+static int dn_listen(struct socket *sock, int backlog)
+{
+	struct sock *sk = sock->sk;
+
+	if (sk->zapped)
+		return -EINVAL;
+
+	if ((sk->protinfo.dn.state != DN_O) || (sk->state == TCP_LISTEN))
+		return -EINVAL;
+
+	if (backlog > SOMAXCONN)
+		backlog = SOMAXCONN;
+
+	sk->max_ack_backlog = backlog;
+	sk->ack_backlog     = 0;
+	sk->state           = TCP_LISTEN;
+
+        return 0;
+}
+
+
+static int dn_shutdown(struct socket *sock, int how)
+{
+	struct sock *sk = sock->sk;
+	struct dn_scp *scp = &sk->protinfo.dn;
+	int err = -ENOTCONN;
+
+	lock_sock(sk);
+
+	if (sock->state == SS_UNCONNECTED)
+		goto out;
+
+	err = 0;
+	if (sock->state == SS_DISCONNECTING)
+		goto out;
+
+	err = -EINVAL;
+	if (scp->state == DN_O)
+		goto out;
+
+	if (how != SHUTDOWN_MASK)
+		goto out;
+
+
+	sk->shutdown = how;
+	dn_destroy_sock(sk);
+	err = 0;
+
+out:
+	release_sock(sk);
+
+	return err;
+}
+
+static int dn_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
+{
+	return _dn_setsockopt(sock, level, optname, optval, optlen, 0);
+}
+
+static int _dn_setsockopt(struct socket *sock, int level,int optname, char *optval, int optlen, int flags) 
+{
+	struct	sock *sk = sock->sk;
+	struct dn_scp *scp = &sk->protinfo.dn;
+	struct optdata_dn opt;
+	struct accessdata_dn acc;
+#ifdef CONFIG_DECNET_FW
+	char tmp_fw[MAX(sizeof(struct dn_fwtest),sizeof(struct dn_fwnew))];
+#endif
+	int err;
+
+	if (optlen && !optval)
+		return -EINVAL;
+
+	switch(optname) {
+		case DSO_CONDATA:
+			if (sock->state == SS_CONNECTED) 
+				return -EISCONN;
+			if ((scp->state != DN_O) && (scp->state != DN_CR))
+				return -EINVAL;
+
+			if (optlen != sizeof(struct optdata_dn))
+				return -EINVAL;
+
+			if (copy_from_user(&opt, optval, optlen))
+				return -EFAULT;
+
+			if (opt.opt_optl > 16)
+				return -EINVAL;
+
+			memcpy(&scp->conndata_out, &opt, sizeof(struct optdata_dn));
+			break;
+
+		case DSO_DISDATA:
+	   	        if (sock->state != SS_CONNECTED && sk->protinfo.dn.accept_mode == ACC_IMMED)
+				return -ENOTCONN;
+
+			if (optlen != sizeof(struct optdata_dn))
+				return -EINVAL;
+
+			if (copy_from_user(&opt, optval, sizeof(struct optdata_dn)))
+				return -EFAULT;
+
+			if (opt.opt_optl > 16)
+				return -EINVAL;
+
+			memcpy(&scp->discdata_out, &opt, sizeof(struct optdata_dn));
+			break;
+
+		case DSO_CONACCESS:
+			if (sock->state == SS_CONNECTED) 
+				return -EISCONN;
+			if (scp->state != DN_O)
+				return -EINVAL;
+
+			if (optlen != sizeof(struct accessdata_dn))
+				return -EINVAL;
+
+			if (copy_from_user(&acc, optval, sizeof(struct accessdata_dn)))
+				return -EFAULT;
+
+			if ((acc.acc_accl > DN_MAXACCL) ||
+					(acc.acc_passl > DN_MAXACCL) ||
+					(acc.acc_userl > DN_MAXACCL))
+				return -EINVAL;
+
+			memcpy(&scp->accessdata, &acc, sizeof(struct accessdata_dn));
+			break;
+
+		case DSO_ACCEPTMODE:
+			if (sock->state == SS_CONNECTED)
+				return -EISCONN;
+			if (scp->state != DN_O)
+				return -EINVAL;
+
+			if (optlen != sizeof(int))
+				return -EINVAL;
+
+			{
+				int mode;
+
+				if (get_user(mode, optval))
+					return -EFAULT;
+				if ((mode != ACC_IMMED) && (mode != ACC_DEFER))
+					return -EINVAL;
+
+				scp->accept_mode = (unsigned char)mode;
+			}
+			break;
+
+		case DSO_CONACCEPT:
+			lock_sock(sk);
+
+			if (scp->state != DN_CR)
+				return -EINVAL;
+
+			scp->state = DN_CC;
+			dn_send_conn_conf(sk);
+			err = dn_wait_accept(sock, sock->file->f_flags);
+			release_sock(sk);
+			return err;
+
+		case DSO_CONREJECT:
+			lock_sock(sk);
+
+			if (scp->state != DN_CR)
+				return -EINVAL;
+
+			scp->state = DN_DR;
+			sk->shutdown = SHUTDOWN_MASK;
+			dn_send_disc(sk, 0x38, 0);
+			release_sock(sk);
+			break;
+
+#ifdef CONFIG_DECNET_FW
+		case DN_FW_MASQ_TIMEOUTS:
+                case DN_FW_APPEND:
+                case DN_FW_REPLACE:
+                case DN_FW_DELETE:
+                case DN_FW_DELETE_NUM:
+                case DN_FW_INSERT:
+                case DN_FW_FLUSH:
+                case DN_FW_ZERO:
+                case DN_FW_CHECK:
+                case DN_FW_CREATECHAIN:
+                case DN_FW_DELETECHAIN:
+                case DN_FW_POLICY:
+
+                        if (!capable(CAP_NET_ADMIN))
+                                return -EACCES;
+                        if ((optlen > sizeof(tmp_fw)) || (optlen < 1))
+                                return -EINVAL;
+                        if (copy_from_user(&tmp_fw, optval, optlen))
+                                return -EFAULT;
+                        err = dn_fw_ctl(optname, &tmp_fw, optlen);
+                        return -err;    /* -0 is 0 after all */
+#endif
+		default:
+		case DSO_LINKINFO:
+		case DSO_STREAM:
+		case DSO_SEQPACKET:
+
+			return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static int dn_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen)
+{
+	return _dn_getsockopt(sock, level, optname, optval, optlen, 0);
+}
+
+static int
+_dn_getsockopt(struct socket *sock, int level,int optname, char *optval,int *optlen, int flags)
+{
+	struct	sock *sk = sock->sk;
+	struct dn_scp *scp = &sk->protinfo.dn;
+	struct linkinfo_dn link;
+	int mode = scp->accept_mode;
+
+	switch(optname) {
+		case DSO_CONDATA:
+			if (*optlen != sizeof(struct optdata_dn))
+				return -EINVAL;
+
+			if (copy_to_user(optval, &scp->conndata_in, sizeof(struct optdata_dn)))
+				return -EFAULT;
+			break;
+
+		case DSO_DISDATA:
+			if (*optlen != sizeof(struct optdata_dn))
+				return -EINVAL;
+
+			if (copy_to_user(optval, &scp->discdata_in, sizeof(struct optdata_dn)))
+				return -EFAULT;
+
+			break;
+
+		case DSO_CONACCESS:
+			if (*optlen != sizeof(struct accessdata_dn))
+				return -EINVAL;
+
+			if (copy_to_user(optval, &scp->accessdata, sizeof(struct accessdata_dn)))
+				return -EFAULT;
+			break;
+
+		case DSO_ACCEPTMODE:
+			if (put_user(mode, optval))
+				return -EFAULT;
+			break;
+
+		case DSO_LINKINFO:
+			if (*optlen != sizeof(struct linkinfo_dn))
+				return -EINVAL;
+
+			switch(sock->state) {
+				case SS_CONNECTING:
+					link.idn_linkstate = LL_CONNECTING;
+					break;
+				case SS_DISCONNECTING:
+					link.idn_linkstate = LL_DISCONNECTING;
+					break;
+				case SS_CONNECTED:
+					link.idn_linkstate = LL_RUNNING;
+					break;
+				default:
+					link.idn_linkstate = LL_INACTIVE;
+			}
+
+			link.idn_segsize = scp->mss;
+
+			if (copy_to_user(optval, &link, sizeof(struct linkinfo_dn)))
+				return -EFAULT;
+			break;
+
+		case DSO_STREAM:
+		case DSO_SEQPACKET:
+		case DSO_CONACCEPT:
+		case DSO_CONREJECT:
+		default:
+        		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+
+/*
+ * Used by send/recvmsg to wait until the socket is connected
+ * before passing data.
+ */
+static int dn_wait_run(struct sock *sk, int flags)
+{
+	struct dn_scp *scp = &sk->protinfo.dn;
+	int err = 0;
+
+	/* printk(KERN_DEBUG "dn_wait_run %d\n", scp->state); */
+
+	switch(scp->state) {
+		case DN_RUN:
+			return 0;
+
+		case DN_CR:
+			scp->state = DN_CC;
+			dn_send_conn_conf(sk);
+			return dn_wait_accept(sk->socket, (flags & MSG_DONTWAIT) ? O_NONBLOCK : 0);
+		case DN_CI:
+		case DN_CC:
+			break;
+		default:
+			return -ENOTCONN;
+			goto out;
+	}
+
+	if (flags & MSG_DONTWAIT)
+		return -EWOULDBLOCK;
+
+	do {
+		if ((err = sock_error(sk)) != 0)
+			goto out;
+
+		if (signal_pending(current)) {
+			err = -ERESTARTSYS;
+			goto out;
+		}
+
+		SOCK_SLEEP_PRE(sk)
+
+		if (scp->state != DN_RUN)
+			schedule();
+
+		SOCK_SLEEP_POST(sk)
+
+	} while(scp->state != DN_RUN);
+
+out:
+
+	return 0;
+}
+
+
+static int dn_data_ready(struct sock *sk, struct sk_buff_head *q, int flags, int target)
+{
+	struct sk_buff *skb = q->next;
+	int len = 0;
+
+	if (flags & MSG_OOB)
+		return skb_queue_len(q) ? 1 : 0;
+
+	while(skb != (struct sk_buff *)q) {
+		struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+		len += skb->len;
+
+		if (cb->nsp_flags & 0x40) {
+			/* SOCK_SEQPACKET reads to EOM */
+			if (sk->type == SOCK_SEQPACKET)
+				return 1;
+			/* so does SOCK_STREAM unless WAITALL is specified */
+			if (!(flags & MSG_WAITALL))
+				return 1;
+		}
+
+		/* minimum data length for read exceeded */
+		if (len >= target)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static int dn_recvmsg(struct socket *sock, struct msghdr *msg, int size,
+	int flags, struct scm_cookie *scm)
+{
+	struct sock *sk = sock->sk;
+	struct dn_scp *scp = &sk->protinfo.dn;
+	struct sk_buff_head *queue = &sk->receive_queue;
+	int target = size > 1 ? 1 : 0;
+	int copied = 0;
+	int rv = 0;
+	struct sk_buff *skb = NULL, **pskb;
+	struct dn_skb_cb *cb = NULL;
+
+	lock_sock(sk);
+
+	if (sk->zapped) {
+		rv = -EADDRNOTAVAIL;
+		goto out;
+	}
+
+	if ((rv = dn_wait_run(sk, flags)) != 0)
+		goto out;
+
+	if (sk->shutdown & RCV_SHUTDOWN) {
+		send_sig(SIGPIPE, current, 0);
+		rv = -EPIPE;
+		goto out;
+	}
+
+	if (flags & ~(MSG_PEEK|MSG_OOB|MSG_WAITALL|MSG_DONTWAIT)) {
+		rv = -EOPNOTSUPP;
+		goto out;
+	}
+
+	if (flags & MSG_OOB)
+		queue = &scp->other_receive_queue;
+
+	if (flags & MSG_WAITALL)
+		target = size;
+
+
+	/*
+	 * See if there is data ready to read, sleep if there isn't
+	 */
+	for(;;) {
+		if (sk->err)
+			goto out;
+
+		if (skb_queue_len(&scp->other_receive_queue)) {
+			if (!(flags & MSG_OOB)) {
+				msg->msg_flags |= MSG_OOB;
+				if (!scp->other_report) {
+					scp->other_report = 1;
+					goto out;
+				}
+			}
+		}
+		
+		if (scp->state != DN_RUN)
+			goto out;
+
+		if (signal_pending(current)) {
+			rv = -ERESTARTSYS;
+			goto out;
+		}
+
+		if (dn_data_ready(sk, queue, flags, target))
+			break;
+
+		if (flags & MSG_DONTWAIT) {
+			rv = -EWOULDBLOCK;
+			goto out;
+		}
+
+		sock->flags |= SO_WAITDATA;
+		SOCK_SLEEP_PRE(sk)
+
+		if (!dn_data_ready(sk, queue, flags, target))
+			schedule();
+
+		SOCK_SLEEP_POST(sk)
+		sock->flags &= ~SO_WAITDATA;
+	}
+
+	pskb = &((struct sk_buff *)queue)->next;
+	while((skb = *pskb) != (struct sk_buff *)queue) {
+		int chunk = skb->len;
+		cb = (struct dn_skb_cb *)skb->cb;
+
+		if ((chunk + copied) > size)
+			chunk = size - copied;
+
+		if (memcpy_toiovec(msg->msg_iov, skb->data, chunk)) {
+			rv = -EFAULT;
+			break;
+		}
+		copied += chunk;
+
+		if (!(flags & MSG_PEEK))
+			skb->len -= chunk;
+
+		if (skb->len == 0) {
+			skb_unlink(skb);
+			kfree_skb(skb);
+			if ((scp->flowloc_sw == DN_DONTSEND) && !dn_congested(sk)) {
+				scp->flowloc_sw = DN_SEND;
+				dn_nsp_send_lnk(sk, DN_SEND);
+			}
+		}
+
+		pskb = &skb->next;
+
+		if (cb->nsp_flags & 0x40) {
+                        if (sk->type == SOCK_SEQPACKET) {
+                                msg->msg_flags |= MSG_EOR;
+				break;
+			}
+		}
+
+		if (!(flags & MSG_WAITALL))
+			break;
+
+		if (flags & MSG_OOB)
+			break;
+
+		if (copied >= target)
+			break;
+	}
+
+	rv = copied;
+out:
+	if (rv == 0)
+		rv = (flags & MSG_PEEK) ? -sk->err : sock_error(sk);
+
+	release_sock(sk);
+
+	return rv;
+}
+
+
+static int dn_sendmsg(struct socket *sock, struct msghdr *msg, int size, 
+	   struct scm_cookie *scm)
+{
+	struct sock *sk = sock->sk;
+	struct dn_scp *scp = &sk->protinfo.dn;
+	int mss = scp->mss;
+	int mtu = 230 - 11; /* maximum value thats always safe */
+	struct sk_buff_head *queue = &scp->data_xmit_queue;
+	int flags = msg->msg_flags;
+	unsigned short numseg = 0;
+	int err = 0;
+	int sent = 0;
+	int addr_len = msg->msg_namelen;
+	struct sockaddr_dn *addr = (struct sockaddr_dn *)msg->msg_name;
+	struct sk_buff *skb = NULL;
+	struct dn_skb_cb *cb;
+	unsigned char msgflg;
+	unsigned char *ptr;
+	unsigned short ack;
+	int len;
+
+	if (flags & ~(MSG_TRYHARD|MSG_OOB|MSG_DONTWAIT))
+		return -EOPNOTSUPP;
+
+	if (addr_len && (addr_len != sizeof(struct sockaddr_dn)))
+		return -EINVAL;
+
+	if (sk->zapped && dn_auto_bind(sock))  {
+		err = -EADDRNOTAVAIL;
+		goto out;
+	}
+
+	if (scp->state == DN_O) {
+		if (!addr_len || !addr) {
+			err = -ENOTCONN;
+			goto out;
+		}
+
+		if ((err = dn_connect(sock, (struct sockaddr *)addr, addr_len, (flags & MSG_DONTWAIT) ? O_NONBLOCK : 0)) < 0)
+			goto out;
+	}
+
+	lock_sock(sk);
+
+	if ((err = dn_wait_run(sk, flags)) < 0)
+		goto out;
+
+	if (sk->shutdown & SEND_SHUTDOWN) {
+		send_sig(SIGPIPE, current, 0);
+		err = -EPIPE;
+		goto out;
+	}
+
+	if ((flags & MSG_TRYHARD) && sk->dst_cache)
+		dst_negative_advice(&sk->dst_cache);
+
+	if (sk->dst_cache && sk->dst_cache->neighbour) {
+		struct dn_neigh *dn = (struct dn_neigh *)sk->dst_cache->neighbour;
+		if (dn->blksize > 230)
+			mtu = dn->blksize - 11;
+	}
+
+	/*
+	 * The only difference between SEQPACKET & STREAM sockets under DECnet
+	 * AFAIK is that SEQPACKET sockets set the MSG_EOR flag for the last
+	 * session control message segment.
+	 */
+
+	if (flags & MSG_OOB) {
+		mss = 16;
+		queue = &scp->other_xmit_queue;
+		if (size > mss) {
+			err = -EMSGSIZE;
+			goto out;
+		}
+	}
+
+	if (mss < mtu)
+		mtu = mss;
+
+	scp->persist_fxn = dn_nsp_xmit_timeout;
+
+	while(sent < size) {
+		if ((err = sock_error(sk) != 0))
+			goto out;
+
+		if (signal_pending(current)) {
+			err = -ERESTARTSYS;
+			goto out;
+		}
+
+		/*
+		 * Calculate size that we wish to send.
+		 */
+		len = size - sent;
+
+		if (len > mtu)
+			len = mtu;
+
+		/*
+		 * Wait for queue size to go down below the window
+		 * size.
+		 */
+		if (skb_queue_len(queue) >= scp->snd_window) {
+			if (flags & MSG_DONTWAIT) {
+				err = -EWOULDBLOCK;
+				goto out;
+			}
+
+			SOCK_SLEEP_PRE(sk)
+
+			if (skb_queue_len(queue) >= scp->snd_window)
+				schedule();
+
+			SOCK_SLEEP_POST(sk)
+
+			continue;
+		}
+
+		/*
+		 * Get a suitably sized skb.
+		 */
+		skb = dn_alloc_send_skb(sk, &len, flags & MSG_DONTWAIT, &err);
+
+		if (err)
+			break;
+
+		if (!skb)
+			continue;
+
+		cb = (struct dn_skb_cb *)skb->cb;
+
+		ptr = skb_put(skb, 9);
+
+		if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) {
+			err = -EFAULT;
+			goto out;
+		}
+
+		if (flags & MSG_OOB) {
+			cb->segnum = scp->numoth++;
+			scp->numoth &= 0x0fff;
+			msgflg = 0x30;
+			ack = scp->ackxmt_oth | 0x8000;
+		} else {
+			cb->segnum = scp->numdat++;
+			scp->numdat &= 0x0fff;
+			if (sock->type == SOCK_STREAM)
+				msgflg = 0x60;
+			else
+				msgflg = 0x00;
+			if (numseg == 0)
+				msgflg |= 0x20;
+			if ((sent + len) == size)
+				msgflg |= 0x40;
+			ack = scp->ackxmt_dat | 0x8000;
+		}
+
+		*ptr++ = msgflg;
+		*(__u16 *)ptr = scp->addrrem;
+		ptr += 2;
+		*(__u16 *)ptr = scp->addrloc;
+		ptr += 2;
+		*(__u16 *)ptr = dn_htons(ack);
+		ptr += 2;
+		*(__u16 *)ptr = dn_htons(cb->segnum);
+
+		sent += len;
+		dn_nsp_queue_xmit(sk, skb, flags & MSG_OOB);
+		numseg++;
+		skb = NULL;
+
+		scp->persist = dn_nsp_persist(sk);
+
+	}
+out:
+
+	if (skb)
+		kfree_skb(skb);
+
+	release_sock(sk);
+
+	return sent ? sent : err;
+}
+
+static int dn_device_event(struct notifier_block *this, unsigned long event,
+			void *ptr)
+{
+	struct device *dev = (struct device *)ptr;
+
+	switch(event) {
+		case NETDEV_UP:
+			dn_dev_up(dev);
+			break;
+		case NETDEV_DOWN:
+			dn_dev_down(dev);
+			break;
+		default:
+			break;
+	}
+
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block dn_dev_notifier = {
+	dn_device_event,
+	0
+};
+
+extern int dn_route_rcv(struct sk_buff *, struct device *, struct packet_type *);
+
+static struct packet_type dn_dix_packet_type = 
+{
+	__constant_htons(ETH_P_DNA_RT),
+	NULL,		/* All devices */
+	dn_route_rcv,
+	NULL,
+	NULL,
+};
+
+#ifdef CONFIG_PROC_FS
+struct proc_dir_entry decnet_linkinfo = {
+	PROC_NET_DN_SKT, 6, "decnet", S_IFREG | S_IRUGO,
+	1, 0, 0, 0, &proc_net_inode_operations, dn_get_info
+};
+
+#ifdef CONFIG_DECNET_RAW
+
+extern int dn_raw_get_info(char *, char **, off_t, int, int);
+
+struct proc_dir_entry decnet_rawinfo = {
+	PROC_NET_DN_RAW, 10, "decnet_raw", S_IFREG | S_IRUGO,
+	1, 0, 0, 0, &proc_net_inode_operations, dn_raw_get_info
+};
+
+#endif /* CONFIG_DECNET_RAW */
+#endif /* CONFIG_PROC_FS */
+static struct net_proto_family	dn_family_ops = {
+	AF_DECnet,
+	dn_create
+};
+
+static struct proto_ops dn_proto_ops = {
+        AF_DECnet,
+
+	sock_no_dup,
+	dn_release,
+	dn_bind,
+	dn_connect,
+	sock_no_socketpair,
+	dn_accept,
+	dn_getname,
+	dn_poll,
+	dn_ioctl,
+	dn_listen,
+	dn_shutdown,
+	dn_setsockopt,
+	dn_getsockopt,
+	sock_no_fcntl,
+        dn_sendmsg,
+	dn_recvmsg
+};
+
+#ifdef CONFIG_SYSCTL
+void dn_register_sysctl(void);
+void dn_unregister_sysctl(void);
+#endif
+
+void __init decnet_proto_init(struct net_proto *pro)
+{
+	sock_register(&dn_family_ops);
+	dev_add_pack(&dn_dix_packet_type);
+	register_netdevice_notifier(&dn_dev_notifier);
+
+#ifdef CONFIG_PROC_FS
+	proc_net_register(&decnet_linkinfo);
+#ifdef CONFIG_DECNET_RAW
+	proc_net_register(&decnet_rawinfo);
+#endif
+#endif
+	dn_dev_init();
+	dn_neigh_init();
+	dn_route_init();
+
+#ifdef CONFIG_DECNET_FW
+	dn_fw_init();
+#endif /* CONFIG_DECNET_FW */
+
+#ifdef CONFIG_DECNET_ROUTER
+	dn_fib_init();
+#endif /* CONFIG_DECNET_ROUTER */
+
+#ifdef CONFIG_SYSCTL
+	dn_register_sysctl();
+#endif /* CONFIG_SYSCTL */
+        printk(KERN_INFO "DECnet for Linux: V.2.2.5s (C) 1995-1999 Linux DECnet Project Team\n");
+
+}
+
+void __init decnet_setup(char *str, int *ints)
+{
+
+	if ((ints[0] == 2) || (ints[0] == 3)) {
+
+		if (ints[1] < 0)
+			ints[1] = 0;
+		if (ints[1] > 63)
+			ints[1] = 63;
+
+		if (ints[2] < 0)
+			ints[2] = 0;
+		if (ints[2] > 1023)
+			ints[2] = 1023;
+
+		decnet_address = dn_htons(ints[1] << 10 | ints[2]);
+		dn_dn2eth(decnet_ether_address, dn_ntohs(decnet_address));
+
+		if (ints[0] == 3) {
+			switch(ints[3]) {
+#ifdef CONFIG_DECNET_ROUTER
+				case 1:
+					decnet_node_type = DN_RT_INFO_L1RT;
+					break;
+				case 2:
+					decnet_node_type = DN_RT_INFO_L2RT;
+					break;
+#endif /* CONFIG_DECNET_ROUTER */
+				default:
+					decnet_node_type = DN_RT_INFO_ENDN;
+			}
+		}
+	} else {
+		printk(KERN_ERR "DECnet: Invalid command line options\n");
+	}
+}
+
+#ifdef MODULE
+EXPORT_NO_SYMBOLS;
+MODULE_DESCRIPTION("The Linux DECnet Network Protocol");
+MODULE_AUTHOR("Linux DECnet Project Team");
+
+static int addr[2] = {0, 0};
+#ifdef CONFIG_DECNET_ROUTER
+static int type = 0;
+#endif
+
+MODULE_PARM(addr, "2i");
+MODULE_PARM_DESC(addr, "The DECnet address of this machine: area,node");
+#ifdef CONFIG_DECNET_ROUTER
+MODULE_PARM(type, "i");
+MODULE_PARM_DESC(type, "The type of this DECnet node: 0=EndNode, 1,2=Router");
+#endif
+
+int init_module(void)
+{
+	if (addr[0] > 63 || addr[0] < 0) {
+		printk(KERN_ERR "DECnet: Area must be between 0 and 63");
+		return 1;
+	}
+
+	if (addr[1] > 1023 || addr[1] < 0) {
+		printk(KERN_ERR "DECnet: Node must be between 0 and 1023");
+		return 1;
+	}
+
+	decnet_address = dn_htons((addr[0] << 10) | addr[1]);
+	dn_dn2eth(decnet_ether_address, dn_ntohs(decnet_address));
+
+#ifdef CONFIG_DECNET_ROUTER
+	switch(type) {
+		case 0:
+			decnet_node_type = DN_RT_INFO_ENDN;
+			break;
+		case 1:
+			decnet_node_type = DN_RT_INFO_L1RT;
+			break;
+		case 2:
+			decnet_node_type = DN_RT_INFO_L2RT;
+			break;
+		default:
+			printk(KERN_ERR "DECnet: Node type must be between 0 and 2 inclusive\n");
+			return 1;
+	}
+#else
+	decnet_node_type = DN_RT_INFO_ENDN;
+#endif
+
+	decnet_proto_init(NULL);
+
+	return 0;
+}
+
+void cleanup_module(void)
+{
+#ifdef CONFIG_SYSCTL
+	dn_unregister_sysctl();
+#endif /* CONFIG_SYSCTL */
+
+	unregister_netdevice_notifier(&dn_dev_notifier);
+
+	dn_route_cleanup();
+	dn_neigh_cleanup();
+	dn_dev_cleanup();
+
+#ifdef CONFIG_DECNET_FW
+	/* dn_fw_cleanup(); */
+#endif /* CONFIG_DECNET_FW */
+
+#ifdef CONFIG_DECNET_ROUTER
+	dn_fib_cleanup();
+#endif /* CONFIG_DECNET_ROUTER */
+
+#ifdef CONFIG_PROC_FS
+	proc_net_unregister(PROC_NET_DN_SKT);
+#ifdef CONFIG_DECNET_RAW
+	proc_net_unregister(PROC_NET_DN_RAW);
+#endif
+#endif
+
+	dev_remove_pack(&dn_dix_packet_type);
+	sock_unregister(AF_DECnet);
+}
+
+#endif
diff -u --recursive --new-file v2.3.3/linux/net/decnet/dn_dev.c linux/net/decnet/dn_dev.c
--- v2.3.3/linux/net/decnet/dn_dev.c	Wed Dec 31 16:00:00 1969
+++ linux/net/decnet/dn_dev.c	Sat May 29 11:09:54 1999
@@ -0,0 +1,1390 @@
+/*
+ * DECnet       An implementation of the DECnet protocol suite for the LINUX
+ *              operating system.  DECnet is implemented using the  BSD Socket
+ *              interface as the means of communication with the user level.
+ *
+ *              DECnet Device Layer
+ *
+ * Authors:     Steve Whitehouse <SteveW@ACM.org>
+ *              Eduardo Marcelo Serrat <emserrat@geocities.com>
+ *
+ * Changes:
+ *          Steve Whitehouse : Devices now see incoming frames so they
+ *                             can mark on who it came from.
+ *          Steve Whitehouse : Fixed bug in creating neighbours. Each neighbour
+ *                             can now have a device specific setup func.
+ *          Steve Whitehouse : Added /proc/sys/net/decnet/conf/<dev>/
+ *          Steve Whitehouse : Fixed bug which sometimes killed timer
+ *          Steve Whitehouse : Multiple ifaddr support
+ *          Steve Whitehouse : SIOCGIFCONF is now a compile time option
+ */
+
+#include <linux/config.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/rtnetlink.h>
+#include <linux/sysctl.h>
+#include <asm/uaccess.h>
+#include <net/neighbour.h>
+#include <net/dst.h>
+#include <net/dn.h>
+#include <net/dn_dev.h>
+#include <net/dn_route.h>
+#include <net/dn_neigh.h>
+#include <net/dn_fib.h>
+
+#define DN_IFREQ_SIZE (sizeof(struct ifreq) - sizeof(struct sockaddr) + sizeof(struct sockaddr_dn))
+
+static char dn_rt_all_end_mcast[ETH_ALEN] = {0xAB,0x00,0x00,0x04,0x00,0x00};
+static char dn_rt_all_rt_mcast[ETH_ALEN]  = {0xAB,0x00,0x00,0x03,0x00,0x00};
+static char dn_hiord[ETH_ALEN]            = {0xAA,0x00,0x04,0x00,0x00,0x00};
+static unsigned char dn_eco_version[3]    = {0x02,0x00,0x00};
+
+extern struct neigh_table dn_neigh_table;
+
+struct device *decnet_default_device = NULL;
+
+static struct dn_dev *dn_dev_create(struct device *dev, int *err);
+static void dn_dev_delete(struct device *dev);
+#ifdef CONFIG_RTNETLINK
+static void rtmsg_ifa(int event, struct dn_ifaddr *ifa);
+#endif
+
+static int dn_eth_up(struct device *);
+static void dn_send_brd_hello(struct device *dev);
+static void dn_send_ptp_hello(struct device *dev);
+static int dn_dev_eth_setsrc(struct sk_buff *skb);
+static int dn_dev_lo_setsrc(struct sk_buff *skb);
+static int dn_dev_ptp_setsrc(struct sk_buff *skb);
+static int dn_dev_eth_neigh_setup(struct neighbour *n);
+
+static struct dn_dev_parms dn_dev_list[] =  {
+{
+	ARPHRD_ETHER, /* Ethernet */
+	DN_DEV_BCAST,
+	DN_DEV_S_RU,
+	1,
+	1498,
+	10,
+	1,
+	10,
+	0,
+	"ethernet",
+	NET_DECNET_CONF_ETHER,
+	dn_eth_up,
+	NULL,
+	NULL,
+	dn_send_brd_hello,
+	dn_dev_eth_setsrc,
+	dn_dev_eth_neigh_setup,
+	NULL
+},
+{
+	ARPHRD_IPGRE, /* DECnet tunneled over GRE in IP */
+	DN_DEV_BCAST,
+	DN_DEV_S_RU,
+	2,
+	1400,
+	10,
+	1,
+	10,
+	0,
+	"ipgre",
+	NET_DECNET_CONF_GRE,
+	NULL,
+	NULL,
+	NULL,
+	dn_send_brd_hello,
+	NULL,
+	NULL,
+	NULL
+},
+#if 0
+{
+	ARPHRD_X25, /* Bog standard X.25 */
+	DN_DEV_UCAST,
+	DN_DEV_S_DS,
+	5,
+	230,
+	10 * 60,
+	1,
+	120,
+	0,
+	"x25",
+	NET_DECNET_CONF_X25,
+	NULL,
+	NULL,
+	NULL,
+	dn_send_ptp_hello,
+	dn_dev_ptp_setsrc,
+	NULL,
+	NULL
+},
+#endif
+#if 0
+{
+	ARPHRD_PPP, /* DECnet over PPP */
+	DN_DEV_BCAST,
+	DN_DEV_S_RU,
+	5,
+	230,
+	10,
+	1,
+	10,
+	0,
+	"ppp",
+	NET_DECNET_CONF_PPP,
+	NULL,
+	NULL,
+	NULL,
+	dn_send_brd_hello,
+	dn_dev_ptp_setsrc,
+	NULL,
+	NULL
+},
+#endif
+#if 0
+{
+	ARPHRD_DDCMP, /* DECnet over DDCMP */
+	DN_DEV_UCAST,
+	DN_DEV_S_DS,
+	5,
+	230,
+	10 * 60,
+	1,
+	120,
+	0,
+	"ddcmp",
+	NET_DECNET_CONF_DDCMP,
+	NULL,
+	NULL,
+	NULL,
+	dn_send_ptp_hello,
+	dn_dev_ptp_setsrc,
+	NULL,
+	NULL
+},
+#endif
+{
+	ARPHRD_LOOPBACK, /* Loopback interface - always last */
+	DN_DEV_BCAST,
+	DN_DEV_S_RU,
+	0,
+	1498,
+	10,
+	1,
+	10,
+	0,
+	"loopback",
+	NET_DECNET_CONF_LOOPBACK,
+	NULL,
+	NULL,
+	NULL,
+	dn_send_brd_hello,
+	dn_dev_lo_setsrc,
+	dn_dev_eth_neigh_setup,
+	NULL
+}
+};
+
+#define DN_DEV_LIST_SIZE (sizeof(dn_dev_list)/sizeof(struct dn_dev_parms))
+
+#define DN_DEV_PARMS_OFFSET(x) ((int) ((char *) &((struct dn_dev_parms *)0)->x))
+
+#ifdef CONFIG_SYSCTL
+
+static int min_t2[] = { 1 };
+static int max_t2[] = { 60 }; /* No max specified, but this seems sensible */
+static int min_t3[] = { 1 };
+static int max_t3[] = { 8191 }; /* Must fit in 16 bits when multiplied by BCT3MULT or T3MULT */
+#ifdef CONFIG_DECNET_ROUTER
+static int min_t1[] = { 1 };
+static int max_t1[] = { 8191 }; /* No max specified, so made it the same as t3 */
+static int min_cost[] = { 0 };
+static int max_cost[] = { 25 }; /* From DECnet spec */
+static int min_priority[] = { 0 };
+static int max_priority[] = { 127 }; /* From DECnet spec */
+#endif /* CONFIG_DECNET_ROUTER */
+
+static struct dn_dev_sysctl_table {
+	struct ctl_table_header *sysctl_header;
+#ifdef CONFIG_DECNET_ROUTER
+	ctl_table dn_dev_vars[6];
+#else
+	ctl_table dn_dev_vars[3];
+#endif
+	ctl_table dn_dev_dev[2];
+	ctl_table dn_dev_conf_dir[2];
+	ctl_table dn_dev_proto_dir[2];
+	ctl_table dn_dev_root_dir[2];
+} dn_dev_sysctl = {
+	NULL,
+	{
+#ifdef CONFIG_DECNET_ROUTER
+	{NET_DECNET_CONF_DEV_COST, "cost", (void *)DN_DEV_PARMS_OFFSET(cost),
+	sizeof(int), 0644, NULL,
+	proc_dointvec_minmax, sysctl_intvec,
+	NULL, &min_cost, &max_cost},
+	{NET_DECNET_CONF_DEV_PRIORITY, "priority", (void *)DN_DEV_PARMS_OFFSET(priority),
+	sizeof(int), 0644, NULL,
+	proc_dointvec_minmax, sysctl_intvec,
+	NULL, &min_priority, &max_priority},
+	{NET_DECNET_CONF_DEV_T1, "t1", (void *)DN_DEV_PARMS_OFFSET(t1),
+	sizeof(int), 0644, NULL,
+	proc_dointvec_minmax, sysctl_intvec,
+	NULL, &min_t1, &max_t1},
+#endif
+	{NET_DECNET_CONF_DEV_T2, "t2", (void *)DN_DEV_PARMS_OFFSET(t2),
+	sizeof(int), 0644, NULL,
+	proc_dointvec_minmax, sysctl_intvec,
+	NULL, &min_t2, &max_t2},
+	{NET_DECNET_CONF_DEV_T3, "t3", (void *)DN_DEV_PARMS_OFFSET(t3),
+	sizeof(int), 0644, NULL,
+	proc_dointvec_minmax, sysctl_intvec,
+	NULL, &min_t3, &max_t3},
+	{0}
+	},
+	{{0, "", NULL, 0, 0555, dn_dev_sysctl.dn_dev_vars}, {0}},
+	{{NET_DECNET_CONF, "conf", NULL, 0, 0555, dn_dev_sysctl.dn_dev_dev}, {0}},
+	{{NET_DECNET, "decnet", NULL, 0, 0555, dn_dev_sysctl.dn_dev_conf_dir}, {0}},
+	{{CTL_NET, "net", NULL, 0, 0555, dn_dev_sysctl.dn_dev_proto_dir}, {0}}
+};
+
+static void dn_dev_sysctl_register(struct device *dev, struct dn_dev_parms *parms)
+{
+	struct dn_dev_sysctl_table *t;
+	int i;
+
+	t = kmalloc(sizeof(*t), GFP_KERNEL);
+	if (t == NULL)
+		return;
+
+	memcpy(t, &dn_dev_sysctl, sizeof(*t));
+
+	for(i = 0; i < (sizeof(t->dn_dev_vars)/sizeof(t->dn_dev_vars[0]) - 1); i++) {
+		long offset = (long)t->dn_dev_vars[i].data;
+		t->dn_dev_vars[i].data = ((char *)parms) + offset;
+		t->dn_dev_vars[i].de = NULL;
+	}
+
+	if (dev) {
+		t->dn_dev_dev[0].procname = dev->name;
+		t->dn_dev_dev[0].ctl_name = dev->ifindex;
+	} else {
+		t->dn_dev_dev[0].procname = parms->name;
+		t->dn_dev_dev[0].ctl_name = parms->ctl_name;
+	}
+
+	t->dn_dev_dev[0].child = t->dn_dev_vars;
+	t->dn_dev_dev[0].de = NULL;
+	t->dn_dev_conf_dir[0].child = t->dn_dev_dev;
+	t->dn_dev_conf_dir[0].de = NULL;
+	t->dn_dev_proto_dir[0].child = t->dn_dev_conf_dir;
+	t->dn_dev_proto_dir[0].de = NULL;
+	t->dn_dev_root_dir[0].child = t->dn_dev_proto_dir;
+	t->dn_dev_root_dir[0].de = NULL;
+
+	t->sysctl_header = register_sysctl_table(t->dn_dev_root_dir, 0);
+	if (t->sysctl_header == NULL)
+		kfree(t);
+	else
+		parms->sysctl = t;
+}
+
+static void dn_dev_sysctl_unregister(struct dn_dev_parms *parms)
+{
+	if (parms->sysctl) {
+		struct dn_dev_sysctl_table *t = parms->sysctl;
+		parms->sysctl = NULL;
+		unregister_sysctl_table(t->sysctl_header);
+		kfree(t);
+	}
+}
+#endif
+
+static struct dn_ifaddr *dn_dev_alloc_ifa(void)
+{
+	struct dn_ifaddr *ifa;
+
+	ifa = kmalloc(sizeof(*ifa), GFP_KERNEL);
+
+	if (ifa) {
+		memset(ifa, 0, sizeof(*ifa));
+	}
+
+	return ifa;
+}
+
+static __inline__ void dn_dev_free_ifa(struct dn_ifaddr *ifa)
+{
+	kfree_s(ifa, sizeof(*ifa));
+}
+
+static void dn_dev_del_ifa(struct dn_dev *dn_db, struct dn_ifaddr **ifap, int destroy)
+{
+	struct dn_ifaddr *ifa1 = *ifap;
+
+	*ifap = ifa1->ifa_next;
+
+#ifdef CONFIG_RTNETLINK
+	rtmsg_ifa(RTM_DELADDR, ifa1);
+#endif /* CONFIG_RTNETLINK */
+
+	if (destroy) {
+		dn_dev_free_ifa(ifa1);
+
+		if (dn_db->ifa_list == NULL)
+			dn_dev_delete(dn_db->dev);
+	}
+}
+
+static int dn_dev_insert_ifa(struct dn_dev *dn_db, struct dn_ifaddr *ifa)
+{
+	/*
+	 * FIXME: Duplicate check here.
+	 */
+
+	ifa->ifa_next = dn_db->ifa_list;
+	dn_db->ifa_list = ifa;
+
+#ifdef CONFIG_RTNETLINK
+	rtmsg_ifa(RTM_NEWADDR, ifa);
+#endif /* CONFIG_RTNETLINK */
+
+	return 0;
+}
+
+static int dn_dev_set_ifa(struct device *dev, struct dn_ifaddr *ifa)
+{
+	struct dn_dev *dn_db = dev->dn_ptr;
+
+	if (dn_db == NULL) {
+		int err;
+		dn_db = dn_dev_create(dev, &err);
+		if (dn_db == NULL)
+			return err;
+	}
+
+	ifa->ifa_dev = dn_db;
+
+	if (dev->flags & IFF_LOOPBACK)
+		ifa->ifa_scope = RT_SCOPE_HOST;
+
+	return dn_dev_insert_ifa(dn_db, ifa);
+}
+
+static struct dn_dev *dn_dev_by_index(int ifindex)
+{
+	struct device *dev;
+	dev = dev_get_by_index(ifindex);
+	if (dev)
+		return dev->dn_ptr;
+
+	return NULL;
+}
+
+
+int dn_dev_ioctl(unsigned int cmd, void *arg)
+{
+	char buffer[DN_IFREQ_SIZE];
+	struct ifreq *ifr = (struct ifreq *)buffer;
+	struct sockaddr_dn *sdn = (struct sockaddr_dn *)&ifr->ifr_addr;
+	struct dn_dev *dn_db;
+	struct device *dev;
+	struct dn_ifaddr *ifa = NULL, **ifap = NULL;
+	int exclusive = 0;
+	int ret = 0;
+
+	if (copy_from_user(ifr, arg, DN_IFREQ_SIZE))
+		return -EFAULT;
+	ifr->ifr_name[IFNAMSIZ-1] = 0;
+
+#ifdef CONFIG_KMOD
+	dev_load(ifr->ifr_name);
+#endif
+
+	switch(cmd) {
+		case SIOCGIFADDR:
+			break;
+		case SIOCSIFADDR:
+			if (!capable(CAP_NET_ADMIN))
+				return -EACCES;
+			if (sdn->sdn_family != AF_DECnet)
+				return -EINVAL;
+			rtnl_lock();
+			exclusive = 1;
+			break;
+		default:
+			return -EINVAL;
+	}
+
+	if ((dev = dev_get(ifr->ifr_name)) == NULL) {
+		ret = -ENODEV;
+		goto done;
+	}
+
+	if ((dn_db = dev->dn_ptr) != NULL) {
+		for (ifap = &dn_db->ifa_list; (ifa=*ifap) != NULL; ifap = &ifa->ifa_next)
+			if (strcmp(ifr->ifr_name, ifa->ifa_label) == 0)
+				break;
+	}
+
+	if (ifa == NULL && cmd != SIOCSIFADDR) {
+		ret = -EADDRNOTAVAIL;
+		goto done;
+	}
+
+	switch(cmd) {
+		case SIOCGIFADDR:
+			*((dn_address *)sdn->sdn_nodeaddr) = ifa->ifa_local;
+			goto rarok;
+
+		case SIOCSIFADDR:
+			if (!ifa) {
+				if ((ifa = dn_dev_alloc_ifa()) == NULL) {
+					ret = -ENOBUFS;
+					break;
+				}
+				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
+			} else {
+				if (ifa->ifa_local == dn_saddr2dn(sdn))
+					break;
+				dn_dev_del_ifa(dn_db, ifap, 0);
+			}
+
+			ifa->ifa_local = dn_saddr2dn(sdn);
+
+			ret = dn_dev_set_ifa(dev, ifa);
+	}
+done:
+	if (exclusive)
+		rtnl_unlock();
+
+	return ret;
+rarok:
+	if (copy_to_user(arg, ifr, DN_IFREQ_SIZE))
+		return -EFAULT;
+
+	return 0;
+}
+
+#ifdef CONFIG_RTNETLINK
+
+static int dn_dev_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+{
+	struct rtattr **rta = arg;
+	struct dn_dev *dn_db;
+	struct ifaddrmsg *ifm = NLMSG_DATA(nlh);
+	struct dn_ifaddr *ifa, **ifap;
+
+	if ((dn_db = dn_dev_by_index(ifm->ifa_index)) == NULL)
+		return -EADDRNOTAVAIL;
+
+	for(ifap = &dn_db->ifa_list; (ifa=*ifap) != NULL; ifap = &ifa->ifa_next) {
+		void *tmp = rta[IFA_LOCAL-1];
+		if ((tmp && memcmp(RTA_DATA(tmp), &ifa->ifa_local, 2)) ||
+				(rta[IFA_LABEL-1] && strcmp(RTA_DATA(rta[IFA_LABEL-1]), ifa->ifa_label)))
+			continue;
+
+		dn_dev_del_ifa(dn_db, ifap, 1);
+		return 0;
+	}
+
+	return -EADDRNOTAVAIL;
+}
+
+static int dn_dev_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+{
+	struct rtattr **rta = arg;
+	struct device *dev;
+	struct dn_dev *dn_db;
+	struct ifaddrmsg *ifm = NLMSG_DATA(nlh);
+	struct dn_ifaddr *ifa;
+
+	if (rta[IFA_LOCAL-1] == NULL)
+		return -EINVAL;
+
+	if ((dev = dev_get_by_index(ifm->ifa_index)) == NULL)
+		return -ENODEV;
+
+	if ((dn_db = dev->dn_ptr) == NULL) {
+		int err;
+		dn_db = dn_dev_create(dev, &err);
+		if (!dn_db)
+			return err;
+	}
+	
+	if ((ifa = dn_dev_alloc_ifa()) == NULL)
+		return -ENOBUFS;
+
+	memcpy(&ifa->ifa_local, RTA_DATA(rta[IFA_LOCAL-1]), 2);
+	ifa->ifa_flags = ifm->ifa_flags;
+	ifa->ifa_scope = ifm->ifa_scope;
+	ifa->ifa_dev = dn_db;
+	if (rta[IFA_LABEL-1])
+		memcpy(ifa->ifa_label, RTA_DATA(rta[IFA_LABEL-1]), IFNAMSIZ);
+	else
+		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
+
+	return dn_dev_insert_ifa(dn_db, ifa);
+}
+
+static int dn_dev_fill_ifaddr(struct sk_buff *skb, struct dn_ifaddr *ifa,
+				u32 pid, u32 seq, int event)
+{
+	struct ifaddrmsg *ifm;
+	struct nlmsghdr *nlh;
+	unsigned char *b = skb->tail;
+
+	nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(*ifm));
+	ifm = NLMSG_DATA(nlh);
+
+	ifm->ifa_family = AF_DECnet;
+	ifm->ifa_prefixlen = 0;
+	ifm->ifa_flags = ifa->ifa_flags | IFA_F_PERMANENT;
+	ifm->ifa_scope = ifa->ifa_scope;
+	ifm->ifa_index = ifa->ifa_dev->dev->ifindex;
+	RTA_PUT(skb, IFA_LOCAL, 2, &ifa->ifa_local);
+	if (ifa->ifa_label[0])
+		RTA_PUT(skb, IFA_LABEL, IFNAMSIZ, &ifa->ifa_label);
+	nlh->nlmsg_len = skb->tail - b;
+	return skb->len;
+nlmsg_failure:
+rtattr_failure:
+        skb_trim(skb, b - skb->data);
+        return -1;
+}
+
+static void rtmsg_ifa(int event, struct dn_ifaddr *ifa)
+{
+	struct sk_buff *skb;
+	int size = NLMSG_SPACE(sizeof(struct ifaddrmsg)+128);
+
+	skb = alloc_skb(size, GFP_KERNEL);
+	if (!skb) {
+		netlink_set_err(rtnl, 0, RTMGRP_DECnet_IFADDR, ENOBUFS);
+		return;
+	}
+	if (dn_dev_fill_ifaddr(skb, ifa, 0, 0, event) < 0) {
+		kfree_skb(skb);
+		netlink_set_err(rtnl, 0, RTMGRP_DECnet_IFADDR, EINVAL);
+		return;
+	}
+	NETLINK_CB(skb).dst_groups = RTMGRP_DECnet_IFADDR;
+	netlink_broadcast(rtnl, skb, 0, RTMGRP_DECnet_IFADDR, GFP_KERNEL);
+}
+
+static int dn_dev_fill_ifinfo(struct sk_buff *skb, struct device *dev,
+				int type, u32 pid, u32 seq)
+{
+	struct ifinfomsg *r;
+	struct nlmsghdr *nlh;
+	unsigned char *b = skb->tail;
+	struct dn_dev *dn_db = (struct dn_dev *)dev->dn_ptr;
+	unsigned char priority = dn_db->parms.priority;
+	unsigned short cost = dn_db->parms.cost;
+
+	nlh = NLMSG_PUT(skb, pid, seq, type, sizeof(*r));
+	if (pid) nlh->nlmsg_flags |= NLM_F_MULTI;
+	r = NLMSG_DATA(nlh);
+
+	r->ifi_family = AF_DECnet;
+	r->ifi_type   = dev->type;
+	r->ifi_index  = dev->ifindex;
+	r->ifi_flags  = dev->flags;
+	r->ifi_change = ~0U;
+
+	RTA_PUT(skb, IFLA_IFNAME, strlen(dev->name)+1, dev->name);
+	RTA_PUT(skb, IFLA_COST, sizeof(unsigned short), &cost);
+	RTA_PUT(skb, IFLA_PRIORITY, sizeof(unsigned char), &priority);
+
+	nlh->nlmsg_len = skb->tail - b;
+	return skb->len;
+
+nlmsg_failure:
+rtattr_failure:
+	skb_trim(skb, b - skb->data);
+	return -1;
+}
+
+static int dn_dev_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	int idx, dn_idx;
+	int s_idx, s_dn_idx;
+	struct device *dev;
+	struct dn_dev *dn_db;
+	struct dn_ifaddr *ifa;
+
+	s_idx = cb->args[0];
+	s_dn_idx = dn_idx = cb->args[1];
+	for(dev = dev_base, idx = 0; dev; dev = dev->next, idx++) {
+		if (idx < s_idx)
+			continue;
+		if (idx > s_idx)
+			s_dn_idx = 0;
+		if ((dn_db = dev->dn_ptr) == NULL)
+			continue;
+
+		for(ifa = dn_db->ifa_list, dn_idx = 0; ifa; ifa = ifa->ifa_next, dn_idx++) {
+			if (dn_idx < s_dn_idx)
+				continue;
+
+			if (dn_dev_fill_ifaddr(skb, ifa, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, RTM_NEWADDR) <= 0)
+				goto done;
+		}
+	}
+done:
+	cb->args[0] = idx;
+	cb->args[1] = dn_idx;
+
+	return skb->len;
+}
+
+static int dn_dev_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	int idx;
+	int s_idx = cb->args[0];
+	struct device *dev;
+
+	for(dev=dev_base, idx=0; dev; dev = dev->next) {
+		if (!dev->dn_ptr)
+			continue;
+		idx++;
+		if (idx < s_idx)
+			continue;
+		if (dn_dev_fill_ifinfo(skb, dev, RTM_NEWLINK, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq) <= 0)
+			break;
+	}
+	cb->args[0] = idx;
+
+	return skb->len;
+}
+
+static void dn_dev_ifinfo(int type, struct device *dev)
+{
+	struct sk_buff *skb;
+	int size = NLMSG_GOODSIZE;
+
+	skb = alloc_skb(size, GFP_KERNEL);
+	if (!skb)
+		return;
+
+	if (dn_dev_fill_ifinfo(skb, dev, type, 0, 0) < 0) {
+		kfree_skb(skb);
+		return;
+	}
+
+	NETLINK_CB(skb).dst_groups = RTMGRP_LINK;
+	netlink_broadcast(rtnl, skb, 0, NETLINK_CB(skb).dst_groups, GFP_KERNEL);
+}
+#endif /* CONFIG_RTNETLINK */
+
+static void dn_send_endnode_hello(struct device *dev)
+{
+        struct endnode_hello_message *msg;
+        struct sk_buff *skb = NULL;
+        unsigned short int *pktlen;
+	struct dn_dev *dn_db = (struct dn_dev *)dev->dn_ptr;
+
+        if ((skb = dn_alloc_skb(NULL, sizeof(*msg), GFP_ATOMIC)) == NULL)
+		return;
+
+        skb->dev = dev;
+
+        msg = (struct endnode_hello_message *)skb_put(skb,sizeof(*msg));
+
+        msg->msgflg  = 0x0D;
+        memcpy(msg->tiver, dn_eco_version, 3);
+        memcpy(msg->id, decnet_ether_address, 6);
+        msg->iinfo   = DN_RT_INFO_ENDN;
+        msg->blksize = dn_htons(dn_db->parms.blksize);
+        msg->area    = 0x00;
+        memset(msg->seed, 0, 8);
+        memcpy(msg->neighbor, dn_hiord, ETH_ALEN);
+
+	if (dn_db->router) {
+		struct dn_neigh *dn = (struct dn_neigh *)dn_db->router;
+		memcpy(msg->neighbor, dn->addr, ETH_ALEN);
+	}
+
+        msg->timer   = dn_htons((unsigned short)dn_db->parms.t3);
+        msg->mpd     = 0x00;
+        msg->datalen = 0x02;
+        memset(msg->data, 0xAA, 2);
+        
+        pktlen = (unsigned short *)skb_push(skb,2);
+        *pktlen = dn_htons(skb->len - 2);
+
+	skb->nh.raw = skb->data;
+
+        if (dev->hard_header(skb,dev, ETH_P_DNA_RT,dn_rt_all_rt_mcast,
+                         decnet_ether_address, skb->len) >= 0)
+        	dn_send_skb(skb);
+	else
+		kfree_skb(skb);
+}
+
+
+#ifdef CONFIG_DECNET_ROUTER
+
+#define DRDELAY (5 * HZ)
+
+static int dn_am_i_a_router(struct dn_neigh *dn, struct dn_dev *dn_db)
+{
+	/* First check time since device went up */
+	if ((jiffies - dn_db->uptime) < DRDELAY)
+		return 0;
+
+	/* If there is no router, then yes... */
+	if (!dn_db->router)
+		return 1;
+
+	/* otherwise only if we have a higher priority or.. */
+	if (dn->priority < dn_db->parms.priority)
+		return 1;
+
+	/* if we have equal priority and a higher node number */
+	if (dn->priority != dn_db->parms.priority)
+		return 0;
+
+	if (dn_ntohs(dn_eth2dn(dn->addr)) < dn_ntohs(decnet_address))
+		return 1;
+
+	return 0;
+}
+
+static void dn_send_router_hello(struct device *dev)
+{
+	int n;
+	struct dn_dev *dn_db = dev->dn_ptr;
+	struct dn_neigh *dn = (struct dn_neigh *)dn_db->router;
+	struct sk_buff *skb;
+	size_t size;
+	unsigned char *ptr;
+	unsigned char *i1, *i2;
+	unsigned short *pktlen;
+
+	if (dn_db->parms.blksize < (26 + 7))
+		return;
+
+	n = dn_db->parms.blksize - 26;
+	n /= 7;
+
+	if (n > 32)
+		n = 32;
+
+	size = 2 + 26 + 7 * n;
+
+	if ((skb = dn_alloc_skb(NULL, size, GFP_ATOMIC)) == NULL)
+		return;
+
+	skb->dev = dev;
+	ptr = skb_put(skb, size);
+
+	*ptr++ = DN_RT_PKT_CNTL | DN_RT_PKT_ERTH;
+	*ptr++ = 2; /* ECO */
+	*ptr++ = 0;
+	*ptr++ = 0;
+	memcpy(ptr, decnet_ether_address, ETH_ALEN);
+	ptr += ETH_ALEN;
+	*ptr++ = (unsigned char)decnet_node_type;
+	*((unsigned short *)ptr) = dn_htons(dn_db->parms.blksize);
+	ptr += 2;
+	*ptr++ = 0; /* Priority */
+	*ptr++ = 0; /* Area: Reserved */
+	*((unsigned short *)ptr) = dn_htons((unsigned short)dn_db->parms.t3);
+	ptr += 2;
+	*ptr++ = 0; /* MPD: Reserved */
+	i1 = ptr++;
+	memset(ptr, 0, 7); /* Name: Reserved */
+	i2 = ptr++;
+
+	n = dn_neigh_elist(dev, ptr, n);
+
+	*i2 = 7 * n;
+	*i1 = 8 + *i2;
+
+	skb_trim(skb, (26 + *i2));
+
+	pktlen = (unsigned short *)skb_push(skb, 2);
+	*pktlen = dn_htons(skb->len - 2);
+
+	skb->nh.raw = skb->data;
+
+	if (dn_am_i_a_router(dn, dn_db)) {
+		struct sk_buff *skb2 = skb_copy(skb, GFP_ATOMIC);
+		if (skb2) {
+			if (dev->hard_header(skb2, dev, ETH_P_DNA_RT, 
+					dn_rt_all_end_mcast, 
+					decnet_ether_address,
+					skb2->len) >= 0)
+				dn_send_skb(skb2);
+			else
+				kfree_skb(skb2);
+		}
+	}
+
+	if (dev->hard_header(skb, dev, ETH_P_DNA_RT, dn_rt_all_rt_mcast,
+			decnet_ether_address, skb->len) >= 0)
+		dn_send_skb(skb);
+	else
+		kfree_skb(skb);
+}
+
+static void dn_send_brd_hello(struct device *dev)
+{
+	if (decnet_node_type == DN_RT_INFO_ENDN)
+		dn_send_endnode_hello(dev);
+	else
+		dn_send_router_hello(dev);
+}
+#else
+static void dn_send_brd_hello(struct device *dev)
+{
+	dn_send_endnode_hello(dev);
+}
+#endif
+
+static void dn_send_ptp_hello(struct device *dev)
+{
+	int tdlen = 16;
+	int size = dev->hard_header_len + 2 + 4 + tdlen;
+	struct sk_buff *skb = dn_alloc_skb(NULL, size, GFP_ATOMIC);
+	struct dn_dev *dn_db = dev->dn_ptr;
+	unsigned char *ptr;
+	int i;
+
+	if (skb == NULL)
+		return ;
+
+	skb->dev = dev;
+	skb_push(skb, dev->hard_header_len);
+	ptr = skb_put(skb, 2 + 4 + tdlen);
+
+	*ptr++ = DN_RT_PKT_HELO;
+	*((dn_address *)ptr) = decnet_address;
+	ptr += 2;
+	*ptr++ = tdlen;
+
+	for(i = 0; i < tdlen; i++)
+		*ptr++ = 0252;
+
+	if (dn_db->router) {
+		struct dn_neigh *dn = (struct dn_neigh *)dn_db->router;
+		if (memcmp(dn->addr, decnet_ether_address, ETH_ALEN) == 0) {
+			struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
+			if (skb2 && dev->hard_header(skb2, dev, ETH_P_DNA_RT,
+					dn_rt_all_end_mcast, 
+					decnet_ether_address,
+					skb->len) >= 0) {
+				
+				dn_send_skb(skb2);
+			}
+		}
+	}
+
+	if (dev->hard_header(skb, dev, ETH_P_DNA_RT, dn_rt_all_rt_mcast,
+				decnet_ether_address, skb->len) < 0);
+		return;
+
+	dn_send_skb(skb);
+}
+
+static int dn_eth_up(struct device *dev)
+{
+	struct dn_dev *dn_db = dev->dn_ptr;
+
+	if (decnet_node_type == DN_RT_INFO_ENDN)
+		dev_mc_add(dev, dn_rt_all_end_mcast, ETH_ALEN, 0);
+
+	if (decnet_node_type == DN_RT_INFO_L1RT || decnet_node_type == DN_RT_INFO_L2RT)
+		dev_mc_add(dev, dn_rt_all_rt_mcast, ETH_ALEN, 0);
+
+	dev_mc_upload(dev);
+
+	dn_db->use_long = 1;
+
+	return 0;
+}
+
+static int dn_dev_eth_setsrc(struct sk_buff *skb)
+{
+	struct ethhdr *h = skb->mac.ethernet;
+	struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+
+	if (h == NULL)
+		return -1;
+
+	cb->neigh = dn_eth2dn(h->h_source);
+
+	return 0;
+}
+
+static int dn_dev_lo_setsrc(struct sk_buff *skb)
+{
+	struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+
+	cb->neigh = decnet_address;
+
+	return 0;
+}
+
+static int dn_dev_eth_neigh_setup(struct neighbour *n)
+{
+	struct dn_neigh *dn = (struct dn_neigh *)n;
+
+	memcpy(n->ha, dn->addr, ETH_ALEN);
+
+	return 0;
+}
+
+static int dn_dev_ptp_setsrc(struct sk_buff *skb)
+{
+	struct device *dev = skb->dev;
+	struct dn_dev *dn_db = dev->dn_ptr;
+	struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+
+	if (!dn_db->peer)
+		return -1;
+
+	cb->neigh = dn_eth2dn(((struct dn_neigh *)dn_db->peer)->addr);
+
+	return 0;
+}
+
+static void dn_dev_set_timer(struct device *dev);
+
+static void dn_dev_timer_func(unsigned long arg)
+{
+	struct device *dev = (struct device *)arg;
+	struct dn_dev *dn_db = dev->dn_ptr;
+
+#ifdef CONFIG_DECNET_ROUTER
+	if (decnet_node_type == DN_RT_INFO_L1RT || decnet_node_type == DN_RT_INFO_L2RT) {
+		if (dn_db->t1 <= dn_db->parms.t2) {
+			if (dn_db->parms.timer1)
+				dn_db->parms.timer1(dev);
+			dn_db->t1 = dn_db->parms.t1;
+		} else {
+			dn_db->t1 -= dn_db->parms.t2;
+		}
+	}
+#endif /* CONFIG_DECNET_ROUTER */
+
+	if (dn_db->t3 <= dn_db->parms.t2) {
+		if (dn_db->parms.timer3)
+			dn_db->parms.timer3(dev);
+		dn_db->t3 = dn_db->parms.t3;
+	} else {
+		dn_db->t3 -= dn_db->parms.t2;
+	}
+
+	dn_dev_set_timer(dev);
+}
+
+static void dn_dev_set_timer(struct device *dev)
+{
+	struct dn_dev *dn_db = dev->dn_ptr;
+
+#ifdef CONFIG_DECNET_ROUTER
+	if (dn_db->parms.t2 > dn_db->parms.t1)
+		dn_db->parms.t2 = dn_db->parms.t1;
+#endif
+
+	if (dn_db->parms.t2 > dn_db->parms.t3)
+		dn_db->parms.t2 = dn_db->parms.t3;
+
+	dn_db->timer.data = (unsigned long)dev;
+	dn_db->timer.function = dn_dev_timer_func;
+	dn_db->timer.expires = jiffies + (dn_db->parms.t2 * HZ);
+
+	add_timer(&dn_db->timer);
+}
+
+struct dn_dev *dn_dev_create(struct device *dev, int *err)
+{
+	int i;
+	struct dn_dev_parms *p = dn_dev_list;
+	struct dn_dev *dn_db;
+
+	for(i = 0; i < DN_DEV_LIST_SIZE; i++, p++) {
+		if (p->type == dev->type)
+			break;
+	}
+
+	*err = -ENODEV;
+	if (i == DN_DEV_LIST_SIZE)
+		return NULL;
+
+	*err = -ENOBUFS;
+	if ((dn_db = kmalloc(sizeof(struct dn_dev), GFP_ATOMIC)) == NULL)
+		return NULL;
+
+	memset(dn_db, 0, sizeof(struct dn_dev));
+	memcpy(&dn_db->parms, p, sizeof(struct dn_dev_parms));
+	dev->dn_ptr = dn_db;
+	dn_db->dev = dev;
+	init_timer(&dn_db->timer);
+
+	memcpy(dn_db->addr, decnet_ether_address, ETH_ALEN); /* To go... */
+
+	dn_db->uptime = jiffies;
+	if (dn_db->parms.up) {
+		if (dn_db->parms.up(dev) < 0) {
+			dev->dn_ptr = NULL;
+			kfree(dn_db);
+			return NULL;
+		}
+	}
+
+	dn_db->neigh_parms = neigh_parms_alloc(dev, &dn_neigh_table);
+	dn_db->neigh_parms->neigh_setup = dn_db->parms.neigh_setup;
+
+	dn_dev_sysctl_register(dev, &dn_db->parms);
+
+	dn_dev_set_timer(dev);
+
+#ifdef CONFIG_RTNETLINK
+	dn_dev_ifinfo(RTM_NEWLINK, dev);
+#endif
+
+	*err = 0;
+	return dn_db;
+}
+
+
+/*
+ * This processes a device up event. We only start up
+ * the loopback device & ethernet devices with correct
+ * MAC addreses automatically. Others must be started
+ * specifically.
+ */
+void dn_dev_up(struct device *dev)
+{
+	struct dn_ifaddr *ifa;
+
+	if ((dev->type != ARPHRD_ETHER) && (dev->type != ARPHRD_LOOPBACK))
+		return;
+
+	if (dev->type == ARPHRD_ETHER)
+		if (memcmp(dev->dev_addr, decnet_ether_address, ETH_ALEN) != 0)
+			return;
+
+	if ((ifa = dn_dev_alloc_ifa()) == NULL)
+		return;
+
+	ifa->ifa_local = decnet_address;
+	ifa->ifa_flags = 0;
+	ifa->ifa_scope = RT_SCOPE_UNIVERSE;
+	strcpy(ifa->ifa_label, dev->name);
+
+	dn_dev_set_ifa(dev, ifa);
+}
+
+static void dn_dev_delete(struct device *dev)
+{
+	struct dn_dev *dn_db = dev->dn_ptr;
+	unsigned long cpuflags;
+
+	if (dn_db == NULL)
+		return;
+
+	save_flags(cpuflags);
+	cli();
+	del_timer(&dn_db->timer);
+	restore_flags(cpuflags);
+
+#ifdef CONFIG_RTNETLINK
+	dn_dev_ifinfo(RTM_DELLINK, dev);
+#endif
+
+	dn_dev_sysctl_unregister(&dn_db->parms);
+
+	neigh_ifdown(&dn_neigh_table, dev);
+
+	if (dev == decnet_default_device)
+		decnet_default_device = NULL;
+
+	if (dn_db->parms.down)
+		dn_db->parms.down(dev);
+
+	dev->dn_ptr = NULL;
+
+	neigh_parms_release(&dn_neigh_table, dn_db->neigh_parms);
+
+	if (dn_db->router)
+		neigh_release(dn_db->router);
+	if (dn_db->peer)
+		neigh_release(dn_db->peer);
+
+	kfree(dn_db);
+}
+
+void dn_dev_down(struct device *dev)
+{
+	struct dn_dev *dn_db = dev->dn_ptr;
+	struct dn_ifaddr *ifa;
+
+	if (dn_db == NULL)
+		return;
+
+	while((ifa = dn_db->ifa_list) != NULL) {
+		dn_dev_del_ifa(dn_db, &dn_db->ifa_list, 0);
+		dn_dev_free_ifa(ifa);
+	}
+
+	dn_dev_delete(dev);
+}
+
+void dn_dev_init_pkt(struct sk_buff *skb)
+{
+	return;
+}
+
+void dn_dev_veri_pkt(struct sk_buff *skb)
+{
+	return;
+}
+
+void dn_dev_hello(struct sk_buff *skb)
+{
+	return;
+}
+
+void dn_dev_devices_off(void)
+{
+	struct device *dev;
+
+	for(dev = dev_base; dev; dev = dev->next)
+		dn_dev_down(dev);
+
+}
+
+void dn_dev_devices_on(void)
+{
+	struct device *dev;
+
+	for(dev = dev_base; dev; dev = dev->next) {
+		if (dev->flags & IFF_UP)
+			dn_dev_up(dev);
+	}
+}
+
+
+#ifdef CONFIG_DECNET_SIOCGIFCONF
+/*
+ * Now we support multiple addresses per interface.
+ * Since we don't want to break existing code, you have to enable
+ * it as a compile time option. Probably you should use the
+ * rtnetlink interface instead.
+ */
+int dnet_gifconf(struct device *dev, char *buf, int len)
+{
+	struct dn_dev *dn_db = (struct dn_dev *)dev->dn_ptr;
+	struct dn_ifaddr *ifa;
+	char buffer[DN_IFREQ_SIZE];
+	struct ifreq *ifr = (struct ifreq *)buffer;
+	int done = 0;
+
+	if ((dn_db == NULL) || ((ifa = dn_db->ifa_list) == NULL))
+		return 0;
+
+	for(; ifa; ifa = ifa->ifa_next) {
+		if (!buf) {
+			done += sizeof(DN_IFREQ_SIZE);
+			continue;
+		}
+		if (len < DN_IFREQ_SIZE)
+			return done;
+		memset(ifr, 0, DN_IFREQ_SIZE);
+
+		if (ifa->ifa_label)
+			strcpy(ifr->ifr_name, ifa->ifa_label);
+		else
+			strcpy(ifr->ifr_name, dev->name);
+
+		(*(struct sockaddr_dn *) &ifr->ifr_addr).sdn_family = AF_DECnet;
+		(*(struct sockaddr_dn *) &ifr->ifr_addr).sdn_add.a_len = 2;
+		(*(dn_address *)(*(struct sockaddr_dn *) &ifr->ifr_addr).sdn_add.a_addr) = ifa->ifa_local;
+
+		if (copy_to_user(buf, ifr, DN_IFREQ_SIZE))
+			return -EFAULT;
+
+		buf  += DN_IFREQ_SIZE;
+		len  -= DN_IFREQ_SIZE;
+		done += DN_IFREQ_SIZE;
+	}
+
+	return done;
+}
+#endif /* CONFIG_DECNET_SIOCGIFCONF */
+
+
+#ifdef CONFIG_PROC_FS
+
+static char *dn_type2asc(char type)
+{
+	switch(type) {
+		case DN_DEV_BCAST:
+			return "B";
+		case DN_DEV_UCAST:
+			return "U";
+		case DN_DEV_MPOINT:
+			return "M";
+	}
+
+	return "?";
+}
+
+static int decnet_dev_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+        struct dn_dev *dn_db;
+	struct device *dev;
+        int len = 0;
+        off_t pos = 0;
+        off_t begin = 0;
+	char peer_buf[DN_ASCBUF_LEN];
+	char router_buf[DN_ASCBUF_LEN];
+
+        cli();
+
+        len += sprintf(buffer, "Name     Flags T1   Timer1 T3   Timer3 BlkSize Pri State DevType    Router Peer\n");
+
+        for (dev = dev_base; dev; dev = dev->next) {
+		if ((dn_db = (struct dn_dev *)dev->dn_ptr) == NULL)
+			continue;
+
+                len += sprintf(buffer + len, "%-8s %1s     %04lu %04lu   %04lu %04lu   %04hu    %03d %02x    %-10s %-7s %-7s\n",
+                             	dev->name ? dev->name : "???",
+                             	dn_type2asc(dn_db->parms.mode),
+                             	dn_db->t1, dn_db->parms.t1,
+				dn_db->t3, dn_db->parms.t3,
+				dn_db->parms.blksize,
+				dn_db->parms.priority,
+				dn_db->parms.state, dn_db->parms.name,
+				dn_db->router ? dn_addr2asc(dn_eth2dn(dn_db->router->primary_key), router_buf) : "",
+				dn_db->peer ? dn_addr2asc(dn_eth2dn(dn_db->peer->primary_key), peer_buf) : "");
+
+
+                pos = begin + len;
+
+                if (pos < offset) {
+                        len   = 0;
+                        begin = pos;
+                }
+                if (pos > offset + length)
+                        break;
+        }
+
+        sti();
+
+        *start = buffer + (offset - begin);
+        len   -= (offset - begin);
+
+        if (len > length) len = length;
+
+        return(len);
+}
+
+static struct proc_dir_entry proc_net_decnet_dev = {
+        PROC_NET_DN_DEV, 10, "decnet_dev",
+        S_IFREG | S_IRUGO, 1, 0, 0,
+        0, &proc_net_inode_operations,
+        decnet_dev_get_info
+};
+
+#endif /* CONFIG_PROC_FS */
+
+#ifdef CONFIG_RTNETLINK
+static struct rtnetlink_link dnet_rtnetlink_table[RTM_MAX-RTM_BASE+1] = 
+{
+	{ NULL,			NULL,			},
+	{ NULL,			NULL,			},
+	{ NULL,			dn_dev_dump_ifinfo,	},
+	{ NULL,			NULL,			},
+
+	{ dn_dev_rtm_newaddr,	NULL,			},
+	{ dn_dev_rtm_deladdr,	NULL,			},
+	{ NULL,			dn_dev_dump_ifaddr,	},
+	{ NULL,			NULL,			},
+#ifdef CONFIG_DECNET_ROUTER
+	{ dn_fib_rtm_newroute,	NULL,			},
+	{ dn_fib_rtm_delroute,	NULL,			},
+	{ dn_fib_rtm_getroute,	dn_fib_dump,		},
+	{ NULL,			NULL,			},
+#else
+	{ NULL,			NULL,			},
+	{ NULL,			NULL,			},
+	{ NULL,			NULL,			},
+	{ NULL,			NULL,			},
+#endif
+	{ NULL,			NULL,			},
+	{ NULL,			NULL,			},
+	{ NULL,			NULL,			},
+	{ NULL,			NULL,			},
+
+	{ NULL,			NULL,			},
+	{ NULL,			NULL,			},
+	{ NULL,			NULL,			},
+	{ NULL,			NULL,			}
+};
+#endif /* CONFIG_RTNETLINK */
+
+void __init dn_dev_init(void)
+{
+
+	dn_dev_devices_on();
+#ifdef CONFIG_DECNET_SIOCGIFCONF
+	register_gifconf(PF_DECnet, dnet_gifconf);
+#endif /* CONFIG_DECNET_SIOCGIFCONF */
+
+#ifdef CONFIG_RTNETLINK
+	rtnetlink_links[PF_DECnet] = dnet_rtnetlink_table;
+#endif /* CONFIG_RTNETLINK */
+
+#ifdef CONFIG_PROC_FS
+	proc_net_register(&proc_net_decnet_dev);
+#endif /* CONFIG_PROC_FS */
+
+#ifdef CONFIG_SYSCTL
+	{
+		int i;
+		for(i = 0; i < DN_DEV_LIST_SIZE; i++)
+			dn_dev_sysctl_register(NULL, &dn_dev_list[i]);
+	}
+#endif /* CONFIG_SYSCTL */
+}
+
+#if defined(CONFIG_DECNET_MODULE)
+void dn_dev_cleanup(void)
+{
+#ifdef CONFIG_RTNETLINK
+	rtnetlink_links[PF_DECnet] = NULL;
+#endif /* CONFIG_RTNETLINK */
+
+#ifdef CONFIG_DECNET_SIOCGIFCONF
+	unregister_gifconf(PF_DECnet);
+#endif /* CONFIG_DECNET_SIOCGIFCONF */
+
+#ifdef CONFIG_SYSCTL
+	{
+		int i;
+		for(i = 0; i < DN_DEV_LIST_SIZE; i++)
+			dn_dev_sysctl_unregister(&dn_dev_list[i]);
+	}
+#endif /* CONFIG_SYSCTL */
+
+#ifdef CONFIG_PROC_FS
+	proc_net_unregister(PROC_NET_DN_DEV);
+#endif /* CONFIG_PROC_FS */
+
+	dn_dev_devices_off();
+}
+#endif /* CONFIG_DECNET_MODULE */
diff -u --recursive --new-file v2.3.3/linux/net/decnet/dn_fib.c linux/net/decnet/dn_fib.c
--- v2.3.3/linux/net/decnet/dn_fib.c	Wed Dec 31 16:00:00 1969
+++ linux/net/decnet/dn_fib.c	Wed May 26 09:36:36 1999
@@ -0,0 +1,805 @@
+/*
+ * DECnet       An implementation of the DECnet protocol suite for the LINUX
+ *              operating system.  DECnet is implemented using the  BSD Socket
+ *              interface as the means of communication with the user level.
+ *
+ *              DECnet Routing Forwarding Information Base
+ *
+ * Author:      Steve Whitehouse <SteveW@ACM.org>
+ *
+ *
+ * Changes:
+ *
+ */
+#include <linux/config.h>
+#include <linux/string.h>
+#include <linux/net.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/proc_fs.h>
+#include <linux/netdevice.h>
+#include <linux/timer.h>
+#include <linux/rtnetlink.h>
+#include <asm/spinlock.h>
+#include <asm/atomic.h>
+#include <asm/uaccess.h>
+#include <net/neighbour.h>
+#include <net/dst.h>
+#include <net/dn.h>
+#include <net/dn_fib.h>
+#include <net/dn_neigh.h>
+#include <net/dn_dev.h>
+
+/*
+ * N.B. Some of the functions here should really be inlines, but
+ * I'll sort out that when its all working properly, for now the
+ * stack frames will be useful for debugging.
+ */
+#define DN_NUM_TABLES 255
+#define DN_MIN_TABLE  1
+#define DN_L1_TABLE 1
+#define DN_L2_TABLE 2
+
+#ifdef CONFIG_RTNETLINK
+static int dn_fib_table_dump(struct dn_fib_table *t, struct sk_buff *skb, struct netlink_callback *cb);
+static void dn_rtmsg_fib(int event, int table, struct dn_fib_action *fa, struct nlmsghdr *nlh, struct netlink_skb_parms *req);
+#endif /* CONFIG_RTNETLINK */
+
+static void dn_fib_del_tree(struct dn_fib_table *t);
+
+static struct dn_fib_table *dn_fib_tables[DN_NUM_TABLES + 1];
+static int dn_fib_allocs = 0;
+static int dn_fib_actions = 0;
+
+static struct dn_fib_node *dn_fib_alloc(void)
+{
+	struct dn_fib_node *fn;
+
+	fn = kmalloc(sizeof(struct dn_fib_node), GFP_KERNEL);
+
+	if (fn) {
+		memset(fn, 0, sizeof(struct dn_fib_node));
+		dn_fib_allocs++;
+	}
+
+	return fn;
+}
+
+
+static __inline__ void dn_fib_free(struct dn_fib_node *fn)
+{
+	kfree_s(fn, sizeof(struct dn_fib_node));
+	dn_fib_allocs--;
+}
+
+static struct dn_fib_action *dn_fib_new_action(void)
+{
+	struct dn_fib_action *fa;
+
+	fa = kmalloc(sizeof(struct dn_fib_action), GFP_KERNEL);
+
+	if (fa) {
+		memset(fa, 0, sizeof(struct dn_fib_action));
+		dn_fib_actions++;
+	}
+
+	return fa;
+}
+
+static __inline__ void dn_fib_del_action(struct dn_fib_action *fa)
+{
+	if ((fa->fa_type == RTN_UNICAST) && fa->fa_neigh)
+		neigh_release(fa->fa_neigh);
+
+	kfree_s(fa, sizeof(struct dn_fib_action));
+	dn_fib_actions--;
+}
+
+static struct dn_fib_node *dn_fib_follow(struct dn_fib_node *fn, dn_address key)
+{
+	while(fn->fn_action == NULL)
+		fn = DN_FIB_NEXT(fn, key);
+
+	return fn;
+}
+
+
+static struct dn_fib_node *dn_fib_follow1(struct dn_fib_node *fn, dn_address key)
+{
+	while((fn->fn_action == NULL) && (((key ^ fn->fn_key) >> fn->fn_shift) == 0))
+		fn = DN_FIB_NEXT(fn, key);
+
+	return fn;
+}
+
+
+static int dn_fib_table_insert1(struct dn_fib_table *t, struct dn_fib_node *leaf)
+{
+	struct dn_fib_node *fn, *fn1, *fn2;
+	int shift = -1;
+	dn_address match;
+	dn_address cmpmask = 1;
+
+	if (!t->root) {
+		t->root = leaf;
+		t->count++;
+		return 0;
+	}
+
+	fn1 = dn_fib_follow1(t->root, leaf->fn_key);
+	fn2 = fn1->fn_up;
+
+	if (fn1->fn_key == leaf->fn_key)
+		return -EEXIST;
+
+	if ((fn = dn_fib_alloc()) == NULL)
+		return -ENOBUFS;
+
+	fn->fn_key = leaf->fn_key;
+	match = fn1->fn_key ^ fn->fn_key;
+
+	while(match) {
+		match >>= 1;
+		shift++;
+	}
+	cmpmask <<= shift;
+
+	fn->fn_cmpmask = cmpmask;
+	fn->fn_shift   = shift;
+
+	if (fn2) {
+		DN_FIB_NEXT(fn2, fn->fn_key) = fn;
+	} else {
+		t->root = fn;
+	}
+
+	t->count++;
+	fn->fn_up = fn2;
+	DN_FIB_NEXT(fn, fn1->fn_key)  = fn1;
+	DN_FIB_NEXT(fn, leaf->fn_key) = leaf;
+
+	return 0;
+}
+
+static __inline__ int dn_maskcmp(dn_address m1, dn_address m2)
+{
+	int cmp = 0;
+
+	while(m1 || m2) {
+		if (m1 & 0x8000)
+			cmp++;
+		if (m2 & 0x8000)
+			cmp--;
+		m1 <<= 1;
+		m2 <<= 1;
+	}
+
+	return cmp;
+}
+
+
+static int dn_fib_table_insert(struct dn_fib_table *t, struct dn_fib_action *fa)
+{
+	struct dn_fib_node *fn;
+	struct dn_fib_action **fap;
+	int err;
+	int cmp;
+
+	if (t->root && ((fn = dn_fib_follow(t->root, fa->fa_key)) != NULL) && 
+			(fn->fn_key == fa->fa_key))
+		goto add_action;
+
+	if ((fn = dn_fib_alloc()) == NULL)
+		return -ENOBUFS;
+
+	fn->fn_key = fa->fa_key;
+	fn->fn_action = fa;
+
+	if ((err = dn_fib_table_insert1(t, fn)) < 0)
+		dn_fib_free(fn);
+
+#ifdef CONFIG_RTNETLINK
+	if (!err)
+		dn_rtmsg_fib(RTM_NEWROUTE, t->n, fa, NULL, NULL);
+#endif /* CONFIG_RTNETLINK */
+
+	return err;
+
+add_action:
+	fap = &fn->fn_action;
+
+	for(; *fap; fap = &((*fap)->fa_next)) {
+		if ((cmp = dn_maskcmp((*fap)->fa_mask, fa->fa_mask)) > 0)
+			break;
+		if (cmp < 0)
+			continue;
+		if ((*fap)->fa_cost > fa->fa_cost)
+			break;
+	}
+
+	fa->fa_next = *fap;
+	*fap = fa;
+
+#ifdef CONFIG_RTNETLINK
+	dn_rtmsg_fib(RTM_NEWROUTE, t->n, fa, NULL, NULL);
+#endif /* CONFIG_RTNETLINK */
+
+	return 0;
+}
+
+static int dn_fib_table_delete1(struct dn_fib_table *t, struct dn_fib_node *fn)
+{
+	struct dn_fib_node *fn1 = fn->fn_up;
+	struct dn_fib_node *fn2;
+	struct dn_fib_node *fn3;
+
+	if (fn == t->root) {
+		t->root = NULL;
+		t->count--;
+		return 0;
+	}
+
+	if (fn1 == NULL)
+		return -EINVAL;
+
+	fn2 = fn1->fn_up;
+	fn3 = DN_FIB_NEXT(fn1, ~fn->fn_key);
+
+	if (fn2)
+		DN_FIB_NEXT(fn2, fn1->fn_key) = fn3;
+	else
+		t->root = fn3;
+
+	fn3->fn_up = fn2;
+
+	dn_fib_free(fn1);
+	t->count--;
+	return 0;
+}
+
+static int dn_fib_table_delete(struct dn_fib_table *t, struct dn_fib_action *fa)
+{
+	struct dn_fib_res res;
+	struct dn_fib_node *fn;
+	struct dn_fib_action **fap, *old;
+	int err;
+
+	res.res_type = 0;
+	res.res_addr = fa->fa_key;
+	res.res_mask = fa->fa_mask;
+	res.res_ifindex = fa->fa_ifindex;
+	res.res_proto = fa->fa_proto;
+	res.res_cost = fa->fa_cost;
+
+	if ((err = t->lookup(t, &res)) < 0)
+		return err;
+
+	fn = res.res_fn;
+	fap = &fn->fn_action; 
+	while((*fap) != res.res_fa)
+		 fap = &((*fap)->fa_next);
+	old = *fap;
+	*fap = (*fap)->fa_next;
+
+	if (fn->fn_action == NULL)
+		dn_fib_table_delete1(t, fn);
+
+	if (t->root == NULL)
+		dn_fib_del_tree(t);
+
+#ifdef CONFIG_RTNETLINK
+	dn_rtmsg_fib(RTM_DELROUTE, t->n, old, NULL, NULL);
+#endif /* CONFIG_RTNETLINK */
+
+	dn_fib_del_action(old);
+
+	return 0;
+}
+
+static int dn_fib_search(struct dn_fib_node *fn, struct dn_fib_res *res)
+{
+	struct dn_fib_action *fa = fn->fn_action;
+
+	for(; fa; fa = fa->fa_next) {
+		if ((fa->fa_key ^ res->res_addr) & fa->fa_mask)
+			continue;
+		if (res->res_ifindex && (res->res_ifindex != fa->fa_ifindex))
+			continue;
+		if (res->res_mask && (res->res_mask != fa->fa_mask))
+			continue;
+		if (res->res_proto && (res->res_proto != fa->fa_proto))
+			continue;
+		if (res->res_cost && (res->res_cost != fa->fa_cost))
+			continue;
+
+		res->res_fn = fn;
+		res->res_fa = fa;
+		return 1;
+	}
+
+	return 0;
+}
+
+static int dn_fib_recurse(struct dn_fib_node *fn, struct dn_fib_res *res)
+{
+	struct dn_fib_node *fn1;
+	int err = -ENOENT;
+
+	fn1 = dn_fib_follow(fn, res->res_addr);
+
+	if (dn_fib_search(fn1, res))
+		return 0;
+
+	while((fn1 = fn1->fn_up) != fn)
+		if ((err = dn_fib_recurse(DN_FIB_NEXT(fn1, ~res->res_addr), res)) == 0)
+			break;
+
+	return err;
+}
+
+static int dn_fib_table_lookup(struct dn_fib_table *t, struct dn_fib_res *res)
+{
+	struct dn_fib_node *fn = t->root;
+	int err = -ENOENT;
+
+	if (t->root == NULL)
+		return err;
+
+	fn = dn_fib_follow(t->root, res->res_addr);
+
+	if (dn_fib_search(fn, res))
+		return 0;
+
+	while((fn = fn->fn_up) != NULL)
+		if ((err = dn_fib_recurse(DN_FIB_NEXT(fn, ~res->res_addr), res)) == 0)
+			break;
+
+	return err;
+}
+
+static int dn_fib_table_walk_recurse(struct dn_fib_walker_t *fwt, struct dn_fib_node *fn)
+{
+	struct dn_fib_table *t = fwt->table;
+
+	if (fn->fn_action) {
+		fwt->fxn(fwt, fn);
+	} else {
+		dn_fib_table_walk_recurse(fwt, t->root->fn_children[0]);
+		dn_fib_table_walk_recurse(fwt, t->root->fn_children[1]);
+	}
+
+	return 0;
+}
+
+static int dn_fib_table_walk(struct dn_fib_walker_t *fwt)
+{
+	struct dn_fib_table *t = fwt->table;
+
+	if (t->root != NULL) {
+		if (t->root->fn_action) {
+			fwt->fxn(fwt, t->root);
+		} else {
+			dn_fib_table_walk_recurse(fwt, t->root->fn_children[0]);
+			dn_fib_table_walk_recurse(fwt, t->root->fn_children[1]);
+		}
+	}
+
+	return 0;
+}
+
+static struct dn_fib_table *dn_fib_get_tree(int n, int create)
+{
+	struct dn_fib_table *t;
+
+	if (n < DN_MIN_TABLE)
+		return NULL;
+
+	if (n > DN_NUM_TABLES)
+		return NULL;
+
+	if (dn_fib_tables[n])
+		return dn_fib_tables[n];
+
+	if (!create)
+		return NULL;
+
+	if ((t = kmalloc(sizeof(struct dn_fib_table), GFP_KERNEL)) == NULL)
+		return NULL;
+
+	dn_fib_tables[n] = t;
+	memset(t, 0, sizeof(struct dn_fib_table));
+
+	t->n = n;
+	t->insert = dn_fib_table_insert;
+	t->delete = dn_fib_table_delete;
+	t->lookup = dn_fib_table_lookup;
+	t->walk   = dn_fib_table_walk;
+#ifdef CONFIG_RTNETLINK
+	t->dump = dn_fib_table_dump;
+#endif
+
+	return t;
+}
+
+static void dn_fib_del_tree(struct dn_fib_table *t)
+{
+	dn_fib_tables[t->n] = NULL;
+
+	if (t) {
+		kfree_s(t, sizeof(struct dn_fib_table));
+	}
+}
+
+
+int dn_fib_resolve(struct dn_fib_res *res)
+{
+	int table = DN_L1_TABLE;
+	int count = 0;
+	struct dn_fib_action *fa;
+	int err;
+
+	if ((res->res_addr ^ dn_ntohs(decnet_address)) & 0xfc00)
+		table = DN_L2_TABLE;
+
+	for(;;) {
+		struct dn_fib_table *t = dn_fib_get_tree(table, 0);
+
+		if (t == NULL)
+			return -ENOBUFS;
+
+		if ((err = t->lookup(t, res)) < 0)
+			return err;
+
+		if ((fa = res->res_fa) == NULL)
+			return -ENOENT;
+
+		if (fa->fa_type != RTN_THROW)
+			break;
+
+		table = fa->fa_table;
+
+		if (count++ > DN_NUM_TABLES)
+			return -ENOENT;
+	}
+
+	return (fa->fa_type == RTN_PROHIBIT) ? -fa->fa_error : 0;
+}
+
+/*
+ * Punt to user via netlink for example, but for now
+ * we just drop it.
+ */
+int dn_fib_rt_message(struct sk_buff *skb)
+{
+	kfree_skb(skb);
+
+	return 0;
+}
+
+
+#ifdef CONFIG_RTNETLINK
+static int dn_fib_convert_rtm(struct dn_fib_action *fa,
+					struct rtmsg *r, struct rtattr **rta, 
+					struct nlmsghdr *n, 
+					struct netlink_skb_parms *req)
+{
+	dn_address dst, gw, mask = 0xffff;
+	int ifindex;
+	struct neighbour *neigh;
+	struct device *dev;
+	unsigned char addr[ETH_ALEN];
+
+	if (r->rtm_family != AF_DECnet)
+		return -EINVAL;
+
+	if (rta[RTA_DST-1])
+		memcpy(&dst, RTA_DATA(rta[RTA_DST-1]), 2);
+
+	if (rta[RTA_OIF-1])
+		memcpy(&ifindex, RTA_DATA(rta[RTA_OIF-1]), sizeof(int));
+
+	if (rta[RTA_GATEWAY-1])
+		memcpy(&gw, RTA_DATA(rta[RTA_GATEWAY-1]), 2);
+
+	fa->fa_key      = dn_ntohs(dst);
+	fa->fa_mask     = mask;
+	fa->fa_ifindex  = ifindex;
+	fa->fa_proto    = r->rtm_protocol;
+	fa->fa_type     = r->rtm_type;
+
+	switch(fa->fa_type) {
+		case RTN_UNICAST:
+			if ((dev = dev_get_by_index(ifindex)) == NULL)
+				return -ENODEV;
+			dn_dn2eth(addr, gw);
+			if ((neigh = __neigh_lookup(&dn_neigh_table, &addr, dev, 1)) == NULL)
+				return -EHOSTUNREACH;
+			fa->fa_neigh = neigh;
+			break;
+		case RTN_THROW:
+			fa->fa_table = 0;
+			break;
+		case RTN_PROHIBIT:
+			fa->fa_error = 0;
+			break;
+		case RTN_UNREACHABLE:
+			fa->fa_error = EHOSTUNREACH;
+			break;
+	}
+
+	return 0;
+}
+
+static int dn_fib_check_attr(struct rtmsg *r, struct rtattr **rta)
+{
+	switch(r->rtm_type) {
+		case RTN_UNICAST:
+		case RTN_BLACKHOLE:
+		case RTN_PROHIBIT:
+		case RTN_UNREACHABLE:
+		case RTN_THROW:
+			break;
+		default:
+			return -1;
+	}
+
+	return 0;
+}
+
+int dn_fib_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+{
+	struct dn_fib_table *t;
+	struct rtattr **rta = arg;
+	struct rtmsg *r = NLMSG_DATA(nlh);
+	struct dn_fib_action *fa;
+	int err;
+
+	if (dn_fib_check_attr(r, rta))
+		return -EINVAL;
+
+	if ((fa = dn_fib_new_action()) == NULL)
+		return -ENOBUFS;
+
+	t = dn_fib_get_tree(r->rtm_table, 0);
+	if (t) {
+		if ((err = dn_fib_convert_rtm(fa, r, rta, nlh, &NETLINK_CB(skb))) < 0)  {
+			dn_fib_del_action(fa);
+			return err;
+		}
+		err = t->delete(t, fa);
+		dn_fib_del_action(fa);
+		return err;
+	}
+	return -ESRCH;
+}
+
+int dn_fib_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+{
+	struct dn_fib_table *t;
+	struct rtattr **rta = arg;
+	struct rtmsg *r = NLMSG_DATA(nlh);
+	struct dn_fib_action *fa;
+	int err;
+
+	if (dn_fib_check_attr(r, rta))
+		return -EINVAL;
+
+	if ((fa = dn_fib_new_action()) == NULL)
+		return -ENOBUFS;
+
+	t = dn_fib_get_tree(r->rtm_table, 1);
+	if (t) {
+		if ((err = dn_fib_convert_rtm(fa, r, rta, nlh, &NETLINK_CB(skb))) < 0) {
+			dn_fib_del_action(fa);
+			return err;
+		}
+		return t->insert(t, fa);
+	}
+	return -ENOBUFS;
+}
+
+int dn_fib_dump_info(struct sk_buff *skb, u32 pid, u32 seq, int event,
+			int table, struct dn_fib_action *fa)
+{
+	struct rtmsg *rtm;
+	struct nlmsghdr *nlh;
+	unsigned char *b = skb->tail;
+
+	nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(*rtm));
+	rtm = NLMSG_DATA(nlh);
+	rtm->rtm_family = AF_DECnet;
+	rtm->rtm_dst_len = 16;
+	rtm->rtm_src_len = 16;
+	rtm->rtm_tos = 0;
+	rtm->rtm_table = table;
+	rtm->rtm_type = fa->fa_type;
+	rtm->rtm_flags = 0;
+	rtm->rtm_protocol = fa->fa_proto;
+	RTA_PUT(skb, RTA_DST, 2, &fa->fa_key);
+	if (fa->fa_ifindex)
+		RTA_PUT(skb, RTA_OIF, sizeof(int), &fa->fa_ifindex);
+
+	nlh->nlmsg_len = skb->tail - b;
+	return skb->len;
+
+nlmsg_failure:
+rtattr_failure:
+        skb_trim(skb, b - skb->data);
+        return -1;
+}
+
+static void dn_rtmsg_fib(int event, int table, struct dn_fib_action *fa,
+			struct nlmsghdr *nlh, struct netlink_skb_parms *req)
+{
+	struct sk_buff *skb;
+	u32 pid = req ? req->pid : 0;
+	int size = NLMSG_SPACE(sizeof(struct rtmsg) + 256);
+
+	skb = alloc_skb(size, GFP_KERNEL);
+	if (!skb)
+		return;
+
+	if (dn_fib_dump_info(skb, pid, nlh->nlmsg_seq, event, table, fa) < 0) {
+		kfree_skb(skb);
+		return;
+	}
+	NETLINK_CB(skb).dst_groups = RTMGRP_DECnet_ROUTE;
+	if (nlh->nlmsg_flags & NLM_F_ECHO)
+		atomic_inc(&skb->users);
+	netlink_broadcast(rtnl, skb, pid, RTMGRP_DECnet_ROUTE, GFP_KERNEL);
+	if (nlh->nlmsg_flags & NLM_F_ECHO)
+		netlink_unicast(rtnl, skb, pid, MSG_DONTWAIT);
+}
+
+static int dn_fib_table_dump(struct dn_fib_table *t, struct sk_buff *skb, struct netlink_callback *cb)
+{
+
+	return skb->len;
+}
+
+int dn_fib_dump(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	int s_t;
+	struct dn_fib_table *t;
+
+	for(s_t = cb->args[0]; s_t < DN_NUM_TABLES; s_t++) {
+		if (s_t > cb->args[0])
+			memset(&cb->args[1], 0, sizeof(cb->args)-sizeof(int));
+		t = dn_fib_get_tree(s_t, 0);
+		if (t == NULL)
+			continue;
+		if (t->dump(t, skb, cb) < 0)
+			break;
+	}
+
+	cb->args[0] = s_t;
+
+	return skb->len;
+}
+#endif /* CONFIG_RTNETLINK */
+
+int dn_fib_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
+	switch(cmd) {
+		case SIOCADDRT:
+		case SIOCDELRT:
+			return 0;
+	}
+
+	return -EINVAL;
+}
+
+struct dn_fib_procfs {
+	int len;
+	off_t pos;
+	off_t begin;
+	off_t offset;
+	int length;
+	char *buffer;
+};
+
+static int dn_proc_action_list(struct dn_fib_walker_t *fwt, struct dn_fib_node *fn)
+{
+	struct dn_fib_procfs *pinfo = (struct dn_fib_procfs *)fwt->arg;
+	struct dn_fib_action *fa;
+	char ab[DN_ASCBUF_LEN];
+
+	if (pinfo->pos > pinfo->offset + pinfo->length)
+		return 0;
+
+	for(fa = fn->fn_action; fa; fa = fa->fa_next) {
+
+		pinfo->len += sprintf(pinfo->buffer + pinfo->len,
+					"%s/%04hx %02x %02x\n", 
+					dn_addr2asc(fa->fa_key, ab),
+					fa->fa_mask,
+					fa->fa_type,
+					fa->fa_proto);
+
+		pinfo->pos = pinfo->begin + pinfo->len;
+		if (pinfo->pos < pinfo->offset) {
+			pinfo->len = 0;
+			pinfo->begin = pinfo->pos;
+		}
+		if (pinfo->pos > pinfo->offset + pinfo->length)
+			break;
+	}
+
+	return 0;
+}
+
+static int decnet_rt_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+	struct dn_fib_procfs pinfo;
+	int i;
+	struct dn_fib_table *t;
+	struct dn_fib_walker_t fwt;
+
+	pinfo.pos = 0;
+	pinfo.len = 0;
+	pinfo.begin = 0;
+	pinfo.offset = offset;
+	pinfo.length = length;
+	pinfo.buffer = buffer;
+
+	fwt.arg = &pinfo;
+	fwt.fxn = dn_proc_action_list;
+
+	start_bh_atomic();
+	for(i = 0; i < DN_NUM_TABLES; i++) {
+		if ((t = dn_fib_get_tree(i, 0)) == NULL)
+			continue;
+
+		fwt.table = t;
+		t->walk(&fwt);
+
+		if (pinfo.pos > pinfo.offset + pinfo.length)
+			break;
+	}
+	end_bh_atomic();
+
+	*start = pinfo.buffer + (pinfo.offset - pinfo.begin);
+	pinfo.len -= (pinfo.offset - pinfo.begin);
+
+	if (pinfo.len > pinfo.length) 
+		pinfo.len = pinfo.length;
+
+	return pinfo.len;
+}
+
+static struct proc_dir_entry proc_net_decnet_route = {
+	PROC_NET_DN_ROUTE, 12, "decnet_route",
+	 S_IFREG | S_IRUGO, 1, 0, 0,
+        0, &proc_net_inode_operations,
+	decnet_rt_get_info
+};
+
+#ifdef CONFIG_DECNET_MODULE
+void dn_fib_cleanup(void)
+{
+#ifdef CONFIG_PROC_FS
+	proc_net_unregister(PROC_NET_DN_ROUTE);
+#endif /* CONFIG_PROC_FS */
+}
+#endif /* CONFIG_DECNET_MODULE */
+
+
+void __init dn_fib_init(void)
+{
+	memset(dn_fib_tables, 0, DN_NUM_TABLES * sizeof(struct dn_fib_table *));
+
+#ifdef CONFIG_PROC_FS
+	proc_net_register(&proc_net_decnet_route);
+#endif
+
+}
+
+
diff -u --recursive --new-file v2.3.3/linux/net/decnet/dn_neigh.c linux/net/decnet/dn_neigh.c
--- v2.3.3/linux/net/decnet/dn_neigh.c	Wed Dec 31 16:00:00 1969
+++ linux/net/decnet/dn_neigh.c	Wed May 26 09:36:36 1999
@@ -0,0 +1,627 @@
+/*
+ * DECnet       An implementation of the DECnet protocol suite for the LINUX
+ *              operating system.  DECnet is implemented using the  BSD Socket
+ *              interface as the means of communication with the user level.
+ *
+ *              DECnet Neighbour Functions (Adjacency Database and 
+ *                                                        On-Ethernet Cache)
+ *
+ * Author:      Steve Whitehouse <SteveW@ACM.org>
+ *
+ *
+ * Changes:
+ *     Steve Whitehouse     : Fixed router listing routine
+ *     Steve Whitehouse     : Added error_report functions
+ *     Steve Whitehouse     : Added default router detection
+ *     Steve Whitehouse     : Hop counts in outgoing messages
+ *     Steve Whitehouse     : Fixed src/dst in outgoing messages so
+ *                            forwarding now stands a good chance of
+ *                            working.
+ *     Steve Whitehouse     : Fixed neighbour states (for now anyway).
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/net.h>
+#include <linux/socket.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/string.h>
+#include <asm/atomic.h>
+#include <asm/spinlock.h>
+#include <net/neighbour.h>
+#include <net/dst.h>
+#include <net/dn.h>
+#include <net/dn_dev.h>
+#include <net/dn_neigh.h>
+#include <net/dn_route.h>
+
+static int dn_neigh_construct(struct neighbour *);
+static void dn_long_error_report(struct neighbour *, struct sk_buff *);
+static void dn_short_error_report(struct neighbour *, struct sk_buff *);
+static int dn_long_output(struct sk_buff *);
+static int dn_short_output(struct sk_buff *);
+static int dn_phase3_output(struct sk_buff *);
+
+
+/*
+ * For talking to broadcast devices: Ethernet & PPP
+ */
+static struct neigh_ops dn_long_ops = {
+	AF_DECnet,
+	NULL,
+	NULL,
+	dn_long_error_report,
+	dn_long_output,
+	dn_long_output,
+	dev_queue_xmit,
+	dev_queue_xmit
+};
+
+/*
+ * For talking to pointopoint and multidrop devices: DDCMP and X.25
+ */
+static struct neigh_ops dn_short_ops = {
+	AF_DECnet,
+	NULL,
+	NULL,
+	dn_short_error_report,
+	dn_short_output,
+	dn_short_output,
+	dev_queue_xmit,
+	dev_queue_xmit
+};
+
+/*
+ * For talking to DECnet phase III nodes
+ */
+static struct neigh_ops dn_phase3_ops = {
+	AF_DECnet,
+	NULL,
+	NULL,
+	dn_short_error_report, /* Can use short version here */
+	dn_phase3_output,
+	dn_phase3_output,
+	dev_queue_xmit,
+	dev_queue_xmit
+};
+
+struct neigh_table dn_neigh_table = {
+	NULL,
+	PF_DECnet,
+	sizeof(struct dn_neigh),
+	ETH_ALEN,
+	dn_neigh_construct,
+	NULL, /* pconstructor */
+	NULL, /* pdestructor */
+	NULL, /* proxyredo */
+	{ 
+		NULL,
+		NULL,
+		&dn_neigh_table,
+		0,
+		NULL,
+		NULL,
+		30 * HZ,	/* base_reachable_time */
+		1 * HZ,		/* retrans_time */
+		60 * HZ,	/* gc_staletime */
+		30 * HZ,	/* reachable_time */
+		5 * HZ,		/* delay_probe_time */
+		3,	/* queue_len */
+		0,	/* ucast_probes */
+		0,	/* app_probes */
+		0,	/* mcast_probes */
+		0,	/* anycast_delay */
+		0,	/* proxy_delay */
+		0,	/* proxy_qlen */
+		1 * HZ,	/* locktime */
+	},
+	30 * HZ,	/* gc_interval */
+	128,		/* gc_thresh1 */
+	512,		/* gc_thresh2 */
+	1024,		/* gc_thresh3 */
+	
+};
+
+static int dn_neigh_construct(struct neighbour *neigh)
+{
+	struct device *dev = neigh->dev;
+	struct dn_neigh *dn = (struct dn_neigh *)neigh;
+	struct dn_dev *dn_db = (struct dn_dev *)dev->dn_ptr;
+
+	if (dn_db == NULL)
+		return -EINVAL;
+
+	if (dn_db->neigh_parms)
+		neigh->parms = dn_db->neigh_parms;
+
+	if (dn_db->use_long)
+		neigh->ops = &dn_long_ops;
+	else
+		neigh->ops = &dn_short_ops;
+
+	if (dn->flags & DN_NDFLAG_P3)
+		neigh->ops = &dn_phase3_ops;
+
+	neigh->nud_state = NUD_NOARP;
+	neigh->output = neigh->ops->connected_output;
+
+	dn->blksize = 230;
+
+	return 0;
+}
+
+static void dn_long_error_report(struct neighbour *neigh, struct sk_buff *skb)
+{
+	struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+	unsigned char *ptr;
+
+	printk(KERN_DEBUG "dn_long_error_report: called\n");
+
+	if (!(cb->rt_flags & DN_RT_F_RQR)) {
+		kfree_skb(skb);
+		return;
+	}
+
+	skb_push(skb, skb->data - skb->nh.raw);
+	ptr = skb->data;
+
+	*(unsigned short *)ptr = dn_htons(skb->len - 2);
+	ptr += 2;
+
+	if (*ptr & DN_RT_F_PF) {
+		char padlen = (*ptr & ~DN_RT_F_PF);
+		ptr += padlen;
+	}
+
+	*ptr++ |= (cb->rt_flags & ~DN_RT_F_RQR) | DN_RT_F_RTS;
+
+	ptr += 2;
+	dn_dn2eth(ptr, dn_ntohs(cb->src));
+	ptr += 8;
+	dn_dn2eth(ptr, dn_ntohs(cb->dst));
+	ptr += 6;
+	*ptr = 0;
+
+	skb->dst->neighbour->ops->queue_xmit(skb);
+}
+
+
+static void dn_short_error_report(struct neighbour *neigh, struct sk_buff *skb)
+{
+	struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+	unsigned char *ptr;
+
+	printk(KERN_DEBUG "dn_short_error_report: called\n");
+
+	if (!(cb->rt_flags & DN_RT_F_RQR)) {
+		kfree_skb(skb);
+		return;
+	}
+
+	skb_push(skb, skb->data - skb->nh.raw);
+	ptr = skb->data;
+
+	*(unsigned short *)ptr = dn_htons(skb->len - 2);
+	ptr += 2;
+	*ptr++ = (cb->rt_flags & ~DN_RT_F_RQR) | DN_RT_F_RTS;
+
+	*(dn_address *)ptr = cb->src;
+	ptr += 2;
+	*(dn_address *)ptr = cb->dst;
+	ptr += 2;
+	*ptr = 0;
+
+	skb->dst->neighbour->ops->queue_xmit(skb);
+}
+
+
+static int dn_long_output(struct sk_buff *skb)
+{
+	struct dst_entry *dst = skb->dst;
+	struct neighbour *neigh = dst->neighbour;
+	struct device *dev = neigh->dev;
+	struct dn_dev *dn_db = dev->dn_ptr;
+	int headroom = dev->hard_header_len + sizeof(struct dn_long_packet) + 3;
+	unsigned char *data;
+	struct dn_long_packet *lp;
+	struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+
+
+	if (skb_headroom(skb) < headroom) {
+		struct sk_buff *skb2 = skb_realloc_headroom(skb, headroom);
+		if (skb2 == NULL) {
+			if (net_ratelimit())
+				printk(KERN_CRIT "dn_long_output: no memory\n");
+			kfree_skb(skb);
+			return -ENOBUFS;
+		}
+		kfree_skb(skb);
+		skb = skb2;
+		if (net_ratelimit())
+			printk(KERN_INFO "dn_long_output: Increasing headroom\n");
+	}
+
+	data = skb_push(skb, sizeof(struct dn_long_packet) + 3);
+	lp = (struct dn_long_packet *)(data+3);
+
+	*((unsigned short *)data) = dn_htons(skb->len - 2);
+	*(data + 2) = 1 | DN_RT_F_PF; /* Padding */
+
+	lp->msgflg   = DN_RT_PKT_LONG|(cb->rt_flags&(DN_RT_F_IE|DN_RT_F_RQR|DN_RT_F_RTS));
+	lp->d_area   = lp->d_subarea = 0;
+	dn_dn2eth(lp->d_id, cb->dst);
+	lp->s_area   = lp->s_subarea = 0;
+	dn_dn2eth(lp->s_id, cb->src);
+	lp->nl2      = 0;
+	lp->visit_ct = cb->hops & 0x3f;
+	lp->s_class  = 0;
+	lp->pt       = 0;
+
+	skb->nh.raw = skb->data;
+
+	if (dev->hard_header(skb, dev, ntohs(skb->protocol), neigh->ha,
+			dn_db->addr, skb->len) >= 0)
+		return neigh->ops->queue_xmit(skb);
+
+	if (net_ratelimit())
+		printk(KERN_DEBUG "dn_long_output: oops, can't sent packet\n");
+
+	kfree_skb(skb);
+	return -EINVAL;
+}
+
+static int dn_short_output(struct sk_buff *skb)
+{
+	struct dst_entry *dst = skb->dst;
+	struct neighbour *neigh = dst->neighbour;
+	struct device *dev = neigh->dev;
+	int headroom = dev->hard_header_len + sizeof(struct dn_short_packet) + 2;
+	struct dn_short_packet *sp;
+	unsigned char *data;
+	struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+
+
+        if (skb_headroom(skb) < headroom) {
+                struct sk_buff *skb2 = skb_realloc_headroom(skb, headroom);
+                if (skb2 == NULL) {
+			if (net_ratelimit())
+                        	printk(KERN_CRIT "dn_short_output: no memory\n");
+                        kfree_skb(skb);
+                        return -ENOBUFS;
+                }
+                kfree_skb(skb);
+                skb = skb2;
+		if (net_ratelimit())
+                	printk(KERN_INFO "dn_short_output: Increasing headroom\n");
+        }
+
+	data = skb_push(skb, sizeof(struct dn_short_packet) + 2);
+	*((unsigned short *)data) = dn_htons(skb->len - 2);
+	sp = (struct dn_short_packet *)(data+2);
+
+	sp->msgflg     = DN_RT_PKT_SHORT|(cb->rt_flags&(DN_RT_F_RQR|DN_RT_F_RTS));
+	sp->dstnode    = cb->dst;
+	sp->srcnode    = cb->src;
+	sp->forward    = cb->hops & 0x3f;
+
+	skb->nh.raw = skb->data;
+
+	if (dev->hard_header(skb, dev, ntohs(skb->protocol), neigh->ha,
+			NULL, skb->len) >= 0)
+		return neigh->ops->queue_xmit(skb);
+
+	kfree_skb(skb);
+	return -EINVAL;
+}
+
+/*
+ * Phase 3 output is the same is short output, execpt that
+ * it clears the area bits before transmission.
+ */
+static int dn_phase3_output(struct sk_buff *skb)
+{
+	struct dst_entry *dst = skb->dst;
+	struct neighbour *neigh = dst->neighbour;
+	struct device *dev = neigh->dev;
+	int headroom = dev->hard_header_len + sizeof(struct dn_short_packet) + 2;
+	struct dn_short_packet *sp;
+	unsigned char *data;
+	struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+
+	if (skb_headroom(skb) < headroom) {
+		struct sk_buff *skb2 = skb_realloc_headroom(skb, headroom);
+		if (skb2 == NULL) {
+			if (net_ratelimit())
+				printk(KERN_CRIT "dn_phase3_output: no memory\n");
+			kfree_skb(skb);
+			return -ENOBUFS;
+		}
+		kfree_skb(skb);
+		skb = skb2;
+		if (net_ratelimit())
+			printk(KERN_INFO "dn_phase3_output: Increasing headroom\n");
+	}
+
+	data = skb_push(skb, sizeof(struct dn_short_packet) + 2);
+	((unsigned short *)data) = dn_htons(skb->len - 2);
+	sp = (struct dn_short_packet *)(data + 2);
+
+	sp->msgflg   = DN_RT_PKT_SHORT|(cb->rt_flags&(DN_RT_F_RQR|DN_RT_F_RTS));
+	sp->dstnode  = cb->dst & __constant_htons(0x03ff);
+	sp->srcnode  = cb->src & __constant_htons(0x03ff);
+	sp->forward  = cb->hops & 0x3f;
+
+	skb->nh.raw = skb->data;
+
+	if (dev->hard_header(skb, dev, ntohs(skb->protocol), neigh->ha,
+			NULL, skb->len) >= 0)
+		return neigh->ops->queue_xmit(skb);
+
+	kfree_skb(skb);
+	return -EINVAL;
+}
+
+/*
+ * Unfortunately, the neighbour code uses the device in its hash
+ * function, so we don't get any advantage from it. This function
+ * basically does a neigh_lookup(), but without comparing the device
+ * field. This is required for the On-Ethernet cache
+ */
+struct neighbour *dn_neigh_lookup(struct neigh_table *tbl, void *ptr)
+{
+	int i;
+	struct neighbour *neigh;
+
+	start_bh_atomic();
+	for(i = 0; i < NEIGH_HASHMASK; i++) {
+		for(neigh = tbl->hash_buckets[i]; neigh != NULL; neigh = neigh->next) {
+			if (memcmp(neigh->primary_key, ptr, ETH_ALEN) == 0) {
+				atomic_inc(&neigh->refcnt);
+				end_bh_atomic();
+				return neigh;
+			}
+		}
+	}
+	end_bh_atomic();
+
+	return NULL;
+}
+
+
+/*
+ * Any traffic on a pointopoint link causes the timer to be reset
+ * for the entry in the neighbour table.
+ */
+void dn_neigh_pointopoint_notify(struct sk_buff *skb)
+{
+	return;
+}
+
+/*
+ * Pointopoint link receives a hello message
+ */
+void dn_neigh_pointopoint_hello(struct sk_buff *skb)
+{
+	kfree_skb(skb);
+}
+
+/*
+ * Ethernet router hello message received
+ */
+void dn_neigh_router_hello(struct sk_buff *skb)
+{
+	struct rtnode_hello_message *msg = (struct rtnode_hello_message *)skb->data;
+
+	struct neighbour *neigh;
+	struct dn_neigh *dn;
+	struct dn_dev *dn_db;
+
+	start_bh_atomic();
+	neigh = __neigh_lookup(&dn_neigh_table, msg->id, skb->dev, 1);
+	end_bh_atomic();
+
+	dn = (struct dn_neigh *)neigh;
+
+	if (neigh) {
+		neigh_update(neigh, msg->id, NUD_NOARP, 1, 0);
+		neigh->used = jiffies;
+
+		dn_db = (struct dn_dev *)neigh->dev->dn_ptr;
+
+		dn->blksize  = dn_ntohs(msg->blksize);
+		dn->priority = msg->priority;
+
+		dn->flags &= ~DN_NDFLAG_P3;
+
+		switch(msg->iinfo & DN_RT_INFO_TYPE) {
+			case DN_RT_INFO_L1RT:
+				dn->flags &=~DN_NDFLAG_R2;
+				dn->flags |= DN_NDFLAG_R1;
+			case DN_RT_INFO_L2RT:
+				dn->flags |= DN_NDFLAG_R2;
+		}
+
+		if (!dn_db->router) {
+			dn_db->router = neigh_clone(neigh);
+		} else {
+			if (msg->priority > ((struct dn_neigh *)dn_db->router)->priority)
+				neigh_release(xchg(&dn_db->router, neigh_clone(neigh)));
+		}
+
+		neigh_release(neigh);
+	}
+
+	kfree_skb(skb);
+}
+
+/*
+ * Endnode hello message received
+ */
+void dn_neigh_endnode_hello(struct sk_buff *skb)
+{
+	struct endnode_hello_message *msg = (struct endnode_hello_message *)skb->data;
+	struct neighbour *neigh;
+	struct dn_neigh *dn;
+
+	start_bh_atomic();
+	neigh = __neigh_lookup(&dn_neigh_table, msg->id, skb->dev, 1);
+	end_bh_atomic();
+
+	dn = (struct dn_neigh *)neigh;
+
+	if (neigh) {
+		neigh_update(neigh, msg->id, NUD_NOARP, 1, 0);
+		neigh->used = jiffies;
+
+		dn->flags   &= ~(DN_NDFLAG_R1 | DN_NDFLAG_R2);
+		dn->blksize  = dn_ntohs(msg->blksize);
+		dn->priority = 0;
+
+		neigh_release(neigh);
+	}
+
+	kfree_skb(skb);
+}
+
+
+#ifdef CONFIG_DECNET_ROUTER
+static char *dn_find_slot(char *base, int max, int priority)
+{
+	int i;
+	unsigned char *min = NULL;
+
+	base += 6; /* skip first id */
+
+	for(i = 0; i < max; i++) {
+		if (!min || (*base < *min))
+			min = base;
+		base += 7; /* find next priority */
+	}
+
+	if (!min)
+		return NULL;
+
+	return (*min < priority) ? (min - 6) : NULL;
+}
+
+int dn_neigh_elist(struct device *dev, unsigned char *ptr, int n)
+{
+	int t = 0;
+	int i;
+	struct neighbour *neigh;
+	struct dn_neigh *dn;
+	struct neigh_table *tbl = &dn_neigh_table;
+	unsigned char *rs = ptr;
+
+	start_bh_atomic();
+
+	for(i = 0; i < NEIGH_HASHMASK; i++) {
+		for(neigh = tbl->hash_buckets[i]; neigh != NULL; neigh = neigh->next) {
+			if (neigh->dev != dev)
+				continue;
+			dn = (struct dn_neigh *)neigh;
+			if (!(dn->flags & (DN_NDFLAG_R1|DN_NDFLAG_R2)))
+				continue;
+			if (decnet_node_type == DN_RT_INFO_L1RT && (dn->flags & DN_NDFLAG_R2))
+				continue;
+			if (t == n)
+				rs = dn_find_slot(ptr, n, dn->priority);
+			else
+				t++;
+			if (rs == NULL)
+				continue;
+			memcpy(rs, dn->addr, ETH_ALEN);
+			rs += 6;
+			*rs = neigh->nud_state & NUD_CONNECTED ? 0x80 : 0x0;
+			*rs |= dn->priority;
+			rs++;
+		}
+	}
+
+	end_bh_atomic();
+
+	return t;
+}
+#endif /* CONFIG_DECNET_ROUTER */
+
+
+
+#ifdef CONFIG_PROC_FS
+int dn_neigh_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+        int len     = 0;
+        off_t pos   = 0;
+        off_t begin = 0;
+	struct neighbour *n;
+	int i;
+	char buf[DN_ASCBUF_LEN];
+
+	len += sprintf(buffer + len, "Addr    Flags State Use Blksize Dev\n");
+
+	neigh_table_lock(&dn_neigh_table);
+	for(i=0;i <= NEIGH_HASHMASK; i++) {
+		n = dn_neigh_table.hash_buckets[i];
+		for(; n != NULL; n = n->next) {
+			struct dn_neigh *dn = (struct dn_neigh *)n;
+
+			len += sprintf(buffer+len, "%-7s %s%s%s   %02x    %02d  %07ld %-8s\n",
+					dn_addr2asc(dn_ntohs(dn_eth2dn(dn->addr)), buf),
+					(dn->flags&DN_NDFLAG_R1) ? "1" : "-",
+					(dn->flags&DN_NDFLAG_R2) ? "2" : "-",
+					(dn->flags&DN_NDFLAG_P3) ? "3" : "-",
+					dn->n.nud_state,
+					atomic_read(&dn->n.refcnt),
+					dn->blksize,
+					(dn->n.dev) ? dn->n.dev->name : "?");
+
+			pos = begin + len;
+
+                	if (pos < offset) {
+                        	len = 0;
+                        	begin = pos;
+                	}
+
+                	if (pos > offset + length)
+                       		break;
+		}
+	}
+	neigh_table_unlock(&dn_neigh_table);
+
+        *start = buffer + (offset - begin);
+        len   -= offset - begin;
+
+        if (len > length) len = length;
+
+        return len;
+}
+
+static struct proc_dir_entry proc_net_dn_neigh = {
+        PROC_NET_DN_ADJ, 12, "decnet_neigh",
+        S_IFREG | S_IRUGO, 1, 0, 0,
+        0, &proc_net_inode_operations,
+        dn_neigh_get_info
+};
+
+#endif
+
+void __init dn_neigh_init(void)
+{
+	neigh_table_init(&dn_neigh_table);
+
+#ifdef CONFIG_PROC_FS
+	proc_net_register(&proc_net_dn_neigh);
+#endif /* CONFIG_PROC_FS */
+}
+
+#ifdef CONFIG_DECNET_MODULE
+void dn_neigh_cleanup(void)
+{
+#ifdef CONFIG_PROC_FS
+	proc_net_unregister(PROC_NET_DN_ADJ);
+#endif /* CONFIG_PROC_FS */
+	neigh_table_clear(&dn_neigh_table);
+}
+#endif /* CONFIG_DECNET_MODULE */
diff -u --recursive --new-file v2.3.3/linux/net/decnet/dn_nsp_in.c linux/net/decnet/dn_nsp_in.c
--- v2.3.3/linux/net/decnet/dn_nsp_in.c	Wed Dec 31 16:00:00 1969
+++ linux/net/decnet/dn_nsp_in.c	Sat May 29 11:09:54 1999
@@ -0,0 +1,703 @@
+
+/*
+ * DECnet       An implementation of the DECnet protocol suite for the LINUX
+ *              operating system.  DECnet is implemented using the  BSD Socket
+ *              interface as the means of communication with the user level.
+ *
+ *              DECnet Network Services Protocol (Input)
+ *
+ * Author:      Eduardo Marcelo Serrat <emserrat@geocities.com>
+ *
+ * Changes:
+ *
+ *    Steve Whitehouse:  Split into dn_nsp_in.c and dn_nsp_out.c from
+ *                       original dn_nsp.c.
+ *    Steve Whitehouse:  Updated to work with my new routing architecture.
+ *    Steve Whitehouse:  Add changes from Eduardo Serrat's patches.
+ *    Steve Whitehouse:  Put all ack handling code in a common routine.
+ *    Steve Whitehouse:  Put other common bits into dn_nsp_rx()
+ *    Steve Whitehouse:  More checks on skb->len to catch bogus packets
+ *                       Fixed various race conditions and possible nasties.
+ *    Steve Whitehouse:  Now handles returned conninit frames.
+ */
+
+/******************************************************************************
+    (c) 1995-1998 E.M. Serrat		emserrat@geocities.com
+    
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    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.
+*******************************************************************************/
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/inet.h>
+#include <linux/route.h>
+#include <net/sock.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/termios.h>      
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <net/neighbour.h>
+#include <net/dst.h>
+#include <net/dn_nsp.h>
+#include <net/dn_dev.h>
+#include <net/dn_route.h>
+#include <net/dn_raw.h>
+
+
+/*
+ * For this function we've flipped the cross-subchannel bit
+ * if the message is an otherdata or linkservice message. Thus
+ * we can use it to work out what to update.
+ */
+static void dn_ack(struct sock *sk, struct sk_buff *skb, unsigned short ack)
+{
+	struct dn_scp *scp = &sk->protinfo.dn;
+	unsigned short type = ((ack >> 12) & 0x0003);
+	int wakeup = 0;
+
+	/* printk(KERN_DEBUG "dn_ack: %hd 0x%04hx\n", type, ack); */
+
+	switch(type) {
+		case 0: /* ACK - Data */
+			if (after(ack, scp->ackrcv_dat)) {
+				scp->ackrcv_dat = ack & 0x0fff;
+				wakeup |= dn_nsp_check_xmit_queue(sk, skb, &scp->data_xmit_queue, ack);
+			}
+			break;
+		case 1: /* NAK - Data */
+			break;
+		case 2: /* ACK - OtherData */
+			if (after(ack, scp->ackrcv_oth)) {
+				scp->ackrcv_oth = ack & 0x0fff;
+				wakeup |= dn_nsp_check_xmit_queue(sk, skb, &scp->other_xmit_queue, ack);
+			}
+			break;
+		case 3: /* NAK - OtherData */
+			break;
+	}
+
+	if (wakeup && !sk->dead)
+		sk->state_change(sk);
+}
+
+/*
+ * This function is a universal ack processor.
+ */
+static int dn_process_ack(struct sock *sk, struct sk_buff *skb, int oth)
+{
+	unsigned short *ptr = (unsigned short *)skb->data;
+	int len = 0;
+	unsigned short ack;
+
+	if (skb->len < 2)
+		return len;
+
+	if ((ack = dn_ntohs(*ptr)) & 0x8000) {
+		skb_pull(skb, 2);
+		ptr++;
+		len += 2;
+		if ((ack & 0x4000) == 0) {
+			if (oth) 
+				ack ^= 0x2000;
+			dn_ack(sk, skb, ack);
+		}
+	}
+
+	if (skb->len < 2)
+		return len;
+
+	if ((ack = dn_ntohs(*ptr)) & 0x8000) {
+		skb_pull(skb, 2);
+		len += 2;
+		if ((ack & 0x4000) == 0) {
+			if (oth) 
+				ack ^= 0x2000;
+			dn_ack(sk, skb, ack);
+		}
+	}
+
+	return len;
+}
+
+
+/*
+ * This function uses a slightly different lookup method
+ * to find its sockets, since it searches on object name/number
+ * rather than port numbers
+ */
+static int dn_conninit_rx(struct sk_buff *skb)
+{
+	struct sock *sk;
+	struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+	struct nsp_conn_init_msg *msg = (struct nsp_conn_init_msg *)skb->data;
+	struct sockaddr_dn addr;
+	unsigned char type = 0;
+
+	memset(&addr, 0, sizeof(struct sockaddr_dn));
+
+	cb->src_port = msg->srcaddr;
+	cb->dst_port = msg->dstaddr;
+	cb->services = msg->services;
+	cb->info     = msg->info;
+	cb->segsize  = dn_ntohs(msg->segsize);
+
+	skb_pull(skb, sizeof(*msg));
+
+	/* printk(KERN_DEBUG "username2sockaddr 1\n"); */
+	if (dn_username2sockaddr(skb->data, skb->len, &addr, &type) < 0)
+		goto free_out;
+
+	if (type > 1)
+		goto free_out;
+
+	/* printk(KERN_DEBUG "looking for listener...\n"); */
+	if ((sk = dn_sklist_find_listener(&addr)) == NULL)
+		return 1;
+
+	/* printk(KERN_DEBUG "checking backlog...\n"); */
+	if (sk->ack_backlog >= sk->max_ack_backlog)
+		goto free_out;
+
+	/* printk(KERN_DEBUG "waking up socket...\n"); */
+	sk->ack_backlog++;
+	skb_queue_tail(&sk->receive_queue, skb);
+	sk->state_change(sk);
+
+	return 0;
+
+free_out:
+	kfree_skb(skb);
+	return 0;
+}
+
+static void dn_nsp_conn_conf(struct sock *sk, struct sk_buff *skb)
+{
+	struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+	struct dn_scp *scp = &sk->protinfo.dn;
+
+	if (skb->len < 3)
+		goto out;
+
+	cb->services = *skb->data;
+	cb->info = *(skb->data+1);
+	skb_pull(skb, 2);
+	cb->segsize = dn_ntohs(*(__u16 *)skb->data);
+	skb_pull(skb, 2);
+
+	/*
+	 * FIXME: Check out services and info fields to check that
+	 * we can talk to this kind of node.
+	 */
+
+	if ((scp->state == DN_CI) || (scp->state == DN_CD)) {
+		scp->persist = 0;
+                scp->addrrem = cb->src_port;
+                sk->state = TCP_ESTABLISHED;
+                scp->state = DN_RUN;
+
+		if (scp->mss > cb->segsize)
+			scp->mss = cb->segsize;
+		if (scp->mss < 230)
+			scp->mss = 230;
+
+		if (skb->len > 0) {
+			unsigned char dlen = *skb->data;
+			if ((dlen <= 16) && (dlen <= skb->len)) {
+				scp->conndata_in.opt_optl = dlen;
+				memcpy(scp->conndata_in.opt_data, skb->data + 1, dlen);
+			}
+		}
+                dn_nsp_send_lnk(sk, DN_NOCHANGE);
+                if (!sk->dead)
+                	sk->state_change(sk);
+        }
+
+out:
+        kfree_skb(skb);
+}
+
+static void dn_nsp_conn_ack(struct sock *sk, struct sk_buff *skb)
+{
+	struct dn_scp *scp = &sk->protinfo.dn;
+
+	if (scp->state == DN_CI) {
+		scp->state = DN_CD;
+		scp->persist = 0;
+	}
+
+	kfree_skb(skb);
+}
+
+static void dn_nsp_disc_init(struct sock *sk, struct sk_buff *skb)
+{
+	struct dn_scp *scp = &sk->protinfo.dn;
+	struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+	unsigned short reason;
+
+	/* printk(KERN_DEBUG "DECnet: discinit %d\n", skb->len); */
+
+	if (skb->len < 2)
+		goto out;
+
+	reason = dn_ntohs(*(__u16 *)skb->data);
+	skb_pull(skb, 2);
+
+	scp->discdata_in.opt_status = reason;
+	scp->discdata_in.opt_optl   = 0;
+	memset(scp->discdata_in.opt_data, 0, 16);
+
+	if (skb->len > 0) {
+		unsigned char dlen = *skb->data;
+		if ((dlen <= 16) && (dlen <= skb->len)) {
+			scp->discdata_in.opt_optl = dlen;
+			memcpy(scp->discdata_in.opt_data, skb->data + 1, dlen);
+		}
+	}
+
+	scp->addrrem = cb->src_port;
+	sk->state    = TCP_CLOSE;
+
+	/* printk(KERN_DEBUG "DECnet: discinit\n"); */
+
+	switch(scp->state) {
+		case DN_CI:
+		case DN_CD:
+			scp->state = DN_RJ;
+			break;
+		case DN_RUN:
+			sk->shutdown |= SHUTDOWN_MASK;
+			scp->state = DN_DN;
+			break;
+		case DN_DI:
+			scp->state = DN_DIC;
+			break;
+	}
+
+	if (!sk->dead)
+		sk->state_change(sk);
+
+	dn_destroy_sock(sk);
+
+out:
+	kfree_skb(skb);
+}
+
+/*
+ * disc_conf messages are also called no_resources or no_link
+ * messages depending upon the "reason" field.
+ */
+static void dn_nsp_disc_conf(struct sock *sk, struct sk_buff *skb)
+{
+	struct dn_scp *scp = &sk->protinfo.dn;
+	unsigned short reason;
+
+	if (skb->len != 2)
+		goto out;
+
+	reason = dn_ntohs(*(__u16 *)skb->data);
+
+	sk->state = TCP_CLOSE;
+
+	switch(scp->state) {
+		case DN_CI:
+			scp->state = DN_NR;
+			break;
+		case DN_DR:
+			if (reason == NSP_REASON_DC)
+				scp->state = DN_DRC;
+			if (reason == NSP_REASON_NL)
+				scp->state = DN_CN;
+			break;
+		case DN_DI:
+			scp->state = DN_DIC;
+			break;
+		case DN_RUN:
+			sk->shutdown |= SHUTDOWN_MASK;
+		case DN_CC:
+			scp->state = DN_CN;
+	}
+
+	if (!sk->dead)
+		sk->state_change(sk);
+
+	dn_destroy_sock(sk);
+
+out:
+	kfree_skb(skb);
+}
+
+static void dn_nsp_linkservice(struct sock *sk, struct sk_buff *skb)
+{
+	struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+	unsigned short segnum;
+	unsigned char lsflags;
+	char fcval;
+
+	if (skb->len != 4)
+		goto out;
+
+	cb->segnum = segnum = dn_ntohs(*(__u16 *)skb->data);
+	skb_pull(skb, 2);
+	lsflags = *(unsigned char *)skb->data;
+	skb_pull(skb, 1);
+	fcval = *(char *)skb->data;
+
+	if (lsflags & 0xf0)
+		goto out;
+
+	if (((sk->protinfo.dn.numoth_rcv + 1) & 0x0FFF) == (segnum & 0x0FFF)) {
+        	sk->protinfo.dn.numoth_rcv += 1;        
+                switch(lsflags & 0x03) {
+                	case 0x00:      
+                        	break;
+                        case 0x01:      
+                                sk->protinfo.dn.flowrem_sw = DN_DONTSEND;
+                                break;
+                        case 0x02:      
+                                sk->protinfo.dn.flowrem_sw = DN_SEND;
+				dn_nsp_output(sk);
+				if (!sk->dead)
+					sk->state_change(sk);
+                }
+                
+        }
+
+	dn_nsp_send_oth_ack(sk);
+
+out:
+	kfree_skb(skb);
+}
+
+/*
+ * Copy of sock_queue_rcv_skb (from net/core/datagram.c) to
+ * queue other data segments. Also we send SIGURG here instead
+ * of the normal SIGIO, 'cos its out of band data.
+ */
+static __inline__ int dn_queue_other_skb(struct sock *sk, struct sk_buff *skb)
+{
+	struct dn_scp *scp = &sk->protinfo.dn;
+
+        /* Cast skb->rcvbuf to unsigned... It's pointless, but reduces
+           number of warnings when compiling with -W --ANK
+         */
+        if (atomic_read(&sk->rmem_alloc) + skb->truesize >= (unsigned)sk->rcvbuf
+)
+                return -ENOMEM;
+
+#ifdef CONFIG_FILTER
+        if (sk->filter)
+        {
+                if (sk_filter(skb, sk->filter))
+                        return -EPERM;  /* Toss packet */
+        }
+#endif /* CONFIG_FILTER */
+
+        skb_set_owner_r(skb, sk);
+        skb_queue_tail(&scp->other_receive_queue, skb);
+
+        if (!sk->dead) {
+		struct socket *sock = sk->socket;
+		wake_up_interruptible(sk->sleep);
+		if (!(sock->flags & SO_WAITDATA) && sock->fasync_list)
+			kill_fasync(sock->fasync_list, SIGURG);
+	}
+
+        return 0;
+}
+
+static void dn_nsp_otherdata(struct sock *sk, struct sk_buff *skb)
+{
+	struct dn_scp *scp = &sk->protinfo.dn;
+	unsigned short segnum;
+	struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+	int queued = 0;
+
+	if (skb->len < 2)
+		goto out;
+
+	cb->segnum = segnum = dn_ntohs(*(__u16 *)skb->data);
+	skb_pull(skb, 2);
+
+	if (((sk->protinfo.dn.numoth_rcv + 1) & 0x0fff) == (segnum & 0x0fff)) {
+
+		if (dn_queue_other_skb(sk, skb) == 0) {
+			sk->protinfo.dn.numoth_rcv++;
+			scp->other_report = 0;
+			queued = 1;
+		}
+	}
+
+	dn_nsp_send_oth_ack(sk);
+out:
+	if (!queued)
+		kfree_skb(skb);
+}
+
+static void dn_nsp_data(struct sock *sk, struct sk_buff *skb)
+{
+	int queued = 0;
+	unsigned short segnum;
+	struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+	struct dn_scp *scp = &sk->protinfo.dn;
+
+	if (skb->len < 2)
+		goto out;
+
+	cb->segnum = segnum = dn_ntohs(*(__u16 *)skb->data);
+	skb_pull(skb, 2);
+
+	if (((sk->protinfo.dn.numdat_rcv + 1) & 0x0FFF) == 
+                     (segnum & 0x0FFF)) {
+
+                if (sock_queue_rcv_skb(sk, skb) == 0) {
+			sk->protinfo.dn.numdat_rcv++;
+                	queued = 1;
+                }
+
+		if ((scp->flowloc_sw == DN_SEND) && dn_congested(sk)) {
+			scp->flowloc_sw = DN_DONTSEND;
+			dn_nsp_send_lnk(sk, DN_DONTSEND);
+		}
+        }
+
+	dn_nsp_send_data_ack(sk);
+out:
+	if (!queued)
+		kfree_skb(skb);
+}
+
+/*
+ * If one of our conninit messages is returned, this function
+ * deals with it. It puts the socket into the NO_COMMUNICATION
+ * state.
+ */
+static void dn_returned_conninit(struct sk_buff *skb)
+{
+	struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+	struct sock *sk;
+
+	cb->dst_port = cb->src_port;
+	cb->src_port = 0;
+
+	if ((sk = dn_find_by_skb(skb)) != NULL) {
+		struct dn_scp *scp = &sk->protinfo.dn;
+		if (scp->state == DN_CI) {
+			scp->state = DN_NC;
+			sk->state = TCP_CLOSE;
+			if (!sk->dead)
+				sk->state_change(sk);
+		}
+	}
+
+	kfree_skb(skb);
+}
+
+int dn_nsp_rx(struct sk_buff *skb)
+{
+	struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+	struct sock *sk = NULL;
+	unsigned char *ptr = (unsigned char *)skb->data;
+
+	skb->h.raw    = skb->data;
+	cb->nsp_flags = *ptr++;
+
+	if (decnet_debug_level & 1)
+		printk(KERN_DEBUG "dn_nsp_rx: Message type 0x%02x\n", (int)cb->nsp_flags);
+
+#ifdef CONFIG_DECNET_RAW
+	dn_raw_rx_nsp(skb);
+#endif /* CONFIG_DECNET_RAW */
+
+	if (skb->len < 2) 
+		goto free_out;
+
+	if (cb->nsp_flags & 0x83) 
+		goto free_out;
+
+	/*
+	 * Returned packets...
+	 */
+	if (cb->rt_flags & DN_RT_F_RTS) {
+		if ((cb->nsp_flags & 0x0c) == 0x08) {
+			switch(cb->nsp_flags & 0x70) {
+				case 0x10:
+				case 0x60:
+					dn_returned_conninit(skb);
+					goto out;
+			}
+		}
+		goto free_out;
+	}
+
+	/*
+	 * Filter out conninits and useless packet types
+	 */
+	if ((cb->nsp_flags & 0x0c) == 0x08) {
+		switch(cb->nsp_flags & 0x70) {
+			case 0x00: /* NOP */
+			case 0x70: /* Reserved */
+			case 0x50: /* Reserved, Phase II node init */
+				goto free_out;
+			case 0x10:
+			case 0x60:
+				return dn_conninit_rx(skb);
+		}
+	}
+
+	if (skb->len < 3)
+		goto free_out;
+
+	/*
+	 * Grab the destination address.
+	 */
+	cb->dst_port = *(unsigned short *)ptr;
+	cb->src_port = 0;
+	ptr += 2;
+
+	/*
+	 * If not a connack, grab the source address too.
+	 */
+	if (skb->len >= 5) {
+		cb->src_port = *(unsigned short *)ptr;
+		ptr += 2;
+		skb_pull(skb, 5);
+	}
+
+	/*
+	 * Find the socket to which this skb is destined.
+	 */
+	if ((sk = dn_find_by_skb(skb)) != NULL) {
+		struct dn_scp *scp = &sk->protinfo.dn;
+		int ret;
+		/* printk(KERN_DEBUG "dn_nsp_rx: Found a socket\n"); */
+
+		/* Reset backoff */
+		scp->nsp_rxtshift = 0;
+
+		bh_lock_sock(sk);
+		ret = 0;
+		if (sk->lock.users == 0)
+			ret = dn_nsp_backlog_rcv(sk, skb);
+		else
+			sk_add_backlog(sk, skb);
+		bh_unlock_sock(sk);
+
+		return ret;
+	}
+	return 1;
+
+free_out:
+	kfree_skb(skb);
+out:
+	return 0;
+}
+
+/*
+ * This is the main receive routine for sockets. It is called
+ * from the above when the socket is not busy, and also from
+ * sock_release() when there is a backlog queued up.
+ */
+int dn_nsp_backlog_rcv(struct sock *sk, struct sk_buff *skb)
+{
+	struct dn_scp *scp = &sk->protinfo.dn;
+	struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+
+	/*
+	 * Control packet.
+	 */
+	if ((cb->nsp_flags & 0x0c) == 0x08) {
+		/* printk(KERN_DEBUG "control type\n"); */
+		switch(cb->nsp_flags & 0x70) {
+			case 0x20:
+				dn_nsp_conn_conf(sk, skb);
+				break;
+			case 0x30:
+				dn_nsp_disc_init(sk, skb);
+				break;
+			case 0x40:      
+				dn_nsp_disc_conf(sk, skb);
+				break;
+		}
+
+	} else if (cb->nsp_flags == 0x24) {
+		/*
+		 * Special for connacks, 'cos they don't have
+		 * ack data or ack otherdata info.
+		 */
+		dn_nsp_conn_ack(sk, skb);
+	} else {
+		int other = 1;
+
+		if ((cb->nsp_flags & 0x1c) == 0)
+			other = 0;
+		if (cb->nsp_flags == 0x04)
+			other = 0;
+
+		/*
+		 * Read out ack data here, this applies equally
+		 * to data, other data, link serivce and both
+		 * ack data and ack otherdata.
+		 */
+		dn_process_ack(sk, skb, other);
+
+		/*
+		 * If we've some sort of data here then call a
+		 * suitable routine for dealing with it, otherwise
+		 * the packet is an ack and can be discarded. All
+		 * data frames can also kick a CC socket into RUN.
+		 */
+		if ((cb->nsp_flags & 0x0c) == 0) {
+
+			if ((scp->state == DN_CC) && !sk->dead) {
+				scp->state = DN_RUN;
+				sk->state = TCP_ESTABLISHED;
+				sk->state_change(sk);
+			}
+
+			if (scp->state != DN_RUN)
+				goto free_out;
+
+			switch(cb->nsp_flags) {
+				case 0x10: /* LS */
+					dn_nsp_linkservice(sk, skb);
+					break;
+				case 0x30: /* OD */
+					dn_nsp_otherdata(sk, skb);
+					break;
+				default:
+					dn_nsp_data(sk, skb);
+			}
+
+		} else { /* Ack, chuck it out here */
+free_out:
+			kfree_skb(skb);
+		}
+	}
+
+	return 0;
+}
+
diff -u --recursive --new-file v2.3.3/linux/net/decnet/dn_nsp_out.c linux/net/decnet/dn_nsp_out.c
--- v2.3.3/linux/net/decnet/dn_nsp_out.c	Wed Dec 31 16:00:00 1969
+++ linux/net/decnet/dn_nsp_out.c	Wed May 26 09:36:36 1999
@@ -0,0 +1,640 @@
+
+/*
+ * DECnet       An implementation of the DECnet protocol suite for the LINUX
+ *              operating system.  DECnet is implemented using the  BSD Socket
+ *              interface as the means of communication with the user level.
+ *
+ *              DECnet Network Services Protocol (Output)
+ *
+ * Author:      Eduardo Marcelo Serrat <emserrat@geocities.com>
+ *
+ * Changes:
+ *
+ *    Steve Whitehouse:  Split into dn_nsp_in.c and dn_nsp_out.c from
+ *                       original dn_nsp.c.
+ *    Steve Whitehouse:  Updated to work with my new routing architecture.
+ *    Steve Whitehouse:  Added changes from Eduardo Serrat's patches.
+ *    Steve Whitehouse:  Now conninits have the "return" bit set.
+ *    Steve Whitehouse:  Fixes to check alloc'd skbs are non NULL!
+ *                       Moved output state machine into one function
+ *    Steve Whitehouse:  New output state machine
+ */
+
+/******************************************************************************
+    (c) 1995-1998 E.M. Serrat		emserrat@geocities.com
+    
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    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.
+*******************************************************************************/
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/inet.h>
+#include <linux/route.h>
+#include <net/sock.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/termios.h>      
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/if_packet.h>
+#include <net/neighbour.h>
+#include <net/dst.h>
+#include <net/dn_nsp.h>
+#include <net/dn_dev.h>
+#include <net/dn_route.h>
+
+
+static int nsp_backoff[NSP_MAXRXTSHIFT + 1] = { 1, 2, 4, 8, 16, 32, 64, 64, 64, 64, 64, 64, 64 };
+
+/*
+ * If sk == NULL, then we assume that we are supposed to be making
+ * a routing layer skb. If sk != NULL, then we are supposed to be
+ * creating an skb for the NSP layer. The dn_send_skb() function will
+ * recognise skbs on the same basis. 
+ *
+ * The eventual aim is for each socket to have a cached header size
+ * for its outgoing packets, and to set hdr from this when sk != NULL.
+ */
+struct sk_buff *dn_alloc_skb(struct sock *sk, int size, int pri)
+{
+	struct sk_buff *skb;
+	int hdr = 64;
+
+	if ((skb = alloc_skb(size + hdr, pri)) == NULL)
+		return NULL;
+
+	skb->protocol = __constant_htons(ETH_P_DNA_RT);
+	skb->sk       = sk;
+	skb->pkt_type = PACKET_OUTGOING;
+
+	skb_reserve(skb, hdr);
+
+	return skb;
+}
+
+/*
+ * Wrapper for the above, for allocs of data skbs. We try and get the
+ * whole size thats been asked for (plus 11 bytes of header). If this
+ * fails, then we try for any size over 16 bytes for SOCK_STREAMS.
+ */
+struct sk_buff *dn_alloc_send_skb(struct sock *sk, int *size, int noblock, int *err)
+{
+	int space;
+	int len;
+	struct sk_buff *skb = NULL;
+
+	*err = 0;
+
+	while(skb == NULL) {
+		if (signal_pending(current)) {
+			*err = ERESTARTSYS;
+			break;
+		}
+
+		if (sk->shutdown & SEND_SHUTDOWN) {
+			*err = EINVAL;
+			break;
+		}
+
+		if (sk->err)
+			break;
+
+		len = *size + 11;
+		space = sk->sndbuf - atomic_read(&sk->wmem_alloc);
+
+		if (space < len) {
+			if ((sk->socket->type == SOCK_STREAM) && (space >= (16 + 11)))
+				len = space;
+		}
+
+		if (space < len) {
+			sk->socket->flags |= SO_NOSPACE;
+			if (noblock) {
+				*err = EWOULDBLOCK;
+				break;
+			}
+
+			sk->socket->flags &= ~SO_NOSPACE;
+			SOCK_SLEEP_PRE(sk)
+
+			if ((sk->sndbuf - atomic_read(&sk->wmem_alloc)) < len)
+				schedule();
+
+			SOCK_SLEEP_POST(sk)
+			continue;
+		}
+
+		if ((skb = dn_alloc_skb(sk, len, GFP_KERNEL)) == NULL)
+			continue;
+
+		skb->destructor = sock_wfree;
+		atomic_add(skb->truesize, &sk->wmem_alloc);
+
+		*size = len - 11;
+	}
+
+	return skb;
+}
+
+/*
+ * Calculate persist timer based upon the smoothed round
+ * trip time and the variance. Backoff according to the
+ * nsp_backoff[] array.
+ */
+unsigned long dn_nsp_persist(struct sock *sk)
+{
+	struct dn_scp *scp = &sk->protinfo.dn;
+
+	unsigned long t = ((scp->nsp_srtt >> 2) + scp->nsp_rttvar) >> 1;
+
+	t *= nsp_backoff[scp->nsp_rxtshift];
+
+	if (t < HZ) t = HZ;
+	if (t > (600*HZ)) t = (600*HZ);
+
+	if (scp->nsp_rxtshift < NSP_MAXRXTSHIFT)
+		scp->nsp_rxtshift++;
+
+	/* printk(KERN_DEBUG "rxtshift %lu, t=%lu\n", scp->nsp_rxtshift, t); */
+
+	return t;
+}
+
+/*
+ * This is called each time we get an estimate for the rtt
+ * on the link.
+ */
+static void dn_nsp_rtt(struct sock *sk, long rtt)
+{
+	struct dn_scp *scp = &sk->protinfo.dn;
+	long srtt = (long)scp->nsp_srtt;
+	long rttvar = (long)scp->nsp_rttvar;
+	long delta;
+
+	/*
+	 * If the jiffies clock flips over in the middle of timestamp
+	 * gathering this value might turn out negative, so we make sure
+	 * that is it always positive here.
+	 */
+	if (rtt < 0) 
+		rtt = -rtt;
+	/*
+	 * Add new rtt to smoothed average
+	 */
+	delta = ((rtt << 3) - srtt);
+	srtt += (delta >> 3);
+	if (srtt >= 1) 
+		scp->nsp_srtt = (unsigned long)srtt;
+	else
+		scp->nsp_srtt = 1;
+
+	/*
+	 * Add new rtt varience to smoothed varience
+	 */
+	delta >>= 1;
+	rttvar += ((((delta>0)?(delta):(-delta)) - rttvar) >> 2);
+	if (rttvar >= 1) 
+		scp->nsp_rttvar = (unsigned long)rttvar;
+	else
+		scp->nsp_rttvar = 1;
+
+	/* printk(KERN_DEBUG "srtt=%lu rttvar=%lu\n", scp->nsp_srtt, scp->nsp_rttvar); */
+}
+
+/*
+ * Walk the queues, otherdata/linkservice first. Send as many
+ * frames as the window allows, increment send counts on all
+ * skbs which are sent. Reduce the window if we are retransmitting
+ * frames.
+ */
+void dn_nsp_output(struct sock *sk)
+{
+	struct dn_scp *scp = &sk->protinfo.dn;
+	unsigned long win = scp->snd_window;
+	struct sk_buff *skb, *skb2, *list;
+	struct dn_skb_cb *cb;
+	int reduce_win = 0;
+
+	/* printk(KERN_DEBUG "dn_nsp_output: ping\n"); */
+
+	/*
+	 * First we check for otherdata/linkservice messages
+	 */
+	skb = scp->other_xmit_queue.next;
+	list = (struct sk_buff *)&scp->other_xmit_queue;
+	while(win && (skb != list)) {
+		if ((skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL) {
+			cb = (struct dn_skb_cb *)skb;
+			if (cb->xmit_count > 0)
+				reduce_win = 1;
+			else
+				cb->stamp = jiffies;
+			cb->xmit_count++;
+			skb2->sk = sk;
+			dn_send_skb(skb2);
+		}
+		skb = skb->next;
+		win--;
+	}
+
+	/*
+	 * If we may not send any data, we don't.
+	 * Should this apply to otherdata as well ? - SJW
+	 */
+	if (scp->flowrem_sw != DN_SEND)
+		goto recalc_window;
+
+	skb = scp->data_xmit_queue.next;
+	list = (struct sk_buff *)&scp->data_xmit_queue;
+	while(win && (skb != list)) {
+		if ((skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL) {
+			cb = (struct dn_skb_cb *)skb;
+			if (cb->xmit_count > 0)
+				reduce_win = 1;
+			else
+				cb->stamp = jiffies;
+			cb->xmit_count++;
+			skb2->sk = sk;
+			dn_send_skb(skb2);
+		}
+		skb = skb->next;
+		win--;
+	}
+
+	/*
+	 * If we've sent any frame more than once, we cut the
+	 * send window size in half. There is always a minimum
+	 * window size of one available.
+	 */
+recalc_window:
+	if (reduce_win) {
+		/* printk(KERN_DEBUG "Window reduction %ld\n", scp->snd_window); */
+		scp->snd_window >>= 1;
+		if (scp->snd_window < NSP_MIN_WINDOW)
+			scp->snd_window = NSP_MIN_WINDOW;
+	}
+}
+
+int dn_nsp_xmit_timeout(struct sock *sk)
+{
+	struct dn_scp *scp = &sk->protinfo.dn;
+
+	dn_nsp_output(sk);
+
+	if (skb_queue_len(&scp->data_xmit_queue) || skb_queue_len(&scp->other_xmit_queue))
+		scp->persist = dn_nsp_persist(sk);
+
+	return 0;
+}
+
+void dn_nsp_queue_xmit(struct sock *sk, struct sk_buff *skb, int oth)
+{
+	struct dn_scp *scp = &sk->protinfo.dn;
+	struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+	unsigned long t = ((scp->nsp_srtt >> 2) + scp->nsp_rttvar) >> 1;
+	struct sk_buff *skb2;
+
+	if (t < HZ) t = HZ;
+	/*
+	 * Slow start: If we have been idle for more than
+	 * one RTT, then reset window to min size.
+	 */
+	if ((jiffies - scp->stamp) > t)
+		scp->snd_window = NSP_MIN_WINDOW;
+
+	/* printk(KERN_DEBUG "Window: %lu\n", scp->snd_window); */
+
+	cb->xmit_count = 0;
+
+	if (oth)
+		skb_queue_tail(&scp->other_xmit_queue, skb);
+	else
+		skb_queue_tail(&scp->data_xmit_queue, skb);
+
+	if (scp->flowrem_sw != DN_SEND)
+		return;
+
+	if ((skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL) {
+		cb->stamp = jiffies;
+		cb->xmit_count++;
+		skb2->sk = sk;
+		dn_send_skb(skb2);
+	}
+}
+
+int dn_nsp_check_xmit_queue(struct sock *sk, struct sk_buff *skb, struct sk_buff_head *q, unsigned short acknum)
+{
+	struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+	struct dn_scp *scp = &sk->protinfo.dn;
+	struct sk_buff *skb2, *list, *ack = NULL;
+	int wakeup = 0;
+	unsigned long reftime = cb->stamp;
+	unsigned long pkttime;
+	unsigned short xmit_count;
+	unsigned short segnum;
+
+	skb2 = q->next;
+	list = (struct sk_buff *)q;
+	while(list != skb2) {
+		struct dn_skb_cb *cb2 = (struct dn_skb_cb *)skb2->cb;
+
+		if (before_or_equal(cb2->segnum, acknum))
+			ack = skb2;
+
+		/* printk(KERN_DEBUG "ack: %s %04x %04x\n", ack ? "ACK" : "SKIP", (int)cb2->segnum, (int)acknum); */
+
+		skb2 = skb2->next;
+
+		if (ack == NULL)
+			continue;
+
+		/* printk(KERN_DEBUG "check_xmit_queue: %04x, %d\n", acknum, cb2->xmit_count); */
+
+		wakeup = 1;
+		pkttime = cb2->stamp;
+		xmit_count = cb2->xmit_count;
+		segnum = cb2->segnum;
+		skb_unlink(ack);
+		kfree_skb(ack);
+		ack = NULL;
+		if (xmit_count == 1) {
+			if (equal(segnum, acknum)) 
+				dn_nsp_rtt(sk, (long)(pkttime - reftime));
+
+			if (scp->snd_window < NSP_MAX_WINDOW)
+				scp->snd_window++;
+		}
+	}
+
+#if 0 /* Turned off due to possible interference in socket shutdown */
+	if ((skb_queue_len(&scp->data_xmit_queue) == 0) &&
+	    (skb_queue_len(&scp->other_xmit_queue) == 0))
+		scp->persist = 0;
+#endif
+
+	return wakeup;
+}
+
+void dn_nsp_send_data_ack(struct sock *sk)
+{
+	struct	sk_buff			*skb = NULL;
+	struct  nsp_data_ack_msg	*msg;
+
+	if ((skb = dn_alloc_skb(sk, 200, GFP_ATOMIC)) == NULL)
+		return;
+	
+	msg = (struct nsp_data_ack_msg *)skb_put(skb,sizeof(*msg));
+
+	msg->msgflg  = 0x04;			/* data ack message	*/
+	msg->dstaddr = sk->protinfo.dn.addrrem;
+	msg->srcaddr = sk->protinfo.dn.addrloc;
+	msg->acknum  = dn_htons((sk->protinfo.dn.numdat_rcv & 0x0FFF) | 0x8000);
+
+	sk->protinfo.dn.ackxmt_dat = sk->protinfo.dn.numdat_rcv;
+
+	dn_send_skb(skb);
+}
+
+void dn_nsp_send_oth_ack(struct sock *sk)
+{
+	struct	sk_buff			*skb = NULL;
+	struct  nsp_data_ack_msg		*msg;
+
+	if ((skb = dn_alloc_skb(sk, 200, GFP_ATOMIC)) == NULL)
+		return;
+	
+	/* printk(KERN_DEBUG "dn_send_oth_ack\n"); */
+
+	msg = (struct nsp_data_ack_msg *)skb_put(skb,sizeof(*msg));
+
+	msg->msgflg = 0x14;			/* oth ack message	*/
+	msg->dstaddr = sk->protinfo.dn.addrrem;
+	msg->srcaddr = sk->protinfo.dn.addrloc;
+	msg->acknum  = dn_htons((sk->protinfo.dn.numoth_rcv & 0x0FFF) | 0x8000);
+
+	sk->protinfo.dn.ackxmt_oth = sk->protinfo.dn.numoth_rcv;
+
+	dn_send_skb(skb);
+}
+
+
+void dn_send_conn_ack (struct sock *sk)
+{
+	struct dn_scp *scp = &sk->protinfo.dn;
+	struct sk_buff *skb = NULL;
+        struct nsp_conn_ack_msg *msg;
+
+	if ((skb = dn_alloc_skb(sk, 3, GFP_KERNEL)) == NULL)
+		return;
+
+        msg = (struct nsp_conn_ack_msg *)skb_put(skb, 3);
+        msg->msgflg = 0x24;                   
+	msg->dstaddr = scp->addrrem;
+
+	dn_send_skb(skb);	
+}
+
+void dn_nsp_delayed_ack(struct sock *sk)
+{
+	struct dn_scp *scp = &sk->protinfo.dn;
+
+	if (scp->ackxmt_oth != scp->numoth_rcv)
+		dn_nsp_send_oth_ack(sk);
+
+	if (scp->ackxmt_dat != scp->numdat_rcv)
+		dn_nsp_send_data_ack(sk);
+}
+
+void dn_send_conn_conf (struct sock *sk)
+{
+	struct dn_scp *scp = &sk->protinfo.dn;
+	struct sk_buff *skb = NULL;
+        struct nsp_conn_init_msg *msg;
+	unsigned short int aux;
+
+	if ((skb = dn_alloc_skb(sk, 50 + scp->conndata_out.opt_optl, GFP_KERNEL)) == NULL)
+		return;
+
+        msg = (struct nsp_conn_init_msg *)skb_put(skb, sizeof(*msg));
+        msg->msgflg = 0x28;                   
+	msg->dstaddr = scp->addrrem;
+        msg->srcaddr = scp->addrloc;
+        msg->services = 0x01;
+        msg->info = 0x03;
+        msg->segsize = dn_htons(0x05B3);
+
+	if (scp->conndata_out.opt_optl > 0) {
+		aux = scp->conndata_out.opt_optl;
+		*skb_put(skb,1) = aux;
+		memcpy(skb_put(skb, aux), scp->conndata_out.opt_data, aux);
+	}
+	
+
+	dn_send_skb(skb);	
+}
+
+void dn_send_disc (struct sock *sk, unsigned char msgflg, unsigned short reason)
+{
+	struct dn_scp *scp = &sk->protinfo.dn;
+	struct sk_buff *skb = NULL;
+	int ddl = (msgflg == NSP_DISCINIT || msgflg == 0x38) ? (1 + scp->discdata_out.opt_optl) : 0;
+	int size = 7 + ddl;
+	unsigned char *msg;
+
+	if ((skb = dn_alloc_skb(sk, size, GFP_ATOMIC)) == NULL)
+		return;
+
+	if (reason == 0) reason = scp->discdata_out.opt_status;
+	
+	msg = skb_put(skb, size);
+	*msg++ = msgflg;
+	*(__u16 *)msg = scp->addrrem;
+	msg += 2;
+	*(__u16 *)msg = scp->addrloc;
+	msg += 2;
+	*(__u16 *)msg = dn_htons(reason);
+	msg += 2;
+
+	if (ddl) {
+		*msg++ = scp->discdata_out.opt_optl;
+		memcpy(msg, scp->discdata_out.opt_data, scp->discdata_out.opt_optl);
+	}
+
+	dn_send_skb(skb);	
+}
+
+void dn_nsp_send_lnk(struct sock *sk, unsigned short flgs)
+{
+	struct dn_scp *scp = &sk->protinfo.dn;
+	struct sk_buff *skb = NULL;
+	struct nsp_data_seg_msg	*msg;
+	struct nsp_data_opt_msg	*msg1;
+	struct dn_skb_cb *cb;
+
+	if ((skb = dn_alloc_skb(sk, 80, GFP_ATOMIC)) == NULL)
+		return;
+
+	cb = (struct dn_skb_cb *)skb->cb;	
+	msg = (struct nsp_data_seg_msg *)skb_put(skb, sizeof(*msg));
+	msg->msgflg = 0x10;			/* Link svc message	*/
+	msg->dstaddr = scp->addrrem;
+	msg->srcaddr = scp->addrloc;
+
+	msg1 = (struct nsp_data_opt_msg *)skb_put(skb, sizeof(*msg1));
+	msg1->acknum = dn_htons((scp->ackxmt_oth & 0x0FFF) | 0x8000);
+	msg1->segnum = dn_htons(cb->segnum = (scp->numoth++ & 0x0FFF));
+        msg1->lsflgs = flgs;
+
+	/* printk(KERN_DEBUG "dn_nsp_send_lnk: %02x\n", flgs); */
+	
+	dn_nsp_queue_xmit(sk, skb, 1);
+
+	scp->persist = dn_nsp_persist(sk);
+	scp->persist_fxn = dn_nsp_xmit_timeout;
+
+}
+
+static int dn_nsp_retrans_conninit(struct sock *sk)
+{
+	struct dn_scp *scp = &sk->protinfo.dn;
+
+	if (scp->state == DN_CI) {
+		dn_nsp_send_conninit(sk, NSP_RCI);
+		scp->persist = dn_nsp_persist(sk);
+	}
+
+	return 0;
+}
+
+void dn_nsp_send_conninit(struct sock *sk, unsigned char msgflg)
+{
+	struct dn_scp *scp = &sk->protinfo.dn;
+	struct sk_buff *skb = NULL;
+	struct nsp_conn_init_msg *msg;
+	unsigned char aux;
+	unsigned char menuver;
+	struct dn_skb_cb *cb;
+	unsigned char type = 1;
+
+	if ((skb = dn_alloc_skb(sk, 200, (msgflg == NSP_CI) ? GFP_KERNEL : GFP_ATOMIC)) == NULL)
+		return;
+
+	cb  = (struct dn_skb_cb *)skb->cb;
+	msg = (struct nsp_conn_init_msg *)skb_put(skb,sizeof(*msg));
+
+	msg->msgflg	= msgflg;
+	msg->dstaddr	= 0x0000;		/* Remote Node will assign it*/
+
+	if (msgflg == NSP_CI)
+		sk->protinfo.dn.addrloc = dn_alloc_port();
+
+	msg->srcaddr	= sk->protinfo.dn.addrloc;
+	msg->services	= 1 | NSP_FC_NONE;	/* Requested flow control    */
+	msg->info	= 0x03;			/* Version Number            */	
+	msg->segsize	= dn_htons(1459);	/* Max segment size	     */	
+
+	if (scp->peer.sdn_objnum)
+		type = 0;
+
+	skb_put(skb, dn_sockaddr2username(&scp->peer, skb->tail, type));
+	skb_put(skb, dn_sockaddr2username(&scp->addr, skb->tail, 2));
+
+	menuver = DN_MENUVER_ACC | DN_MENUVER_USR;
+	if (scp->peer.sdn_flags & SDF_PROXY)
+		menuver |= DN_MENUVER_PRX;
+	if (scp->peer.sdn_flags & SDF_UICPROXY)
+		menuver |= DN_MENUVER_UIC;
+
+	*skb_put(skb, 1) = menuver;	/* Menu Version		*/
+	
+	aux = scp->accessdata.acc_userl;
+	*skb_put(skb, 1) = aux;
+	if (aux > 0)
+	memcpy(skb_put(skb, aux), scp->accessdata.acc_user, aux);
+
+	aux = scp->accessdata.acc_passl;
+	*skb_put(skb, 1) = aux;
+	if (aux > 0)
+	memcpy(skb_put(skb, aux), scp->accessdata.acc_pass, aux);
+
+	aux = scp->accessdata.acc_accl;
+	*skb_put(skb, 1) = aux;
+	if (aux > 0)
+	memcpy(skb_put(skb, aux), scp->accessdata.acc_acc, aux);
+
+	aux = scp->conndata_out.opt_optl;
+	*skb_put(skb, 1) = aux;
+	if (aux > 0)
+	memcpy(skb_put(skb,aux), scp->conndata_out.opt_data, aux);
+
+	sk->protinfo.dn.persist = dn_nsp_persist(sk);
+	sk->protinfo.dn.persist_fxn = dn_nsp_retrans_conninit;
+
+	cb->rt_flags = DN_RT_F_RQR;
+
+	dn_send_skb(skb);	
+}
+
diff -u --recursive --new-file v2.3.3/linux/net/decnet/dn_raw.c linux/net/decnet/dn_raw.c
--- v2.3.3/linux/net/decnet/dn_raw.c	Wed Dec 31 16:00:00 1969
+++ linux/net/decnet/dn_raw.c	Wed May 26 09:36:36 1999
@@ -0,0 +1,383 @@
+/*
+ * DECnet       An implementation of the DECnet protocol suite for the LINUX
+ *              operating system.  DECnet is implemented using the  BSD Socket
+ *              interface as the means of communication with the user level.
+ *
+ *              DECnet Raw Sockets Interface
+ *
+ * Author:      Steve Whitehouse <SteveW@ACM.org>
+ *
+ *
+ * Changes:
+ *           Steve Whitehouse - connect() function.
+ */
+
+#include <linux/config.h>
+#include <linux/net.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <net/sock.h>
+#include <net/dn.h>
+#include <net/dn_raw.h>
+
+static struct sock *dn_raw_nsp_sklist = NULL;
+static struct sock *dn_raw_routing_sklist = NULL;
+#ifdef CONFIG_DECNET_MOP
+static struct sock *dn_raw_mop_sklist = NULL;
+#endif /* CONFIG_DECNET_MOP */
+
+static void dn_raw_autobind(struct sock *sk)
+{
+
+	switch(sk->protocol) {
+		case DNPROTO_NSP:
+			sklist_insert_socket(&dn_raw_nsp_sklist, sk);
+			break;
+		case DNPROTO_ROU:
+			sklist_insert_socket(&dn_raw_routing_sklist, sk);
+			break;
+#ifdef CONFIG_DECNET_MOP
+		case DNPROTO_MOP:
+			sklist_insert_socket(&dn_raw_mop_sklist, sk);
+#endif /* CONFIG_DECNET_MOP */
+		default:
+			printk(KERN_DEBUG "dn_raw_autobind: Unknown protocol\n");
+			return;
+	}
+
+	sk->zapped = 0;
+}
+
+static int dn_raw_release(struct socket *sock, struct socket *peer)
+{
+	struct sock *sk = sock->sk;
+
+	if (sk == NULL)
+		return 0;
+
+	if (!sk->dead) sk->state_change(sk);
+
+	sk->dead = 1;
+	sk->socket = NULL;
+	sock->sk = NULL;
+
+	switch(sk->protocol) {
+		case DNPROTO_NSP:
+			sklist_destroy_socket(&dn_raw_nsp_sklist, sk);
+			break;
+		case DNPROTO_ROU:
+			sklist_destroy_socket(&dn_raw_routing_sklist, sk);
+			break;
+#ifdef CONFIG_DECNET_MOP
+		case DNPROTO_MOP:
+			sklist_destroy_socket(&dn_raw_mop_sklist, sk);
+			break;
+#endif /* CONFIG_DECNET_MOP */
+	}
+
+	return 0;
+}
+
+/*
+ * Bind does odd things with raw sockets. Its basically used to filter
+ * the incomming packets, but this differs with the different layers
+ * at which you extract packets.
+ *
+ * For Routing layer sockets, the object name is a host ordered unsigned
+ * short which is a mask for the 16 different types of possible routing
+ * packet. I'd like to also select by destination address of the packets
+ * but alas, this is rather too dificult to do at the moment.
+ */
+static int dn_raw_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
+{
+	struct sock *sk = sock->sk;
+	struct sockaddr_dn *addr = (struct sockaddr_dn *)uaddr;
+
+	if (addr_len != sizeof(struct sockaddr_dn))
+		return -EINVAL;
+
+	if (sk->zapped == 0)
+		return -EINVAL;
+
+	switch(sk->protocol) {
+		case DNPROTO_ROU:
+			if (addr->sdn_objnamel && (addr->sdn_objnamel != 2))
+				return -EINVAL;
+			/* Fall through here */
+		case DNPROTO_NSP:
+			if (addr->sdn_add.a_len && (addr->sdn_add.a_len != 2))
+				return -EINVAL;
+			break;
+		default:
+			return -EPROTONOSUPPORT;
+	}
+
+	if (addr->sdn_objnamel > (DN_MAXOBJL-1))
+		return -EINVAL;
+
+	if (addr->sdn_add.a_len > DN_MAXADDL)
+		return -EINVAL;
+
+
+	memcpy(&sk->protinfo.dn.addr, addr, sizeof(struct sockaddr_dn));
+
+	dn_raw_autobind(sk);
+
+	return 0;
+}
+
+/*
+ * This is to allow send() and write() to work. You set the destination address
+ * with this function.
+ */
+static int dn_raw_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags)
+{
+	struct sock *sk = sock->sk;
+	struct dn_scp *scp = &sk->protinfo.dn;
+	struct sockaddr_dn *saddr = (struct sockaddr_dn *)uaddr;
+
+	if (addr_len != sizeof(struct sockaddr_dn))
+		return -EINVAL;
+
+	if (saddr->sdn_family != AF_DECnet)
+		return -EINVAL;
+
+	if (saddr->sdn_objnamel > (DN_MAXOBJL-1))
+		return -EINVAL;
+
+	if (saddr->sdn_add.a_len > DN_MAXADDL)
+		return -EINVAL;
+
+	if (sk->zapped)
+		dn_raw_autobind(sk);
+
+	memcpy(&scp->peer, saddr, sizeof(struct sockaddr_dn));
+
+	return 0;
+}
+
+/*
+ * TBD.
+ */
+static int dn_raw_sendmsg(struct socket *sock, struct msghdr *hdr, int size,
+			struct scm_cookie *scm)
+{
+	struct sock *sk = sock->sk;
+
+	if (sk->zapped)
+		dn_raw_autobind(sk);
+
+	if (sk->protocol != DNPROTO_NSP)
+		return -EOPNOTSUPP;
+
+	return 0;
+}
+
+/*
+ * This works fine, execpt that it doesn't report the originating address
+ * or anything at the moment.
+ */
+static int dn_raw_recvmsg(struct socket *sock, struct msghdr *msg, int size,
+			int flags, struct scm_cookie *scm)
+{
+	struct sock *sk = sock->sk;
+	struct sk_buff *skb;
+	int err = 0;
+	int copied = 0;
+
+	if (sk->zapped)
+		dn_raw_autobind(sk);
+
+	if ((skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &err)) == NULL)
+		goto out;
+
+	copied = skb->len;
+
+	if (copied > size) {
+		copied = size;
+		msg->msg_flags |= MSG_TRUNC;
+	}
+
+	if ((err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied)) != 0) {
+		if (flags & MSG_PEEK)
+			atomic_dec(&skb->users);
+		else
+			skb_queue_head(&sk->receive_queue, skb);
+
+		goto out;
+	}
+
+	skb_free_datagram(sk, skb);
+
+out:
+	return copied ? copied : err;
+}
+
+struct proto_ops dn_raw_proto_ops = {
+	AF_DECnet,
+
+	sock_no_dup,
+	dn_raw_release,
+	dn_raw_bind,
+	dn_raw_connect,
+	sock_no_socketpair,
+	sock_no_accept,
+	sock_no_getname,
+	datagram_poll,
+	sock_no_ioctl,
+	sock_no_listen,
+	sock_no_shutdown,
+	sock_no_setsockopt,
+	sock_no_getsockopt,
+	sock_no_fcntl,
+	dn_raw_sendmsg,
+	dn_raw_recvmsg
+};
+
+#ifdef CONFIG_PROC_FS
+int dn_raw_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+	int len = 0;
+	off_t pos = 0;
+	off_t begin = 0;
+	struct sock *sk;
+
+	cli();	
+	for(sk = dn_raw_nsp_sklist; sk; sk = sk->next) {	
+		len += sprintf(buffer+len, "NSP\n");
+
+		pos = begin + len;
+
+		if (pos < offset) {
+			len = 0;
+			begin = pos;
+		}
+
+		if (pos > offset + length)
+			goto all_done;
+
+	}
+
+	for(sk = dn_raw_routing_sklist; sk; sk = sk->next) {
+		len += sprintf(buffer+len, "ROU\n");
+
+		pos = begin + len;
+
+		if (pos < offset) {
+			len = 0;
+			begin = pos;
+		}
+
+		if (pos > offset + length)
+			goto all_done;
+	}
+
+#ifdef CONFIG_DECNET_MOP
+	for(sk = dn_raw_mop_sklist; sk; sk = sk->next) {
+		len += sprintf(buffer+len, "MOP\n");
+
+		pos = begin + len;
+
+		if (pos < offset) {
+			len = 0;
+			begin = pos;
+		}
+
+		if (pos > offset + length)
+			goto all_done;
+	}
+#endif /* CONFIG_DECNET_MOP */
+
+all_done:
+	sti();
+
+	*start = buffer + (offset - begin);
+	len -= (offset - begin);
+
+	if (len > length) len = length;
+
+	return(len);
+}
+#endif /* CONFIG_PROC_FS */
+
+void dn_raw_rx_nsp(struct sk_buff *skb)
+{
+	struct sock *sk;
+	struct sk_buff *skb2;
+	unsigned long cpuflags;
+
+	save_flags(cpuflags);
+	cli();
+	for(sk = dn_raw_nsp_sklist; sk != NULL; sk = sk->next) {
+		if (skb->len > sock_rspace(sk))
+			continue;
+		if (sk->dead)
+			continue;
+		if ((skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL) {
+			skb_set_owner_r(skb2, sk);
+			__skb_queue_tail(&sk->receive_queue, skb2);
+			sk->data_ready(sk, skb->len);	
+		}
+	}
+	restore_flags(cpuflags);
+}
+
+void dn_raw_rx_routing(struct sk_buff *skb)
+{
+	struct sock *sk;
+	struct sk_buff *skb2;
+	struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+	unsigned long cpuflags;
+	unsigned short rt_flagmask;
+	unsigned short objnamel;
+	struct dn_scp *scp;
+
+	save_flags(cpuflags);
+	cli();
+	for(sk = dn_raw_routing_sklist; sk != NULL; sk = sk->next) {
+		if (skb->len > sock_rspace(sk))
+			continue;
+		if (sk->dead)
+			continue;
+		scp = &sk->protinfo.dn;
+
+		rt_flagmask = *(unsigned short *)scp->addr.sdn_objname;
+		objnamel = dn_ntohs(scp->addr.sdn_objnamel);
+
+		if ((objnamel == 2) && (!((1 << (cb->rt_flags & 0x0f)) & rt_flagmask)))
+			continue;
+
+		if ((skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL) {
+			skb_set_owner_r(skb2, sk);
+			__skb_queue_tail(&sk->receive_queue, skb2);
+			sk->data_ready(sk, skb->len);
+		}
+	}
+	restore_flags(cpuflags);
+}
+
+#ifdef CONFIG_DECNET_MOP
+void dn_raw_rx_mop(struct sk_buff *skb)
+{
+	struct sock *sk;
+	struct sk_buff *skb2;
+	unsigned long cpuflags;
+
+	save_flags(cpuflags);
+	cli();
+	for(sk = dn_raw_mop_sklist; sk != NULL; sk = sk->next) {
+		if (skb->len > sock_rspace(sk))
+			continue;
+		if (sk->dead)
+			continue;
+		if ((skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL) {
+			skb_set_owner_r(skb2, sk);
+			__skb_queue_tail(&sk->receive_queue, skb2);
+			sk->data_ready(sk, skb->len);
+		}
+	}
+	restore_flags(cpuflags);
+}
+#endif /* CONFIG_DECNET_MOP */
diff -u --recursive --new-file v2.3.3/linux/net/decnet/dn_route.c linux/net/decnet/dn_route.c
--- v2.3.3/linux/net/decnet/dn_route.c	Wed Dec 31 16:00:00 1969
+++ linux/net/decnet/dn_route.c	Wed May 26 09:36:36 1999
@@ -0,0 +1,1028 @@
+
+/*
+ * DECnet       An implementation of the DECnet protocol suite for the LINUX
+ *              operating system.  DECnet is implemented using the  BSD Socket
+ *              interface as the means of communication with the user level.
+ *
+ *              DECnet Routing Functions (Endnode and Router)
+ *
+ * Authors:     Steve Whitehouse <SteveW@ACM.org>
+ *              Eduardo Marcelo Serrat <emserrat@geocities.com>
+ *
+ * Changes:
+ *              Steve Whitehouse : Fixes to allow "intra-ethernet" and
+ *                                 "return-to-sender" bits on outgoing
+ *                                 packets.
+ *		Steve Whitehouse : Timeouts for cached routes.
+ *              Steve Whitehouse : Use dst cache for input routes too.
+ *              Steve Whitehouse : Fixed error values in dn_send_skb.
+ */
+
+/******************************************************************************
+    (c) 1995-1998 E.M. Serrat		emserrat@geocities.com
+    
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    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.
+*******************************************************************************/
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/inet.h>
+#include <linux/route.h>
+#include <net/sock.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+#include <linux/firewall.h>
+#include <linux/rtnetlink.h>
+#include <linux/string.h>
+#include <asm/errno.h>
+#include <net/neighbour.h>
+#include <net/dst.h>
+#include <net/dn.h>
+#include <net/dn_dev.h>
+#include <net/dn_nsp.h>
+#include <net/dn_route.h>
+#include <net/dn_neigh.h>
+#include <net/dn_fib.h>
+#include <net/dn_raw.h>
+
+extern struct neigh_table dn_neigh_table;
+
+#define DN_HASHBUCKETS 16
+
+static unsigned char dn_hiord_addr[6] = {0xAA,0x00,0x04,0x00,0x00,0x00};
+
+static int dn_dst_gc(void);
+static struct dst_entry *dn_dst_check(struct dst_entry *, __u32);
+static struct dst_entry *dn_dst_reroute(struct dst_entry *, struct sk_buff *skb);
+static struct dst_entry *dn_dst_negative_advice(struct dst_entry *);
+static void dn_dst_link_failure(struct sk_buff *);
+static int dn_route_input(struct sk_buff *);
+
+static struct dn_route *dn_route_cache[DN_HASHBUCKETS];
+static struct timer_list dn_route_timer = { NULL, NULL, 0, 0L, NULL };
+int decnet_dst_gc_interval = 2;
+
+static struct dst_ops dn_dst_ops = {
+	PF_DECnet,
+	__constant_htons(ETH_P_DNA_RT),
+	128,
+	dn_dst_gc,
+	dn_dst_check,
+	dn_dst_reroute,
+	NULL,
+	dn_dst_negative_advice,
+	dn_dst_link_failure,
+	ATOMIC_INIT(0)
+};
+
+static __inline__ unsigned dn_hash(unsigned short dest)
+{
+	unsigned short tmp = (dest&0xff) ^ (dest>>8);
+	return (tmp&0x0f) ^ (tmp>>4);
+}
+
+static void dn_dst_check_expire(unsigned long dummy)
+{
+	int i;
+	struct dn_route *rt, **rtp;
+	unsigned long now = jiffies;
+	unsigned long expire = 120 * HZ;
+
+	for(i = 0; i < DN_HASHBUCKETS; i++) {
+		rtp = &dn_route_cache[i];
+		for(;(rt=*rtp); rtp = &rt->u.rt_next) {
+			if (atomic_read(&rt->u.dst.use) ||
+					(now - rt->u.dst.lastuse) < expire)
+				continue;
+			*rtp = rt->u.rt_next;
+			rt->u.rt_next = NULL;
+			dst_free(&rt->u.dst);
+		}
+
+		if ((jiffies - now) > 0)
+			break;
+	}
+
+	dn_route_timer.expires = now + decnet_dst_gc_interval * HZ;
+	add_timer(&dn_route_timer);
+}
+
+static int dn_dst_gc(void)
+{
+	struct dn_route *rt, **rtp;
+	int i;
+	unsigned long now = jiffies;
+	unsigned long expire = 10 * HZ;
+
+	start_bh_atomic();
+	for(i = 0; i < DN_HASHBUCKETS; i++) {
+		rtp = &dn_route_cache[i];
+		for(; (rt=*rtp); rtp = &rt->u.rt_next) {
+			if (atomic_read(&rt->u.dst.use) ||
+					(now - rt->u.dst.lastuse) < expire)
+				continue;
+			*rtp = rt->u.rt_next;
+			rt->u.rt_next = NULL;
+			dst_free(&rt->u.dst);
+			break;
+		}
+	}
+	end_bh_atomic();
+
+	return 0;
+}
+
+static struct dst_entry *dn_dst_check(struct dst_entry *dst, __u32 cookie)
+{
+	dst_release(dst);
+	return NULL;
+}
+
+static struct dst_entry *dn_dst_reroute(struct dst_entry *dst,
+					struct sk_buff *skb)
+{
+	return NULL;
+}
+
+/*
+ * This is called through sendmsg() when you specify MSG_TRYHARD
+ * and there is already a route in cache.
+ */
+static struct dst_entry *dn_dst_negative_advice(struct dst_entry *dst)
+{
+	dst_release(dst);
+	return NULL;
+}
+
+static void dn_dst_link_failure(struct sk_buff *skb)
+{
+	return;
+}
+
+static void dn_insert_route(struct dn_route *rt)
+{
+	unsigned hash = dn_hash(rt->rt_daddr);
+	unsigned long now = jiffies;
+
+	start_bh_atomic();
+
+	rt->u.rt_next = dn_route_cache[hash];
+	dn_route_cache[hash] = rt;
+	
+	atomic_inc(&rt->u.dst.refcnt);
+	atomic_inc(&rt->u.dst.use);
+	rt->u.dst.lastuse = now;
+
+	end_bh_atomic();
+}
+
+#if defined(CONFIG_DECNET_MODULE)
+static void dn_run_flush(unsigned long dummy)
+{
+	int i;
+	struct dn_route *rt, *next;
+
+	for(i = 0; i < DN_HASHBUCKETS; i++) {
+		if ((rt = xchg(&dn_route_cache[i], NULL)) == NULL)
+			continue;
+
+		for(; rt; rt=next) {
+			next = rt->u.rt_next;
+			rt->u.rt_next = NULL;
+			dst_free((struct dst_entry *)rt);
+		}
+	}
+}
+#endif /* CONFIG_DECNET_MODULE */
+
+static int dn_route_rx_long(struct sk_buff *skb)
+{
+	struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+	unsigned char *ptr = skb->data;
+
+	if (skb->len < 21) /* 20 for long header, 1 for shortest nsp */
+		goto drop_it;
+
+	skb_pull(skb, 20);
+	skb->h.raw = skb->data;
+
+        /* Destination info */
+        ptr += 2;
+	cb->dst = dn_htons(dn_eth2dn(ptr));
+        if (memcmp(ptr, dn_hiord_addr, 4) != 0)
+                goto drop_it;
+        ptr += 6;
+
+
+        /* Source info */
+        ptr += 2;
+	cb->src = dn_htons(dn_eth2dn(ptr));
+        if (memcmp(ptr, dn_hiord_addr, 4) != 0)
+                goto drop_it;
+        ptr += 6;
+        /* Other junk */
+        ptr++;
+        cb->hops = *ptr++; /* Visit Count */
+
+	if (dn_route_input(skb) == 0) {
+
+#ifdef CONFIG_DECNET_FW
+		struct neighbour *neigh = skb->dst->neighbour;
+
+		switch(call_in_firewall(PF_DECnet, skb->dev, NULL, NULL, &skb)) {
+			case FW_REJECT:
+				neigh->ops->error_report(neigh, skb);
+				return 0;
+			case FW_BLOCK:
+			default:
+				goto drop_it;
+			case FW_ACCEPT:
+		}
+#endif /* CONFIG_DECNET_FW */
+
+		return skb->dst->input(skb);
+	}
+
+drop_it:
+	kfree_skb(skb);
+	return 0;
+}
+
+
+
+static int dn_route_rx_short(struct sk_buff *skb)
+{
+	struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+	unsigned char *ptr = skb->data;
+
+	if (skb->len < 6) /* 5 for short header + 1 for shortest nsp */
+		goto drop_it;
+
+	skb_pull(skb, 5);
+	skb->h.raw = skb->data;
+
+	cb->dst = *(dn_address *)ptr;
+        ptr += 2;
+        cb->src = *(dn_address *)ptr;
+        ptr += 2;
+        cb->hops = *ptr & 0x3f;
+
+        if (dn_route_input(skb) == 0) {
+
+#ifdef CONFIG_DECNET_FW
+		struct neighbour *neigh = skb->dst->neighbour;
+
+                switch(call_in_firewall(PF_DECnet, skb->dev, NULL, NULL, &skb)) {
+			case FW_REJECT:
+				neigh->ops->error_report(neigh, skb);
+				return 0;
+			case FW_BLOCK:
+			default:
+				goto drop_it;
+
+                        case FW_ACCEPT:
+		}
+#endif /* CONFIG_DECNET_FW */
+
+		return skb->dst->input(skb);
+        }
+
+drop_it:
+        kfree_skb(skb);
+        return 0;
+}
+
+int dn_route_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+{
+	struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+	unsigned char flags = 0;
+	int padlen = 0;
+	__u16 len = dn_ntohs(*(__u16 *)skb->data);
+	struct dn_dev *dn = (struct dn_dev *)dev->dn_ptr;
+
+	if (dn == NULL)
+		goto dump_it;
+
+	cb->stamp = jiffies;
+	cb->iif = dev->ifindex;
+
+	skb_pull(skb, 2);
+
+	if (len > skb->len)
+		goto dump_it;
+
+	skb_trim(skb, len);
+
+	flags = *skb->data;
+
+	/*
+	 * If we have padding, remove it.
+	 */
+	if (flags & DN_RT_F_PF) {
+		padlen = flags & ~DN_RT_F_PF;
+		skb_pull(skb, padlen);
+		flags = *skb->data;
+	}
+
+	skb->nh.raw = skb->data;
+
+	/*
+	 * Weed out future version DECnet
+	 */
+	if (flags & DN_RT_F_VER)
+		goto dump_it;
+
+	cb->rt_flags = flags;
+
+	if (dn->parms.setsrc)
+		dn->parms.setsrc(skb);
+
+	/* printk(KERN_DEBUG "dn_route_rcv: got 0x%02x from %s [%d %d %d]\n", (int)flags,
+				(dev) ? dev->name : "???", len, skb->len, padlen);  */
+
+#ifdef CONFIG_DECNET_RAW
+	dn_raw_rx_routing(skb);
+#endif /* CONFIG_DECNET_RAW */
+
+        if (flags & DN_RT_PKT_CNTL) {
+                switch(flags & DN_RT_CNTL_MSK) {
+        	        case DN_RT_PKT_INIT:
+				dn_dev_init_pkt(skb);
+				break;
+                	case DN_RT_PKT_VERI:
+				dn_dev_veri_pkt(skb);
+				break;
+		}
+
+		if (dn->parms.state != DN_DEV_S_RU)
+			goto dump_it;
+
+		switch(flags & DN_RT_CNTL_MSK) {
+                	case DN_RT_PKT_HELO:
+				dn_dev_hello(skb);
+				dn_neigh_pointopoint_hello(skb);
+				return 0;
+
+                	case DN_RT_PKT_L1RT:
+                	case DN_RT_PKT_L2RT:
+#ifdef CONFIG_DECNET_ROUTER
+				return dn_fib_rt_message(skb);
+#else
+                      		break;
+#endif /* CONFIG_DECNET_ROUTER */
+                	case DN_RT_PKT_ERTH:
+				dn_neigh_router_hello(skb);
+                        	return 0;
+
+                	case DN_RT_PKT_EEDH:
+				dn_neigh_endnode_hello(skb);
+                        	return 0;
+                }
+        } else {
+		if (dn->parms.state != DN_DEV_S_RU)
+			goto dump_it;
+
+		skb_pull(skb, 1); /* Pull flags */
+
+                switch(flags & DN_RT_PKT_MSK) {
+                	case DN_RT_PKT_LONG:
+                        	return dn_route_rx_long(skb);
+                	case DN_RT_PKT_SHORT:
+                        	return dn_route_rx_short(skb);
+		}
+        }
+
+dump_it:
+	if (net_ratelimit())
+		printk(KERN_DEBUG "dn_route_rcv: Dumping packet\n");
+	kfree_skb(skb);
+	return 0;
+}
+
+
+void dn_send_skb(struct sk_buff *skb)
+{
+	struct sock *sk = skb->sk;
+	struct dn_scp *scp = &sk->protinfo.dn;
+
+	if (sk == NULL) {
+		dev_queue_xmit(skb);
+		return ;
+	}
+
+	skb->h.raw = skb->data;
+
+	scp->stamp = jiffies; /* Record time packet was sent */
+
+	/* printk(KERN_DEBUG "dn_send_skb\n"); */
+
+	if (sk->dst_cache && sk->dst_cache->obsolete) {
+		dst_release(sk->dst_cache);
+		sk->dst_cache = NULL;
+	}
+
+	if (sk->dst_cache == NULL) {
+		if (dn_route_output(sk) != 0) {
+			kfree_skb(skb);
+			sk->err = EHOSTUNREACH;
+			if (!sk->dead)
+				sk->state_change(sk);
+			return;
+		}
+	}
+
+	skb->dst = dst_clone(sk->dst_cache);
+
+	sk->dst_cache->output(skb);
+}
+
+
+static int dn_output(struct sk_buff *skb)
+{
+	struct dst_entry *dst = skb->dst;
+	struct dn_route *rt = (struct dn_route *)dst;
+	struct device *dev = dst->dev;
+	struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+	int err = -EINVAL;
+
+	if (!dst->neighbour)
+		goto error;
+
+	skb->dev = dev;
+
+	cb->src = rt->rt_saddr;
+	cb->dst = rt->rt_daddr;
+
+	/*
+	 * Always set the Intra-Ethernet bit on all outgoing packets
+	 * originated on this node. Only valid flag from upper layers
+	 * is return-to-sender-requested. Set hop count to 0 too.
+	 */
+	cb->rt_flags &= ~DN_RT_F_RQR;
+	cb->rt_flags |= DN_RT_F_IE;
+	cb->hops = 0;
+
+	/*
+	 * Filter through the outgoing firewall
+	 */
+#ifdef CONFIG_DECNET_FW
+        switch(call_out_firewall(PF_DECnet, dst->dev, NULL, NULL, &skb)) {
+                case FW_REJECT:
+			err = -EPERM;
+			goto drop;
+                case FW_BLOCK:
+                default:
+                        err = 0;
+                        goto drop;
+                case FW_ACCEPT:
+        }
+#endif /* CONFIG_DECNET_FW */
+
+	return dst->neighbour->output(skb);
+
+error:
+	if (net_ratelimit())
+		printk(KERN_DEBUG "dn_output: This should not happen\n");
+
+#ifdef CONFIG_DECNET_FW
+drop:
+#endif
+	kfree_skb(skb);
+
+	return err;
+}
+
+#ifdef CONFIG_DECNET_ROUTER
+static int dn_l2_forward(struct sk_buff *skb)
+{
+	struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+	struct dst_entry *dst = skb->dst;
+	int err = -EINVAL;
+
+	if (!dst->neighbour)
+		goto error;
+
+	/*
+	 * Hop count exceeded.
+	 */
+	err = 0;
+	if (++cb->hops > 30)
+		goto drop;
+
+	/*
+	 * Forwarding firewall
+	 */
+#ifdef CONFIG_DECNET_FW
+	switch(call_fw_firewall(PF_DECnet, dst->dev, NULL, NULL, &skb)) {
+		case FW_REJECT:
+			dst->neighbour->ops->error_report(dst->neighbour, skb);
+			return -EPERM;
+		case FW_BLOCK:
+		default:
+			goto drop;
+		case FW_ACCEPT:
+	}
+#endif /* CONFIG_DECNET_FW */
+
+	skb->dev = dst->dev;
+
+	/*
+	 * If packet goes out same interface it came in on, then set
+	 * the Intra-Ethernet bit. This has no effect for short
+	 * packets, so we don't need to test for them here.
+	 */
+	if (cb->iif == dst->dev->ifindex)
+		cb->rt_flags |= DN_RT_F_IE;
+	else
+		cb->rt_flags &= ~DN_RT_F_IE;
+
+#ifdef CONFIG_DECNET_FW
+	switch(call_out_firewall(PF_DECnet, dst->dev, NULL, NULL, &skb)) {
+		case FW_REJECT:
+			dst->neighbour->ops->error_report(dst->neighbour, skb);
+			return -EPERM;
+		case FW_BLOCK:
+		default:
+			goto drop;
+		case FW_ACCEPT:
+	}
+#endif /* CONFIG_DECNET_FW */
+
+	return dst->neighbour->output(skb);
+
+
+error:
+	if (net_ratelimit())
+		printk(KERN_DEBUG "dn_forward: This should not happen\n");
+drop:
+	kfree_skb(skb);
+
+	return err;
+}
+
+/*
+ * Simple frontend to the l2 routing function which filters
+ * traffic not in our area when we should only do l1
+ * routing.
+ */
+static int dn_l1_forward(struct sk_buff *skb)
+{
+	struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+
+	if ((dn_ntohs(cb->dst ^ decnet_address) & 0xfc00) == 0)
+		return dn_l2_forward(skb);
+
+	kfree_skb(skb);
+	return 0;
+}
+#endif
+
+/*
+ * Drop packet. This is used for endnodes and for
+ * when we should not be forwarding packets from
+ * this dest.
+ */
+static int dn_blackhole(struct sk_buff *skb)
+{
+	kfree_skb(skb);
+	return 0;
+}
+
+/*
+ * Used to catch bugs. This should never normally get
+ * called.
+ */
+static int dn_rt_bug(struct sk_buff *skb)
+{
+	if (net_ratelimit()) {
+		struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+
+		printk(KERN_DEBUG "dn_rt_bug: skb from:%04x to:%04x\n",
+				cb->src, cb->dst);
+	}
+
+	kfree_skb(skb);
+
+	return -EINVAL;
+}
+
+static int dn_route_output_slow(struct sock *sk)
+{
+	struct dn_scp *scp = &sk->protinfo.dn;
+	dn_address dest = dn_saddr2dn(&scp->peer);
+	struct dn_route *rt = NULL;
+	struct device *dev  = decnet_default_device;
+	struct neighbour *neigh = NULL;
+	struct dn_dev *dn_db;
+	unsigned char addr[6];
+
+#ifdef CONFIG_DECNET_ROUTER
+	if ((decnet_node_type == DN_RT_INFO_L1RT) || (decnet_node_type == DN_RT_INFO_L2RT)) {
+#if 0
+		struct dn_fib_ent *fe = dn_fib_lookup(dest, decnet_address);
+
+		if (fe != NULL) {
+			neigh = neigh_clone(fe->neigh);
+			dn_fib_release(fe);
+			goto got_route;
+		}
+#endif
+	}
+#endif 
+
+	dn_dn2eth(addr, dest);
+
+	/* Look in On-Ethernet cache first */
+	if ((neigh = dn_neigh_lookup(&dn_neigh_table, &addr)) != NULL)
+		goto got_route;
+
+	if (dev == NULL)
+		return -EINVAL;
+
+	/* FIXME: We need to change this for routing nodes */
+	/* Send to default router if that doesn't work */
+	if ((neigh = neigh_lookup(&dn_neigh_table, &addr, dev)) != NULL)
+		goto got_route;
+
+	/* Send to default device (and hope for the best) if above fail */
+	if ((neigh = __neigh_lookup(&dn_neigh_table, &addr, dev, 1)) != NULL)
+		goto got_route;
+
+
+	return -EINVAL;
+
+got_route:
+
+	if ((rt = dst_alloc(sizeof(struct dn_route), &dn_dst_ops)) == NULL) {
+		neigh_release(neigh);
+		return -EINVAL;
+	}
+
+	dn_db = (struct dn_dev *)neigh->dev->dn_ptr;
+	
+	rt->rt_saddr = decnet_address;
+	rt->rt_daddr = dest;
+	rt->rt_oif = neigh->dev->ifindex;
+	rt->rt_iif = 0;
+
+	rt->u.dst.neighbour = neigh;
+	rt->u.dst.dev = neigh->dev;
+	rt->u.dst.lastuse = jiffies;
+	rt->u.dst.output = dn_output;
+	rt->u.dst.input  = dn_rt_bug;
+
+	if (dn_dev_islocal(neigh->dev, rt->rt_daddr))
+		rt->u.dst.input = dn_nsp_rx;
+
+	dn_insert_route(rt);
+	sk->dst_cache = &rt->u.dst;
+
+	return 0;
+}
+
+int dn_route_output(struct sock *sk)
+{
+	struct dn_scp *scp = &sk->protinfo.dn;
+	dn_address dest = dn_saddr2dn(&scp->peer);
+	unsigned hash = dn_hash(dest);
+	struct dn_route *rt = NULL;
+	unsigned short src = dn_saddr2dn(&scp->addr);
+
+	start_bh_atomic();
+	for(rt = dn_route_cache[hash]; rt; rt = rt->u.rt_next) {
+		if ((dest == rt->rt_daddr) &&
+				(src == rt->rt_saddr) &&
+				(rt->rt_iif == 0) &&
+				(rt->rt_oif != 0)) {
+			rt->u.dst.lastuse = jiffies;
+			atomic_inc(&rt->u.dst.use);
+			atomic_inc(&rt->u.dst.refcnt);
+			end_bh_atomic();
+			sk->dst_cache = &rt->u.dst;
+			return 0;
+		}
+	}
+	end_bh_atomic();
+
+	return dn_route_output_slow(sk);
+}
+
+static int dn_route_input_slow(struct sk_buff *skb)
+{
+	struct dn_route *rt = NULL;
+	struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+	struct device *dev = skb->dev;
+	struct neighbour *neigh = NULL;
+	unsigned char addr[6];
+
+	/*
+	 * In this case we've just received a packet from a source
+	 * outside ourselves pretending to come from us. We don't
+	 * allow it any further to prevent routing loops, spoofing and
+	 * other nasties. Loopback packets already have the dst attached
+	 * so this only affects packets which have originated elsewhere.
+	 */
+	if (dn_dev_islocal(dev, cb->src))
+		return 1;
+
+#ifdef CONFIG_DECNET_ROUTER
+	if ((decnet_node_type == DN_RT_INFO_L1RT) || (decnet_node_type == DN_RT_INFO_L2RT)) {
+#if 0
+		struct dn_fib_ent *fe = NULL;
+
+		fe = dn_fib_lookup(cb->src, cb->dst);
+
+		/* Try routing table first */
+		if (fe != NULL) {
+			neigh = neigh_clone(fe->neigh);
+			dn_fib_release(fe);
+			goto got_route;
+		}
+#endif
+	}
+#endif
+
+	dn_dn2eth(addr, cb->src);
+
+	/* Now see if we are directly connected */
+	if ((neigh = dn_neigh_lookup(&dn_neigh_table, &addr)) != NULL)
+		goto got_route;
+
+	if (dev == NULL)
+		return -EINVAL;
+
+	/* FIXME: Try the default router here .... */
+
+	if ((neigh = __neigh_lookup(&dn_neigh_table, &addr, dev, 1)) != NULL)
+		goto got_route;
+
+	return -EINVAL;
+
+got_route:
+
+	if ((rt = dst_alloc(sizeof(struct dn_route), &dn_dst_ops)) == NULL) {
+                neigh_release(neigh);
+                return -EINVAL;
+        }
+
+	rt->rt_saddr = cb->dst;
+	rt->rt_daddr = cb->src;
+	rt->rt_oif = 0;
+	rt->rt_iif = neigh->dev->ifindex;
+
+	rt->u.dst.neighbour = neigh;
+	rt->u.dst.dev = neigh->dev;
+	rt->u.dst.lastuse = jiffies;
+	rt->u.dst.output = dn_output;
+
+	switch(decnet_node_type) {
+		case DN_RT_INFO_ENDN:
+			rt->u.dst.input = dn_blackhole;
+			break;
+#ifdef CONFIG_DECNET_ROUTER
+		case DN_RT_INFO_L1RT:
+			rt->u.dst.input = dn_l1_forward;
+			break;
+		case DN_RT_INFO_L2RT:
+			rt->u.dst.input = dn_l2_forward;
+			break;
+#endif /* CONFIG_DECNET_ROUTER */
+		default:
+			rt->u.dst.input = dn_blackhole;
+			if (net_ratelimit())
+				printk(KERN_DEBUG "dn_route_input_slow: What kind of node are we?\n");
+	}
+
+	if (dn_dev_islocal(dev, cb->dst))
+		rt->u.dst.input = dn_nsp_rx;
+
+	dn_insert_route(rt);
+	skb->dst = (struct dst_entry *)rt;
+
+	return 0;
+}
+
+int dn_route_input(struct sk_buff *skb)
+{
+	struct dn_route *rt;
+	struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+	unsigned hash = dn_hash(cb->src);
+
+	if (skb->dst)
+		return 0;
+
+	for(rt = dn_route_cache[hash]; rt != NULL; rt = rt->u.rt_next) {
+		if ((rt->rt_saddr == cb->dst) &&
+				(rt->rt_daddr == cb->src) &&
+				(rt->rt_oif == 0) &&
+				(rt->rt_iif == cb->iif)) {
+			rt->u.dst.lastuse = jiffies;
+			atomic_inc(&rt->u.dst.use);
+			atomic_inc(&rt->u.dst.refcnt);
+			skb->dst = (struct dst_entry *)rt;
+			return 0;
+		}
+	}
+
+	return dn_route_input_slow(skb);
+}
+
+#ifdef CONFIG_DECNET_ROUTER
+#ifdef CONFIG_RTNETLINK
+static int dn_rt_fill_info(struct sk_buff *skb, u32 pid, u32 seq, int event, int nowait)
+{
+	struct dn_route *rt = (struct dn_route *)skb->dst;
+	struct rtmsg *r;
+	struct nlmsghdr *nlh;
+	unsigned char *b = skb->tail;
+
+	nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(*r));
+	r = NLMSG_DATA(nlh);
+	nlh->nlmsg_flags = nowait ? NLM_F_MULTI : 0;
+	r->rtm_family = AF_DECnet;
+	r->rtm_dst_len = 16;
+	r->rtm_src_len = 16;
+	r->rtm_tos = 0;
+	r->rtm_table = 0;
+	r->rtm_type = 0;
+	r->rtm_scope = RT_SCOPE_UNIVERSE;
+	r->rtm_protocol = RTPROT_UNSPEC;
+	RTA_PUT(skb, RTA_DST, 2, &rt->rt_daddr);
+	RTA_PUT(skb, RTA_SRC, 2, &rt->rt_saddr);
+	if (rt->u.dst.dev)
+		RTA_PUT(skb, RTA_OIF, sizeof(int), &rt->u.dst.dev->ifindex);
+	if (rt->u.dst.window)
+		RTA_PUT(skb, RTAX_WINDOW, sizeof(unsigned), &rt->u.dst.window);
+	if (rt->u.dst.rtt)
+		RTA_PUT(skb, RTAX_RTT, sizeof(unsigned), &rt->u.dst.rtt);
+
+	nlh->nlmsg_len = skb->tail - b;
+	return skb->len;
+
+nlmsg_failure:
+rtattr_failure:
+        skb_trim(skb, b - skb->data);
+        return -1;
+}
+
+int dn_fib_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, void *arg)
+{
+	struct rtattr **rta = arg;
+	/* struct rtmsg *rtm = NLMSG_DATA(nlh); */
+	struct dn_route *rt = NULL;
+	dn_address dst = 0;
+	dn_address src = 0;
+	int iif = 0;
+	int err;
+	struct sk_buff *skb;
+
+	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (skb == NULL)
+		return -ENOBUFS;
+	skb->mac.raw = skb->data;
+
+	if (rta[RTA_SRC-1])
+		memcpy(&src, RTA_DATA(rta[RTA_SRC-1]), 2);
+	if (rta[RTA_DST-1])
+		memcpy(&dst, RTA_DATA(rta[RTA_DST-1]), 2);
+	if (rta[RTA_IIF-1])
+		memcpy(&iif, RTA_DATA(rta[RTA_IIF-1]), sizeof(int));
+
+	if (iif) {
+		struct device *dev;
+		if ((dev = dev_get_by_index(iif)) == NULL)
+			return -ENODEV;
+		if (!dev->dn_ptr)
+			return -ENODEV;
+		skb->protocol = __constant_htons(ETH_P_DNA_RT);
+		skb->dev = dev;
+		start_bh_atomic();
+		err = dn_route_input(skb);
+		end_bh_atomic();
+		rt = (struct dn_route *)skb->dst;
+		if (!err && rt->u.dst.error)
+			err = rt->u.dst.error;
+	} else {
+		int oif = 0;
+		if (rta[RTA_OIF-1])
+			memcpy(&oif, RTA_DATA(rta[RTA_OIF-1]), sizeof(int));
+		err = -EOPNOTSUPP;
+	}
+	if (err) {
+		kfree_skb(skb);
+		return err;
+	}
+	skb->dst = &rt->u.dst;
+
+	NETLINK_CB(skb).dst_pid = NETLINK_CB(in_skb).pid;
+
+	err = dn_rt_fill_info(skb, NETLINK_CB(in_skb).pid, nlh->nlmsg_seq, RTM_NEWROUTE, 0);
+
+	if (err == 0)
+		return 0;
+	if (err < 0)
+		return -EMSGSIZE;
+
+	err = netlink_unicast(rtnl, skb, NETLINK_CB(in_skb).pid, MSG_DONTWAIT);
+
+	return err;
+}
+#endif /* CONFIG_RTNETLINK */
+#endif /* CONFIG_DECNET_ROUTER */
+
+#ifdef CONFIG_PROC_FS
+
+static int decnet_cache_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+        int len = 0;
+        off_t pos = 0;
+        off_t begin = 0;
+	struct dn_route *rt;
+	int i;
+	char buf1[DN_ASCBUF_LEN], buf2[DN_ASCBUF_LEN];
+
+	start_bh_atomic();
+	for(i = 0; i < DN_HASHBUCKETS; i++) {
+		rt = dn_route_cache[i];
+		for(; rt != NULL; rt = rt->u.rt_next) {
+			len += sprintf(buffer + len, "%-8s %-7s %-7s %04d %04d %04d\n",
+					rt->u.dst.dev ? rt->u.dst.dev->name : "*",
+					dn_addr2asc(dn_ntohs(rt->rt_daddr), buf1),
+					dn_addr2asc(dn_ntohs(rt->rt_saddr), buf2),
+					atomic_read(&rt->u.dst.use),
+					atomic_read(&rt->u.dst.refcnt),
+					(int)rt->u.dst.rtt
+					);
+
+	                pos = begin + len;
+	
+        	        if (pos < offset) {
+                	        len   = 0;
+                        	begin = pos;
+                	}
+              		if (pos > offset + length)
+                	        break;
+		}
+		if (pos > offset + length)
+			break;
+	}
+	end_bh_atomic();
+
+        *start = buffer + (offset - begin);
+        len   -= (offset - begin);
+
+        if (len > length) len = length;
+
+        return(len);
+} 
+
+static struct proc_dir_entry proc_net_decnet_cache = {
+        PROC_NET_DN_CACHE, 12, "decnet_cache",
+        S_IFREG | S_IRUGO, 1, 0, 0,
+        0, &proc_net_inode_operations,
+        decnet_cache_get_info
+};
+
+#endif /* CONFIG_PROC_FS */
+
+void __init dn_route_init(void)
+{
+	memset(dn_route_cache, 0, sizeof(struct dn_route *) * DN_HASHBUCKETS);
+
+	dn_route_timer.function = dn_dst_check_expire;
+	dn_route_timer.expires = jiffies + decnet_dst_gc_interval * HZ;
+	add_timer(&dn_route_timer);
+
+#ifdef CONFIG_PROC_FS
+	proc_net_register(&proc_net_decnet_cache);
+#endif /* CONFIG_PROC_FS */
+}
+
+#ifdef CONFIG_DECNET_MODULE
+void dn_route_cleanup(void)
+{
+	del_timer(&dn_route_timer);
+	dn_run_flush(0);
+#ifdef CONFIG_PROC_FS
+	proc_net_unregister(PROC_NET_DN_CACHE);
+#endif /* CONFIG_PROC_FS */
+}
+#endif /* CONFIG_DECNET_MODULE */
diff -u --recursive --new-file v2.3.3/linux/net/decnet/dn_timer.c linux/net/decnet/dn_timer.c
--- v2.3.3/linux/net/decnet/dn_timer.c	Wed Dec 31 16:00:00 1969
+++ linux/net/decnet/dn_timer.c	Sat May 29 11:09:54 1999
@@ -0,0 +1,164 @@
+/*
+ * DECnet       An implementation of the DECnet protocol suite for the LINUX
+ *              operating system.  DECnet is implemented using the  BSD Socket
+ *              interface as the means of communication with the user level.
+ *
+ *              DECnet Socket Timer Functions
+ *
+ * Author:      Steve Whitehouse <SteveW@ACM.org>
+ *
+ *
+ * Changes:
+ *       Steve Whitehouse      : Made keepalive timer part of the same
+ *                               timer idea.
+ *       Steve Whitehouse      : Added checks for sk->sock_readers
+ */
+#include <linux/config.h>
+#include <linux/net.h>
+#include <linux/socket.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/timer.h>
+#include <net/sock.h>
+#include <asm/spinlock.h>
+#include <asm/atomic.h>
+#include <net/dn.h>
+
+/*
+ * Fast timer is for delayed acks (200mS max)
+ * Slow timer is for everything else (n * 500mS)
+ */
+
+#define FAST_INTERVAL (HZ/5)
+#define SLOW_INTERVAL (HZ/2)
+
+static void dn_slow_timer(unsigned long arg);
+
+void dn_start_slow_timer(struct sock *sk)
+{
+	sk->timer.expires = jiffies + SLOW_INTERVAL;
+	sk->timer.function = dn_slow_timer;
+	sk->timer.data = (unsigned long)sk;
+
+	add_timer(&sk->timer);
+}
+
+void dn_stop_slow_timer(struct sock *sk)
+{
+	unsigned long cpuflags;
+
+	save_flags(cpuflags);
+	cli();
+	del_timer(&sk->timer);
+	restore_flags(cpuflags);
+}
+
+static void dn_slow_timer(unsigned long arg)
+{
+	struct sock *sk = (struct sock *)arg;
+	struct dn_scp *scp = &sk->protinfo.dn;
+
+	bh_lock_sock(sk);
+
+	if (sk->lock.users != 0) {
+		sk->timer.expires = jiffies + HZ / 10;
+		add_timer(&sk->timer);
+		goto out;
+	}
+
+	/*
+	 * The persist timer is the standard slow timer used for retransmits
+	 * in both connection establishment and disconnection as well as
+	 * in the RUN state. The different states are catered for by changing
+	 * the function pointer in the socket. Setting the timer to a value
+	 * of zero turns it off. We allow the persist_fxn to turn the
+	 * timer off in a permant way by returning non-zero, so that
+	 * timer based routines may remove sockets.
+	 */
+	if (scp->persist && scp->persist_fxn) {
+		if (scp->persist <= SLOW_INTERVAL) {
+			scp->persist = 0;
+
+			if (scp->persist_fxn(sk))
+				goto out;
+		} else {
+			scp->persist -= SLOW_INTERVAL;
+		}
+	}
+
+	/*
+	 * Check for keepalive timeout. After the other timer 'cos if
+	 * the previous timer caused a retransmit, we don't need to
+	 * do this. scp->stamp is the last time that we sent a packet.
+	 * The keepalive function sends a link service packet to the
+	 * other end. If it remains unacknowledged, the standard
+	 * socket timers will eventually shut the socket down. Each
+	 * time we do this, scp->stamp will be updated, thus
+	 * we won't try and send another until scp->keepalive has passed
+	 * since the last successful transmission.
+	 */
+	if (scp->keepalive && scp->keepalive_fxn && (scp->state == DN_RUN)) {
+		if ((jiffies - scp->stamp) >= scp->keepalive)
+			scp->keepalive_fxn(sk);
+	}
+
+	sk->timer.expires = jiffies + SLOW_INTERVAL;
+
+	add_timer(&sk->timer);
+out:
+	bh_unlock_sock(sk);
+}
+
+static void dn_fast_timer(unsigned long arg)
+{
+	struct sock *sk = (struct sock *)arg;
+	struct dn_scp *scp = &sk->protinfo.dn;
+
+	bh_lock_sock(sk);
+	if (sk->lock.users != 0) {
+		scp->delack_timer.expires = jiffies + HZ / 20;
+		add_timer(&scp->delack_timer);
+		goto out;
+	}
+
+	scp->delack_pending = 0;
+
+	if (scp->delack_fxn)
+		scp->delack_fxn(sk);
+out:
+	bh_unlock_sock(sk);
+}
+
+void dn_start_fast_timer(struct sock *sk)
+{
+	struct dn_scp *scp = &sk->protinfo.dn;
+	unsigned long cpuflags;
+
+	save_flags(cpuflags);
+	cli();
+	if (!scp->delack_pending) {
+		scp->delack_pending = 1;
+		scp->delack_timer.next =
+		scp->delack_timer.prev = NULL;
+		scp->delack_timer.expires = jiffies + FAST_INTERVAL;
+		scp->delack_timer.data = (unsigned long)sk;
+		scp->delack_timer.function = dn_fast_timer;
+		add_timer(&scp->delack_timer);
+	}
+	restore_flags(cpuflags);
+}
+
+void dn_stop_fast_timer(struct sock *sk)
+{
+	struct dn_scp *scp = &sk->protinfo.dn;
+	unsigned long cpuflags;
+
+	save_flags(cpuflags);
+	cli();
+	if (scp->delack_pending) {
+		scp->delack_pending = 0;
+		del_timer(&scp->delack_timer);
+	}
+	restore_flags(cpuflags);
+}
+
diff -u --recursive --new-file v2.3.3/linux/net/decnet/sysctl_net_decnet.c linux/net/decnet/sysctl_net_decnet.c
--- v2.3.3/linux/net/decnet/sysctl_net_decnet.c	Wed Dec 31 16:00:00 1969
+++ linux/net/decnet/sysctl_net_decnet.c	Wed May 26 09:36:36 1999
@@ -0,0 +1,473 @@
+/*
+ * DECnet       An implementation of the DECnet protocol suite for the LINUX
+ *              operating system.  DECnet is implemented using the  BSD Socket
+ *              interface as the means of communication with the user level.
+ *
+ *              DECnet sysctl support functions
+ *
+ * Author:      Steve Whitehouse <SteveW@ACM.org>
+ *
+ *
+ * Changes:
+ *
+ */
+#include <linux/config.h>
+#include <linux/mm.h>
+#include <linux/sysctl.h>
+#include <linux/fs.h>
+#include <linux/netdevice.h>
+#include <linux/string.h>
+#include <net/neighbour.h>
+#include <net/dst.h>
+
+#include <asm/uaccess.h>
+
+#include <net/dn.h>
+#include <net/dn_dev.h>
+#include <net/dn_route.h>
+
+
+int decnet_debug_level = 0;
+int decnet_time_wait = 30;
+int decnet_dn_count = 3;
+int decnet_di_count = 5;
+int decnet_dr_count = 5;
+extern int decnet_dst_gc_interval;
+static int min_decnet_time_wait[] = { 5 };
+static int max_decnet_time_wait[] = { 600 };
+static int min_state_count[] = { 1 };
+static int max_state_count[] = { NSP_MAXRXTSHIFT };
+static int min_decnet_dst_gc_interval[] = { 1 };
+static int max_decnet_dst_gc_interval[] = { 60 };
+static char node_name[7] = "???";
+
+static struct ctl_table_header *dn_table_header = NULL;
+
+/*
+ * ctype.h :-)
+ */
+#define ISNUM(x) (((x) >= '0') && ((x) <= '9'))
+#define ISLOWER(x) (((x) >= 'a') && ((x) <= 'z'))
+#define ISUPPER(x) (((x) >= 'A') && ((x) <= 'Z'))
+#define ISALPHA(x) (ISLOWER(x) || ISUPPER(x))
+#define INVALID_END_CHAR(x) (ISNUM(x) || ISALPHA(x))
+
+/*
+ * Simple routine to parse an ascii DECnet address
+ * into a network order address.
+ */
+static int parse_addr(dn_address *addr, char *str)
+{
+	dn_address area, node;
+
+	while(*str && !ISNUM(*str)) str++;
+
+	if (*str == 0)
+		return -1;
+
+	area = (*str++ - '0');
+	if (ISNUM(*str)) {
+		area *= 10;
+		area += (*str++ - '0');
+	}
+
+	if (*str++ != '.')
+		return -1;
+
+	if (!ISNUM(*str))
+		return -1;
+
+	node = *str++ - '0';
+	if (ISNUM(*str)) {
+		node *= 10;
+		node += (*str++ - '0');
+	}
+	if (ISNUM(*str)) {
+		node *= 10;
+		node += (*str++ - '0');
+	}
+	if (ISNUM(*str)) {
+		node *= 10;
+		node += (*str++ - '0');
+	}
+
+	if ((node > 1023) || (area > 63))
+		return -1;
+
+	if (INVALID_END_CHAR(*str))
+		return -1;
+
+	*addr = dn_htons((area << 10) | node);
+
+	return 0;
+}
+
+static char *node2str(int n)
+{
+	switch(n) {
+		case DN_RT_INFO_ENDN:
+			return "EndNode\n";
+		case DN_RT_INFO_L1RT:
+			return "Level 1 Router\n";
+		case DN_RT_INFO_L2RT:
+			return "Level 2 Router\n";
+	}
+
+	return "Unknown\n";
+}
+
+static int dn_node_type_strategy(ctl_table *table, int *name, int nlen,
+				void *oldval, size_t *oldlenp,
+				void *newval, size_t newlen,
+				void **context)
+{
+	int len;
+	int type;
+
+	if (oldval && oldlenp) {
+		if (get_user(len, oldlenp))
+			return -EFAULT;
+		if (len) {
+			if (len != sizeof(int))
+				return -EINVAL;
+			if (put_user(decnet_node_type, (int *)oldval))
+				return -EFAULT;
+		}
+	}
+
+	if (newval && newlen) {
+		if (newlen != sizeof(int))
+			return -EINVAL;
+
+		if (get_user(type, (int *)newval))
+			return -EFAULT;
+
+		switch(type) {
+			case DN_RT_INFO_ENDN: /* EndNode        */
+#ifdef CONFIG_DECNET_ROUTER
+			case DN_RT_INFO_L1RT: /* Level 1 Router */
+			case DN_RT_INFO_L2RT: /* Level 2 Router */
+#endif
+				break;
+			default:
+				return -EINVAL;
+		}
+
+		if (decnet_node_type != type) {
+			dn_dev_devices_off();
+			decnet_node_type = type;
+			dn_dev_devices_on();
+		}
+	}
+	return 0;
+}
+
+static int dn_node_type_handler(ctl_table *table, int write, 
+				struct file * filp,
+				void *buffer, size_t *lenp)
+{
+	char *s = node2str(decnet_node_type);
+	int len = strlen(s);
+
+	if (!*lenp || (filp->f_pos && !write)) {
+		*lenp = 0;
+		return 0;
+	}
+
+	if (write) {
+		char c = *(char *)buffer;
+		int type = 0;
+
+		switch(c) {
+			case 'e':
+			case 'E':
+			case '0':
+				type = DN_RT_INFO_ENDN;
+				break;
+#ifdef CONFIG_DECNET_ROUTER
+			case 'r':
+			case '1':
+				type = DN_RT_INFO_L1RT;
+				break;
+			case 'R':
+			case '2':
+				type = DN_RT_INFO_L2RT;
+				break;
+#endif /* CONFIG_DECNET_ROUTER */
+			default:
+				return -EINVAL;	
+		}
+
+		if (decnet_node_type != type) {
+			dn_dev_devices_off();
+			decnet_node_type = type;
+			dn_dev_devices_on();
+		}
+
+		filp->f_pos += 1;
+
+		return 0;
+	}
+
+	if (len > *lenp) len = *lenp;
+
+	if (copy_to_user(buffer, s, len))
+		return -EFAULT;
+
+	*lenp = len;
+	filp->f_pos += len;
+
+	return 0;
+}
+
+static int dn_node_address_strategy(ctl_table *table, int *name, int nlen,
+				void *oldval, size_t *oldlenp,
+				void *newval, size_t newlen,
+				void **context)
+{
+	int len;
+	dn_address addr;
+
+	if (oldval && oldlenp) {
+		if (get_user(len, oldlenp))
+			return -EFAULT;
+		if (len) {
+			if (len != sizeof(unsigned short))
+				return -EINVAL;
+			if (put_user(decnet_address, (unsigned short *)oldval))
+				return -EFAULT;
+		}
+	}
+	if (newval && newlen) {
+		if (newlen != sizeof(unsigned short))
+			return -EINVAL;
+		if (get_user(addr, (unsigned short *)newval))
+			return -EFAULT;
+
+		dn_dev_devices_off();
+
+		decnet_address = addr;
+		dn_dn2eth(decnet_ether_address, decnet_address);
+
+		dn_dev_devices_on();
+	}
+	return 0;
+}
+
+static int dn_node_address_handler(ctl_table *table, int write, 
+				struct file *filp,
+				void *buffer, size_t *lenp)
+{
+	char addr[DN_ASCBUF_LEN];
+	int len;
+	dn_address dnaddr;
+
+	if (!*lenp || (filp->f_pos && !write)) {
+		*lenp = 0;
+		return 0;
+	}
+
+	if (write) {
+		int len = (*lenp < DN_ASCBUF_LEN) ? *lenp : (DN_ASCBUF_LEN-1);
+
+		if (copy_from_user(addr, buffer, len))
+			return -EFAULT;
+
+		addr[len] = 0;
+
+		if (parse_addr(&dnaddr, buffer))
+			return -EINVAL;
+
+		dn_dev_devices_off();
+
+		decnet_address = dnaddr;
+		dn_dn2eth(decnet_ether_address, decnet_address);
+
+		dn_dev_devices_on();
+
+		filp->f_pos += len;
+
+		return 0;
+	}
+
+	dn_addr2asc(dn_ntohs(decnet_address), addr);
+	len = strlen(addr);
+	addr[len++] = '\n';
+
+	if (len > *lenp) len = *lenp;
+
+	if (copy_to_user(buffer, addr, len))
+		return -EFAULT;
+
+	*lenp = len;
+	filp->f_pos += len;
+
+	return 0;
+}
+
+
+static int dn_def_dev_strategy(ctl_table *table, int *name, int nlen,
+				void *oldval, size_t *oldlenp,
+				void *newval, size_t newlen,
+				void **context)
+{
+	size_t len;
+	struct device *dev = decnet_default_device;
+	char devname[17];
+	size_t namel;
+
+	devname[0] = 0;
+
+	if (oldval && oldlenp) {
+		if (get_user(len, oldlenp))
+			return -EFAULT;
+		if (len) {
+			if (dev)
+				strcpy(devname, dev->name);
+
+			namel = strlen(devname) + 1;
+			if (len > namel) len = namel;	
+
+			if (copy_to_user(oldval, devname, len))
+				return -EFAULT;
+
+			if (put_user(len, oldlenp))
+				return -EFAULT;
+		}
+	}
+
+	if (newval && newlen) {
+		if (newlen > 16)
+			return -E2BIG;
+
+		if (copy_from_user(devname, newval, newlen))
+			return -EFAULT;
+
+		devname[newlen] = 0;
+
+		if ((dev = dev_get(devname)) == NULL)
+			return -ENODEV;
+
+		if (dev->dn_ptr == NULL)
+			return -ENODEV;
+
+		decnet_default_device = dev;
+	}
+
+	return 0;
+}
+
+
+static int dn_def_dev_handler(ctl_table *table, int write, 
+				struct file * filp,
+				void *buffer, size_t *lenp)
+{
+	int len;
+	struct device *dev = decnet_default_device;
+	char devname[17];
+
+	if (!*lenp || (filp->f_pos && !write)) {
+		*lenp = 0;
+		return 0;
+	}
+
+	if (write) {
+
+		if (*lenp > 16)
+			return -E2BIG;
+
+		if (copy_from_user(devname, buffer, *lenp))
+			return -EFAULT;
+
+		devname[*lenp] = 0;
+
+		if ((dev = dev_get(devname)) == NULL)
+			return -ENODEV;
+
+		if (dev->dn_ptr == NULL)
+			return -ENODEV;
+
+		decnet_default_device = dev;
+		filp->f_pos += *lenp;
+
+		return 0;
+	}
+
+	if (dev == NULL) {
+		*lenp = 0;
+		return 0;
+	}
+
+	strcpy(devname, dev->name);
+	len = strlen(devname);
+	devname[len++] = '\n';
+
+	if (len > *lenp) len = *lenp;
+
+	if (copy_to_user(buffer, devname, len))
+		return -EFAULT;
+
+	*lenp = len;
+	filp->f_pos += len;
+
+	return 0;
+}
+
+static ctl_table dn_table[] = {
+	{NET_DECNET_NODE_TYPE, "node_type", NULL, 1, 0644, NULL,
+	dn_node_type_handler, dn_node_type_strategy, NULL,
+	NULL, NULL},
+	{NET_DECNET_NODE_ADDRESS, "node_address", NULL, 7, 0644, NULL,
+	dn_node_address_handler, dn_node_address_strategy, NULL,
+	NULL, NULL},
+	{NET_DECNET_NODE_NAME, "node_name", node_name, 7, 0644, NULL,
+	&proc_dostring, &sysctl_string, NULL, NULL, NULL},
+	{NET_DECNET_DEFAULT_DEVICE, "default_device", NULL, 16, 0644, NULL,
+	dn_def_dev_handler, dn_def_dev_strategy, NULL, NULL, NULL},
+	{NET_DECNET_TIME_WAIT, "time_wait", &decnet_time_wait,
+	sizeof(int), 0644,
+	NULL, &proc_dointvec_minmax, &sysctl_intvec, NULL,
+	&min_decnet_time_wait, &max_decnet_time_wait},
+	{NET_DECNET_DN_COUNT, "dn_count", &decnet_dn_count,
+	sizeof(int), 0644,
+	NULL, &proc_dointvec_minmax, &sysctl_intvec, NULL,
+	&min_state_count, &max_state_count},
+	{NET_DECNET_DI_COUNT, "di_count", &decnet_di_count,
+	sizeof(int), 0644,
+	NULL, &proc_dointvec_minmax, &sysctl_intvec, NULL,
+	&min_state_count, &max_state_count},
+	{NET_DECNET_DR_COUNT, "dr_count", &decnet_dr_count,
+	sizeof(int), 0644,
+	NULL, &proc_dointvec_minmax, &sysctl_intvec, NULL,
+	&min_state_count, &max_state_count},
+	{NET_DECNET_DST_GC_INTERVAL, "dst_gc_interval", &decnet_dst_gc_interval,
+	sizeof(int), 0644,
+	NULL, &proc_dointvec_minmax, &sysctl_intvec, NULL,
+	&min_decnet_dst_gc_interval, &max_decnet_dst_gc_interval},
+	{NET_DECNET_DEBUG_LEVEL, "debug", &decnet_debug_level, 
+	sizeof(int), 0644, 
+	NULL, &proc_dointvec, &sysctl_intvec, NULL,
+	NULL, NULL},
+	{0}
+};
+
+static ctl_table dn_dir_table[] = {
+	{NET_DECNET, "decnet", NULL, 0, 0555, dn_table},
+	{0}
+};
+
+static ctl_table dn_root_table[] = {
+	{CTL_NET, "net", NULL, 0, 0555, dn_dir_table},
+	{0}
+};
+
+void dn_register_sysctl(void)
+{
+	dn_table_header = register_sysctl_table(dn_root_table, 1);
+}
+
+void dn_unregister_sysctl(void)
+{
+	unregister_sysctl_table(dn_table_header);
+}
+
diff -u --recursive --new-file v2.3.3/linux/net/ethernet/eth.c linux/net/ethernet/eth.c
--- v2.3.3/linux/net/ethernet/eth.c	Mon Jan 12 15:28:25 1998
+++ linux/net/ethernet/eth.c	Tue May 25 13:06:34 1999
@@ -63,10 +63,12 @@
 
 __initfunc(void eth_setup(char *str, int *ints))
 {
-	struct device *d = dev_base;
+	struct device *d;
 
 	if (!str || !*str)
 		return;
+	read_lock_bh(&dev_base_lock);
+	d = dev_base;
 	while (d) 
 	{
 		if (!strcmp(str,d->name)) 
@@ -83,6 +85,7 @@
 		}
 		d=d->next;
 	}
+	read_unlock_bh(&dev_base_lock);
 }
 
 
diff -u --recursive --new-file v2.3.3/linux/net/ipv4/af_inet.c linux/net/ipv4/af_inet.c
--- v2.3.3/linux/net/ipv4/af_inet.c	Fri May 14 18:55:30 1999
+++ linux/net/ipv4/af_inet.c	Mon May 31 22:07:43 1999
@@ -5,7 +5,7 @@
  *
  *		PF_INET protocol family socket handler.
  *
- * Version:	$Id: af_inet.c,v 1.88 1999/05/12 11:24:27 davem Exp $
+ * Version:	$Id: af_inet.c,v 1.90 1999/05/29 04:30:38 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -147,22 +147,17 @@
 	struct sk_buff *skb;
 
 	/* First the read buffer. */
-	while((skb = skb_dequeue(&sk->receive_queue)) != NULL) {
-		/* This will take care of closing sockets that were
-		 * listening and didn't accept everything.
-		 */
-		if (skb->sk != NULL && skb->sk != sk)
-			skb->sk->prot->close(skb->sk, 0);
+	while((skb = skb_dequeue(&sk->receive_queue)) != NULL)
 		kfree_skb(skb);
-	}
 
 	/* Next, the error queue. */
 	while((skb = skb_dequeue(&sk->error_queue)) != NULL)
 		kfree_skb(skb);
 
-  	/* Now the backlog. */
-  	while((skb=skb_dequeue(&sk->back_log)) != NULL)
-		kfree_skb(skb);
+	/* It is _impossible_ for the backlog to contain anything
+	 * when we get here.  All user references to this socket
+	 * have gone away, only the net layer knows can touch it.
+	 */
 }
 
 static __inline__ void kill_sk_now(struct sock *sk)
@@ -195,14 +190,19 @@
 
 	sk->destroy = 1;
 	sk->ack_backlog = 0;
-	release_sock(sk);
+	bh_unlock_sock(sk);
 	net_reset_timer(sk, TIME_DESTROY, SOCK_DESTROY_TIME);
 }
 
+/* Callers must hold the BH spinlock.
+ *
+ * At this point, there should be no process reference to this
+ * socket, and thus no user references at all.  Therefore we
+ * can assume the socket waitqueue is inactive and nobody will
+ * try to jump onto it.
+ */
 void destroy_sock(struct sock *sk)
 {
-	lock_sock(sk);			/* just to be safe. */
-
   	/* Now we can no longer get new packets or once the
   	 * timers are killed, send them.
   	 */
@@ -213,12 +213,6 @@
 
 	kill_sk_queues(sk);
 
-	/* Now if it has a half accepted/ closed socket. */
-	if (sk->pair) {
-		sk->pair->prot->close(sk->pair, 0);
-		sk->pair = NULL;
-  	}
-
 	/* Now if everything is gone we can free the socket
 	 * structure, otherwise we need to keep it around until
 	 * everything is gone.
@@ -284,6 +278,14 @@
 	return 0;
 }
 
+/* Listening INET sockets never sleep to wait for memory, so
+ * it is completely silly to wake them up on queue space
+ * available events.  So we hook them up to this dummy callback.
+ */
+static void inet_listen_write_space(struct sock *sk)
+{
+}
+
 /*
  *	Move a socket into listening state.
  */
@@ -310,6 +312,7 @@
 		dst_release(xchg(&sk->dst_cache, NULL));
 		sk->prot->rehash(sk);
 		add_to_prot_sklist(sk);
+		sk->write_space = inet_listen_write_space;
 	}
 	sk->socket->flags |= SO_ACCEPTCON;
 	return(0);
@@ -684,14 +687,8 @@
 	if (sk1->prot->accept == NULL)
 		goto do_err;
 
-	/* Restore the state if we have been interrupted, and then returned. */
-	if (sk1->pair != NULL) {
-		sk2 = sk1->pair;
-		sk1->pair = NULL;
-	} else {
-		if((sk2 = sk1->prot->accept(sk1,flags)) == NULL)
-			goto do_sk1_err;
-	}
+	if((sk2 = sk1->prot->accept(sk1,flags)) == NULL)
+		goto do_sk1_err;
 
 	/*
 	 *	We've been passed an extra socket.
diff -u --recursive --new-file v2.3.3/linux/net/ipv4/devinet.c linux/net/ipv4/devinet.c
--- v2.3.3/linux/net/ipv4/devinet.c	Mon May 10 09:55:25 1999
+++ linux/net/ipv4/devinet.c	Wed May 26 18:14:37 1999
@@ -1,7 +1,7 @@
 /*
  *	NET3	IP device support routines.
  *
- *	Version: $Id: devinet.c,v 1.28 1999/05/08 20:00:16 davem Exp $
+ *	Version: $Id: devinet.c,v 1.29 1999/05/27 00:37:57 davem Exp $
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -661,15 +661,19 @@
 	   in this case. It is importnat that lo is the first interface
 	   in dev_base list.
 	 */
+	read_lock_bh(&dev_base_lock);
 	for (dev=dev_base; dev; dev=dev->next) {
 		if ((in_dev=dev->ip_ptr) == NULL)
 			continue;
 
 		for_primary_ifa(in_dev) {
-			if (ifa->ifa_scope <= scope)
+			if (ifa->ifa_scope <= scope) {
+				read_unlock_bh(&dev_base_lock);
 				return ifa->ifa_local;
+			}
 		} endfor_ifa(in_dev);
 	}
+	read_unlock_bh(&dev_base_lock);
 
 	return 0;
 }
@@ -790,6 +794,7 @@
 
 	s_idx = cb->args[0];
 	s_ip_idx = ip_idx = cb->args[1];
+	read_lock_bh(&dev_base_lock);
 	for (dev=dev_base, idx=0; dev; dev = dev->next, idx++) {
 		if (idx < s_idx)
 			continue;
@@ -807,6 +812,7 @@
 		}
 	}
 done:
+	read_unlock_bh(&dev_base_lock);
 	cb->args[0] = idx;
 	cb->args[1] = ip_idx;
 
@@ -881,11 +887,13 @@
 	ipv4_devconf.accept_redirects = !on;
 	ipv4_devconf_dflt.forwarding = on;
 
+	read_lock_bh(&dev_base_lock);
 	for (dev = dev_base; dev; dev = dev->next) {
 		struct in_device *in_dev = dev->ip_ptr;
 		if (in_dev)
 			in_dev->cnf.forwarding = on;
 	}
+	read_unlock_bh(&dev_base_lock);
 
 	rt_cache_flush(0);
 
diff -u --recursive --new-file v2.3.3/linux/net/ipv4/fib_hash.c linux/net/ipv4/fib_hash.c
--- v2.3.3/linux/net/ipv4/fib_hash.c	Thu Mar 25 09:23:34 1999
+++ linux/net/ipv4/fib_hash.c	Wed May 26 18:14:37 1999
@@ -5,7 +5,7 @@
  *
  *		IPv4 FIB: lookup engine and maintenance routines.
  *
- * Version:	$Id: fib_hash.c,v 1.8 1999/03/25 10:04:17 davem Exp $
+ * Version:	$Id: fib_hash.c,v 1.9 1999/05/27 00:38:05 davem Exp $
  *
  * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
@@ -145,13 +145,16 @@
 	return a.datum <= b.datum;
 }
 
+static rwlock_t fib_hash_lock = RW_LOCK_UNLOCKED;
+
 #define FZ_MAX_DIVISOR 1024
 
 #ifdef CONFIG_IP_ROUTE_LARGE_TABLES
 
+/* The fib hash lock must be held when this is called. */
 static __inline__ void fn_rebuild_zone(struct fn_zone *fz,
-					struct fib_node **old_ht,
-					int old_divisor)
+				       struct fib_node **old_ht,
+				       int old_divisor)
 {
 	int i;
 	struct fib_node *f, **fp, *next;
@@ -198,13 +201,13 @@
 
 	if (ht)	{
 		memset(ht, 0, new_divisor*sizeof(struct fib_node*));
-		start_bh_atomic();
+		write_lock_bh(&fib_hash_lock);
 		old_ht = fz->fz_hash;
 		fz->fz_hash = ht;
 		fz->fz_hashmask = new_hashmask;
 		fz->fz_divisor = new_divisor;
 		fn_rebuild_zone(fz, old_ht, old_divisor);
-		end_bh_atomic();
+		write_unlock_bh(&fib_hash_lock);
 		kfree(old_ht);
 	}
 }
@@ -243,6 +246,7 @@
 	fz->fz_mask = inet_make_mask(z);
 
 	/* Find the first not empty zone with more specific mask */
+	write_lock_bh(&fib_hash_lock);
 	for (i=z+1; i<=32; i++)
 		if (table->fn_zones[i])
 			break;
@@ -255,6 +259,7 @@
 		table->fn_zones[i]->fz_next = fz;
 	}
 	table->fn_zones[z] = fz;
+	write_unlock_bh(&fib_hash_lock);
 	return fz;
 }
 
@@ -265,6 +270,7 @@
 	struct fn_zone *fz;
 	struct fn_hash *t = (struct fn_hash*)tb->tb_data;
 
+	read_lock_bh(&fib_hash_lock);
 	for (fz = t->fn_zone_list; fz; fz = fz->fz_next) {
 		struct fib_node *f;
 		fn_key_t k = fz_key(key->dst, fz);
@@ -293,13 +299,16 @@
 				res->scope = f->fn_scope;
 				res->prefixlen = fz->fz_order;
 				res->prefix = &fz_prefix(f->fn_key, fz);
-				return 0;
+				goto out;
 			}
 			if (err < 0)
-				return err;
+				goto out;
 		}
 	}
-	return 1;
+	err = 1;
+out:
+	read_unlock_bh(&fib_hash_lock);
+	return err;
 }
 
 static int fn_hash_last_dflt=-1;
@@ -344,6 +353,7 @@
 	last_resort = NULL;
 	order = -1;
 
+	read_lock_bh(&fib_hash_lock);
 	for (f = fz->fz_hash[0]; f; f = f->fn_next) {
 		struct fib_info *next_fi = FIB_INFO(f);
 
@@ -364,7 +374,7 @@
 		} else if (!fib_detect_death(fi, order, &last_resort, &last_idx)) {
 			res->fi = fi;
 			fn_hash_last_dflt = order;
-			return;
+			goto out;
 		}
 		fi = next_fi;
 		order++;
@@ -372,18 +382,20 @@
 
 	if (order<=0 || fi==NULL) {
 		fn_hash_last_dflt = -1;
-		return;
+		goto out;
 	}
 
 	if (!fib_detect_death(fi, order, &last_resort, &last_idx)) {
 		res->fi = fi;
 		fn_hash_last_dflt = order;
-		return;
+		goto out;
 	}
 
 	if (last_idx >= 0)
 		res->fi = last_resort;
 	fn_hash_last_dflt = last_idx;
+out:
+	read_unlock_bh(&fib_hash_lock);
 }
 
 #define FIB_SCAN(f, fp) \
@@ -457,6 +469,8 @@
 
 	fp = fz_chain_p(key, fz);
 
+	write_lock_bh(&fib_hash_lock);
+
 	/*
 	 * Scan list to find the first route with the same destination
 	 */
@@ -567,7 +581,7 @@
 		f = *del_fp;
 		/* Unlink replaced node */
 		*del_fp = f->fn_next;
-		synchronize_bh();
+		write_unlock_bh(&fib_hash_lock);
 
 		if (!(f->fn_state&FN_S_ZOMBIE))
 			rtmsg_fib(RTM_DELROUTE, f, z, tb->tb_id, n, req);
@@ -576,12 +590,14 @@
 		fn_free_node(f);
 		fz->fz_nent--;
 	} else {
+		write_unlock_bh(&fib_hash_lock);
 		rt_cache_flush(-1);
 	}
 	rtmsg_fib(RTM_NEWROUTE, new_f, z, tb->tb_id, n, req);
 	return 0;
 
 out:
+	write_unlock_bh(&fib_hash_lock);
 	fib_release_info(fi);
 	return err;
 }
@@ -619,11 +635,15 @@
 
 	fp = fz_chain_p(key, fz);
 
+	write_lock_bh(&fib_hash_lock);
+
 	FIB_SCAN(f, fp) {
 		if (fn_key_eq(f->fn_key, key))
 			break;
-		if (fn_key_leq(key, f->fn_key))
+		if (fn_key_leq(key, f->fn_key)) {
+			write_unlock_bh(&fib_hash_lock);
 			return -ESRCH;
+		}
 	}
 #ifdef CONFIG_IP_ROUTE_TOS
 	FIB_SCAN_KEY(f, fp, key) {
@@ -637,9 +657,10 @@
 	FIB_SCAN_TOS(f, fp, key, tos) {
 		struct fib_info * fi = FIB_INFO(f);
 
-		if (f->fn_state&FN_S_ZOMBIE)
+		if (f->fn_state&FN_S_ZOMBIE) {
+			write_unlock_bh(&fib_hash_lock);
 			return -ESRCH;
-
+		}
 		matched++;
 
 		if (del_fp == NULL &&
@@ -656,13 +677,14 @@
 
 		if (matched != 1) {
 			*del_fp = f->fn_next;
-			synchronize_bh();
+			write_unlock_bh(&fib_hash_lock);
 
 			if (f->fn_state&FN_S_ACCESSED)
 				rt_cache_flush(-1);
 			fn_free_node(f);
 			fz->fz_nent--;
 		} else {
+			write_unlock_bh(&fib_hash_lock);
 			f->fn_state |= FN_S_ZOMBIE;
 			if (f->fn_state&FN_S_ACCESSED) {
 				f->fn_state &= ~FN_S_ACCESSED;
@@ -674,6 +696,7 @@
 
 		return 0;
 	}
+	write_unlock_bh(&fib_hash_lock);
 	return -ESRCH;
 }
 
@@ -688,7 +711,6 @@
 
 		if (fi && ((f->fn_state&FN_S_ZOMBIE) || (fi->fib_flags&RTNH_F_DEAD))) {
 			*fp = f->fn_next;
-			synchronize_bh();
 
 			fn_free_node(f);
 			found++;
@@ -706,6 +728,7 @@
 	int found = 0;
 
 	fib_hash_zombies = 0;
+	write_lock_bh(&fib_hash_lock);
 	for (fz = table->fn_zone_list; fz; fz = fz->fz_next) {
 		int i;
 		int tmp = 0;
@@ -714,6 +737,7 @@
 		fz->fz_nent -= tmp;
 		found += tmp;
 	}
+	write_unlock_bh(&fib_hash_lock);
 	return found;
 }
 
@@ -727,6 +751,7 @@
 	int pos = 0;
 	int n = 0;
 
+	read_lock_bh(&fib_hash_lock);
 	for (fz=table->fn_zone_list; fz; fz = fz->fz_next) {
 		int i;
 		struct fib_node *f;
@@ -752,10 +777,12 @@
 						  FZ_MASK(fz), buffer);
 				buffer += 128;
 				if (++n >= count)
-					return n;
+					goto out;
 			}
 		}
 	}
+out:
+	read_unlock_bh(&fib_hash_lock);
   	return n;
 }
 #endif
@@ -818,15 +845,18 @@
 	struct fn_hash *table = (struct fn_hash*)tb->tb_data;
 
 	s_m = cb->args[1];
+	read_lock_bh(&fib_hash_lock);
 	for (fz = table->fn_zone_list, m=0; fz; fz = fz->fz_next, m++) {
 		if (m < s_m) continue;
 		if (m > s_m)
 			memset(&cb->args[2], 0, sizeof(cb->args) - 2*sizeof(cb->args[0]));
 		if (fn_hash_dump_zone(skb, cb, tb, fz) < 0) {
 			cb->args[1] = m;
+			read_unlock_bh(&fib_hash_lock);
 			return -1;
 		}
 	}
+	read_unlock_bh(&fib_hash_lock);
 	cb->args[1] = m;
 	return skb->len;
 }
diff -u --recursive --new-file v2.3.3/linux/net/ipv4/fib_rules.c linux/net/ipv4/fib_rules.c
--- v2.3.3/linux/net/ipv4/fib_rules.c	Thu Mar 25 09:23:34 1999
+++ linux/net/ipv4/fib_rules.c	Wed May 26 18:14:37 1999
@@ -5,7 +5,7 @@
  *
  *		IPv4 Forwarding Information Base: policy rules.
  *
- * Version:	$Id: fib_rules.c,v 1.9 1999/03/25 10:04:23 davem Exp $
+ * Version:	$Id: fib_rules.c,v 1.10 1999/05/27 00:38:03 davem Exp $
  *
  * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
@@ -79,13 +79,16 @@
 static struct fib_rule local_rule = { &main_rule, 0, RT_TABLE_LOCAL, RTN_UNICAST, };
 
 static struct fib_rule *fib_rules = &local_rule;
+static rwlock_t fib_rules_lock = RW_LOCK_UNLOCKED;
 
 int inet_rtm_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
 {
 	struct rtattr **rta = arg;
 	struct rtmsg *rtm = NLMSG_DATA(nlh);
 	struct fib_rule *r, **rp;
+	int err = -ESRCH;
 
+	write_lock_bh(&fib_rules_lock);
 	for (rp=&fib_rules; (r=*rp) != NULL; rp=&r->r_next) {
 		if ((!rta[RTA_SRC-1] || memcmp(RTA_DATA(rta[RTA_SRC-1]), &r->r_src, 4) == 0) &&
 		    rtm->rtm_src_len == r->r_src_len &&
@@ -99,18 +102,19 @@
 		    (!rta[RTA_PRIORITY-1] || memcmp(RTA_DATA(rta[RTA_PRIORITY-1]), &r->r_preference, 4) == 0) &&
 		    (!rta[RTA_IIF-1] || strcmp(RTA_DATA(rta[RTA_IIF-1]), r->r_ifname) == 0) &&
 		    (!rtm->rtm_table || (r && rtm->rtm_table == r->r_table))) {
+			err = -EPERM;
 			if (r == &local_rule)
-				return -EPERM;
+				break;
 
 			*rp = r->r_next;
-			synchronize_bh();
-
 			if (r != &default_rule && r != &main_rule)
 				kfree(r);
-			return 0;
+			err = 0;
+			break;
 		}
 	}
-	return -ESRCH;
+	write_unlock_bh(&fib_rules_lock);
+	return err;
 }
 
 /* Allocate new unique table id */
@@ -188,6 +192,7 @@
 		memcpy(&new_r->r_tclassid, RTA_DATA(rta[RTA_FLOW-1]), 4);
 #endif
 
+	write_lock_bh(&fib_rules_lock);
 	rp = &fib_rules;
 	if (!new_r->r_preference) {
 		r = fib_rules;
@@ -206,6 +211,7 @@
 
 	new_r->r_next = r;
 	*rp = new_r;
+	write_unlock_bh(&fib_rules_lock);
 	return 0;
 }
 
@@ -249,20 +255,24 @@
 {
 	struct fib_rule *r;
 
+	write_lock_bh(&fib_rules_lock);
 	for (r=fib_rules; r; r=r->r_next) {
 		if (r->r_ifindex == dev->ifindex)
 			r->r_ifindex = -1;
 	}
+	write_unlock_bh(&fib_rules_lock);
 }
 
 static void fib_rules_attach(struct device *dev)
 {
 	struct fib_rule *r;
 
+	write_lock_bh(&fib_rules_lock);
 	for (r=fib_rules; r; r=r->r_next) {
 		if (r->r_ifindex == -1 && strcmp(dev->name, r->r_ifname) == 0)
 			r->r_ifindex = dev->ifindex;
 	}
+	write_unlock_bh(&fib_rules_lock);
 }
 
 int fib_lookup(const struct rt_key *key, struct fib_result *res)
@@ -275,6 +285,7 @@
 	u32 saddr = key->src;
 
 FRprintk("Lookup: %08x <- %08x ", key->dst, key->src);
+	read_lock_bh(&fib_rules_lock);
 	for (r = fib_rules; r; r=r->r_next) {
 		if (((saddr^r->r_src) & r->r_srcmask) ||
 		    ((daddr^r->r_dst) & r->r_dstmask) ||
@@ -294,11 +305,14 @@
 			policy = r;
 			break;
 		case RTN_UNREACHABLE:
+			read_unlock_bh(&fib_rules_lock);
 			return -ENETUNREACH;
 		default:
 		case RTN_BLACKHOLE:
+			read_unlock_bh(&fib_rules_lock);
 			return -EINVAL;
 		case RTN_PROHIBIT:
+			read_unlock_bh(&fib_rules_lock);
 			return -EACCES;
 		}
 
@@ -308,12 +322,16 @@
 		if (err == 0) {
 FRprintk("ok\n");
 			res->r = policy;
+			read_unlock_bh(&fib_rules_lock);
 			return 0;
 		}
-		if (err < 0 && err != -EAGAIN)
+		if (err < 0 && err != -EAGAIN) {
+			read_unlock_bh(&fib_rules_lock);
 			return err;
+		}
 	}
 FRprintk("FAILURE\n");
+	read_unlock_bh(&fib_rules_lock);
 	return -ENETUNREACH;
 }
 
@@ -400,12 +418,14 @@
 	int s_idx = cb->args[0];
 	struct fib_rule *r;
 
+	read_lock_bh(&fib_rules_lock);
 	for (r=fib_rules, idx=0; r; r = r->r_next, idx++) {
 		if (idx < s_idx)
 			continue;
 		if (inet_fill_rule(skb, r, cb) < 0)
 			break;
 	}
+	read_unlock_bh(&fib_rules_lock);
 	cb->args[0] = idx;
 
 	return skb->len;
diff -u --recursive --new-file v2.3.3/linux/net/ipv4/icmp.c linux/net/ipv4/icmp.c
--- v2.3.3/linux/net/ipv4/icmp.c	Fri May 14 18:55:30 1999
+++ linux/net/ipv4/icmp.c	Mon May 31 22:07:43 1999
@@ -3,7 +3,7 @@
  *	
  *		Alan Cox, <alan@cymru.net>
  *
- *	Version: $Id: icmp.c,v 1.53 1999/05/12 11:24:32 davem Exp $
+ *	Version: $Id: icmp.c,v 1.54 1999/05/30 01:16:22 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *	modify it under the terms of the GNU General Public License
@@ -1152,6 +1152,11 @@
 	if ((err=ops->create(icmp_socket, IPPROTO_ICMP))<0)
 		panic("Failed to create the ICMP control socket.\n");
 	icmp_socket->sk->allocation=GFP_ATOMIC;
-	icmp_socket->sk->num = 256;		/* Don't receive any data */
 	icmp_socket->sk->ip_ttl = MAXTTL;
+
+	/* Unhash it so that IP input processing does not even
+	 * see it, we do not wish this socket to see incoming
+	 * packets.
+	 */
+	icmp_socket->sk->prot->unhash(icmp_socket->sk);
 }
diff -u --recursive --new-file v2.3.3/linux/net/ipv4/igmp.c linux/net/ipv4/igmp.c
--- v2.3.3/linux/net/ipv4/igmp.c	Thu Mar 25 09:23:34 1999
+++ linux/net/ipv4/igmp.c	Wed May 26 18:14:37 1999
@@ -8,7 +8,7 @@
  *	the older version didn't come out right using gcc 2.5.8, the newer one
  *	seems to fall out with gcc 2.6.2.
  *
- *	Version: $Id: igmp.c,v 1.30 1999/03/25 10:04:10 davem Exp $
+ *	Version: $Id: igmp.c,v 1.31 1999/05/27 00:37:59 davem Exp $
  *
  *	Authors:
  *		Alan Cox <Alan.Cox@linux.org>
@@ -656,8 +656,8 @@
 	
 	len=sprintf(buffer,"Idx\tDevice    : Count Querier\tGroup    Users Timer\tReporter\n");  
 	
-	for(dev = dev_base; dev; dev = dev->next)
-	{
+	read_lock_bh(&dev_base_lock);
+	for(dev = dev_base; dev; dev = dev->next) {
 		struct in_device *in_dev = dev->ip_ptr;
 		char   *querier = "NONE";
 		
@@ -686,6 +686,8 @@
 		}
 	}
 done:
+	read_unlock_bh(&dev_base_lock);
+
 	*start=buffer+(offset-begin);
 	len-=(offset-begin);
 	if(len>length)
diff -u --recursive --new-file v2.3.3/linux/net/ipv4/ip_fragment.c linux/net/ipv4/ip_fragment.c
--- v2.3.3/linux/net/ipv4/ip_fragment.c	Sun Mar 21 07:22:00 1999
+++ linux/net/ipv4/ip_fragment.c	Wed May 26 18:14:37 1999
@@ -5,7 +5,7 @@
  *
  *		The IP fragmentation functionality.
  *		
- * Version:	$Id: ip_fragment.c,v 1.40 1999/03/20 23:58:34 davem Exp $
+ * Version:	$Id: ip_fragment.c,v 1.41 1999/05/27 00:38:07 davem Exp $
  *
  * Authors:	Fred N. van Kempen <waltje@uWalt.NL.Mugnet.ORG>
  *		Alan Cox <Alan.Cox@linux.org>
@@ -71,7 +71,8 @@
 
 #define IPQ_HASHSZ	64
 
-struct ipq *ipq_hash[IPQ_HASHSZ];
+static struct ipq *ipq_hash[IPQ_HASHSZ];
+static spinlock_t ipfrag_lock = SPIN_LOCK_UNLOCKED;
 
 #define ipqhashfn(id, saddr, daddr, prot) \
 	((((id) >> 1) ^ (saddr) ^ (daddr) ^ (prot)) & (IPQ_HASHSZ - 1))
@@ -141,7 +142,9 @@
 	unsigned int hash = ipqhashfn(id, saddr, daddr, protocol);
 	struct ipq *qp;
 
-	/* Always, we are in a BH context, so no locking.  -DaveM */
+	/* We are always in BH context, and protected by the
+	 * ipfrag lock.
+	 */
 	for(qp = ipq_hash[hash]; qp; qp = qp->next) {
 		if(qp->iph->id == id		&&
 		   qp->iph->saddr == saddr	&&
@@ -158,8 +161,9 @@
  * because we completed, reassembled and processed it, or because
  * it timed out.
  *
- * This is called _only_ from BH contexts, on packet reception
- * processing and from frag queue expiration timers.  -DaveM
+ * This is called _only_ from BH contexts with the ipfrag lock held,
+ * on packet reception processing and from frag queue expiration
+ * timers.  -DaveM
  */
 static void ip_free(struct ipq *qp)
 {
@@ -197,6 +201,7 @@
 {
 	struct ipq *qp = (struct ipq *) arg;
 
+	spin_lock(&ipfrag_lock);
   	if(!qp->fragments)
         {	
 #ifdef IP_EXPIRE_DEBUG
@@ -213,10 +218,13 @@
 out:
 	/* Nuke the fragment queue. */
 	ip_free(qp);
+	spin_lock(&ipfrag_lock);
 }
 
 /* Memory limiting on fragments.  Evictor trashes the oldest 
  * fragment queue until we are back under the low threshold.
+ *
+ * We are always called in BH with the ipfrag lock held.
  */
 static void ip_evictor(void)
 {
@@ -229,9 +237,6 @@
 		struct ipq *qp;
 		if (atomic_read(&ip_frag_mem) <= sysctl_ipfrag_low_thresh)
 			return;
-		/* We are in a BH context, so these queue
-		 * accesses are safe.  -DaveM
-		 */
 		qp = ipq_hash[i];
 		if (qp) {
 			/* find the oldest queue for this hash bucket */
@@ -283,7 +288,7 @@
 	/* Add this entry to the queue. */
 	hash = ipqhashfn(iph->id, iph->saddr, iph->daddr, iph->protocol);
 
-	/* We are in a BH context, no locking necessary.  -DaveM */
+	/* In a BH context and ipfrag lock is held.  -DaveM */
 	if((qp->next = ipq_hash[hash]) != NULL)
 		qp->next->pprev = &qp->next;
 	ipq_hash[hash] = qp;
@@ -421,6 +426,8 @@
 	
 	ip_statistics.IpReasmReqds++;
 
+	spin_lock(&ipfrag_lock);
+
 	/* Start by cleaning up the memory. */
 	if (atomic_read(&ip_frag_mem) > sysctl_ipfrag_high_thresh)
 		ip_evictor();
@@ -565,6 +572,7 @@
 out_freequeue:
 		ip_free(qp);
 out_skb:
+		spin_unlock(&ipfrag_lock);
 		return skb;
 	}
 
@@ -574,6 +582,7 @@
 out_timer:
 	mod_timer(&qp->timer, jiffies + sysctl_ipfrag_time); /* ~ 30 seconds */
 out:
+	spin_unlock(&ipfrag_lock);
 	return NULL;
 
 	/*
diff -u --recursive --new-file v2.3.3/linux/net/ipv4/ip_input.c linux/net/ipv4/ip_input.c
--- v2.3.3/linux/net/ipv4/ip_input.c	Thu Apr 22 19:45:19 1999
+++ linux/net/ipv4/ip_input.c	Mon May 31 22:07:43 1999
@@ -5,7 +5,7 @@
  *
  *		The Internet Protocol (IP) module.
  *
- * Version:	$Id: ip_input.c,v 1.37 1999/04/22 10:38:36 davem Exp $
+ * Version:	$Id: ip_input.c,v 1.39 1999/05/30 01:16:25 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -154,44 +154,11 @@
 
 struct ip_mib ip_statistics={2,IPDEFTTL,};	/* Forwarding=No, Default TTL=64 */
 
-
-/*
- *	Handle the issuing of an ioctl() request
- *	for the ip device. This is scheduled to
- *	disappear
- */
-
-int ip_ioctl(struct sock *sk, int cmd, unsigned long arg)
-{
-	switch(cmd)
-	{
-		default:
-			return(-EINVAL);
-	}
-}
-
-
 #if defined(CONFIG_IP_TRANSPARENT_PROXY) && !defined(CONFIG_IP_ALWAYS_DEFRAG)
 #define CONFIG_IP_ALWAYS_DEFRAG 1
 #endif
 
 /*
- *	0 - deliver
- *	1 - block
- */
-static __inline__ int icmp_filter(struct sock *sk, struct sk_buff *skb)
-{
-	int    type;
-
-	type = skb->h.icmph->type;
-	if (type < 32)
-		return test_bit(type, &sk->tp_pinfo.tp_raw4.filter);
-
-	/* Do not block unknown ICMP types */
-	return 0;
-}
-
-/*
  *	Process Router Attention IP option
  */ 
 int ip_call_ra_chain(struct sk_buff *skb)
@@ -224,16 +191,37 @@
 	return 0;
 }
 
+/* Handle this out of line, it is rare. */
+static int ip_run_ipprot(struct sk_buff *skb, struct iphdr *iph,
+			 struct inet_protocol *ipprot, int force_copy)
+{
+	int ret = 0;
+
+	do {
+		if (ipprot->protocol == iph->protocol) {
+			struct sk_buff *skb2 = skb;
+			if (ipprot->copy || force_copy)
+				skb2 = skb_clone(skb, GFP_ATOMIC);
+			if(skb2 != NULL) {
+				ret = 1;
+				ipprot->handler(skb2,
+						ntohs(iph->tot_len) - (iph->ihl * 4));
+			}
+		}
+		ipprot = (struct inet_protocol *) ipprot->next;
+	} while(ipprot != NULL);
+
+	return ret;
+}
+
+extern struct sock *raw_v4_input(struct sk_buff *, struct iphdr *, int);
+
 /*
  * 	Deliver IP Packets to the higher protocol layers.
  */ 
 int ip_local_deliver(struct sk_buff *skb)
 {
 	struct iphdr *iph = skb->nh.iph;
-	struct inet_protocol *ipprot;
-	struct sock *raw_sk=NULL;
-	unsigned char hash;
-	int flag = 0;
 
 #ifndef CONFIG_IP_ALWAYS_DEFRAG
 	/*
@@ -249,34 +237,29 @@
 #endif
 
 #ifdef CONFIG_IP_MASQUERADE
-	/*
-	 * Do we need to de-masquerade this packet?
-	 */
-        {
-		int ret;
-		/*
-		 *	Some masq modules can re-inject packets if
-		 *	bad configured.
+	/* Do we need to de-masquerade this packet? */
+	if((IPCB(skb)->flags&IPSKB_MASQUERADED)) {
+		/* Some masq modules can re-inject packets if
+		 * bad configured.
 		 */
+		printk(KERN_DEBUG "ip_input(): demasq recursion detected. "
+		       "Check masq modules configuration\n");
+		kfree_skb(skb);
+		return 0;
+	} else {
+		int ret = ip_fw_demasquerade(&skb);
 
-		if((IPCB(skb)->flags&IPSKB_MASQUERADED)) {
-			printk(KERN_DEBUG "ip_input(): demasq recursion detected. Check masq modules configuration\n");
-			kfree_skb(skb);
-			return 0;
-		}
-
-		ret = ip_fw_demasquerade(&skb);
 		if (ret < 0) {
 			kfree_skb(skb);
 			return 0;
 		}
-
 		if (ret) {
-			iph=skb->nh.iph;
+			iph = skb->nh.iph;
 			IPCB(skb)->flags |= IPSKB_MASQUERADED;
 			dst_release(skb->dst);
 			skb->dst = NULL;
-			if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, skb->dev)) {
+			if (ip_route_input(skb, iph->daddr, iph->saddr,
+					   iph->tos, skb->dev)) {
 				kfree_skb(skb);
 				return 0;
 			}
@@ -285,112 +268,50 @@
         }
 #endif
 
-        /*
-	 *	Point into the IP datagram, just past the header.
-	 */
-
+        /* Point into the IP datagram, just past the header. */
         skb->h.raw = skb->nh.raw + iph->ihl*4;
 
-	/*
-	 *	Deliver to raw sockets. This is fun as to avoid copies we want to make no 
-	 *	surplus copies.
-	 *
-	 *	RFC 1122: SHOULD pass TOS value up to the transport layer.
-	 *	-> It does. And not only TOS, but all IP header.
-	 */
- 
-	/* Note: See raw.c and net/raw.h, RAWV4_HTABLE_SIZE==MAX_INET_PROTOS */
-	hash = iph->protocol & (MAX_INET_PROTOS - 1);
-
-	/* 
-	 *	If there maybe a raw socket we must check - if not we don't care less 
-	 */
-		 
-	if((raw_sk = raw_v4_htable[hash]) != NULL) {
-		struct sock *sknext = NULL;
-		struct sk_buff *skb1;
-		raw_sk = raw_v4_lookup(raw_sk, iph->protocol, iph->saddr, iph->daddr, skb->dev->ifindex);
-		if(raw_sk) {	/* Any raw sockets */
-			do {
-				/* Find the next */
-				sknext = raw_v4_lookup(raw_sk->next, iph->protocol,
-						       iph->saddr, iph->daddr, skb->dev->ifindex);
-				if (iph->protocol != IPPROTO_ICMP || !icmp_filter(raw_sk, skb)) {
-					if (sknext == NULL)
-						break;
-					skb1 = skb_clone(skb, GFP_ATOMIC);
-					if(skb1)
-					{
-						raw_rcv(raw_sk, skb1);
-					}
-				}
-				raw_sk = sknext;
-			} while(raw_sk!=NULL);
-				
-			/*	Here either raw_sk is the last raw socket, or NULL if
-			 *	none.  We deliver to the last raw socket AFTER the
-			 *	protocol checks as it avoids a surplus copy.
-			 */
-		}
-	}
-	
-	/*
-	 *	skb->h.raw now points at the protocol beyond the IP header.
-	 */
-	
-	for (ipprot = (struct inet_protocol *)inet_protos[hash];ipprot != NULL;ipprot=(struct inet_protocol *)ipprot->next)
 	{
-		struct sk_buff *skb2;
-	
-		if (ipprot->protocol != iph->protocol)
-			continue;
-		/*
-		 * 	See if we need to make a copy of it.  This will
-		 * 	only be set if more than one protocol wants it.
-		 * 	and then not for the last one. If there is a pending
-		 *	raw delivery wait for that
-		 */
-	
-		if (ipprot->copy || raw_sk)
-		{
-			skb2 = skb_clone(skb, GFP_ATOMIC);
-			if(skb2==NULL)
-				continue;
-		}
-		else
-		{
-			skb2 = skb;
-		}
-		flag = 1;
+		/* Note: See raw.c and net/raw.h, RAWV4_HTABLE_SIZE==MAX_INET_PROTOS */
+		int hash = iph->protocol & (MAX_INET_PROTOS - 1);
+		struct sock *raw_sk = raw_v4_htable[hash];
+		struct inet_protocol *ipprot;
+		int flag;
 
-		/*
-		 *	Pass on the datagram to each protocol that wants it,
-		 *	based on the datagram protocol.  We should really
-		 *	check the protocol handler's return values here...
+		/* If there maybe a raw socket we must check - if not we
+		 * don't care less
 		 */
+		if(raw_sk != NULL)
+			raw_sk = raw_v4_input(skb, iph, hash);
 
-		ipprot->handler(skb2, ntohs(iph->tot_len) - (iph->ihl * 4));
-	}
-
-	/*
-	 *	All protocols checked.
-	 *	If this packet was a broadcast, we may *not* reply to it, since that
-	 *	causes (proven, grin) ARP storms and a leakage of memory (i.e. all
-	 *	ICMP reply messages get queued up for transmission...)
-	 */
-
-	if(raw_sk!=NULL)	/* Shift to last raw user */
-	{
-		raw_rcv(raw_sk, skb);
+		ipprot = (struct inet_protocol *) inet_protos[hash];
+		flag = 0;
+		if(ipprot != NULL) {
+			if(raw_sk == NULL &&
+			   ipprot->next == NULL &&
+			   ipprot->protocol == iph->protocol) {
+				/* Fast path... */
+				return ipprot->handler(skb, (ntohs(iph->tot_len) -
+							     (iph->ihl * 4)));
+			} else {
+				flag = ip_run_ipprot(skb, iph, ipprot, (raw_sk != NULL));
+			}
+		}	
 
-	}
-	else if (!flag)		/* Free and report errors */
-	{
-		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0);	
-		kfree_skb(skb);
+		/* All protocols checked.
+		 * If this packet was a broadcast, we may *not* reply to it, since that
+		 * causes (proven, grin) ARP storms and a leakage of memory (i.e. all
+		 * ICMP reply messages get queued up for transmission...)
+		 */
+		if(raw_sk != NULL) {	/* Shift to last raw user */
+			raw_rcv(raw_sk, skb);
+		} else if (!flag) {		/* Free and report errors */
+			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0);	
+			kfree_skb(skb);
+		}
 	}
 
-	return(0);
+	return 0;
 }
 
 /*
@@ -404,9 +325,8 @@
 	u16 rport;
 #endif /* CONFIG_FIREWALL */
 
-	/*
-	 * 	When the interface is in promisc. mode, drop all the crap
-	 * 	that it receives, do not try to analyse it.
+	/* When the interface is in promisc. mode, drop all the crap
+	 * that it receives, do not try to analyse it.
 	 */
 	if (skb->pkt_type == PACKET_OTHERHOST)
 		goto drop;
@@ -430,17 +350,15 @@
 		goto inhdr_error; 
 
 	{
-	__u32 len = ntohs(iph->tot_len); 
-	if (skb->len < len)
-		goto inhdr_error; 
-
-	/*
-	 *	Our transport medium may have padded the buffer out. Now we know it
-	 *	is IP we can trim to the true length of the frame.
-	 *	Note this now means skb->len holds ntohs(iph->tot_len).
-	 */
-
-	__skb_trim(skb, len);
+		__u32 len = ntohs(iph->tot_len); 
+		if (skb->len < len)
+			goto inhdr_error; 
+
+		/* Our transport medium may have padded the buffer out. Now we know it
+		 * is IP we can trim to the true length of the frame.
+		 * Note this now means skb->len holds ntohs(iph->tot_len).
+		 */
+		__skb_trim(skb, len);
 	}
 	
 #ifdef CONFIG_IP_ALWAYS_DEFRAG
diff -u --recursive --new-file v2.3.3/linux/net/ipv4/ip_masq_mfw.c linux/net/ipv4/ip_masq_mfw.c
--- v2.3.3/linux/net/ipv4/ip_masq_mfw.c	Fri May 14 18:55:31 1999
+++ linux/net/ipv4/ip_masq_mfw.c	Wed May 26 18:14:37 1999
@@ -3,7 +3,7 @@
  *
  *	Does (reverse-masq) forwarding based on skb->fwmark value
  *
- *	$Id: ip_masq_mfw.c,v 1.3 1999/01/26 05:33:47 davem Exp $
+ *	$Id: ip_masq_mfw.c,v 1.4 1999/05/13 23:25:07 davem Exp $
  *
  * Author:	Juan Jose Ciarlante   <jjciarla@raiz.uncu.edu.ar>
  *		  based on Steven Clarke's portfw
diff -u --recursive --new-file v2.3.3/linux/net/ipv4/ipconfig.c linux/net/ipv4/ipconfig.c
--- v2.3.3/linux/net/ipv4/ipconfig.c	Sun Mar 28 09:07:47 1999
+++ linux/net/ipv4/ipconfig.c	Wed May 26 18:14:37 1999
@@ -1,5 +1,5 @@
 /*
- *  $Id: ipconfig.c,v 1.20 1999/03/28 10:18:28 davem Exp $
+ *  $Id: ipconfig.c,v 1.21 1999/05/27 00:38:01 davem Exp $
  *
  *  Automatic Configuration of IP -- use BOOTP or RARP or user-supplied
  *  information to configure own IP address and routes.
@@ -112,7 +112,8 @@
 	unsigned short oflags;
 
 	last = &ic_first_dev;
-	for (dev = dev_base; dev; dev = dev->next)
+	read_lock_bh(&dev_base_lock);
+	for (dev = dev_base; dev; dev = dev->next) {
 		if (user_dev_name[0] ? !strcmp(dev->name, user_dev_name) :
 		    (!(dev->flags & IFF_LOOPBACK) &&
 		     (dev->flags & (IFF_POINTOPOINT|IFF_BROADCAST)) &&
@@ -142,6 +143,9 @@
 			ic_proto_have_if |= able;
 			DBG(("IP-Config: Opened %s (able=%d)\n", dev->name, able));
 		}
+	}
+	read_unlock_bh(&dev_base_lock);
+
 	*last = NULL;
 
 	if (!ic_first_dev) {
diff -u --recursive --new-file v2.3.3/linux/net/ipv4/proc.c linux/net/ipv4/proc.c
--- v2.3.3/linux/net/ipv4/proc.c	Mon Feb  8 09:26:29 1999
+++ linux/net/ipv4/proc.c	Wed May 26 18:14:37 1999
@@ -7,7 +7,7 @@
  *		PROC file system.  It is mainly used for debugging and
  *		statistics.
  *
- * Version:	$Id: proc.c,v 1.34 1999/02/08 11:20:34 davem Exp $
+ * Version:	$Id: proc.c,v 1.35 1999/05/27 00:37:38 davem Exp $
  *
  * Authors:	Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
  *		Gerald J. Heim, <heim@peanuts.informatik.uni-tuebingen.de>
@@ -114,10 +114,8 @@
 			slot_dist = tcp_tw_death_row_slot - slot_dist;
 		timer_expires	= jiffies + (slot_dist * TCP_TWKILL_PERIOD);
 	} else {
-		timer_active1 = del_timer(&tp->retransmit_timer);
-		timer_active2 = del_timer(&sp->timer);
-		if (!timer_active1) tp->retransmit_timer.expires=0;
-		if (!timer_active2) sp->timer.expires=0;
+		timer_active1 = tp->retransmit_timer.prev != NULL;
+		timer_active2 = sp->timer.prev != NULL;
 		timer_active	= 0;
 		timer_expires	= (unsigned) -1;
 	}
@@ -147,9 +145,6 @@
 		(!tw_bucket && sp->socket) ? sp->socket->inode->i_uid : 0,
 		(!tw_bucket && timer_active) ? sp->timeout : 0,
 		(!tw_bucket && sp->socket) ? sp->socket->inode->i_ino : 0);
-	
-	if (timer_active1) add_timer(&tp->retransmit_timer);
-	if (timer_active2) add_timer(&sp->timer);	
 }
 
 /*
@@ -176,7 +171,7 @@
 			       "  sl  local_address rem_address   st tx_queue "
 			       "rx_queue tr tm->when retrnsmt   uid  timeout inode");
 	pos = 128;
-	SOCKHASH_LOCK(); 
+	SOCKHASH_LOCK_READ();
 	sp = pro->sklist_next;
 	while(sp != (struct sock *)pro) {
 		if (format == 0 && sp->state == TCP_LISTEN) {
@@ -211,7 +206,7 @@
 		i++;
 	}
 out: 
-	SOCKHASH_UNLOCK();
+	SOCKHASH_UNLOCK_READ();
 	
 	begin = len - (pos - offset);
 	*start = buffer + begin;
diff -u --recursive --new-file v2.3.3/linux/net/ipv4/raw.c linux/net/ipv4/raw.c
--- v2.3.3/linux/net/ipv4/raw.c	Mon Nov 16 10:39:27 1998
+++ linux/net/ipv4/raw.c	Mon May 31 22:07:43 1999
@@ -5,7 +5,7 @@
  *
  *		RAW - implementation of IP "raw" sockets.
  *
- * Version:	$Id: raw.c,v 1.39 1998/11/08 11:17:04 davem Exp $
+ * Version:	$Id: raw.c,v 1.41 1999/05/30 01:16:19 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -75,11 +75,11 @@
 
 	num &= (RAWV4_HTABLE_SIZE - 1);
 	skp = &raw_v4_htable[num];
-	SOCKHASH_LOCK();
+	SOCKHASH_LOCK_WRITE();
 	sk->next = *skp;
 	*skp = sk;
 	sk->hashent = num;
-	SOCKHASH_UNLOCK();
+	SOCKHASH_UNLOCK_WRITE();
 }
 
 static void raw_v4_unhash(struct sock *sk)
@@ -90,7 +90,7 @@
 	num &= (RAWV4_HTABLE_SIZE - 1);
 	skp = &raw_v4_htable[num];
 
-	SOCKHASH_LOCK();
+	SOCKHASH_LOCK_WRITE();
 	while(*skp != NULL) {
 		if(*skp == sk) {
 			*skp = sk->next;
@@ -98,7 +98,7 @@
 		}
 		skp = &((*skp)->next);
 	}
-	SOCKHASH_UNLOCK();
+	SOCKHASH_UNLOCK_WRITE();
 }
 
 static void raw_v4_rehash(struct sock *sk)
@@ -110,7 +110,7 @@
 	num &= (RAWV4_HTABLE_SIZE - 1);
 	skp = &raw_v4_htable[oldnum];
 
-	SOCKHASH_LOCK();
+	SOCKHASH_LOCK_WRITE();
 	while(*skp != NULL) {
 		if(*skp == sk) {
 			*skp = sk->next;
@@ -121,16 +121,15 @@
 	sk->next = raw_v4_htable[num];
 	raw_v4_htable[num] = sk;
 	sk->hashent = num;
-	SOCKHASH_UNLOCK();
+	SOCKHASH_UNLOCK_WRITE();
 }
 
-/* Grumble... icmp and ip_input want to get at this... */
-struct sock *raw_v4_lookup(struct sock *sk, unsigned short num,
-			   unsigned long raddr, unsigned long laddr, int dif)
+static __inline__ struct sock *__raw_v4_lookup(struct sock *sk, unsigned short num,
+					       unsigned long raddr, unsigned long laddr,
+					       int dif)
 {
 	struct sock *s = sk;
 
-	SOCKHASH_LOCK();
 	for(s = sk; s; s = s->next) {
 		if((s->num == num) 				&&
 		   !(s->dead && (s->state == TCP_CLOSE))	&&
@@ -139,10 +138,79 @@
 		   !(s->bound_dev_if && s->bound_dev_if != dif))
 			break; /* gotcha */
 	}
-	SOCKHASH_UNLOCK();
 	return s;
 }
 
+struct sock *raw_v4_lookup(struct sock *sk, unsigned short num,
+			   unsigned long raddr, unsigned long laddr,
+			   int dif)
+{
+	SOCKHASH_LOCK_READ();
+	sk = __raw_v4_lookup(sk, num, raddr, laddr, dif);
+	SOCKHASH_UNLOCK_READ();
+
+	return sk;
+}
+
+/*
+ *	0 - deliver
+ *	1 - block
+ */
+static __inline__ int icmp_filter(struct sock *sk, struct sk_buff *skb)
+{
+	int    type;
+
+	type = skb->h.icmph->type;
+	if (type < 32)
+		return test_bit(type, &sk->tp_pinfo.tp_raw4.filter);
+
+	/* Do not block unknown ICMP types */
+	return 0;
+}
+
+/* IP input processing comes here for RAW socket delivery.
+ * This is fun as to avoid copies we want to make no surplus
+ * copies.
+ *
+ * RFC 1122: SHOULD pass TOS value up to the transport layer.
+ * -> It does. And not only TOS, but all IP header.
+ */
+struct sock *raw_v4_input(struct sk_buff *skb, struct iphdr *iph, int hash)
+{
+	struct sock *sk;
+
+	SOCKHASH_LOCK_READ_BH();
+	if ((sk = raw_v4_htable[hash]) == NULL)
+		goto out;
+	sk = __raw_v4_lookup(sk, iph->protocol,
+			     iph->saddr, iph->daddr,
+			     skb->dev->ifindex);
+	while(sk != NULL) {
+		struct sock *sknext = __raw_v4_lookup(sk->next, iph->protocol,
+						      iph->saddr, iph->daddr,
+						      skb->dev->ifindex);
+
+		if (iph->protocol != IPPROTO_ICMP ||
+		    ! icmp_filter(sk, skb)) {
+			struct sk_buff *clone;
+
+			if(sknext == NULL)
+				break;
+			clone = skb_clone(skb, GFP_ATOMIC);
+			if(clone) {
+				SOCKHASH_UNLOCK_READ_BH();
+				raw_rcv(sk, clone);
+				SOCKHASH_LOCK_READ_BH();
+			}
+		}
+		sk = sknext;
+	}
+out:
+	SOCKHASH_UNLOCK_READ_BH();
+
+	return sk;
+}
+
 void raw_err (struct sock *sk, struct sk_buff *skb)
 {
 	int type = skb->h.icmph->type;
@@ -402,6 +470,8 @@
 
 static void raw_close(struct sock *sk, long timeout)
 {
+	bh_lock_sock(sk);
+
 	/* Observation: when raw_close is called, processes have
 	   no access to socket anymore. But net still has.
 	   Step one, detach it from networking:
diff -u --recursive --new-file v2.3.3/linux/net/ipv4/route.c linux/net/ipv4/route.c
--- v2.3.3/linux/net/ipv4/route.c	Mon May 10 09:55:25 1999
+++ linux/net/ipv4/route.c	Wed May 26 18:14:37 1999
@@ -5,7 +5,7 @@
  *
  *		ROUTE - implementation of the IP router.
  *
- * Version:	$Id: route.c,v 1.67 1999/05/08 20:00:20 davem Exp $
+ * Version:	$Id: route.c,v 1.68 1999/05/27 00:37:54 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -174,7 +174,18 @@
  * Route cache.
  */
 
-struct rtable 	*rt_hash_table[RT_HASH_DIVISOR];
+/* The locking scheme is rather straight forward:
+ *
+ * 1) A BH protected rwlock protects the central route hash.
+ * 2) Only writers remove entries, and they hold the lock
+ *    as they look at rtable reference counts.
+ * 3) Only readers acquire references to rtable entries,
+ *    they do so with atomic increments and with the
+ *    lock held.
+ */
+
+static struct rtable 	*rt_hash_table[RT_HASH_DIVISOR];
+static rwlock_t		 rt_hash_lock = RW_LOCK_UNLOCKED;
 
 static int rt_intern_hash(unsigned hash, struct rtable * rth, struct rtable ** res);
 
@@ -204,7 +215,7 @@
   	}
 	
   	
-	start_bh_atomic();
+	read_lock_bh(&rt_hash_lock);
 
 	for (i = 0; i<RT_HASH_DIVISOR; i++) {
 		for (r = rt_hash_table[i]; r; r = r->u.rt_next) {
@@ -239,7 +250,7 @@
         }
 
 done:
-	end_bh_atomic();
+	read_unlock_bh(&rt_hash_lock);
   	
   	*start = buffer+len-(pos-offset);
   	len = pos-offset;
@@ -292,6 +303,7 @@
 	return 1;
 }
 
+/* This runs via a timer and thus is always in BH context. */
 static void rt_check_expire(unsigned long dummy)
 {
 	int i;
@@ -305,6 +317,7 @@
 		rover = (rover + 1) & (RT_HASH_DIVISOR-1);
 		rthp = &rt_hash_table[rover];
 
+		write_lock(&rt_hash_lock);
 		while ((rth = *rthp) != NULL) {
 			if (rth->u.dst.expires) {
 				/* Entrie is expired even if it is in use */
@@ -325,6 +338,7 @@
 			*rthp = rth->u.rt_next;
 			rt_free(rth);
 		}
+		write_unlock(&rt_hash_lock);
 
 		/* Fallback loop breaker. */
 		if ((jiffies - now) > 0)
@@ -334,6 +348,9 @@
 	add_timer(&rt_periodic_timer);
 }
 
+/* This can run from both BH and non-BH contexts, the latter
+ * in the case of a forced flush event.
+ */
 static void rt_run_flush(unsigned long dummy)
 {
 	int i;
@@ -341,23 +358,23 @@
 
 	rt_deadline = 0;
 
-	start_bh_atomic();
 	for (i=0; i<RT_HASH_DIVISOR; i++) {
-		if ((rth = xchg(&rt_hash_table[i], NULL)) == NULL)
-			continue;
-		end_bh_atomic();
+		write_lock_bh(&rt_hash_lock);
+		rth = rt_hash_table[i];
+		if(rth != NULL)
+			rt_hash_table[i] = NULL;
+		write_unlock_bh(&rt_hash_lock);
 
 		for (; rth; rth=next) {
 			next = rth->u.rt_next;
 			rth->u.rt_next = NULL;
 			rt_free(rth);
 		}
-
-		start_bh_atomic();
 	}
-	end_bh_atomic();
 }
   
+static spinlock_t rt_flush_lock = SPIN_LOCK_UNLOCKED;
+
 void rt_cache_flush(int delay)
 {
 	unsigned long now = jiffies;
@@ -366,7 +383,7 @@
 	if (delay < 0)
 		delay = ip_rt_min_delay;
 
-	start_bh_atomic();
+	spin_lock_bh(&rt_flush_lock);
 
 	if (del_timer(&rt_flush_timer) && delay > 0 && rt_deadline) {
 		long tmo = (long)(rt_deadline - now);
@@ -386,7 +403,7 @@
 	}
 
 	if (delay <= 0) {
-		end_bh_atomic();
+		spin_unlock_bh(&rt_flush_lock);
 		rt_run_flush(0);
 		return;
 	}
@@ -396,7 +413,7 @@
 
 	rt_flush_timer.expires = now + delay;
 	add_timer(&rt_flush_timer);
-	end_bh_atomic();
+	spin_unlock_bh(&rt_flush_lock);
 }
 
 /*
@@ -459,7 +476,10 @@
 	do {
 		int i, k;
 
-		start_bh_atomic();
+		/* The write lock is held during the entire hash
+		 * traversal to ensure consistent state of the rover.
+		 */
+		write_lock_bh(&rt_hash_lock);
 		for (i=0, k=rover; i<RT_HASH_DIVISOR; i++) {
 			unsigned tmo = expire;
 
@@ -480,7 +500,7 @@
 				break;
 		}
 		rover = k;
-		end_bh_atomic();
+		write_unlock_bh(&rt_hash_lock);
 
 		if (goal <= 0)
 			goto work_done;
@@ -530,10 +550,9 @@
 	int attempts = !in_interrupt();
 
 restart:
-	start_bh_atomic();
-
 	rthp = &rt_hash_table[hash];
 
+	write_lock_bh(&rt_hash_lock);
 	while ((rth = *rthp) != NULL) {
 		if (memcmp(&rth->key, &rt->key, sizeof(rt->key)) == 0) {
 			/* Put it first */
@@ -544,7 +563,7 @@
 			atomic_inc(&rth->u.dst.refcnt);
 			atomic_inc(&rth->u.dst.use);
 			rth->u.dst.lastuse = now;
-			end_bh_atomic();
+			write_unlock_bh(&rt_hash_lock);
 
 			rt_drop(rt);
 			*rp = rth;
@@ -559,7 +578,7 @@
 	 */
 	if (rt->rt_type == RTN_UNICAST || rt->key.iif == 0) {
 		if (!arp_bind_neighbour(&rt->u.dst)) {
-			end_bh_atomic();
+			write_unlock_bh(&rt_hash_lock);
 
 			/* Neighbour tables are full and nothing
 			   can be released. Try to shrink route cache,
@@ -594,7 +613,7 @@
 	}
 #endif
 	rt_hash_table[hash] = rt;
-	end_bh_atomic();
+	write_unlock_bh(&rt_hash_lock);
 	*rp = rt;
 	return 0;
 }
@@ -633,6 +652,7 @@
 
 			rthp=&rt_hash_table[hash];
 
+			write_lock_bh(&rt_hash_lock);
 			while ( (rth = *rthp) != NULL) {
 				struct rtable *rt;
 
@@ -657,6 +677,7 @@
 				rt = dst_alloc(sizeof(struct rtable), &ipv4_dst_ops);
 				if (rt == NULL) {
 					ip_rt_put(rth);
+					write_unlock_bh(&rt_hash_lock);
 					return;
 				}
 
@@ -688,11 +709,15 @@
 				}
 
 				*rthp = rth->u.rt_next;
+				write_unlock_bh(&rt_hash_lock);
 				if (!rt_intern_hash(hash, rt, &rt))
 					ip_rt_put(rt);
 				rt_drop(rth);
-				break;
+				goto do_next;
 			}
+			write_unlock_bh(&rt_hash_lock);
+		do_next:
+			;
 		}
 	}
 	return;
@@ -722,8 +747,8 @@
 #if RT_CACHE_DEBUG >= 1
 			printk(KERN_DEBUG "ip_rt_advice: redirect to %d.%d.%d.%d/%02x dropped\n", NIPQUAD(rt->rt_dst), rt->key.tos);
 #endif
-			start_bh_atomic();
 			ip_rt_put(rt);
+			write_lock_bh(&rt_hash_lock);
 			for (rthp = &rt_hash_table[hash]; *rthp; rthp = &(*rthp)->u.rt_next) {
 				if (*rthp == rt) {
 					*rthp = rt->u.rt_next;
@@ -731,7 +756,7 @@
 					break;
 				}
 			}
-			end_bh_atomic();
+			write_unlock_bh(&rt_hash_lock);
 			return NULL;
 		}
 	}
@@ -861,6 +886,7 @@
 	for (i=0; i<2; i++) {
 		unsigned hash = rt_hash_code(daddr, skeys[i], tos);
 
+		read_lock_bh(&rt_hash_lock);
 		for (rth = rt_hash_table[hash]; rth; rth = rth->u.rt_next) {
 			if (rth->key.dst == daddr &&
 			    rth->key.src == skeys[i] &&
@@ -890,6 +916,7 @@
 				}
 			}
 		}
+		read_unlock_bh(&rt_hash_lock);
 	}
 	return est_mtu ? : new_mtu;
 }
@@ -1362,6 +1389,7 @@
 	tos &= IPTOS_TOS_MASK;
 	hash = rt_hash_code(daddr, saddr^(iif<<5), tos);
 
+	read_lock_bh(&rt_hash_lock);
 	for (rth=rt_hash_table[hash]; rth; rth=rth->u.rt_next) {
 		if (rth->key.dst == daddr &&
 		    rth->key.src == saddr &&
@@ -1374,10 +1402,12 @@
 			rth->u.dst.lastuse = jiffies;
 			atomic_inc(&rth->u.dst.use);
 			atomic_inc(&rth->u.dst.refcnt);
+			read_unlock_bh(&rt_hash_lock);
 			skb->dst = (struct dst_entry*)rth;
 			return 0;
 		}
 	}
+	read_unlock_bh(&rt_hash_lock);
 
 	/* Multicast recognition logic is moved from route cache to here.
 	   The problem was that too many Ethernet cards have broken/missing
@@ -1657,7 +1687,7 @@
 
 	hash = rt_hash_code(daddr, saddr^(oif<<5), tos);
 
-	start_bh_atomic();
+	read_lock_bh(&rt_hash_lock);
 	for (rth=rt_hash_table[hash]; rth; rth=rth->u.rt_next) {
 		if (rth->key.dst == daddr &&
 		    rth->key.src == saddr &&
@@ -1673,12 +1703,12 @@
 			rth->u.dst.lastuse = jiffies;
 			atomic_inc(&rth->u.dst.use);
 			atomic_inc(&rth->u.dst.refcnt);
-			end_bh_atomic();
+			read_unlock_bh(&rt_hash_lock);
 			*rp = rth;
 			return 0;
 		}
 	}
-	end_bh_atomic();
+	read_unlock_bh(&rt_hash_lock);
 
 	return ip_route_output_slow(rp, daddr, saddr, tos, oif);
 }
@@ -1821,9 +1851,7 @@
 			return -ENODEV;
 		skb->protocol = __constant_htons(ETH_P_IP);
 		skb->dev = dev;
-		start_bh_atomic();
 		err = ip_route_input(skb, dst, src, rtm->rtm_tos, dev);
-		end_bh_atomic();
 		rt = (struct rtable*)skb->dst;
 		if (!err && rt->u.dst.error)
 			err = -rt->u.dst.error;
@@ -1869,7 +1897,7 @@
 		if (h < s_h) continue;
 		if (h > s_h)
 			s_idx = 0;
-		start_bh_atomic();
+		read_lock_bh(&rt_hash_lock);
 		for (rt = rt_hash_table[h], idx = 0; rt; rt = rt->u.rt_next, idx++) {
 			if (idx < s_idx)
 				continue;
@@ -1877,12 +1905,12 @@
 			if (rt_fill_info(skb, NETLINK_CB(cb->skb).pid,
 					 cb->nlh->nlmsg_seq, RTM_NEWROUTE, 1) <= 0) {
 				dst_release(xchg(&skb->dst, NULL));
-				end_bh_atomic();
+				read_unlock_bh(&rt_hash_lock);
 				goto done;
 			}
 			dst_release(xchg(&skb->dst, NULL));
 		}
-		end_bh_atomic();
+		read_unlock_bh(&rt_hash_lock);
 	}
 
 done:
diff -u --recursive --new-file v2.3.3/linux/net/ipv4/tcp.c linux/net/ipv4/tcp.c
--- v2.3.3/linux/net/ipv4/tcp.c	Fri May 14 18:55:31 1999
+++ linux/net/ipv4/tcp.c	Wed May 26 18:14:37 1999
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp.c,v 1.141 1999/05/12 11:24:40 davem Exp $
+ * Version:	$Id: tcp.c,v 1.144 1999/05/27 01:03:37 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -416,6 +416,7 @@
 #include <linux/fcntl.h>
 #include <linux/poll.h>
 #include <linux/init.h>
+#include <linux/smp_lock.h>
 
 #include <net/icmp.h>
 #include <net/tcp.h>
@@ -432,7 +433,7 @@
 
 /*
  *	Find someone to 'accept'. Must be called with
- *	the socket locked or with interrupts disabled
+ *	the listening socket locked.
  */
 
 static struct open_request *tcp_find_established(struct tcp_opt *tp, 
@@ -441,10 +442,11 @@
 	struct open_request *req = tp->syn_wait_queue;
 	struct open_request *prev = (struct open_request *)&tp->syn_wait_queue; 
 	while(req) {
-		if (req->sk && 
-		    ((1 << req->sk->state) &
-		     ~(TCPF_SYN_SENT|TCPF_SYN_RECV)))
-			break;
+		if (req->sk) {
+			if((1 << req->sk->state) &
+			   ~(TCPF_SYN_SENT|TCPF_SYN_RECV))
+				break;
+		}
 		prev = req; 
 		req = req->dl_next;
 	}
@@ -655,7 +657,8 @@
 /*
  *	Wait for a socket to get into the connected state
  *
- *	Note: must be called with the socket locked.
+ *	Note: Must be called with the socket locked, and it
+ *	      runs with the kernel fully unlocked.
  */
 static int wait_for_tcp_connect(struct sock * sk, int flags)
 {
@@ -698,6 +701,8 @@
 
 /*
  *	Wait for more memory for a socket
+ *
+ * NOTE: This runs with the kernel fully unlocked.
  */
 static void wait_for_tcp_memory(struct sock * sk)
 {
@@ -744,6 +749,7 @@
 	int mss_now;
 	int err, copied;
 
+	unlock_kernel();
 	lock_sock(sk);
 
 	err = 0;
@@ -896,6 +902,7 @@
 					err = -ERESTARTSYS;
 					goto do_interrupted;
 				}
+				tcp_push_pending_frames(sk, tp);
 				wait_for_tcp_memory(sk);
 
 				/* If SACK's were formed or PMTU events happened,
@@ -969,6 +976,7 @@
 out:
 	tcp_push_pending_frames(sk, tp);
 	release_sock(sk);
+	lock_kernel();
 	return err;
 }
 
@@ -1148,6 +1156,7 @@
 	if (flags & MSG_WAITALL)
 		target=len;
 
+	unlock_kernel();
 	add_wait_queue(sk->sleep, &wait);
 	lock_sock(sk);
 	
@@ -1300,6 +1309,8 @@
 		/*	We now will not sleep again until we are finished
 		 *	with skb. Sorry if you are doing the SMP port
 		 *	but you'll just have to fix it neatly ;)
+		 *
+		 *	Very funny Alan... -DaveM
 		 */
 		atomic_dec(&skb->users);
 
@@ -1344,6 +1355,7 @@
 	/* Clean up data we have read: This will do ACK frames. */
 	cleanup_rbuf(sk, copied);
 	release_sock(sk);
+	lock_kernel();
 	return copied;
 }
 
@@ -1415,16 +1427,15 @@
 		return;
 
 	/* If we've already sent a FIN, or it's a closed state, skip this. */
+	lock_sock(sk);
 	if ((1 << sk->state) &
 	    (TCPF_ESTABLISHED|TCPF_SYN_SENT|TCPF_SYN_RECV|TCPF_CLOSE_WAIT)) {
-		lock_sock(sk);
 
 		/* Clear out any half completed packets.  FIN if needed. */
 		if (tcp_close_state(sk,0))
 			tcp_send_fin(sk);
-
-		release_sock(sk);
 	}
+	release_sock(sk);
 }
 
 
@@ -1471,13 +1482,6 @@
 	struct sk_buff *skb;
 	int data_was_unread = 0;
 
-	/*
-	 * Check whether the socket is locked ... supposedly
-	 * it's impossible to tcp_close() a locked socket.
-	 */
-	if (atomic_read(&sk->sock_readers))
-		printk("tcp_close: socket already locked!\n");
-
 	/* We need to grab some memory, and put together a FIN,
 	 * and then put it into the queue to be sent.
 	 */
@@ -1491,6 +1495,8 @@
 		return;
 	}
 
+	unlock_kernel();
+
 	/* It is questionable, what the role of this is now.
 	 * In any event either it should be removed, or
 	 * increment of SLT_KEEPALIVE be done, this is causing
@@ -1537,21 +1543,20 @@
 		DECLARE_WAITQUEUE(wait, current);
 
 		add_wait_queue(sk->sleep, &wait);
-		release_sock(sk);
 
 		while (1) {
 			tsk->state = TASK_INTERRUPTIBLE;
 			if (!closing(sk))
 				break;
+			release_sock(sk);
 			timeout = schedule_timeout(timeout);
+			lock_sock(sk);
 			if (signal_pending(tsk) || !timeout)
 				break;
 		}
 
 		tsk->state = TASK_RUNNING;
 		remove_wait_queue(sk->sleep, &wait);
-		
-		lock_sock(sk);
 	}
 
 	/* Now that the socket is dead, if we are in the FIN_WAIT2 state
@@ -1559,13 +1564,16 @@
          */
 	tcp_check_fin_timer(sk);
 
-	release_sock(sk);
 	sk->dead = 1;
+
+	release_sock(sk);
+	lock_kernel();
 }
 
 /*
  *	Wait for an incoming connection, avoid race
- *	conditions. This must be called with the socket locked.
+ *	conditions. This must be called with the socket locked,
+ *	and without the kernel lock held.
  */
 static struct open_request * wait_for_connect(struct sock * sk,
 					      struct open_request **pprev)
@@ -1579,9 +1587,6 @@
 	 * Since we do not 'race & poll' for established sockets
 	 * anymore, the common case will execute the loop only once.
 	 *
-	 * Or rather, it _would_ execute only once if it wasn't for
-	 * some extraneous wakeups that currently happen.
-	 *
 	 * Subtle issue: "add_wait_queue_exclusive()" will be added
 	 * after any current non-exclusive waiters, and we know that
 	 * it will always _stay_ after any new non-exclusive waiters
@@ -1620,6 +1625,7 @@
 	struct sock *newsk = NULL;
 	int error;
 
+	unlock_kernel();
 	lock_sock(sk); 
 
 	/* We need to make sure that this socket is listening,
@@ -1650,16 +1656,17 @@
 	sk->ack_backlog--; 
 	if(sk->keepopen)
 		tcp_inc_slow_timer(TCP_SLT_KEEPALIVE);
-
 	release_sock(sk);
+	lock_kernel();
 	return newsk;
 
 out:
 	/* sk should be in LISTEN state, thus accept can use sk->err for
-	 * internal purposes without stomping one anyone's feed.
+	 * internal purposes without stomping on anyone's feed.
 	 */ 
 	sk->err = error; 
 	release_sock(sk);
+	lock_kernel();
 	return newsk;
 }
 
@@ -1782,6 +1789,8 @@
 void __init tcp_init(void)
 {
 	struct sk_buff *skb = NULL;
+	unsigned long goal;
+	int order;
 
 	if(sizeof(struct tcp_skb_cb) > sizeof(skb->cb))
 		__skb_cb_too_small_for_tcp(sizeof(struct tcp_skb_cb),
@@ -1807,4 +1816,37 @@
 						NULL, NULL);
 	if(!tcp_timewait_cachep)
 		panic("tcp_init: Cannot alloc tcp_tw_bucket cache.");
+
+	/* Size and allocate the main established and bind bucket
+	 * hash tables.
+	 *
+	 * The methodology is similar to that of the buffer cache.
+	 */
+	goal = num_physpages >> (20 - PAGE_SHIFT);
+	for(order = 5; (1UL << order) < goal; order++)
+		;
+	do {
+		tcp_ehash_size = (1UL << order) * PAGE_SIZE /
+			sizeof(struct sock *);
+		tcp_ehash = (struct sock **)
+			__get_free_pages(GFP_ATOMIC, order);
+	} while (tcp_ehash == NULL && --order > 4);
+
+	if (!tcp_ehash)
+		panic("Failed to allocate TCP established hash table\n");
+	memset(tcp_ehash, 0, tcp_ehash_size * sizeof(struct sock *));
+
+	do {
+		tcp_bhash_size = (1UL << order) * PAGE_SIZE /
+			sizeof(struct tcp_bind_bucket *);
+		tcp_bhash = (struct tcp_bind_bucket **)
+			__get_free_pages(GFP_ATOMIC, order);
+	} while (tcp_bhash == NULL && --order > 4);
+
+	if (!tcp_bhash)
+		panic("Failed to allocate TCP bind hash table\n");
+	memset(tcp_bhash, 0, tcp_bhash_size * sizeof(struct tcp_bind_bucket *));
+
+	printk("TCP: Hash tables configured (established %d bind %d)\n",
+	       tcp_ehash_size, tcp_bhash_size);
 }
diff -u --recursive --new-file v2.3.3/linux/net/ipv4/tcp_input.c linux/net/ipv4/tcp_input.c
--- v2.3.3/linux/net/ipv4/tcp_input.c	Sat May 15 23:46:05 1999
+++ linux/net/ipv4/tcp_input.c	Mon May 31 22:07:43 1999
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_input.c,v 1.165 1999/05/14 23:10:08 davem Exp $
+ * Version:	$Id: tcp_input.c,v 1.167 1999/05/29 22:37:54 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -914,8 +914,11 @@
 extern void tcp_tw_reschedule(struct tcp_tw_bucket *tw);
 extern void tcp_tw_deschedule(struct tcp_tw_bucket *tw);
 
+/* Must be called only from BH context. */
 void tcp_timewait_kill(struct tcp_tw_bucket *tw)
 {
+	SOCKHASH_LOCK_WRITE_BH();
+
 	/* Unlink from various places. */
 	if(tw->bind_next)
 		tw->bind_next->bind_pprev = tw->bind_pprev;
@@ -933,6 +936,8 @@
 	tw->sklist_next->sklist_prev = tw->sklist_prev;
 	tw->sklist_prev->sklist_next = tw->sklist_next;
 
+	SOCKHASH_UNLOCK_WRITE_BH();
+
 	/* Ok, now free it up. */
 	kmem_cache_free(tcp_timewait_cachep, tw);
 }
@@ -963,6 +968,7 @@
 		struct sock *sk;
 		struct tcp_func *af_specific = tw->af_specific;
 		__u32 isn;
+		int ret;
 
 		isn = tw->rcv_nxt + 128000;
 		if(isn == 0)
@@ -971,14 +977,25 @@
 		tcp_timewait_kill(tw);
 		sk = af_specific->get_sock(skb, th);
 		if(sk == NULL ||
-		   !ipsec_sk_policy(sk,skb) ||
-		   atomic_read(&sk->sock_readers) != 0)
+		   !ipsec_sk_policy(sk,skb))
 			return 0;
+
+		bh_lock_sock(sk);
+
+		/* Default is to discard the frame. */
+		ret = 0;
+
+		if(sk->lock.users)
+			goto out_unlock;
+
 		skb_set_owner_r(skb, sk);
 		af_specific = sk->tp_pinfo.af_tcp.af_specific;
+
 		if(af_specific->conn_request(sk, skb, isn) < 0)
-			return 1; /* Toss a reset back. */
-		return 0; /* Discard the frame. */
+			ret = 1; /* Toss a reset back. */
+	out_unlock:
+		bh_unlock_sock(sk);
+		return ret;
 	}
 
 	/* Check RST or SYN */
@@ -1031,7 +1048,7 @@
 	sk->prot->inuse--;
 
 	/* Step 4: Hash TW into TIMEWAIT half of established hash table. */
-	head = &tcp_established_hash[sk->hashent + (TCP_HTABLE_SIZE/2)];
+	head = &tcp_ehash[sk->hashent + (tcp_ehash_size >> 1)];
 	sktw = (struct sock *)tw;
 	if((sktw->next = *head) != NULL)
 		(*head)->pprev = &sktw->next;
@@ -1069,7 +1086,9 @@
 		}
 #endif
 		/* Linkage updates. */
+		SOCKHASH_LOCK_WRITE();
 		tcp_tw_hashdance(sk, tw);
+		SOCKHASH_UNLOCK_WRITE();
 
 		/* Get the TIME_WAIT timeout firing. */
 		tcp_tw_schedule(tw);
@@ -1819,7 +1838,7 @@
 		}
 	}
 
-	flg = *(((u32 *)th) + 3) & ~htonl(0x8 << 16);
+	flg = *(((u32 *)th) + 3) & ~htonl(0xFC8 << 16);
 
 	/*	pred_flags is 0xS?10 << 16 + snd_wnd
 	 *	if header_predition is to be made
@@ -2049,8 +2068,26 @@
 		/* These use the socket TOS.. 
 		 * might want to be the received TOS 
 		 */
-		if(th->ack)
-			return 1;
+		if(th->ack) {
+			struct sock *realsk;
+			int ret;
+
+			realsk = tp->af_specific->get_sock(skb, th);
+			if(realsk == sk)
+				return 1;
+
+			bh_lock_sock(realsk);
+			ret = 0;
+			if(realsk->lock.users != 0) {
+				skb_orphan(skb);
+				sk_add_backlog(realsk, skb);
+			} else {
+				ret = tcp_rcv_state_process(realsk, skb,
+							    skb->h.th, skb->len);
+			}
+			bh_unlock_sock(realsk);
+			return ret;
+		}
 		
 		if(th->syn) {
 			if(tp->af_specific->conn_request(sk, skb, 0) < 0)
diff -u --recursive --new-file v2.3.3/linux/net/ipv4/tcp_ipv4.c linux/net/ipv4/tcp_ipv4.c
--- v2.3.3/linux/net/ipv4/tcp_ipv4.c	Fri May 14 18:55:32 1999
+++ linux/net/ipv4/tcp_ipv4.c	Mon May 31 22:07:43 1999
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_ipv4.c,v 1.176 1999/05/12 11:24:46 davem Exp $
+ * Version:	$Id: tcp_ipv4.c,v 1.178 1999/05/30 01:16:27 davem Exp $
  *
  *		IPv4 specific functions
  *
@@ -90,12 +90,14 @@
  * First half of the table is for sockets not in TIME_WAIT, second half
  * is for TIME_WAIT sockets only.
  */
-struct sock *tcp_established_hash[TCP_HTABLE_SIZE];
+struct sock **tcp_ehash;
+int tcp_ehash_size;
 
 /* Ok, let's try this, I give up, we do need a local binding
  * TCP hash as well as the others for fast bind/connect.
  */
-struct tcp_bind_bucket *tcp_bound_hash[TCP_BHTABLE_SIZE];
+struct tcp_bind_bucket **tcp_bhash;
+int tcp_bhash_size;
 
 /* All sockets in TCP_LISTEN state will be in here.  This is the only table
  * where wildcard'd TCP sockets can exist.  Hash function here is just local
@@ -117,7 +119,7 @@
 static __inline__ int tcp_hashfn(__u32 laddr, __u16 lport,
 				 __u32 faddr, __u16 fport)
 {
-	return ((laddr ^ lport) ^ (faddr ^ fport)) & ((TCP_HTABLE_SIZE/2) - 1);
+	return ((laddr ^ lport) ^ (faddr ^ fport)) & ((tcp_ehash_size >> 1) - 1);
 }
 
 static __inline__ int tcp_sk_hashfn(struct sock *sk)
@@ -136,8 +138,8 @@
 	struct tcp_bind_bucket *tb;
 	unsigned short snum = sk->num;
 
-	SOCKHASH_LOCK();
-	for(tb = tcp_bound_hash[tcp_bhashfn(snum)]; tb; tb = tb->next) {
+	SOCKHASH_LOCK_WRITE();
+	for(tb = tcp_bhash[tcp_bhashfn(snum)]; tb; tb = tb->next) {
 		if(tb->port == snum) {
 			if(tb->owners == NULL &&
 			   (tb->flags & TCPB_FLAG_LOCKED)) {
@@ -148,9 +150,10 @@
 			break;
 		}
 	}
-	SOCKHASH_UNLOCK();
+	SOCKHASH_UNLOCK_WRITE();
 }
 
+/* The sockhash lock must be held as a writer here. */
 struct tcp_bind_bucket *tcp_bucket_create(unsigned short snum)
 {
 	struct tcp_bind_bucket *tb;
@@ -158,7 +161,7 @@
 	tb = kmem_cache_alloc(tcp_bucket_cachep, SLAB_ATOMIC);
 	if(tb != NULL) {
 		struct tcp_bind_bucket **head =
-			&tcp_bound_hash[tcp_bhashfn(snum)];
+			&tcp_bhash[tcp_bhashfn(snum)];
 		tb->port = snum;
 		tb->flags = TCPB_FLAG_LOCKED;
 		tb->owners = NULL;
@@ -176,13 +179,18 @@
  */
 static __inline__ int tcp_bucket_check(unsigned short snum)
 {
-	struct tcp_bind_bucket *tb = tcp_bound_hash[tcp_bhashfn(snum)];
+	struct tcp_bind_bucket *tb;
+	int ret = 0;
+
+	SOCKHASH_LOCK_WRITE();
+	tb = tcp_bhash[tcp_bhashfn(snum)];
 	for( ; (tb && (tb->port != snum)); tb = tb->next)
 		;
 	if(tb == NULL && tcp_bucket_create(snum) == NULL)
-		return 1;
-	else
-		return 0;
+		ret = 1;
+	SOCKHASH_UNLOCK_WRITE();
+
+	return ret;
 }
 #endif
 
@@ -191,8 +199,8 @@
 	struct tcp_bind_bucket *tb;
 	int result = 0;
 
-	SOCKHASH_LOCK();
-	for(tb = tcp_bound_hash[tcp_bhashfn(snum)];
+	SOCKHASH_LOCK_WRITE();
+	for(tb = tcp_bhash[tcp_bhashfn(snum)];
 	    (tb && (tb->port != snum));
 	    tb = tb->next)
 		;
@@ -256,7 +264,7 @@
 		}
 	}
 go_like_smoke:
-	SOCKHASH_UNLOCK();
+	SOCKHASH_UNLOCK_WRITE();
 	return result;
 }
 
@@ -268,13 +276,13 @@
 	int remaining = (high - low) + 1;
 	int rover;
 
-	SOCKHASH_LOCK();
+	SOCKHASH_LOCK_WRITE();
 	rover = tcp_port_rover;
 	do {
 		rover += 1;
 		if((rover < low) || (rover > high))
 			rover = low;
-		tb = tcp_bound_hash[tcp_bhashfn(rover)];
+		tb = tcp_bhash[tcp_bhashfn(rover)];
 		for( ; tb; tb = tb->next) {
 			if(tb->port == rover)
 				goto next;
@@ -288,7 +296,7 @@
 		rover = 0;
 	if (tb != NULL)
 		tb->flags |= TCPB_FLAG_GOODSOCKNUM;
-	SOCKHASH_UNLOCK();
+	SOCKHASH_UNLOCK_WRITE();
 
 	return rover;
 }
@@ -298,20 +306,20 @@
 	if (sk->state != TCP_CLOSE) {
 		struct sock **skp;
 
-		SOCKHASH_LOCK();
-		skp = &tcp_established_hash[(sk->hashent = tcp_sk_hashfn(sk))];
+		SOCKHASH_LOCK_WRITE();
+		skp = &tcp_ehash[(sk->hashent = tcp_sk_hashfn(sk))];
 		if((sk->next = *skp) != NULL)
 			(*skp)->pprev = &sk->next;
 		*skp = sk;
 		sk->pprev = skp;
 		tcp_sk_bindify(sk);
-		SOCKHASH_UNLOCK();
+		SOCKHASH_UNLOCK_WRITE();
 	}
 }
 
 static void tcp_v4_unhash(struct sock *sk)
 {
-	SOCKHASH_LOCK();
+	SOCKHASH_LOCK_WRITE();
 	if(sk->pprev) {
 		if(sk->next)
 			sk->next->pprev = sk->pprev;
@@ -320,14 +328,14 @@
 		tcp_reg_zap(sk);
 		tcp_sk_unbindify(sk);
 	}
-	SOCKHASH_UNLOCK();
+	SOCKHASH_UNLOCK_WRITE();
 }
 
 static void tcp_v4_rehash(struct sock *sk)
 {
 	unsigned char state;
 
-	SOCKHASH_LOCK();
+	SOCKHASH_LOCK_WRITE();
 	state = sk->state;
 	if(sk->pprev != NULL) {
 		if(sk->next)
@@ -342,7 +350,7 @@
 		if(state == TCP_LISTEN)
 			skp = &tcp_listening_hash[tcp_sk_listen_hashfn(sk)];
 		else
-			skp = &tcp_established_hash[(sk->hashent = tcp_sk_hashfn(sk))];
+			skp = &tcp_ehash[(sk->hashent = tcp_sk_hashfn(sk))];
 
 		if((sk->next = *skp) != NULL)
 			(*skp)->pprev = &sk->next;
@@ -351,7 +359,7 @@
 		if(state == TCP_LISTEN)
 			tcp_sk_bindify(sk);
 	}
-	SOCKHASH_UNLOCK();
+	SOCKHASH_UNLOCK_WRITE();
 }
 
 /* Don't inline this cruft.  Here are some nice properties to
@@ -395,10 +403,10 @@
 
 /* Sockets in TCP_CLOSE state are _always_ taken out of the hash, so
  * we need not check it for TCP lookups anymore, thanks Alexey. -DaveM
- * It is assumed that this code only gets called from within NET_BH.
+ *
+ * The sockhash lock must be held as a reader here.
  */
-static inline struct sock *__tcp_v4_lookup(struct tcphdr *th,
-					   u32 saddr, u16 sport,
+static inline struct sock *__tcp_v4_lookup(u32 saddr, u16 sport,
 					   u32 daddr, u16 dport, int dif)
 {
 	TCP_V4_ADDR_COOKIE(acookie, saddr, daddr)
@@ -416,7 +424,7 @@
 	 * have wildcards anyways.
 	 */
 	hash = tcp_hashfn(daddr, hnum, saddr, sport);
-	for(sk = tcp_established_hash[hash]; sk; sk = sk->next) {
+	for(sk = tcp_ehash[hash]; sk; sk = sk->next) {
 		if(TCP_IPV4_MATCH(sk, acookie, saddr, daddr, ports, dif)) {
 			if (sk->state == TCP_ESTABLISHED)
 				TCP_RHASH(sport) = sk;
@@ -424,7 +432,7 @@
 		}
 	}
 	/* Must check for a TIME_WAIT'er before going to listener hash. */
-	for(sk = tcp_established_hash[hash+(TCP_HTABLE_SIZE/2)]; sk; sk = sk->next)
+	for(sk = tcp_ehash[hash+(tcp_ehash_size >> 1)]; sk; sk = sk->next)
 		if(TCP_IPV4_MATCH(sk, acookie, saddr, daddr, ports, dif))
 			goto hit;
 	sk = tcp_v4_lookup_listener(daddr, hnum, dif);
@@ -434,7 +442,13 @@
 
 __inline__ struct sock *tcp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport, int dif)
 {
-	return __tcp_v4_lookup(0, saddr, sport, daddr, dport, dif);
+	struct sock *sk;
+
+	SOCKHASH_LOCK_READ();
+	sk = __tcp_v4_lookup(saddr, sport, daddr, dport, dif);
+	SOCKHASH_UNLOCK_READ();
+
+	return sk;
 }
 
 #ifdef CONFIG_IP_TRANSPARENT_PROXY
@@ -462,9 +476,12 @@
 			paddr = idev->ifa_list->ifa_local;
 	}
 
-	/* This code must run only from NET_BH. */
+	/* We must obtain the sockhash lock here, we are always
+	 * in BH context.
+	 */
+	SOCKHASH_LOCK_READ_BH();
 	{
-		struct tcp_bind_bucket *tb = tcp_bound_hash[tcp_bhashfn(hnum)];
+		struct tcp_bind_bucket *tb = tcp_bhash[tcp_bhashfn(hnum)];
 		for( ; (tb && tb->port != hnum); tb = tb->next)
 			;
 		if(tb == NULL)
@@ -505,7 +522,7 @@
 	}
 next:
 	if(firstpass--) {
-		struct tcp_bind_bucket *tb = tcp_bound_hash[tcp_bhashfn(hpnum)];
+		struct tcp_bind_bucket *tb = tcp_bhash[tcp_bhashfn(hpnum)];
 		for( ; (tb && tb->port != hpnum); tb = tb->next)
 			;
 		if(tb) {
@@ -514,6 +531,7 @@
 		}
 	}
 gotit:
+	SOCKHASH_UNLOCK_READ_BH();
 	return result;
 }
 #endif /* CONFIG_IP_TRANSPARENT_PROXY */
@@ -540,21 +558,23 @@
 	int retval = 1;
 
 	/* Freeze the hash while we snoop around. */
-	SOCKHASH_LOCK();
-	tb = tcp_bound_hash[tcp_bhashfn(snum)];
+	SOCKHASH_LOCK_READ();
+	tb = tcp_bhash[tcp_bhashfn(snum)];
 	for(; tb; tb = tb->next) {
 		if(tb->port == snum && tb->owners != NULL) {
 			/* Almost certainly the re-use port case, search the real hashes
 			 * so it actually scales.
 			 */
-			sk = __tcp_v4_lookup(NULL, sk->daddr, sk->dport,
+			sk = __tcp_v4_lookup(sk->daddr, sk->dport,
 					     sk->rcv_saddr, snum, sk->bound_dev_if);
+			SOCKHASH_UNLOCK_READ();
+
 			if((sk != NULL) && (sk->state != TCP_LISTEN))
 				retval = 0;
-			break;
+			return retval;
 		}
 	}
-	SOCKHASH_UNLOCK();
+	SOCKHASH_UNLOCK_READ();
 	return retval;
 }
 
@@ -727,16 +747,17 @@
 {
 	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
 
-	if (atomic_read(&sk->sock_readers))
-		return;
-
-	/* Don't interested in TCP_LISTEN and open_requests (SYN-ACKs
+	/* We are not interested in TCP_LISTEN and open_requests (SYN-ACKs
 	 * send out by Linux are always <576bytes so they should go through
 	 * unfragmented).
 	 */
 	if (sk->state == TCP_LISTEN)
 		return; 
 
+	bh_lock_sock(sk);
+	if(sk->lock.users != 0)
+		goto out;
+
 	/* We don't check in the destentry if pmtu discovery is forbidden
 	 * on this route. We just assume that no packet_to_big packets
 	 * are send back when pmtu discovery is not active.
@@ -744,7 +765,8 @@
 	 * route, but I think that's acceptable.
 	 */
 	if (sk->dst_cache == NULL)
-		return;
+		goto out;
+
 	ip_rt_update_pmtu(sk->dst_cache, mtu);
 	if (sk->ip_pmtudisc != IP_PMTUDISC_DONT &&
 	    tp->pmtu_cookie > sk->dst_cache->pmtu) {
@@ -757,6 +779,8 @@
 		 */
 		tcp_simple_retransmit(sk);
 	} /* else let the usual retransmit timer handle it */
+out:
+	bh_unlock_sock(sk);
 }
 
 /*
@@ -849,17 +873,6 @@
 	switch (sk->state) {
 		struct open_request *req, *prev;
 	case TCP_LISTEN:
-		/* Prevent race conditions with accept() - 
-		 * ICMP is unreliable. 
-		 */
-		if (atomic_read(&sk->sock_readers)) {
-			net_statistics.LockDroppedIcmps++;
-			 /* If too many ICMPs get dropped on busy
-			  * servers this needs to be solved differently.
-			  */
-			return;
-		}
-
 		/* The final ACK of the handshake should be already 
 		 * handled in the new socket context, not here.
 		 * Strictly speaking - an ICMP error for the final
@@ -869,12 +882,24 @@
 		if (!no_flags && !th->syn && !th->ack)
 			return;
 
+		/* Prevent race conditions with accept() - 
+		 * ICMP is unreliable. 
+		 */
+		bh_lock_sock(sk);
+		if (sk->lock.users != 0) {
+			net_statistics.LockDroppedIcmps++;
+			 /* If too many ICMPs get dropped on busy
+			  * servers this needs to be solved differently.
+			  */
+			goto out_unlock;
+		}
+
 		req = tcp_v4_search_req(tp, iph, th, &prev); 
 		if (!req)
-			return;
+			goto out_unlock;
 		if (seq != req->snt_isn) {
 			net_statistics.OutOfWindowIcmps++;
-			return;
+			goto out_unlock;
 		}
 		if (req->sk) {	
 			/* 
@@ -884,6 +909,7 @@
 			 * but only with the next operation on the socket after
 			 * accept. 
 			 */
+			bh_unlock_sock(sk);
 			sk = req->sk;
 		} else {
 			/* 
@@ -896,6 +922,8 @@
 			tcp_synq_unlink(tp, req, prev);
 			req->class->destructor(req);
 			tcp_openreq_free(req);
+	out_unlock:
+			bh_unlock_sock(sk);
 			return; 
 		}
 		break;
@@ -1025,9 +1053,10 @@
 {
 	struct iphdr *iph = skb->nh.iph;
 	struct tcphdr *th = (struct tcphdr *)(skb->nh.raw + iph->ihl*4);
-	struct sock *sk;
+	struct sock *sk = NULL;
 	int i;
 
+	SOCKHASH_LOCK_READ();
 	for (i=0; i<TCP_LHTABLE_SIZE; i++) {
 		for(sk = tcp_listening_hash[i]; sk; sk = sk->next) {
 			struct open_request *dummy;
@@ -1035,10 +1064,12 @@
 					      th, &dummy) &&
 			    (!sk->bound_dev_if ||
 			     sk->bound_dev_if == skb->dev->ifindex))
-				return sk;
+				goto out;
 		}
 	}
-	return NULL;
+out:
+	SOCKHASH_UNLOCK_READ();
+	return sk;
 }
 
 /*
@@ -1319,7 +1350,8 @@
 		/* Clone the TCP header template */
 		newsk->dport = req->rmt_port;
 
-		atomic_set(&newsk->sock_readers, 0);
+		sock_lock_init(newsk);
+
 		atomic_set(&newsk->rmem_alloc, 0);
 		skb_queue_head_init(&newsk->receive_queue);
 		atomic_set(&newsk->wmem_alloc, 0);
@@ -1328,9 +1360,9 @@
 
 		newsk->done = 0;
 		newsk->proc = 0;
-		newsk->pair = NULL;
-		skb_queue_head_init(&newsk->back_log);
+		newsk->backlog.head = newsk->backlog.tail = NULL;
 		skb_queue_head_init(&newsk->error_queue);
+		newsk->write_space = tcp_write_space;
 #ifdef CONFIG_FILTER
 		if ((filter = newsk->filter) != NULL)
 			sk_filter_charge(newsk, filter);
@@ -1552,7 +1584,8 @@
 	}
 
 	/* Check for SYN|ACK */
-	if (flg & __constant_htonl(0x00120000)) {
+	flg &= __constant_htonl(0x00120000);
+	if (flg) {
 		struct open_request *req, *dummy; 
 		struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 
@@ -1570,8 +1603,17 @@
 	return sk; 
 }
 
+/* The socket must have it's spinlock held when we get
+ * here.
+ *
+ * We have a potential double-lock case here, so even when
+ * doing backlog processing we use the BH locking scheme.
+ * This is because we cannot sleep with the original spinlock
+ * held.
+ */
 int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
 {
+	int need_unlock = 0;
 #ifdef CONFIG_FILTER
 	struct sk_filter *filter = sk->filter;
 	if (filter && sk_filter(skb, filter))
@@ -1591,7 +1633,6 @@
 		return 0; 
 	} 
 
-
 	if (sk->state == TCP_LISTEN) { 
 		struct sock *nsk;
 		
@@ -1604,17 +1645,22 @@
 		 * otherwise we just shortcircuit this and continue with
 		 * the new socket..
 		 */
-		if (atomic_read(&nsk->sock_readers)) {
-			skb_orphan(skb);
-			__skb_queue_tail(&nsk->back_log, skb);
-			return 0;
+		if (nsk != sk) {
+			bh_lock_sock(nsk);
+			if (nsk->lock.users != 0) {
+				skb_orphan(skb);
+				sk_add_backlog(nsk, skb);
+				bh_unlock_sock(nsk);
+				return 0;
+			}
+			need_unlock = 1;
+			sk = nsk;
 		}
-		sk = nsk;
 	}
 	
 	if (tcp_rcv_state_process(sk, skb, skb->h.th, skb->len))
 		goto reset;
-	return 0;
+	goto out_maybe_unlock;
 
 reset:
 	tcp_v4_send_reset(skb);
@@ -1625,6 +1671,9 @@
 	 * might be destroyed here. This current version compiles correctly,
 	 * but you have been warned.
 	 */
+out_maybe_unlock:
+	if(need_unlock)
+		bh_unlock_sock(sk);
 	return 0;
 }
 
@@ -1636,6 +1685,7 @@
 {
 	struct tcphdr *th;
 	struct sock *sk;
+	int ret;
 
 	if (skb->pkt_type!=PACKET_HOST)
 		goto discard_it;
@@ -1681,8 +1731,10 @@
 					 IPCB(skb)->redirport, skb->dev->ifindex);
 	else {
 #endif
-		sk = __tcp_v4_lookup(th, skb->nh.iph->saddr, th->source,
+		SOCKHASH_LOCK_READ_BH();
+		sk = __tcp_v4_lookup(skb->nh.iph->saddr, th->source,
 				     skb->nh.iph->daddr, th->dest, skb->dev->ifindex);
+		SOCKHASH_UNLOCK_READ_BH();
 #ifdef CONFIG_IP_TRANSPARENT_PROXY
 		if (!sk)
 			sk = tcp_v4_search_proxy_openreq(skb);
@@ -1702,11 +1754,16 @@
 
 	if (sk->state == TCP_TIME_WAIT)
 		goto do_time_wait;
-	if (!atomic_read(&sk->sock_readers))
-		return tcp_v4_do_rcv(sk, skb);
 
-	__skb_queue_tail(&sk->back_log, skb);
-	return 0;
+	bh_lock_sock(sk);
+	ret = 0;
+	if (!sk->lock.users)
+		ret = tcp_v4_do_rcv(sk, skb);
+	else
+		sk_add_backlog(sk, skb);
+	bh_unlock_sock(sk);
+
+	return ret;
 
 no_tcp_socket:
 	tcp_v4_send_reset(skb);
@@ -1954,6 +2011,11 @@
 	if ((err=ops->create(tcp_socket, IPPROTO_TCP))<0)
 		panic("Failed to create the TCP control socket.\n");
 	tcp_socket->sk->allocation=GFP_ATOMIC;
-	tcp_socket->sk->num = 256;		/* Don't receive any data */
 	tcp_socket->sk->ip_ttl = MAXTTL;
+
+	/* Unhash it so that IP input processing does not even
+	 * see it, we do not wish this socket to see incoming
+	 * packets.
+	 */
+	tcp_socket->sk->prot->unhash(tcp_socket->sk);
 }
diff -u --recursive --new-file v2.3.3/linux/net/ipv4/tcp_output.c linux/net/ipv4/tcp_output.c
--- v2.3.3/linux/net/ipv4/tcp_output.c	Sat May 15 23:46:05 1999
+++ linux/net/ipv4/tcp_output.c	Wed May 26 18:14:38 1999
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_output.c,v 1.109 1999/05/14 23:10:13 davem Exp $
+ * Version:	$Id: tcp_output.c,v 1.110 1999/05/27 00:37:45 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -36,6 +36,8 @@
 
 #include <net/tcp.h>
 
+#include <linux/smp_lock.h>
+
 extern int sysctl_tcp_timestamps;
 extern int sysctl_tcp_window_scaling;
 extern int sysctl_tcp_sack;
@@ -966,6 +968,7 @@
 	/* Ok, now lock the socket before we make it visible to
 	 * the incoming packet engine.
 	 */
+	unlock_kernel();
 	lock_sock(sk);
 
 	/* Socket identity change complete, no longer
@@ -993,6 +996,7 @@
 
 	/* Now, it is safe to release the socket. */
 	release_sock(sk);
+	lock_kernel();
 }
 
 /* Send out a delayed ack, the caller does the policy checking
diff -u --recursive --new-file v2.3.3/linux/net/ipv4/tcp_timer.c linux/net/ipv4/tcp_timer.c
--- v2.3.3/linux/net/ipv4/tcp_timer.c	Mon May 17 09:55:23 1999
+++ linux/net/ipv4/tcp_timer.c	Wed May 26 18:14:38 1999
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_timer.c,v 1.63 1999/05/15 23:02:21 davem Exp $
+ * Version:	$Id: tcp_timer.c,v 1.64 1999/05/27 00:37:31 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -168,15 +168,16 @@
 {
 	struct sock *sk = (struct sock*)data;
 
+	bh_lock_sock(sk);
 	if(!sk->zapped &&
 	   sk->tp_pinfo.af_tcp.delayed_acks &&
 	   sk->state != TCP_CLOSE) {
-		/* If socket is currently locked, defer the ACK. */
-		if (!atomic_read(&sk->sock_readers))
+		if (!sk->lock.users)
 			tcp_send_ack(sk);
 		else
 			tcp_send_delayed_ack(&(sk->tp_pinfo.af_tcp), HZ/10);
 	}
+	bh_unlock_sock(sk);
 }
 
 void tcp_probe_timer(unsigned long data)
@@ -187,9 +188,11 @@
 	if(sk->zapped) 
 		return;
 	
-	if (atomic_read(&sk->sock_readers)) {
+	bh_lock_sock(sk);
+	if (sk->lock.users) {
 		/* Try again later. */
 		tcp_reset_xmit_timer(sk, TIME_PROBE0, HZ/5);
+		bh_unlock_sock(sk);
 		return;
 	}
 
@@ -216,6 +219,7 @@
 		/* Only send another probe if we didn't close things up. */
 		tcp_send_probe0(sk);
 	}
+	bh_unlock_sock(sk);
 }
 
 static __inline__ int tcp_keepopen_proc(struct sock *sk)
@@ -253,8 +257,9 @@
 {
 	int i, reaped = 0;;
 
-	for(i = 0; i < TCP_BHTABLE_SIZE; i++) {
-		struct tcp_bind_bucket *tb = tcp_bound_hash[i];
+	SOCKHASH_LOCK_WRITE_BH();
+	for(i = 0; i < tcp_bhash_size; i++) {
+		struct tcp_bind_bucket *tb = tcp_bhash[i];
 
 		while(tb) {
 			struct tcp_bind_bucket *next = tb->next;
@@ -274,6 +279,8 @@
 			tb = next;
 		}
 	}
+	SOCKHASH_UNLOCK_WRITE_BH();
+
 	if(reaped != 0) {
 		struct tcp_sl_timer *slt = (struct tcp_sl_timer *)data;
 
@@ -294,8 +301,14 @@
 	struct tcp_tw_bucket *tw;
 	int killed = 0;
 
+	/* The death-row tw chains are only ever touched
+	 * in BH context so no locking is needed.
+	 */
 	tw = tcp_tw_death_row[tcp_tw_death_row_slot];
 	tcp_tw_death_row[tcp_tw_death_row_slot] = NULL;
+	tcp_tw_death_row_slot =
+	  ((tcp_tw_death_row_slot + 1) & (TCP_TWKILL_SLOTS - 1));
+
 	while(tw != NULL) {
 		struct tcp_tw_bucket *next = tw->next_death;
 
@@ -307,8 +320,6 @@
 		struct tcp_sl_timer *slt = (struct tcp_sl_timer *)data;
 		atomic_sub(killed, &slt->count);
 	}
-	tcp_tw_death_row_slot =
-	  ((tcp_tw_death_row_slot + 1) & (TCP_TWKILL_SLOTS - 1));
 }
 
 /* These are always called from BH context.  See callers in
@@ -319,12 +330,14 @@
 	int slot = (tcp_tw_death_row_slot - 1) & (TCP_TWKILL_SLOTS - 1);
 	struct tcp_tw_bucket **tpp = &tcp_tw_death_row[slot];
 
+	SOCKHASH_LOCK_WRITE_BH();
 	if((tw->next_death = *tpp) != NULL)
 		(*tpp)->pprev_death = &tw->next_death;
 	*tpp = tw;
 	tw->pprev_death = tpp;
 
 	tw->death_slot = slot;
+	SOCKHASH_UNLOCK_WRITE_BH();
 
 	tcp_inc_slow_timer(TCP_SLT_TWKILL);
 }
@@ -335,6 +348,7 @@
 	struct tcp_tw_bucket **tpp;
 	int slot;
 
+	SOCKHASH_LOCK_WRITE_BH();
 	if(tw->next_death)
 		tw->next_death->pprev_death = tw->pprev_death;
 	*tw->pprev_death = tw->next_death;
@@ -348,16 +362,21 @@
 	tw->pprev_death = tpp;
 
 	tw->death_slot = slot;
+	SOCKHASH_UNLOCK_WRITE_BH();
+
 	/* Timer was incremented when we first entered the table. */
 }
 
 /* This is for handling early-kills of TIME_WAIT sockets. */
 void tcp_tw_deschedule(struct tcp_tw_bucket *tw)
 {
+	SOCKHASH_LOCK_WRITE_BH();
 	if(tw->next_death)
 		tw->next_death->pprev_death = tw->pprev_death;
 	*tw->pprev_death = tw->next_death;
 	tw->pprev_death = NULL;
+	SOCKHASH_UNLOCK_WRITE_BH();
+
 	tcp_dec_slow_timer(TCP_SLT_TWKILL);
 }
 
@@ -399,20 +418,30 @@
 	int count = 0;
 	int i;
 	
-	for(i = chain_start; i < (chain_start + ((TCP_HTABLE_SIZE/2) >> 2)); i++) {
-		struct sock *sk = tcp_established_hash[i];
+	SOCKHASH_LOCK_READ_BH();
+	for(i = chain_start; i < (chain_start + ((tcp_ehash_size >> 1) >> 2)); i++) {
+		struct sock *sk;
+
+		sk = tcp_ehash[i];
 		while(sk) {
-			if(!atomic_read(&sk->sock_readers) && sk->keepopen) {
+			struct sock *next = sk->next;
+
+			bh_lock_sock(sk);
+			if (sk->keepopen && !sk->lock.users) {
+				SOCKHASH_UNLOCK_READ_BH();
 				count += tcp_keepopen_proc(sk);
-				if(count == sysctl_tcp_max_ka_probes)
-					goto out;
+				SOCKHASH_LOCK_READ_BH();
 			}
-			sk = sk->next;
+			bh_unlock_sock(sk);
+			if(count == sysctl_tcp_max_ka_probes)
+				goto out;
+			sk = next;
 		}
 	}
 out:
-	chain_start = ((chain_start + ((TCP_HTABLE_SIZE/2)>>2)) &
-		       ((TCP_HTABLE_SIZE/2) - 1));
+	SOCKHASH_UNLOCK_READ_BH();
+	chain_start = ((chain_start + ((tcp_ehash_size >> 1)>>2)) &
+		       ((tcp_ehash_size >> 1) - 1));
 }
 
 /*
@@ -439,9 +468,11 @@
 		return;
 	}
 
-	if (atomic_read(&sk->sock_readers)) {
+	bh_lock_sock(sk);
+	if (sk->lock.users) {
 		/* Try again later */  
 		tcp_reset_xmit_timer(sk, TIME_RETRANS, HZ/20);
+		bh_unlock_sock(sk);
 		return;
 	}
 
@@ -508,12 +539,51 @@
 	tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
 
 	tcp_write_timeout(sk);
+
+	bh_unlock_sock(sk);
 }
 
 /*
  *	Slow timer for SYN-RECV sockets
  */
 
+static void tcp_do_syn_queue(struct sock *sk, struct tcp_opt *tp, unsigned long now)
+{
+	struct open_request *prev, *req;
+
+	prev = (struct open_request *) &tp->syn_wait_queue;
+	for(req = tp->syn_wait_queue; req; ) {
+		struct open_request *next = req->dl_next;
+
+		if (! req->sk) {
+			tcp_synq_unlink(tp, req, prev);
+			if(req->retrans >= sysctl_tcp_retries1) {
+				(*req->class->destructor)(req);
+				tcp_dec_slow_timer(TCP_SLT_SYNACK);
+				tp->syn_backlog--;
+				tcp_openreq_free(req);
+				if (! tp->syn_wait_queue)
+					break;
+			} else {
+				unsigned long timeo;
+				struct open_request *rp;
+
+				(*req->class->rtx_syn_ack)(sk, req);
+				req->retrans++;
+				timeo = min((TCP_TIMEOUT_INIT << req->retrans),
+					    (120 * HZ));
+				req->expires = now + timeo;
+				rp = prev->dl_next;
+				tcp_synq_queue(tp, req);
+				if(rp != prev->dl_next)
+					prev = prev->dl_next;
+			}
+		} else
+			prev = req;
+		req = next;
+	}
+}
+
 /* This now scales very nicely. -DaveM */
 static void tcp_syn_recv_timer(unsigned long data)
 {
@@ -521,66 +591,21 @@
 	unsigned long now = jiffies;
 	int i;
 
+	SOCKHASH_LOCK_READ_BH();
 	for(i = 0; i < TCP_LHTABLE_SIZE; i++) {
 		sk = tcp_listening_hash[i];
-
 		while(sk) {
 			struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
 			
 			/* TCP_LISTEN is implied. */
-			if (!atomic_read(&sk->sock_readers) && tp->syn_wait_queue) {
-				struct open_request *prev = (struct open_request *)(&tp->syn_wait_queue);
-				struct open_request *req = tp->syn_wait_queue;
-				do {
-					struct open_request *conn;
-				  
-					conn = req;
-					req = req->dl_next;
-
-					if (conn->sk) {
-						prev = conn; 
-						continue; 
-					}
-
-					tcp_synq_unlink(tp, conn, prev);
-					if (conn->retrans >= sysctl_tcp_retries1) {
-#ifdef TCP_DEBUG
-						printk(KERN_DEBUG "syn_recv: "
-						       "too many retransmits\n");
-#endif
-						(*conn->class->destructor)(conn);
-						tcp_dec_slow_timer(TCP_SLT_SYNACK);
-						tp->syn_backlog--;
-						tcp_openreq_free(conn);
-
-						if (!tp->syn_wait_queue)
-							break;
-					} else {
-						unsigned long timeo;
-						struct open_request *op; 
-
-						(*conn->class->rtx_syn_ack)(sk, conn);
-
-						conn->retrans++;
-#ifdef TCP_DEBUG
-						printk(KERN_DEBUG "syn_ack rtx %d\n",
-						       conn->retrans);
-#endif
-						timeo = min((TCP_TIMEOUT_INIT 
-							     << conn->retrans),
-							    120*HZ);
-						conn->expires = now + timeo;
-						op = prev->dl_next; 
-						tcp_synq_queue(tp, conn);
-						if (op != prev->dl_next)
-							prev = prev->dl_next;
-					}
-					/* old prev still valid here */
-				} while (req);
-			}
+			bh_lock_sock(sk);
+			if (!sk->lock.users && tp->syn_wait_queue)
+				tcp_do_syn_queue(sk, tp, now);
+			bh_unlock_sock(sk);
 			sk = sk->next;
 		}
 	}
+	SOCKHASH_UNLOCK_READ_BH();
 }
 
 void tcp_sltimer_handler(unsigned long data)
diff -u --recursive --new-file v2.3.3/linux/net/ipv4/timer.c linux/net/ipv4/timer.c
--- v2.3.3/linux/net/ipv4/timer.c	Mon Feb 22 09:05:55 1999
+++ linux/net/ipv4/timer.c	Wed May 26 18:14:38 1999
@@ -5,7 +5,7 @@
  *
  *		TIMER - implementation of software timers for IP.
  *
- * Version:	$Id: timer.c,v 1.15 1999/02/22 13:54:29 davem Exp $
+ * Version:	$Id: timer.c,v 1.16 1999/05/27 00:37:39 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -69,13 +69,15 @@
  */
 void net_timer (unsigned long data)
 {
-	struct sock *sk = (struct sock*)data;
+	struct sock *sk = (struct sock *) data;
 	int why = sk->timeout;
 
 	/* Only process if socket is not in use. */
-	if (atomic_read(&sk->sock_readers)) {
+	bh_lock_sock(sk);
+	if (sk->lock.users) {
 		/* Try again later. */ 
 		mod_timer(&sk->timer, jiffies+HZ/20);
+		bh_unlock_sock(sk);
 		return;
 	}
 
@@ -99,15 +101,15 @@
 				printk (KERN_DEBUG "non CLOSE socket in time_done\n");
 				break;
 			}
-			destroy_sock (sk);
-			break;
+			destroy_sock(sk);
+			return;
 
 		case TIME_DESTROY:
 			/* We've waited for a while for all the memory associated with
 			 * the socket to be freed.
 			 */
 			destroy_sock(sk);
-			break;
+			return;
 
 		case TIME_CLOSE:
 			/* We've waited long enough, close the socket. */
@@ -123,5 +125,8 @@
 			printk ("net_timer: timer expired - reason %d is unknown\n", why);
 			break;
 	}
+
+	/* We only need to unlock if the socket was not destroyed. */
+	bh_unlock_sock(sk);
 }
 
diff -u --recursive --new-file v2.3.3/linux/net/ipv4/udp.c linux/net/ipv4/udp.c
--- v2.3.3/linux/net/ipv4/udp.c	Mon May 10 09:55:25 1999
+++ linux/net/ipv4/udp.c	Wed May 26 18:14:38 1999
@@ -5,7 +5,7 @@
  *
  *		The User Datagram Protocol (UDP).
  *
- * Version:	$Id: udp.c,v 1.66 1999/05/08 20:00:25 davem Exp $
+ * Version:	$Id: udp.c,v 1.67 1999/05/27 00:37:50 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -128,7 +128,7 @@
 	struct sock *sk2;
 	int retval = 0, sk_reuse = sk->reuse;
 
-	SOCKHASH_LOCK();
+	SOCKHASH_LOCK_READ();
 	for(sk2 = udp_hash[snum & (UDP_HTABLE_SIZE - 1)]; sk2 != NULL; sk2 = sk2->next) {
 		if((sk2->num == snum) && (sk2 != sk)) {
 			unsigned char state = sk2->state;
@@ -158,7 +158,7 @@
 			}
 		}
 	}
-	SOCKHASH_UNLOCK();
+	SOCKHASH_UNLOCK_READ();
 	return retval;
 }
 
@@ -180,7 +180,7 @@
 	static int start = 0;
 	int i, best, best_size_so_far;
 
-	SOCKHASH_LOCK();
+	SOCKHASH_LOCK_READ();
         if (start > sysctl_local_port_range[1] || start < sysctl_local_port_range[0])
                 start = sysctl_local_port_range[0];
 
@@ -223,15 +223,10 @@
         }
 out:
 	start = result;
-	SOCKHASH_UNLOCK();
+	SOCKHASH_UNLOCK_READ();
 	return result;
 }
 
-/* Last hit UDP socket cache, this is ipv4 specific so make it static. */
-static u32 uh_cache_saddr, uh_cache_daddr;
-static u16 uh_cache_dport, uh_cache_sport;
-static struct sock *uh_cache_sk = NULL;
-
 static void udp_v4_hash(struct sock *sk)
 {
 	struct sock **skp;
@@ -240,11 +235,11 @@
 	num &= (UDP_HTABLE_SIZE - 1);
 	skp = &udp_hash[num];
 
-	SOCKHASH_LOCK();
+	SOCKHASH_LOCK_WRITE();
 	sk->next = *skp;
 	*skp = sk;
 	sk->hashent = num;
-	SOCKHASH_UNLOCK();
+	SOCKHASH_UNLOCK_WRITE();
 }
 
 static void udp_v4_unhash(struct sock *sk)
@@ -255,7 +250,7 @@
 	num &= (UDP_HTABLE_SIZE - 1);
 	skp = &udp_hash[num];
 
-	SOCKHASH_LOCK();
+	SOCKHASH_LOCK_WRITE();
 	while(*skp != NULL) {
 		if(*skp == sk) {
 			*skp = sk->next;
@@ -263,9 +258,7 @@
 		}
 		skp = &((*skp)->next);
 	}
-	if(uh_cache_sk == sk)
-		uh_cache_sk = NULL;
-	SOCKHASH_UNLOCK();
+	SOCKHASH_UNLOCK_WRITE();
 }
 
 static void udp_v4_rehash(struct sock *sk)
@@ -277,7 +270,7 @@
 	num &= (UDP_HTABLE_SIZE - 1);
 	skp = &udp_hash[oldnum];
 
-	SOCKHASH_LOCK();
+	SOCKHASH_LOCK_WRITE();
 	while(*skp != NULL) {
 		if(*skp == sk) {
 			*skp = sk->next;
@@ -288,13 +281,11 @@
 	sk->next = udp_hash[num];
 	udp_hash[num] = sk;
 	sk->hashent = num;
-	if(uh_cache_sk == sk)
-		uh_cache_sk = NULL;
-	SOCKHASH_UNLOCK();
+	SOCKHASH_UNLOCK_WRITE();
 }
 
 /* UDP is nearly always wildcards out the wazoo, it makes no sense to try
- * harder than this here plus the last hit cache. -DaveM
+ * harder than this. -DaveM
  */
 struct sock *udp_v4_lookup_longway(u32 saddr, u16 sport, u32 daddr, u16 dport, int dif)
 {
@@ -341,21 +332,9 @@
 {
 	struct sock *sk;
 
-	if(!dif && uh_cache_sk		&&
-	   uh_cache_saddr == saddr	&&
-	   uh_cache_sport == sport	&&
-	   uh_cache_dport == dport	&&
-	   uh_cache_daddr == daddr)
-		return uh_cache_sk;
-
+	SOCKHASH_LOCK_READ();
 	sk = udp_v4_lookup_longway(saddr, sport, daddr, dport, dif);
-	if(!dif) {
-		uh_cache_sk	= sk;
-		uh_cache_saddr	= saddr;
-		uh_cache_daddr	= daddr;
-		uh_cache_sport	= sport;
-		uh_cache_dport	= dport;
-	}
+	SOCKHASH_UNLOCK_READ();
 	return sk;
 }
 
@@ -393,7 +372,7 @@
 			paddr = idev->ifa_list->ifa_local;
 	}
 
-	SOCKHASH_LOCK();
+	SOCKHASH_LOCK_READ();
 	for(s = udp_v4_proxy_loop_init(hnum, hpnum, s, firstpass);
 	    s != NULL;
 	    s = udp_v4_proxy_loop_next(hnum, hpnum, s, firstpass)) {
@@ -431,7 +410,7 @@
 			}
 		}
 	}
-	SOCKHASH_UNLOCK();
+	SOCKHASH_UNLOCK_READ();
 	return result;
 }
 
@@ -979,8 +958,6 @@
 		sk->rcv_saddr=INADDR_ANY;
 		sk->daddr=INADDR_ANY;
 		sk->state = TCP_CLOSE;
-		if(uh_cache_sk == sk)
-			uh_cache_sk = NULL;
 		return 0;
 	}
 
@@ -1005,9 +982,6 @@
 	sk->dport = usin->sin_port;
 	sk->state = TCP_ESTABLISHED;
 
-	if(uh_cache_sk == sk)
-		uh_cache_sk = NULL;
-
 	sk->dst_cache = &rt->u.dst;
 	return(0);
 }
@@ -1015,6 +989,8 @@
 
 static void udp_close(struct sock *sk, long timeout)
 {
+	bh_lock_sock(sk);
+
 	/* See for explanation: raw_close in ipv4/raw.c */
 	sk->state = TCP_CLOSE;
 	udp_v4_unhash(sk);
diff -u --recursive --new-file v2.3.3/linux/net/ipv6/addrconf.c linux/net/ipv6/addrconf.c
--- v2.3.3/linux/net/ipv6/addrconf.c	Thu Mar 25 09:23:34 1999
+++ linux/net/ipv6/addrconf.c	Wed May 26 18:14:38 1999
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: addrconf.c,v 1.48 1999/03/25 10:04:43 davem Exp $
+ *	$Id: addrconf.c,v 1.49 1999/05/27 00:38:20 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -982,6 +982,7 @@
 		return;
 	}
 
+	read_lock_bh(&dev_base_lock);
         for (dev = dev_base; dev != NULL; dev = dev->next) {
 		if (dev->ip_ptr && (dev->flags & IFF_UP)) {
 			struct in_device * in_dev = dev->ip_ptr;
@@ -1000,6 +1001,7 @@
 					flag |= IFA_HOST;
 				}
 
+				read_unlock_bh(&dev_base_lock);
 				addrconf_lock();
 				ifp = ipv6_add_addr(idev, &addr, flag);
 				if (ifp) {
@@ -1011,9 +1013,11 @@
 					ipv6_ifa_notify(RTM_NEWADDR, ifp);
 				}
 				addrconf_unlock();
+				read_lock_bh(&dev_base_lock);
 			}
 		}
         }
+	read_unlock_bh(&dev_base_lock);
 }
 
 static void init_loopback(struct device *dev)
@@ -1842,11 +1846,12 @@
 	struct device *dev;
 
 	/* This takes sense only during module load. */
-
+	read_lock_bh(&dev_base_lock);
 	for (dev = dev_base; dev; dev = dev->next) {
 		if (!(dev->flags&IFF_UP))
 			continue;
 
+		read_unlock_bh(&dev_base_lock);
 		switch (dev->type) {
 		case ARPHRD_LOOPBACK:	
 			init_loopback(dev);
@@ -1857,7 +1862,9 @@
 		default:
 			/* Ignore all other */
 		}
+		read_lock_bh(&dev_base_lock);
 	}
+	read_unlock_bh(&dev_base_lock);
 #endif
 	
 #ifdef CONFIG_PROC_FS
diff -u --recursive --new-file v2.3.3/linux/net/ipv6/exthdrs.c linux/net/ipv6/exthdrs.c
--- v2.3.3/linux/net/ipv6/exthdrs.c	Mon May 17 09:55:23 1999
+++ linux/net/ipv6/exthdrs.c	Wed May 26 18:14:38 1999
@@ -7,7 +7,7 @@
  *	Andi Kleen		<ak@muc.de>
  *	Alexey Kuznetsov	<kuznet@ms2.inr.ac.ru>
  *
- *	$Id: exthdrs.c,v 1.8 1998/10/03 09:38:27 davem Exp $
+ *	$Id: exthdrs.c,v 1.9 1999/05/17 23:47:35 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
diff -u --recursive --new-file v2.3.3/linux/net/ipv6/icmp.c linux/net/ipv6/icmp.c
--- v2.3.3/linux/net/ipv6/icmp.c	Sun Mar 21 07:22:00 1999
+++ linux/net/ipv6/icmp.c	Wed May 26 18:14:38 1999
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>
  *
- *	$Id: icmp.c,v 1.21 1999/03/21 05:22:51 davem Exp $
+ *	$Id: icmp.c,v 1.22 1999/05/19 22:06:39 davem Exp $
  *
  *	Based on net/ipv4/icmp.c
  *
@@ -315,6 +315,7 @@
 	fl.nl_u.ip6_u.daddr = &hdr->saddr;
 	fl.nl_u.ip6_u.saddr = saddr;
 	fl.oif = iif;
+	fl.fl6_flowlabel = 0;
 	fl.uli_u.icmpt.type = type;
 	fl.uli_u.icmpt.code = code;
 
@@ -388,6 +389,7 @@
 	fl.nl_u.ip6_u.daddr = &hdr->saddr;
 	fl.nl_u.ip6_u.saddr = saddr;
 	fl.oif = skb->dev->ifindex;
+	fl.fl6_flowlabel = 0;
 	fl.uli_u.icmpt.type = ICMPV6_ECHO_REPLY;
 	fl.uli_u.icmpt.code = 0;
 
diff -u --recursive --new-file v2.3.3/linux/net/ipv6/mcast.c linux/net/ipv6/mcast.c
--- v2.3.3/linux/net/ipv6/mcast.c	Thu Mar 25 09:23:34 1999
+++ linux/net/ipv6/mcast.c	Wed May 26 18:14:38 1999
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: mcast.c,v 1.19 1999/03/25 10:04:50 davem Exp $
+ *	$Id: mcast.c,v 1.20 1999/05/27 00:38:23 davem Exp $
  *
  *	Based on linux/ipv4/igmp.c and linux/ipv4/ip_sockglue.c 
  *
@@ -615,6 +615,7 @@
 	int len=0;
 	struct device *dev;
 	
+	read_lock_bh(&dev_base_lock);
 	for (dev = dev_base; dev; dev = dev->next) {
 		struct inet6_dev *idev;
 
@@ -647,6 +648,8 @@
 	*eof = 1;
 
 done:
+	read_unlock_bh(&dev_base_lock);
+
 	*start=buffer+(offset-begin);
 	len-=(offset-begin);
 	if(len>length)
diff -u --recursive --new-file v2.3.3/linux/net/ipv6/proc.c linux/net/ipv6/proc.c
--- v2.3.3/linux/net/ipv6/proc.c	Thu Aug 27 19:33:09 1998
+++ linux/net/ipv6/proc.c	Wed May 26 18:14:38 1999
@@ -7,7 +7,7 @@
  *		PROC file system.  This is very similar to the IPv4 version,
  *		except it reports the sockets in the INET6 address family.
  *
- * Version:	$Id: proc.c,v 1.9 1998/08/26 12:05:11 davem Exp $
+ * Version:	$Id: proc.c,v 1.10 1999/05/27 00:38:14 davem Exp $
  *
  * Authors:	David S. Miller (davem@caip.rutgers.edu)
  *
@@ -52,7 +52,7 @@
 										/*144 */
 
 	pos = 149;
-	SOCKHASH_LOCK();
+	SOCKHASH_LOCK_READ();
 	sp = pro->sklist_next;
 	while(sp != (struct sock *)pro) {
 		struct tcp_tw_bucket *tw = (struct tcp_tw_bucket *)sp;
@@ -72,6 +72,7 @@
 		}
 		destp = ntohs(sp->dport);
 		srcp  = ntohs(sp->sport);
+
 		if((format == 0) && (sp->state == TCP_TIME_WAIT)) {
 			extern int tcp_tw_death_row_slot;
 			int slot_dist;
@@ -85,10 +86,8 @@
 				slot_dist = tcp_tw_death_row_slot - slot_dist;
 			timer_expires	= jiffies + (slot_dist * TCP_TWKILL_PERIOD);
 		} else {
-			timer_active1 = del_timer(&tp->retransmit_timer);
-			timer_active2 = del_timer(&sp->timer);
-			if(!timer_active1) tp->retransmit_timer.expires = 0;
-			if(!timer_active2) sp->timer.expires = 0;
+			timer_active1 = tp->retransmit_timer.prev != NULL;
+			timer_active2 = sp->timer.prev != NULL;
 			timer_active = 0;
 			timer_expires = (unsigned) -1;
 		}
@@ -128,8 +127,6 @@
 			((!tw_bucket && sp->socket) ?
 			 sp->socket->inode->i_ino : 0));
 
-		if(timer_active1) add_timer(&tp->retransmit_timer);
-		if(timer_active2) add_timer(&sp->timer);
 		len += sprintf(buffer+len, "%-148s\n", tmpbuf);
 		if(len >= length)
 			break;
@@ -137,7 +134,7 @@
 		sp = sp->sklist_next;
 		i++;
 	}
-	SOCKHASH_UNLOCK();
+	SOCKHASH_UNLOCK_READ();
 
 	begin = len - (pos - offset);
 	*start = buffer + begin;
diff -u --recursive --new-file v2.3.3/linux/net/ipv6/raw.c linux/net/ipv6/raw.c
--- v2.3.3/linux/net/ipv6/raw.c	Thu Apr 22 19:45:20 1999
+++ linux/net/ipv6/raw.c	Wed May 26 18:14:38 1999
@@ -7,7 +7,7 @@
  *
  *	Adapted from linux/net/ipv4/raw.c
  *
- *	$Id: raw.c,v 1.24 1999/04/22 10:07:45 davem Exp $
+ *	$Id: raw.c,v 1.25 1999/05/27 00:38:16 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -50,11 +50,11 @@
 
 	num &= (RAWV6_HTABLE_SIZE - 1);
 	skp = &raw_v6_htable[num];
-	SOCKHASH_LOCK();
+	SOCKHASH_LOCK_WRITE();
 	sk->next = *skp;
 	*skp = sk;
 	sk->hashent = num;
-	SOCKHASH_UNLOCK();
+	SOCKHASH_UNLOCK_WRITE();
 }
 
 static void raw_v6_unhash(struct sock *sk)
@@ -65,7 +65,7 @@
 	num &= (RAWV6_HTABLE_SIZE - 1);
 	skp = &raw_v6_htable[num];
 
-	SOCKHASH_LOCK();
+	SOCKHASH_LOCK_WRITE();
 	while(*skp != NULL) {
 		if(*skp == sk) {
 			*skp = sk->next;
@@ -73,7 +73,7 @@
 		}
 		skp = &((*skp)->next);
 	}
-	SOCKHASH_UNLOCK();
+	SOCKHASH_UNLOCK_WRITE();
 }
 
 static void raw_v6_rehash(struct sock *sk)
@@ -85,7 +85,7 @@
 	num &= (RAWV6_HTABLE_SIZE - 1);
 	skp = &raw_v6_htable[oldnum];
 
-	SOCKHASH_LOCK();
+	SOCKHASH_LOCK_WRITE();
 	while(*skp != NULL) {
 		if(*skp == sk) {
 			*skp = sk->next;
@@ -96,7 +96,7 @@
 	sk->next = raw_v6_htable[num];
 	raw_v6_htable[num] = sk;
 	sk->hashent = num;
-	SOCKHASH_UNLOCK();
+	SOCKHASH_UNLOCK_WRITE();
 }
 
 static __inline__ int inet6_mc_check(struct sock *sk, struct in6_addr *addr)
@@ -631,6 +631,8 @@
 
 static void rawv6_close(struct sock *sk, long timeout)
 {
+	bh_lock_sock(sk);
+
 	/* See for explanation: raw_close in ipv4/raw.c */
 	sk->state = TCP_CLOSE;
 	raw_v6_unhash(sk);
diff -u --recursive --new-file v2.3.3/linux/net/ipv6/tcp_ipv6.c linux/net/ipv6/tcp_ipv6.c
--- v2.3.3/linux/net/ipv6/tcp_ipv6.c	Fri Apr 23 18:39:47 1999
+++ linux/net/ipv6/tcp_ipv6.c	Wed May 26 18:14:38 1999
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: tcp_ipv6.c,v 1.104 1999/04/24 00:27:25 davem Exp $
+ *	$Id: tcp_ipv6.c,v 1.106 1999/05/27 01:12:44 davem Exp $
  *
  *	Based on: 
  *	linux/net/ipv4/tcp.c
@@ -67,7 +67,7 @@
 	int hashent = (lport ^ fport);
 
 	hashent ^= (laddr->s6_addr32[3] ^ faddr->s6_addr32[3]);
-	return (hashent & ((TCP_HTABLE_SIZE/2) - 1));
+	return (hashent & ((tcp_ehash_size >> 1) - 1));
 }
 
 static __inline__ int tcp_v6_sk_hashfn(struct sock *sk)
@@ -89,8 +89,8 @@
 	struct tcp_bind_bucket *tb;
 	int result = 0;
 
-	SOCKHASH_LOCK();
-	for(tb = tcp_bound_hash[tcp_bhashfn(snum)];
+	SOCKHASH_LOCK_WRITE();
+	for(tb = tcp_bhash[tcp_bhashfn(snum)];
 	    (tb && (tb->port != snum));
 	    tb = tb->next)
 		;
@@ -156,7 +156,7 @@
 		}
 	}
 go_like_smoke:
-	SOCKHASH_UNLOCK();
+	SOCKHASH_UNLOCK_WRITE();
 	return result;
 }
 
@@ -172,20 +172,20 @@
 	if(sk->state != TCP_CLOSE) {
 		struct sock **skp;
 
-		SOCKHASH_LOCK();
-		skp = &tcp_established_hash[(sk->hashent = tcp_v6_sk_hashfn(sk))];
+		SOCKHASH_LOCK_WRITE();
+		skp = &tcp_ehash[(sk->hashent = tcp_v6_sk_hashfn(sk))];
 		if((sk->next = *skp) != NULL)
 			(*skp)->pprev = &sk->next;
 		*skp = sk;
 		sk->pprev = skp;
 		tcp_sk_bindify(sk);
-		SOCKHASH_UNLOCK();
+		SOCKHASH_UNLOCK_WRITE();
 	}
 }
 
 static void tcp_v6_unhash(struct sock *sk)
 {
-	SOCKHASH_LOCK();
+	SOCKHASH_LOCK_WRITE();
 	if(sk->pprev) {
 		if(sk->next)
 			sk->next->pprev = sk->pprev;
@@ -194,14 +194,14 @@
 		tcp_sk_unbindify(sk);
 		tcp_reg_zap(sk);
 	}
-	SOCKHASH_UNLOCK();
+	SOCKHASH_UNLOCK_WRITE();
 }
 
 static void tcp_v6_rehash(struct sock *sk)
 {
 	unsigned char state;
 
-	SOCKHASH_LOCK();
+	SOCKHASH_LOCK_WRITE();
 	state = sk->state;
 	if(sk->pprev != NULL) {
 		if(sk->next)
@@ -216,7 +216,7 @@
 		if(state == TCP_LISTEN)
 			skp = &tcp_listening_hash[tcp_sk_listen_hashfn(sk)];
 		else
-			skp = &tcp_established_hash[(sk->hashent = tcp_v6_sk_hashfn(sk))];
+			skp = &tcp_ehash[(sk->hashent = tcp_v6_sk_hashfn(sk))];
 
 		if((sk->next = *skp) != NULL)
 			(*skp)->pprev = &sk->next;
@@ -225,7 +225,7 @@
 		if(state == TCP_LISTEN)
 			tcp_sk_bindify(sk);
 	}
-	SOCKHASH_UNLOCK();
+	SOCKHASH_UNLOCK_WRITE();
 }
 
 static struct sock *tcp_v6_lookup_listener(struct in6_addr *daddr, unsigned short hnum, int dif)
@@ -264,10 +264,10 @@
 
 /* Sockets in TCP_CLOSE state are _always_ taken out of the hash, so
  * we need not check it for TCP lookups anymore, thanks Alexey. -DaveM
- * It is assumed that this code only gets called from within NET_BH.
+ *
+ * The sockhash lock must be held as a reader here.
  */
-static inline struct sock *__tcp_v6_lookup(struct tcphdr *th,
-					   struct in6_addr *saddr, u16 sport,
+static inline struct sock *__tcp_v6_lookup(struct in6_addr *saddr, u16 sport,
 					   struct in6_addr *daddr, u16 dport,
 					   int dif)
 {
@@ -285,7 +285,7 @@
 	 * have wildcards anyways.
 	 */
 	hash = tcp_v6_hashfn(daddr, hnum, saddr, sport);
-	for(sk = tcp_established_hash[hash]; sk; sk = sk->next) {
+	for(sk = tcp_ehash[hash]; sk; sk = sk->next) {
 		/* For IPV6 do the cheaper port and family tests first. */
 		if(TCP_IPV6_MATCH(sk, saddr, daddr, ports, dif)) {
 			if (sk->state == TCP_ESTABLISHED)
@@ -294,7 +294,7 @@
 		}
 	}
 	/* Must check for a TIME_WAIT'er before going to listener hash. */
-	for(sk = tcp_established_hash[hash+(TCP_HTABLE_SIZE/2)]; sk; sk = sk->next) {
+	for(sk = tcp_ehash[hash+(tcp_ehash_size >> 1)]; sk; sk = sk->next) {
 		if(*((__u32 *)&(sk->dport))	== ports	&&
 		   sk->family			== PF_INET6) {
 			struct tcp_tw_bucket *tw = (struct tcp_tw_bucket *)sk;
@@ -309,7 +309,13 @@
 	return sk;
 }
 
-#define tcp_v6_lookup(sa, sp, da, dp, dif) __tcp_v6_lookup((0),(sa),(sp),(da),(dp),(dif))
+#define tcp_v6_lookup(sa, sp, da, dp, dif) \
+({	struct sock *___sk; \
+	SOCKHASH_LOCK_READ(); \
+	___sk = __tcp_v6_lookup((sa),(sp),(da),(dp),(dif)); \
+	SOCKHASH_UNLOCK_READ(); \
+	___sk; \
+})
 
 static __inline__ u16 tcp_v6_check(struct tcphdr *th, int len,
 				   struct in6_addr *saddr, 
@@ -344,24 +350,26 @@
 	int retval = 1;
 
 	/* Freeze the hash while we snoop around. */
-	SOCKHASH_LOCK();
-	tb = tcp_bound_hash[tcp_bhashfn(snum)];
+	SOCKHASH_LOCK_READ();
+	tb = tcp_bhash[tcp_bhashfn(snum)];
 	for(; tb; tb = tb->next) {
 		if(tb->port == snum && tb->owners != NULL) {
 			/* Almost certainly the re-use port case, search the real hashes
 			 * so it actually scales.  (we hope that all ipv6 ftp servers will
 			 * use passive ftp, I just cover this case for completeness)
 			 */
-			sk = __tcp_v6_lookup(NULL, &sk->net_pinfo.af_inet6.daddr,
+			sk = __tcp_v6_lookup(&sk->net_pinfo.af_inet6.daddr,
 					     sk->dport,
 					     &sk->net_pinfo.af_inet6.rcv_saddr, snum,
 					     sk->bound_dev_if);
+			SOCKHASH_UNLOCK_READ();
+
 			if((sk != NULL) && (sk->state != TCP_LISTEN))
 				retval = 0;
-			break;
+			return retval;
 		}
 	}
-	SOCKHASH_UNLOCK();
+	SOCKHASH_UNLOCK_READ();
 	return retval;
 }
 
@@ -551,7 +559,7 @@
 
 failure:
 	dst_release(xchg(&sk->dst_cache, NULL));
-	memcpy(&np->daddr, 0, sizeof(struct in6_addr));
+	memset(&np->daddr, 0, sizeof(struct in6_addr));
 	sk->daddr = 0;
 	return err;
 }
@@ -628,11 +636,14 @@
 	if (type == ICMPV6_PKT_TOOBIG) {
 		struct dst_entry *dst = NULL;
 
-		if (atomic_read(&sk->sock_readers))
+		if (sk->state == TCP_LISTEN)
 			return;
 
-		if (sk->state == TCP_LISTEN)
+		bh_lock_sock(sk);
+		if(sk->lock.users) {
+			bh_unlock_sock(sk);
 			return;
+		}
 
 		/* icmp should have updated the destination cache entry */
 		if (sk->dst_cache)
@@ -664,7 +675,7 @@
 			tcp_simple_retransmit(sk);
 		} /* else let the usual retransmit timer handle it */
 		dst_release(dst);
-		return;
+		bh_unlock_sock(sk);
 	}
 
 	icmpv6_err_convert(type, code, &err);
@@ -674,11 +685,13 @@
 		struct open_request *req, *prev;
 		struct ipv6hdr hd;
 	case TCP_LISTEN:
-		if (atomic_read(&sk->sock_readers)) {
+		bh_lock_sock(sk);
+		if (sk->lock.users) {
 			net_statistics.LockDroppedIcmps++;
 			 /* If too many ICMPs get dropped on busy
 			  * servers this needs to be solved differently.
 			  */
+			bh_unlock_sock(sk);
  			return;
 		}
 
@@ -686,20 +699,22 @@
 		ipv6_addr_copy(&hd.saddr, saddr);
 		ipv6_addr_copy(&hd.daddr, daddr); 
 		req = tcp_v6_search_req(tp, &hd, th, tcp_v6_iif(skb), &prev);
-		if (!req)
-			return;
-		if (seq != req->snt_isn) {
+		if (!req || (seq != req->snt_isn)) {
 			net_statistics.OutOfWindowIcmps++;
+			bh_unlock_sock(sk);
 			return;
 		}
 		if (req->sk) {
+			bh_unlock_sock(sk);
 			sk = req->sk; /* report error in accept */
 		} else {
 			tp->syn_backlog--;
 			tcp_synq_unlink(tp, req, prev);
 			req->class->destructor(req);
 			tcp_openreq_free(req);
+			bh_unlock_sock(sk);
 		}
+
 		/* FALL THROUGH */ 
 	case TCP_SYN_SENT:
 	case TCP_SYN_RECV:  /* Cannot happen */ 
@@ -1210,12 +1225,20 @@
 	return sk;
 }
 
+/* The socket must have it's spinlock held when we get
+ * here.
+ *
+ * We have a potential double-lock case here, so even when
+ * doing backlog processing we use the BH locking scheme.
+ * This is because we cannot sleep with the original spinlock
+ * held.
+ */
 static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
 {
 #ifdef CONFIG_FILTER
 	struct sk_filter *filter;
 #endif
-	int users = 0;
+	int users = 0, need_unlock = 0;
 
 	/* Imagine: socket is IPv6. IPv4 packet arrives,
 	   goes to IPv4 receive handler and backlogged.
@@ -1286,19 +1309,24 @@
 		 * otherwise we just shortcircuit this and continue with
 		 * the new socket..
 		 */
-		if (atomic_read(&nsk->sock_readers)) {
-			skb_orphan(skb);
-			__skb_queue_tail(&nsk->back_log, skb);
-			return 0;
+		if(nsk != sk) {
+			bh_lock_sock(nsk);
+			if (nsk->lock.users) {
+				skb_orphan(skb);
+				sk_add_backlog(nsk, skb);
+				bh_unlock_sock(nsk);
+				return 0;
+			}
+			need_unlock = 1;
+			sk = nsk;
 		}
-		sk = nsk;
 	}
 
 	if (tcp_rcv_state_process(sk, skb, skb->h.th, skb->len))
 		goto reset;
 	if (users)
 		goto ipv6_pktoptions;
-	return 0;
+	goto out_maybe_unlock;
 
 reset:
 	tcp_v6_send_reset(skb);
@@ -1306,7 +1334,7 @@
 	if (users)
 		kfree_skb(skb);
 	kfree_skb(skb);
-	return 0;
+	goto out_maybe_unlock;
 
 ipv6_pktoptions:
 	/* Do you ask, what is it?
@@ -1335,6 +1363,9 @@
 
 	if (skb)
 		kfree_skb(skb);
+out_maybe_unlock:
+	if (need_unlock)
+		bh_unlock_sock(sk);
 	return 0;
 }
 
@@ -1344,6 +1375,7 @@
 	struct sock *sk;
 	struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
 	struct in6_addr *daddr = &skb->nh.ipv6h->daddr;
+	int ret;
 
 	th = skb->h.th;
 
@@ -1383,7 +1415,9 @@
 		/* CHECKSUM_UNNECESSARY */
 	};
 
-	sk = __tcp_v6_lookup(th, saddr, th->source, daddr, th->dest, tcp_v6_iif(skb));
+	SOCKHASH_LOCK_READ_BH();
+	sk = __tcp_v6_lookup(saddr, th->source, daddr, th->dest, tcp_v6_iif(skb));
+	SOCKHASH_UNLOCK_READ_BH();
 
 	if (!sk)
 		goto no_tcp_socket;
@@ -1396,11 +1430,15 @@
 	if(sk->state == TCP_TIME_WAIT)
 		goto do_time_wait;
 
-	if (!atomic_read(&sk->sock_readers))
-		return tcp_v6_do_rcv(sk, skb);
+	bh_lock_sock(sk);
+	ret = 0;
+	if (!sk->lock.users)
+		ret = tcp_v6_do_rcv(sk, skb);
+	else
+		sk_add_backlog(sk, skb);
+	bh_unlock_sock(sk);
 
-	__skb_queue_tail(&sk->back_log, skb);
-	return(0);
+	return ret;
 
 no_tcp_socket:
 	tcp_v6_send_reset(skb);
diff -u --recursive --new-file v2.3.3/linux/net/ipv6/udp.c linux/net/ipv6/udp.c
--- v2.3.3/linux/net/ipv6/udp.c	Mon May 10 09:55:25 1999
+++ linux/net/ipv6/udp.c	Wed May 26 18:14:38 1999
@@ -7,7 +7,7 @@
  *
  *	Based on linux/ipv4/udp.c
  *
- *	$Id: udp.c,v 1.40 1999/05/08 20:00:32 davem Exp $
+ *	$Id: udp.c,v 1.41 1999/05/27 00:38:18 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -55,7 +55,7 @@
 	int addr_type = ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr);
 	int retval = 0, sk_reuse = sk->reuse;
 
-	SOCKHASH_LOCK();
+	SOCKHASH_LOCK_READ();
 	for(sk2 = udp_hash[snum & (UDP_HTABLE_SIZE - 1)]; sk2 != NULL; sk2 = sk2->next) {
 		if((sk2->num == snum) && (sk2 != sk)) {
 			unsigned char state = sk2->state;
@@ -86,7 +86,7 @@
 			}
 		}
 	}
-	SOCKHASH_UNLOCK();
+	SOCKHASH_UNLOCK_READ();
 	return retval;
 }
 
@@ -98,11 +98,11 @@
 	num &= (UDP_HTABLE_SIZE - 1);
 	skp = &udp_hash[num];
 
-	SOCKHASH_LOCK();
+	SOCKHASH_LOCK_WRITE();
 	sk->next = *skp;
 	*skp = sk;
 	sk->hashent = num;
-	SOCKHASH_UNLOCK();
+	SOCKHASH_UNLOCK_WRITE();
 }
 
 static void udp_v6_unhash(struct sock *sk)
@@ -113,7 +113,7 @@
 	num &= (UDP_HTABLE_SIZE - 1);
 	skp = &udp_hash[num];
 
-	SOCKHASH_LOCK();
+	SOCKHASH_LOCK_WRITE();
 	while(*skp != NULL) {
 		if(*skp == sk) {
 			*skp = sk->next;
@@ -121,7 +121,7 @@
 		}
 		skp = &((*skp)->next);
 	}
-	SOCKHASH_UNLOCK();
+	SOCKHASH_UNLOCK_WRITE();
 }
 
 static void udp_v6_rehash(struct sock *sk)
@@ -133,7 +133,7 @@
 	num &= (UDP_HTABLE_SIZE - 1);
 	skp = &udp_hash[oldnum];
 
-	SOCKHASH_LOCK();
+	SOCKHASH_LOCK_WRITE();
 	while(*skp != NULL) {
 		if(*skp == sk) {
 			*skp = sk->next;
@@ -144,7 +144,7 @@
 	sk->next = udp_hash[num];
 	udp_hash[num] = sk;
 	sk->hashent = num;
-	SOCKHASH_UNLOCK();
+	SOCKHASH_UNLOCK_WRITE();
 }
 
 static struct sock *udp_v6_lookup(struct in6_addr *saddr, u16 sport,
@@ -154,6 +154,7 @@
 	unsigned short hnum = ntohs(dport);
 	int badness = -1;
 
+	SOCKHASH_LOCK_READ();
 	for(sk = udp_hash[hnum & (UDP_HTABLE_SIZE - 1)]; sk != NULL; sk = sk->next) {
 		if((sk->num == hnum)		&&
 		   (sk->family == PF_INET6)	&&
@@ -189,6 +190,7 @@
 			}
 		}
 	}
+	SOCKHASH_UNLOCK_READ();
 	return result;
 }
 
@@ -331,6 +333,8 @@
 
 static void udpv6_close(struct sock *sk, long timeout)
 {
+	bh_lock_sock(sk);
+
 	/* See for explanation: raw_close in ipv4/raw.c */
 	sk->state = TCP_CLOSE;
 	udp_v6_unhash(sk);
diff -u --recursive --new-file v2.3.3/linux/net/irda/af_irda.c linux/net/irda/af_irda.c
--- v2.3.3/linux/net/irda/af_irda.c	Fri May 14 18:55:32 1999
+++ linux/net/irda/af_irda.c	Sun May 30 10:28:16 1999
@@ -6,7 +6,7 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Sun May 31 10:12:43 1998
- * Modified at:   Thu Apr 22 12:08:04 1999
+ * Modified at:   Tue May 11 12:42:26 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * Sources:       af_netroom.c, af_ax25.c, af_rose.c, af_x25.c etc.
  * 
@@ -30,6 +30,7 @@
 #include <linux/if_arp.h>
 #include <linux/net.h>
 #include <linux/irda.h>
+#include <linux/poll.h>
 
 #include <asm/uaccess.h>
 
@@ -46,11 +47,12 @@
 extern int  irlap_driver_rcv(struct sk_buff *, struct device *, 
 			     struct packet_type *);
 
-static struct proto_ops irda_proto_ops;
+static struct proto_ops irda_stream_ops;
+static struct proto_ops irda_dgram_ops;
 static hashbin_t *cachelog = NULL;
 static DECLARE_WAIT_QUEUE_HEAD(discovery_wait); /* Wait for discovery */
 
-#define IRDA_MAX_HEADER (TTP_HEADER+LMP_HEADER+LAP_HEADER)
+#define IRDA_MAX_HEADER (TTP_MAX_HEADER)
 
 /*
  * Function irda_data_indication (instance, sap, skb)
@@ -121,7 +123,8 @@
  */
 static void irda_connect_confirm(void *instance, void *sap, 
 				 struct qos_info *qos,
-				 __u32 max_sdu_size, struct sk_buff *skb)
+				 __u32 max_sdu_size, __u8 max_header_size, 
+				 struct sk_buff *skb)
 {
 	struct irda_sock *self;
 	struct sock *sk;
@@ -130,13 +133,28 @@
 
 	self = (struct irda_sock *) instance;
 
+	/* How much header space do we need to reserve */
+	self->max_header_size = max_header_size;
+
+	/* IrTTP max SDU size in transmit direction */
 	self->max_sdu_size_tx = max_sdu_size;
+
+	/* Find out what the largest chunk of data that we can transmit is */
+	if (max_sdu_size == SAR_DISABLE)
+		self->max_data_size = qos->data_size.value - max_header_size;
+	else
+		self->max_data_size = max_sdu_size;
+
+	DEBUG(0, __FUNCTION__ "(), max_data_size=%d\n", self->max_data_size);
+
 	memcpy(&self->qos_tx, qos, sizeof(struct qos_info));
 
 	sk = self->sk;
 	if (sk == NULL)
 		return;
 
+	skb_queue_tail(&sk->receive_queue, skb);
+
 	/* We are now connected! */
 	sk->state = TCP_ESTABLISHED;
 	sk->state_change(sk);
@@ -150,7 +168,7 @@
  */
 static void irda_connect_indication(void *instance, void *sap, 
 				    struct qos_info *qos, __u32 max_sdu_size,
-				    struct sk_buff *skb)
+				    __u8 max_header_size, struct sk_buff *skb)
 {
 	struct irda_sock *self;
 	struct sock *sk;
@@ -158,8 +176,21 @@
 	DEBUG(1, __FUNCTION__ "()\n");
 
 	self = (struct irda_sock *) instance;
-	
-	self->max_sdu_size_tx = max_sdu_size;
+
+	/* How much header space do we need to reserve */
+	self->max_header_size = max_header_size;
+
+	/* IrTTP max SDU size in transmit direction */
+	self->max_sdu_size_tx = max_sdu_size;	
+
+	/* Find out what the largest chunk of data that we can transmit is */
+	if (max_sdu_size == SAR_DISABLE)
+		self->max_data_size = qos->data_size.value - max_header_size;
+	else
+		self->max_data_size = max_sdu_size;
+
+	DEBUG(0, __FUNCTION__ "(), max_data_size=%d\n", self->max_data_size);
+
 	memcpy(&self->qos_tx, qos, sizeof(struct qos_info));
 
 	sk = self->sk;
@@ -187,12 +218,12 @@
 
 	skb = dev_alloc_skb(64);
 	if (skb == NULL) {
-		DEBUG( 0, __FUNCTION__ "() Could not allocate sk_buff!\n");
+		DEBUG(0, __FUNCTION__ "() Unable to allocate sk_buff!\n");
 		return;
 	}
 
 	/* Reserve space for MUX_CONTROL and LAP header */
-	skb_reserve(skb, TTP_HEADER+LMP_CONTROL_HEADER+LAP_HEADER);
+	skb_reserve(skb, IRDA_MAX_HEADER);
 
 	irttp_connect_response(self->tsap, self->max_sdu_size_rx, skb);
 }
@@ -514,10 +545,13 @@
 	new->stsap_sel = new->tsap->stsap_sel;
 	new->dtsap_sel = new->tsap->dtsap_sel;
 	new->saddr = irttp_get_saddr(new->tsap);
-	new->saddr = irttp_get_saddr(new->tsap);
+	new->daddr = irttp_get_daddr(new->tsap);
 
 	new->max_sdu_size_tx = self->max_sdu_size_tx;
 	new->max_sdu_size_rx = self->max_sdu_size_rx;
+	new->max_data_size   = self->max_data_size;
+	new->max_header_size = self->max_header_size;
+
 	memcpy(&new->qos_tx, &self->qos_tx, sizeof(struct qos_info));
 
 	/* Clean up the original one to keep it in listen state */
@@ -669,7 +703,7 @@
 
 	sock_init_data(sock, sk);
 
-	sock->ops    = &irda_proto_ops;
+	sock->ops    = &irda_stream_ops;
 	sk->protocol = protocol;
 
 	/* Register as a client with IrLMP */
@@ -786,12 +820,20 @@
 			return -ENOTCONN;
 	}
 
-	skb = sock_alloc_send_skb(sk, len + IRDA_MAX_HEADER, 0, 
+	/* Check that we don't send out to big frames */
+	if (len > self->max_data_size) {
+		DEBUG(0, __FUNCTION__ "(), Warning to much data! "
+		      "Chopping frame from %d to %d bytes!\n", len, 
+		      self->max_data_size);
+		len = self->max_data_size;
+	}
+
+	skb = sock_alloc_send_skb(sk, len + self->max_header_size, 0, 
 				  msg->msg_flags & MSG_DONTWAIT, &err);
 	if (!skb)
 		return -ENOBUFS;
 
-	skb_reserve(skb, IRDA_MAX_HEADER);
+	skb_reserve(skb, self->max_header_size);
 	
 	DEBUG(4, __FUNCTION__ "(), appending user data\n");
 	asmptr = skb->h.raw = skb_put(skb, len);
@@ -815,8 +857,8 @@
  *    Try to receive message and copy it to user
  *
  */
-static int irda_recvmsg(struct socket *sock, struct msghdr *msg, int size,
-			int flags, struct scm_cookie *scm)
+static int irda_recvmsg_dgram(struct socket *sock, struct msghdr *msg, 
+			      int size, int flags, struct scm_cookie *scm)
 {
 	struct irda_sock *self;
 	struct sock *sk = sock->sk;
@@ -862,6 +904,161 @@
 }
 
 /*
+ * Function irda_data_wait (sk)
+ *
+ *    Sleep until data has arrive. But check for races..
+ *
+ */
+static void irda_data_wait(struct sock *sk)
+{
+	if (!skb_peek(&sk->receive_queue)) {
+		sk->socket->flags |= SO_WAITDATA;
+		interruptible_sleep_on(sk->sleep);
+		sk->socket->flags &= ~SO_WAITDATA;
+	}
+}
+
+/*
+ * Function irda_recvmsg_stream (sock, msg, size, flags, scm)
+ *
+ *    
+ *
+ */
+static int irda_recvmsg_stream(struct socket *sock, struct msghdr *msg, 
+			       int size, int flags, struct scm_cookie *scm)
+{
+	struct irda_sock *self;
+	struct sock *sk = sock->sk;
+	int noblock = flags & MSG_DONTWAIT;
+	int copied = 0;
+	int target = 1;
+
+	DEBUG(3, __FUNCTION__ "()\n");
+
+	self = sk->protinfo.irda;
+	ASSERT(self != NULL, return -1;);
+
+	if (sock->flags & SO_ACCEPTCON) 
+		return(-EINVAL);
+
+	if (flags & MSG_OOB)
+		return -EOPNOTSUPP;
+
+	if (flags & MSG_WAITALL)
+		target = size;
+		
+		
+	msg->msg_namelen = 0;
+
+	/* Lock the socket to prevent queue disordering
+	 * while sleeps in memcpy_tomsg
+	 */
+/* 	down(&self->readsem); */
+
+	do {
+		int chunk;
+		struct sk_buff *skb;
+
+		skb=skb_dequeue(&sk->receive_queue);
+		if (skb==NULL) {
+			if (copied >= target)
+				break;
+			
+			/*
+			 *	POSIX 1003.1g mandates this order.
+			 */
+			
+			if (sk->err) {
+				/* up(&self->readsem); */
+				return sock_error(sk);
+			}
+
+			if (sk->shutdown & RCV_SHUTDOWN)
+				break;
+
+		/* 	up(&self->readsem); */
+
+			if (noblock)
+				return -EAGAIN;
+			irda_data_wait(sk);
+			if (signal_pending(current))
+				return -ERESTARTSYS;
+		/* 	down(&self->readsem); */
+			continue;
+		}
+
+		/* Never glue messages from different writers */
+/* 		if (check_creds && */
+/* 		    memcmp(UNIXCREDS(skb), &scm->creds, sizeof(scm->creds)) != 0) */
+/* 		{ */
+/* 			skb_queue_head(&sk->receive_queue, skb); */
+/* 			break; */
+/* 		} */
+
+		chunk = min(skb->len, size);
+		if (memcpy_toiovec(msg->msg_iov, skb->data, chunk)) {
+			skb_queue_head(&sk->receive_queue, skb);
+			if (copied == 0)
+				copied = -EFAULT;
+			break;
+		}
+		copied += chunk;
+		size -= chunk;
+
+ 		/* Copy credentials */
+/* 		scm->creds = *UNIXCREDS(skb); */
+/* 		check_creds = 1; */
+
+		/* Mark read part of skb as used */
+		if (!(flags & MSG_PEEK)) {
+			skb_pull(skb, chunk);
+
+/* 			if (UNIXCB(skb).fp) */
+/* 				unix_detach_fds(scm, skb); */
+
+			/* put the skb back if we didn't use it up.. */
+			if (skb->len) {
+				DEBUG(1, __FUNCTION__ "(), back on q!\n");
+				skb_queue_head(&sk->receive_queue, skb);
+				break;
+			}
+
+			kfree_skb(skb);
+			
+/* 			if (scm->fp) */
+/* 				break; */
+		} else {
+			DEBUG(0, __FUNCTION__ "() questionable!?\n");
+			/* It is questionable, see note in unix_dgram_recvmsg. */
+/* 			if (UNIXCB(skb).fp) */
+/* 				scm->fp = scm_fp_dup(UNIXCB(skb).fp); */
+
+			/* put message back and return */
+			skb_queue_head(&sk->receive_queue, skb);
+			break;
+		}
+	} while (size);
+
+	/*
+	 *  Check if we have previously stopped IrTTP and we know
+	 *  have more free space in our rx_queue. If so tell IrTTP
+	 *  to start delivering frames again before our rx_queue gets
+	 *  empty
+	 */
+	if (self->rx_flow == FLOW_STOP) {
+		if ((atomic_read(&sk->rmem_alloc) << 2) <= sk->rcvbuf) {
+			DEBUG(2, __FUNCTION__ "(), Starting IrTTP\n");
+			self->rx_flow = FLOW_START;
+			irttp_flow_request(self->tsap, FLOW_START);
+		}
+	}
+
+	/* up(&self->readsem); */
+
+	return copied;
+}
+
+/*
  * Function irda_shutdown (sk, how)
  *
  *    
@@ -875,19 +1072,45 @@
         return -EOPNOTSUPP;
 }
 
-
 /*
  * Function irda_poll (file, sock, wait)
  *
  *    
  *
  */
-unsigned int irda_poll(struct file *file, struct socket *sock, 
-		       struct poll_table_struct *wait)
+static unsigned int irda_poll(struct file * file, struct socket *sock, 
+			      poll_table *wait)
 {
-	DEBUG(0, __FUNCTION__ "()\n");
+	struct sock *sk = sock->sk;
+	unsigned int mask;
 
-	return 0;
+	DEBUG(1, __FUNCTION__ "()\n");
+
+	poll_wait(file, sk->sleep, wait);
+	mask = 0;
+
+	/* exceptional events? */
+	if (sk->err)
+		mask |= POLLERR;
+	if (sk->shutdown & RCV_SHUTDOWN)
+		mask |= POLLHUP;
+
+	/* readable? */
+	if (!skb_queue_empty(&sk->receive_queue))
+		mask |= POLLIN | POLLRDNORM;
+
+	/* Connection-based need to check for termination and startup */
+	if (sk->type == SOCK_STREAM && sk->state==TCP_CLOSE)
+		mask |= POLLHUP;
+
+	/*
+	 * we set writable also when the other side has shut down the
+	 * connection. This prevents stuck sockets.
+	 */
+	if (sk->sndbuf - (int)atomic_read(&sk->wmem_alloc) >= MIN_WRITE_SPACE)
+			mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
+
+	return mask;
 }
 
 /*
@@ -947,6 +1170,7 @@
 		return -EINVAL;
 		
 	default:
+		DEBUG(0, __FUNCTION__ "(), doing device ioctl!\n");
 		return dev_ioctl(cmd, (void *) arg);
 	}
 
@@ -1082,13 +1306,7 @@
 			return -EFAULT;
 		break;
 	case IRTTP_MAX_SDU_SIZE:
-		if (self->max_sdu_size_tx != SAR_DISABLE)
-			val = self->max_sdu_size_tx;
-		else
-			/* SAR is disabled, so use the IrLAP data size
-			 * instead */
-			val = self->qos_tx.data_size.value - IRDA_MAX_HEADER;
-
+		val = self->max_data_size;
 		DEBUG(0, __FUNCTION__ "(), getting max_sdu_size = %d\n", val);
 		len = sizeof(int);
 		if (put_user(len, optlen))
@@ -1110,7 +1328,7 @@
 	irda_create
 };
 
-static struct proto_ops irda_proto_ops = {
+static struct proto_ops irda_stream_ops = {
 	PF_IRDA,
 	
 	sock_no_dup,
@@ -1128,7 +1346,28 @@
 	irda_getsockopt,
 	sock_no_fcntl,
 	irda_sendmsg,
-	irda_recvmsg
+	irda_recvmsg_stream
+};
+
+static struct proto_ops irda_dgram_ops = {
+	PF_IRDA,
+	
+	sock_no_dup,
+	irda_release,
+	irda_bind,
+	irda_connect,
+	sock_no_socketpair,
+	irda_accept,
+	irda_getname,
+	datagram_poll,
+	irda_ioctl,
+	irda_listen,
+	irda_shutdown,
+	irda_setsockopt,
+	irda_getsockopt,
+	sock_no_fcntl,
+	irda_sendmsg,
+	irda_recvmsg_dgram
 };
 
 /*
@@ -1215,7 +1454,7 @@
 	irda_packet_type.type = htons(ETH_P_IRDA);
         dev_remove_pack(&irda_packet_type);
 
-        unregister_netdevice_notifier( &irda_dev_notifier);
+        unregister_netdevice_notifier(&irda_dev_notifier);
 	
 	sock_unregister(PF_IRDA);
 	irda_cleanup();
diff -u --recursive --new-file v2.3.3/linux/net/irda/crc.c linux/net/irda/crc.c
--- v2.3.3/linux/net/irda/crc.c	Thu Dec 17 09:01:03 1998
+++ linux/net/irda/crc.c	Sun May 30 10:27:04 1999
@@ -6,7 +6,7 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Mon Aug  4 20:40:53 1997
- * Modified at:   Sat Dec 12 09:56:35 1998
+ * Modified at:   Sun May  2 20:28:08 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * Sources:       ppp.c by Michael Callahan <callahan@maths.ox.ac.uk>
  *                Al Longyear <longyear@netcom.com>
@@ -59,7 +59,7 @@
 
 unsigned short crc_calc( __u16 fcs, __u8 const *buf, size_t len) 
 {
-    while ( len--)
-		fcs = IR_FCS(fcs, *buf++);
-    return fcs;
+	while (len--)
+                fcs = irda_fcs(fcs, *buf++);
+	return fcs;
 }
diff -u --recursive --new-file v2.3.3/linux/net/irda/discovery.c linux/net/irda/discovery.c
--- v2.3.3/linux/net/irda/discovery.c	Sat Apr 24 17:50:06 1999
+++ linux/net/irda/discovery.c	Sun May 30 10:27:04 1999
@@ -6,7 +6,7 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Tue Apr  6 15:33:50 1999
- * Modified at:   Sun Apr 11 00:41:58 1999
+ * Modified at:   Sun May  9 22:40:43 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
  *     Copyright (c) 1999 Dag Brattli, All Rights Reserved.
@@ -39,28 +39,51 @@
 /*
  * Function irlmp_add_discovery (cachelog, discovery)
  *
- *    
- *
+ *    Add a new discovery to the cachelog, and remove any old discoveries
+ *    from the same device
  */
-void irlmp_add_discovery(hashbin_t *cachelog, discovery_t *discovery)
+void irlmp_add_discovery(hashbin_t *cachelog, discovery_t *new)
 {
-	discovery_t *old;
+	discovery_t *discovery, *node;
+	unsigned long flags;
+
+	spin_lock_irqsave(&irlmp->lock, flags);
 
-	DEBUG(4, __FUNCTION__ "()\n");
+	/* 
+	 * Remove all discoveries of devices that has previously been 
+	 * discovered on the same link with the same name (info), or the 
+	 * same daddr. We do this since some devices (mostly PDAs) change
+	 * their device address between every discovery.
+	 */
+	discovery = (discovery_t *) hashbin_get_first(cachelog);
+	while (discovery != NULL ) {
+			node = discovery;
+
+			/* Be sure to stay one item ahead */
+			discovery = (discovery_t *) hashbin_get_next(cachelog);
+			
+			if ((node->daddr == new->daddr) || 
+			    (strcmp(node->info, new->info) == 0))
+			{
+				/* This discovery is a previous discovery 
+				 * from the same device, so just remove it
+				 */
+				hashbin_remove(cachelog, node->daddr, NULL);
+				kfree(node);
+			}
+		}
 
-	/* Check if we have discovered this device before */
-	old = hashbin_remove(cachelog, discovery->daddr, NULL);
-	if (old)
-		kfree(old);
 
 	/* Insert the new and updated version */
-	hashbin_insert(cachelog, (QUEUE *) discovery, discovery->daddr, NULL);
+	hashbin_insert(cachelog, (QUEUE *) new, new->daddr, NULL);
+
+	spin_unlock_irqrestore(&irlmp->lock, flags);
 }
 
 /*
  * Function irlmp_add_discovery_log (cachelog, log)
  *
- *    
+ *    Merge a disovery log into the cachlog.
  *
  */
 void irlmp_add_discovery_log(hashbin_t *cachelog, hashbin_t *log)
diff -u --recursive --new-file v2.3.3/linux/net/irda/ircomm/ircomm_common.c linux/net/irda/ircomm/ircomm_common.c
--- v2.3.3/linux/net/irda/ircomm/ircomm_common.c	Thu May  6 16:40:53 1999
+++ linux/net/irda/ircomm/ircomm_common.c	Sun May 30 10:27:04 1999
@@ -8,7 +8,7 @@
  * Author:        Takahide Higuchi <thiguchi@pluto.dti.ne.jp>
  * Source:        irlpt_event.c
  *
- *     Copyright (c) 1998, Takahide Higuchi, <thiguchi@pluto.dti.ne.jp>,
+ *     Copyright (c) 1998-1999, Takahide Higuchi, <thiguchi@pluto.dti.ne.jp>,
  *     All Rights Reserved.
  *
  *     This program is free software; you can redistribute it and/or
@@ -44,18 +44,17 @@
 static char *revision_date = "Sun Apr 18 00:40:19 1999";
 
 
-static void ircomm_state_idle( struct ircomm_cb *self, IRCOMM_EVENT event, 
-			       struct sk_buff *skb );
-
-static void ircomm_state_discoverywait( struct ircomm_cb *self, IRCOMM_EVENT event, 
+static void ircomm_state_idle(struct ircomm_cb *self, IRCOMM_EVENT event, 
+			      struct sk_buff *skb );
+static void ircomm_state_discoverywait(struct ircomm_cb *self, 
+				       IRCOMM_EVENT event, 
+				       struct sk_buff *skb );
+static void ircomm_state_queryparamwait(struct ircomm_cb *self, 
+					IRCOMM_EVENT event, 
 					struct sk_buff *skb );
-
-static void ircomm_state_queryparamwait( struct ircomm_cb *self, IRCOMM_EVENT event, 
-					 struct sk_buff *skb );
-
-static void ircomm_state_querylsapwait( struct ircomm_cb *self, IRCOMM_EVENT event, 
-					struct sk_buff *skb );
-
+static void ircomm_state_querylsapwait(struct ircomm_cb *self, 
+				       IRCOMM_EVENT event, 
+				       struct sk_buff *skb );
 static void ircomm_state_waiti( struct ircomm_cb *self, IRCOMM_EVENT event, 
 				struct sk_buff *skb );
 static void ircomm_state_waitr( struct ircomm_cb *self, IRCOMM_EVENT event, 
@@ -207,7 +206,7 @@
 		ircomm[i]->ack_char = 0x06;  
 
 		ircomm[i]->max_txbuff_size = COMM_DEFAULT_DATA_SIZE;   /* 64 */
-		ircomm[i]->maxsdusize = SAR_DISABLE;  
+		ircomm[i]->max_sdu_size = SAR_DISABLE;  
 		ircomm[i]->ctrl_skb = dev_alloc_skb(COMM_DEFAULT_DATA_SIZE);
 		if (ircomm[i]->ctrl_skb == NULL){
 			DEBUG(0,"ircomm:init_module:alloc_skb failed!\n");
@@ -226,7 +225,6 @@
 	create_proc_entry("ircomm", 0, proc_irda)->get_info = ircomm_proc_read;
 #endif /* CONFIG_PROC_FS */
 
-
 	discovering_instance = NULL;
 	return 0;
 }
@@ -275,51 +273,53 @@
 static int ircomm_accept_data_indication(void *instance, void *sap, 
 					 struct sk_buff *skb)
 {
-	
-	struct ircomm_cb *self = (struct ircomm_cb *)instance;
+	struct ircomm_cb *self = (struct ircomm_cb *) instance;
 
-	ASSERT( self != NULL, return -1;);
-	ASSERT( self->magic == IRCOMM_MAGIC, return -1;);
-	ASSERT( skb != NULL, return -1;);
+	ASSERT(self != NULL, return -1;);
+	ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
+	ASSERT(skb != NULL, return -1;);
        
 	DEBUG(4,__FUNCTION__"():\n");
-	ircomm_do_event( self, TTP_DATA_INDICATION, skb);
+	ircomm_do_event(self, TTP_DATA_INDICATION, skb);
 	self->rx_packets++;
 	
 	return 0;
 }
 
 static void ircomm_accept_connect_confirm(void *instance, void *sap,
-				   struct qos_info *qos, 
-				   __u32 maxsdusize, struct sk_buff *skb)
+					  struct qos_info *qos, 
+					  __u32 max_sdu_size, 
+					  __u8 max_header_size,
+					  struct sk_buff *skb)
 {
+	struct ircomm_cb *self = (struct ircomm_cb *) instance;
 
-	struct ircomm_cb *self = (struct ircomm_cb *)instance;
-
-	ASSERT( self != NULL, return;);
-	ASSERT( self->magic == IRCOMM_MAGIC, return;);
-	ASSERT( skb != NULL, return;);
-	ASSERT( qos != NULL, return;);
+	ASSERT(self != NULL, return;);
+	ASSERT(self->magic == IRCOMM_MAGIC, return;);
+	ASSERT(skb != NULL, return;);
+	ASSERT(qos != NULL, return;);
 
 	DEBUG(0,__FUNCTION__"(): got connected!\n");
 
-	if(maxsdusize == SAR_DISABLE)
-		self->max_txbuff_size = qos->data_size.value; 
+	if (max_sdu_size == SAR_DISABLE)
+		self->max_txbuff_size = qos->data_size.value - max_header_size;
 	else {
-		ASSERT(maxsdusize >= COMM_DEFAULT_DATA_SIZE, return;);
-		self->max_txbuff_size = maxsdusize; /* use fragmentation */
+		ASSERT(max_sdu_size >= COMM_DEFAULT_DATA_SIZE, return;);
+		self->max_txbuff_size = max_sdu_size; /* use fragmentation */
 	}
 
 	self->qos = qos;
-	self->null_modem_mode = 0;            	/* disable null modem emulation */
+	self->max_header_size = max_header_size;
+	self->null_modem_mode = 0;         /* disable null modem emulation */
 
-	ircomm_do_event( self, TTP_CONNECT_CONFIRM, skb);
+	ircomm_do_event(self, TTP_CONNECT_CONFIRM, skb);
 }
 
 static void ircomm_accept_connect_indication(void *instance, void *sap,
-				      struct qos_info *qos,
-				      __u32 maxsdusize,
-				      struct sk_buff *skb )
+					     struct qos_info *qos,
+					     __u32 max_sdu_size,
+					     __u8 max_header_size,
+					     struct sk_buff *skb)
 {
 	struct ircomm_cb *self = (struct ircomm_cb *)instance;
 
@@ -330,12 +330,14 @@
 
 	DEBUG(0,__FUNCTION__"()\n");
 
-	if(maxsdusize == SAR_DISABLE)
-		self->max_txbuff_size = qos->data_size.value;
+	if (max_sdu_size == SAR_DISABLE)
+		self->max_txbuff_size = qos->data_size.value - max_header_size;
 	else
-		self->max_txbuff_size = maxsdusize;
+		self->max_txbuff_size = max_sdu_size;
 
 	self->qos = qos;
+	self->max_header_size = max_header_size;
+
 	ircomm_do_event( self, TTP_CONNECT_INDICATION, skb);
 
 	/* stop connecting */
@@ -556,7 +558,7 @@
 
 		irttp_connect_request(self->tsap, self->dlsap, 
 				      self->saddr, self->daddr, 
-				      NULL, self->maxsdusize, userdata); 
+				      NULL, self->max_sdu_size, userdata); 
 		break;
 
 	default:
@@ -588,9 +590,9 @@
 /* 	if( !ircomm_parse_controlchannel( self, data)) */
 /* 		self->servicetype = DEFAULT;     TODOD:fix this! TH */
 
-	if(self->notify.connect_indication)
+	if (self->notify.connect_indication)
 		self->notify.connect_indication(self->notify.instance, self, 
-						qos, 0, skb);
+						qos, 0, 0, skb);
 }
     
 #if 0
@@ -602,28 +604,26 @@
 #endif 
 
 
-static void connect_confirmation(struct ircomm_cb *self, struct sk_buff *skb)
+static void connect_confirm(struct ircomm_cb *self, struct sk_buff *skb)
 {
 	DEBUG(4 ,__FUNCTION__"()\n");
 
 	/* give a connect_confirm to the client */
 	if( self->notify.connect_confirm )
 		self->notify.connect_confirm(self->notify.instance,
-					     self, NULL, SAR_DISABLE, skb);
+					     self, NULL, SAR_DISABLE, 0, skb);
 }
 
 static void issue_connect_response(struct ircomm_cb *self,
 				   struct sk_buff *skb)
 {
-
 	DEBUG(0,__FUNCTION__"()\n");
 	
 	if( self->servicetype == THREE_WIRE_RAW){
 		DEBUG(0,__FUNCTION__"():THREE_WIRE_RAW is not implemented yet\n");
 		/* irlmp_connect_rsp(); */
-	} else {
-		irttp_connect_response(self->tsap, self->maxsdusize, skb);
-	}
+	} else
+		irttp_connect_response(self->tsap, self->max_sdu_size, skb);
 }
 
 static void issue_disconnect_request(struct ircomm_cb *self,
@@ -642,30 +642,29 @@
 {
 	int err;
 
-	if(self->servicetype == THREE_WIRE_RAW){
+	if (self->servicetype == THREE_WIRE_RAW){
 		/* irlmp_data_request(self->lmhandle,userdata); */
 		DEBUG(0,__FUNCTION__"():not implemented!");
 		return;
 	}
 
 	DEBUG(4,__FUNCTION__"():sending frame\n");
-	err = irttp_data_request(self->tsap , userdata  );
-	if(err){
+	err = irttp_data_request(self->tsap, userdata);
+	if (err){
 		printk(KERN_ERR __FUNCTION__":ttp_data_request failed\n");
-		if(userdata)
+		if (userdata)
 			dev_kfree_skb( userdata);
 	}
 	self->tx_packets++;
 }
 
 static void issue_control_request(struct ircomm_cb *self,
-				  struct sk_buff *userdata )
+				  struct sk_buff *userdata)
 {
 	int err;
 
 	DEBUG(4,__FUNCTION__"()\n"); 
-	if(self->servicetype == THREE_WIRE_RAW)
-	{
+	if (self->servicetype == THREE_WIRE_RAW) {
 		DEBUG(0,__FUNCTION__"():THREE_WIRE_RAW is not implemented\n");
 		
 	}
@@ -676,7 +675,7 @@
 		{
 			printk( __FUNCTION__"():ttp_data_request failed\n");
 			if(userdata)
-				dev_kfree_skb( userdata);
+				dev_kfree_skb(userdata);
 		}
 		else
 			self->tx_controls++;
@@ -701,7 +700,7 @@
 
  	/* ircomm_parse_control(self, skb, CONTROL_CHANNEL); */
 
-	if(self->notify.data_indication && skb->len)
+	if (self->notify.data_indication && skb->len)
 		self->notify.data_indication(self->notify.instance, self,
 					     skb);
 }
@@ -728,7 +727,7 @@
 	
 	DEBUG( 4, __FUNCTION__": STATE = %s, EVENT = %s\n",
 	       ircommstate[self->state], ircommevent[event]);
-	(*state[ self->state ]) ( self, event, skb);
+	(*state[self->state])(self, event, skb);
 }
 
 static void ircomm_next_state( struct ircomm_cb *self, IRCOMM_STATE state) 
@@ -747,7 +746,7 @@
 static void ircomm_state_idle( struct ircomm_cb *self, IRCOMM_EVENT event, 
 			       struct sk_buff *skb )
 {
-	switch(event){
+	switch (event){
 	case IRCOMM_CONNECT_REQUEST:
 
 		/* ircomm_next_state(self, COMM_WAITI); */
@@ -779,7 +778,8 @@
 /*
  * ircomm_state_discoverywait
  */
-static void ircomm_state_discoverywait(struct ircomm_cb *self, IRCOMM_EVENT event, 
+static void ircomm_state_discoverywait(struct ircomm_cb *self, 
+				       IRCOMM_EVENT event, 
 				       struct sk_buff *skb )
 {
 	switch(event){
@@ -817,11 +817,11 @@
  * ircomm_state_queryparamwait
  */
 
-static void ircomm_state_queryparamwait(struct ircomm_cb *self, IRCOMM_EVENT event, 
-					struct sk_buff *skb )
+static void ircomm_state_queryparamwait(struct ircomm_cb *self, 
+					IRCOMM_EVENT event, 
+					struct sk_buff *skb)
 {
-	switch(event){
-
+	switch (event) {
 	case TTP_CONNECT_INDICATION:
 
 		ircomm_next_state(self, COMM_WAITR);
@@ -855,10 +855,11 @@
  * ircomm_state_querylsapwait
  */
 
-static void ircomm_state_querylsapwait(struct ircomm_cb *self, IRCOMM_EVENT event, 
+static void ircomm_state_querylsapwait(struct ircomm_cb *self, 
+				       IRCOMM_EVENT event, 
 				       struct sk_buff *skb )
 {
-	switch(event){
+	switch (event) {
 
 	case TTP_CONNECT_INDICATION:
 
@@ -898,10 +899,10 @@
 static void ircomm_state_waiti(struct ircomm_cb *self, IRCOMM_EVENT event, 
 			  struct sk_buff *skb )
 {
-	switch(event){
+	switch (event) {
 	case TTP_CONNECT_CONFIRM:
 		ircomm_next_state(self, COMM_CONN);
-		connect_confirmation( self, skb );
+		connect_confirm(self, skb );
 		break;
 	case TTP_DISCONNECT_INDICATION:
 		ircomm_next_state(self, COMM_IDLE);
@@ -921,21 +922,18 @@
 	}
 }
 
-
-
 /*
  * ircomm_state_waitr
  */
 static void ircomm_state_waitr(struct ircomm_cb *self, IRCOMM_EVENT event, 
-			  struct sk_buff *skb ) 
+			       struct sk_buff *skb ) 
 {
-	
-	switch(event){
+	switch (event) {
 	case IRCOMM_CONNECT_RESPONSE:
 
 	        /* issue_connect_response */
 		
-		if(self->servicetype==THREE_WIRE_RAW){
+		if (self->servicetype==THREE_WIRE_RAW) {
 			DEBUG(0,__FUNCTION__"():3WIRE_RAW is not implemented\n");
 			/* irlmp_connect_response(Vpeersap,
 			 *                         ACCEPT,null);
@@ -987,7 +985,7 @@
 static void ircomm_state_conn(struct ircomm_cb *self, IRCOMM_EVENT event, 
 			      struct sk_buff *skb )
 {
-	switch(event){
+	switch (event) {
 	case TTP_DATA_INDICATION:
 		process_data(self, skb);
 		break;
@@ -1033,8 +1031,6 @@
 	}
 }
 
-
-
 /*
  *  ----------------------------------------------------------------------
  *  IrCOMM service interfaces and supporting functions
@@ -1042,12 +1038,12 @@
  *  ----------------------------------------------------------------------
  */
 
-/* 
- * start_discovering()
+/*
+ * Function start_discovering (self)
+ *
+ *    Start discovering and enter DISCOVERY_WAIT state
  *
- * start discovering and enter DISCOVERY_WAIT state
  */
-
 static void start_discovering(struct ircomm_cb *self)
 {
 	__u16  hints; 
@@ -1092,19 +1088,26 @@
 /*
  * queryias_done(self)
  *
- * called when discovery process got wrong results, completed, or terminated.
+ * 
  */
 
+/*
+ * Function queryias_done (self)
+ *
+ *    Called when discovery process got wrong results, completed, or
+ *    terminated.
+ * 
+ */
 static void queryias_done(struct ircomm_cb *self)
 {
 	DEBUG(0, __FUNCTION__"():\n");
-	if(self->queryias_lock){
+	if (self->queryias_lock){
 		self->queryias_lock = 0;
 		discovering_instance = NULL;
 		MOD_DEC_USE_COUNT;
 		irlmp_unregister_client(self->ckey);
 	}
-	if(ircomm_cs != 1)
+	if (ircomm_cs != 1)
 		irlmp_unregister_service(self->skey);
 	return;
 }
@@ -1120,7 +1123,6 @@
 				       ircomm_getvalue_confirm, self );
 }
 
-
 static void query_lsapsel(struct ircomm_cb * self)
 {
 	DEBUG(0, __FUNCTION__"():querying IAS: Lsapsel...\n");
@@ -1135,13 +1137,13 @@
 	}
 }
 
-/* 
- * ircomm_connect_request()
- * Impl. of this function is differ from one of the reference.
- * This functin does discovery as well as sending connect request
+/*
+ * Function ircomm_connect_request (self, servicetype)
+ *
+ *    Impl. of this function is differ from one of the reference. This
+ *    function does discovery as well as sending connect request
+ * 
  */
-
-
 void ircomm_connect_request(struct ircomm_cb *self, __u8 servicetype)
 {
 	/*
@@ -1158,12 +1160,12 @@
 	self->servicetype= servicetype;
 	/* ircomm_control_request(self, SERVICETYPE); */ /*servictype*/
 
-	self->maxsdusize = SAR_DISABLE;
-	ircomm_do_event( self, IRCOMM_CONNECT_REQUEST, NULL);
+	self->max_sdu_size = SAR_DISABLE;
+	ircomm_do_event(self, IRCOMM_CONNECT_REQUEST, NULL);
 }
 
 void ircomm_connect_response(struct ircomm_cb *self, struct sk_buff *userdata,
-			     __u32 maxsdusize)
+			     __u32 max_sdu_size)
 {
 
 	ASSERT( self != NULL, return;);
@@ -1177,10 +1179,11 @@
 	 * and send it with connect_response
 	 */
 
-	if(!userdata){
+	if (!userdata){
 		/* FIXME: check for errors and initialize? DB */
 		userdata = dev_alloc_skb(COMM_DEFAULT_DATA_SIZE);
-		ASSERT(userdata != NULL, return;);
+		if (userdata == NULL)
+			return;
 
 		skb_reserve(userdata,COMM_HEADER_SIZE);
 	}
@@ -1188,9 +1191,10 @@
 	/* enable null-modem emulation (i.e. server mode )*/
 	self->null_modem_mode = 1;
 
-	self->maxsdusize = maxsdusize;
-	if(maxsdusize != SAR_DISABLE)
-		self->max_txbuff_size = maxsdusize;
+	self->max_sdu_size = max_sdu_size;
+	if (max_sdu_size != SAR_DISABLE)
+		self->max_txbuff_size = max_sdu_size;
+
 	ircomm_do_event(self, IRCOMM_CONNECT_RESPONSE, userdata);
 }	
 
@@ -1341,14 +1345,13 @@
 	self->control_ch_pending = 1;
 }
 
-
-
 /*
- * ircomm_control_request();
- * this function is exported as a request to send some control-channel tuples
- * to peer device
+ * Function ircomm_control_request (self, instruction)
+ *
+ *    This function is exported as a request to send some control-channel
+ *    tuples * to peer device
+ * 
  */
-
 void ircomm_control_request(struct ircomm_cb *self,  __u8 instruction)
 {
 
diff -u --recursive --new-file v2.3.3/linux/net/irda/ircomm/irvtd_driver.c linux/net/irda/ircomm/irvtd_driver.c
--- v2.3.3/linux/net/irda/ircomm/irvtd_driver.c	Fri May 14 18:55:32 1999
+++ linux/net/irda/ircomm/irvtd_driver.c	Sun May 30 10:27:04 1999
@@ -8,7 +8,7 @@
  * Source:        serial.c by Linus Torvalds
  *                isdn_tty.c by Fritz Elfert
  *
- *     Copyright (c) 1998, Takahide Higuchi, <thiguchi@pluto.dti.ne.jp>,
+ *     Copyright (c) 1998-1999, Takahide Higuchi, <thiguchi@pluto.dti.ne.jp>,
  *     All Rights Reserved.
  *
  *     This program is free software; you can redistribute it and/or
@@ -341,15 +341,15 @@
  ***********************************************************************
  */
 
-
 /*
  * Function irvtd_connect_confirm (instance, sap, qos, max_sdu_size, skb)
  *
- *    ircomm_connect_request which we have send have succeed!
+ *    ircomm_connect_request which we have send, has succeeded!
  *
  */
 void irvtd_connect_confirm(void *instance, void *sap, struct qos_info *qos,
-			   __u32 max_sdu_size, struct sk_buff *skb)
+			   __u32 max_sdu_size, __u8 max_header_size, 
+			   struct sk_buff *skb)
 {
 	struct irvtd_cb *driver = (struct irvtd_cb *)instance;
 	ASSERT(driver != NULL, return;);
@@ -364,7 +364,7 @@
 	/*
 	 * sending initial control parameters here
 	 */
-	if(driver->comm->servicetype ==	THREE_WIRE_RAW)
+	if (driver->comm->servicetype ==	THREE_WIRE_RAW)
 		return;                /* do nothing */
 
 	driver->comm->dte |= (MCR_DTR | MCR_RTS | DELTA_DTR | DELTA_RTS);
@@ -376,7 +376,7 @@
 	ircomm_control_request(driver->comm, XON_XOFF_CHAR);
 	/* ircomm_control_request(driver->comm, ENQ_ACK_CHAR); */
 
-	switch(driver->comm->servicetype){
+	switch (driver->comm->servicetype) {
 	case CENTRONICS:
 		break;
 
@@ -397,17 +397,18 @@
  *
  */
 void irvtd_connect_indication(void *instance, void *sap, struct qos_info *qos,
-			      __u32 max_sdu_size, struct sk_buff *skb)
+			      __u32 max_sdu_size, __u8 max_header_size, 
+			      struct sk_buff *skb)
 {
-
 	struct irvtd_cb *driver = (struct irvtd_cb *)instance;
 	struct ircomm_cb *comm = (struct ircomm_cb *)sap;
+
 	ASSERT(driver != NULL, return;);
 	ASSERT(driver->magic == IRVTD_MAGIC, return;);
 	ASSERT(comm != NULL, return;);
 	ASSERT(comm->magic == IRCOMM_MAGIC, return;);
 
-	DEBUG(4,"irvtd_connect_indication:sending connect_response...\n");
+	DEBUG(4, __FUNCTION__ "():sending connect_response...\n");
 
 	ircomm_connect_response(comm, NULL, SAR_DISABLE );
 
@@ -416,7 +417,7 @@
 	/*
 	 * send initial control parameters
 	 */
-	if(driver->comm->servicetype ==	THREE_WIRE_RAW)
+	if (driver->comm->servicetype == THREE_WIRE_RAW)
 		return;                /* do nothing */
 
 	driver->comm->dte |= (MCR_DTR | MCR_RTS | DELTA_DTR | DELTA_RTS);
@@ -426,6 +427,7 @@
 		ircomm_control_request(driver->comm, DTELINE_STATE);
 		break;
 	default:
+		DEBUG(0, __FUNCTION__ "(), not implemented!\n");
 	}
 
 
@@ -576,6 +578,7 @@
 	case DATA_RATE:
 	case XON_XOFF_CHAR:
 	case DTELINE_STATE:
+	case ENQ_ACK_CHAR:	/* got this from win95 */
 		/* (maybe) nothing to do  */
 		break;
 	default:
@@ -778,7 +781,7 @@
 	skb_queue_head_init(&driver->rxbuff);
 	driver->txbuff = dev_alloc_skb(COMM_DEFAULT_DATA_SIZE); 
 	if (!driver->txbuff){
-		DEBUG(0,__FUNCTION__"():alloc_skb failed!\n");
+		DEBUG(0,__FUNCTION__"(), alloc_skb failed!\n");
 		return -ENOMEM;
 	}
 	skb_reserve(driver->txbuff, COMM_HEADER_SIZE);
@@ -793,9 +796,8 @@
 	irvtd_notify.instance = driver;
 
 	driver->comm = ircomm_open_instance(irvtd_notify);
-	if(!driver->comm){
+	if (!driver->comm)
 		return -ENODEV;
-	}
 
 
 	/* 
diff -u --recursive --new-file v2.3.3/linux/net/irda/irda_device.c linux/net/irda/irda_device.c
--- v2.3.3/linux/net/irda/irda_device.c	Sat Apr 24 17:50:06 1999
+++ linux/net/irda/irda_device.c	Sun May 30 10:27:04 1999
@@ -6,10 +6,10 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Wed Sep  2 20:22:08 1998
- * Modified at:   Wed Apr 21 09:48:19 1999
+ * Modified at:   Mon May 10 23:02:47 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1998 Dag Brattli, All Rights Reserved.
+ *     Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
  *      
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
@@ -31,6 +31,8 @@
 #include <linux/netdevice.h>
 #include <linux/init.h>
 #include <linux/tty.h>
+#include <linux/kmod.h>
+#include <linux/wireless.h>
 
 #include <asm/ioctls.h>
 #include <asm/segment.h>
@@ -53,7 +55,8 @@
 extern int actisys_init(void);
 extern int girbil_init(void);
 
-hashbin_t *irda_device = NULL;
+static hashbin_t *irda_device = NULL;
+static hashbin_t *dongles = NULL;
 
 /* Netdevice functions */
 static int irda_device_net_rebuild_header(struct sk_buff *skb);
@@ -61,9 +64,9 @@
 				       struct device *dev,
 				       unsigned short type, void *daddr, 
 				       void *saddr, unsigned len);
-static int irda_device_net_set_config( struct device *dev, struct ifmap *map);
-static int irda_device_net_change_mtu( struct device *dev, int new_mtu);
-
+static int irda_device_net_set_config(struct device *dev, struct ifmap *map);
+static int irda_device_net_change_mtu(struct device *dev, int new_mtu);
+static int irda_device_net_ioctl(struct device *dev, struct ifreq *rq,int cmd);
 #ifdef CONFIG_PROC_FS
 int irda_device_proc_read( char *buf, char **start, off_t offset, int len, 
 			   int unused);
@@ -74,8 +77,15 @@
 {
 	/* Allocate master array */
 	irda_device = hashbin_new( HB_LOCAL);
-	if ( irda_device == NULL) {
-		printk( KERN_WARNING "IrDA: Can't allocate irda_device hashbin!\n");
+	if (irda_device == NULL) {
+		WARNING("IrDA: Can't allocate irda_device hashbin!\n");
+		return -ENOMEM;
+	}
+
+	dongles = hashbin_new(HB_LOCAL);
+	if (dongles == NULL) {
+		printk(KERN_WARNING 
+		       "IrDA: Can't allocate dongles hashbin!\n");
 		return -ENOMEM;
 	}
 
@@ -113,6 +123,7 @@
 
 	ASSERT(irda_device != NULL, return;);
 
+	hashbin_delete(dongles, NULL);
 	hashbin_delete(irda_device, (FREE_FUNC) irda_device_close);
 }
 
@@ -238,7 +249,7 @@
 /*
  * Function irda_device_close (self)
  *
- *    
+ *    Close the device
  *
  */
 void irda_device_close(struct irda_device *self)
@@ -248,6 +259,10 @@
 	ASSERT(self != NULL, return;);
 	ASSERT(self->magic == IRDA_DEVICE_MAGIC, return;);
 
+	/* We are not using any dongle anymore! */
+	if (self->dongle)
+		self->dongle->close(self);
+
 	/* Stop and remove instance of IrLAP */
 	if (self->irlap)
 		irlap_close(self->irlap);
@@ -298,15 +313,19 @@
 	 */
 	if (self->wait_until_sent) {
 		self->wait_until_sent(self);
+		
+		if (self->dongle)
+			self->dongle->change_speed(self, speed);
+		
 		if (self->change_speed) {
 			self->change_speed(self, speed);
-
+			
 			/* Update the QoS value only */
 			self->qos.baud_rate.value = speed;
 		}
 	} else {
-		printk(KERN_WARNING "wait_until_sent() "
-		       "has not implemented by the IrDA device driver!\n");
+		WARNING("IrDA: wait_until_sent() "
+			"has not implemented by the IrDA device driver!\n");
 	}
 }
 
@@ -386,6 +405,7 @@
 	dev->set_config      = irda_device_net_set_config;
 	dev->change_mtu      = irda_device_net_change_mtu;
 /*  	dev->hard_header     = irda_device_net_hard_header; */
+	dev->do_ioctl        = irda_device_net_ioctl;
         dev->hard_header_len = 0;
         dev->addr_len        = 0;
 
@@ -444,6 +464,125 @@
      return 0;  
 }
 
+static int irda_device_net_ioctl(struct device *dev, /* ioctl device */
+				 struct ifreq *rq,   /* Data passed */
+				 int	cmd)	     /* Ioctl number */
+{
+	unsigned long flags;
+	int			ret = 0;
+#ifdef WIRELESS_EXT
+	struct iwreq *wrq = (struct iwreq *) rq;
+#endif
+	struct irda_device *self;
+
+	DEBUG(4, __FUNCTION__ "()\n");
+
+	ASSERT(dev != NULL, return -1;);
+
+	self = (struct irda_device *) dev->priv;
+
+	ASSERT(self != NULL, return -1;);
+	ASSERT(self->magic == IRDA_DEVICE_MAGIC, return -1;);
+
+	DEBUG(0, "%s: ->irda_device_net_ioctl(cmd=0x%X)\n", dev->name, cmd);
+	
+	/* Disable interrupts & save flags */
+	save_flags(flags);
+	cli();
+	
+	/* Look what is the request */
+	switch (cmd) {
+#ifdef WIRELESS_EXT
+	case SIOCGIWNAME:
+		/* Get name */
+		strcpy(wrq->u.name, self->name);
+		break;
+	case SIOCSIWNWID:
+		/* Set domain */
+		if (wrq->u.nwid.on) {
+			
+		} break;
+	case SIOCGIWNWID:
+		/* Read domain*/
+/* 		wrq->u.nwid.nwid = domain; */
+/* 		wrq->u.nwid.on = 1; */
+		break;
+	case SIOCGIWENCODE:
+		/* Get scramble key */
+		/* 	wrq->u.encoding.code = scramble_key; */
+/* 		wrq->u.encoding.method = 1; */
+		break;
+	case SIOCSIWENCODE:
+		/* Set  scramble key */
+		/* scramble_key = wrq->u.encoding.code; */
+		break;
+	case SIOCGIWRANGE:
+		/* Basic checking... */
+		if(wrq->u.data.pointer != (caddr_t) 0) {
+			struct iw_range	range;
+			
+			/* Verify the user buffer */
+			ret = verify_area(VERIFY_WRITE, wrq->u.data.pointer,
+					  sizeof(struct iw_range));
+			if(ret)
+				break;
+			
+			/* Set the length (useless : its constant...) */
+			wrq->u.data.length = sizeof(struct iw_range);
+			
+			/* Set information in the range struct */
+			range.throughput = 1.6 * 1024 * 1024;	/* don't argue on this ! */
+			range.min_nwid = 0x0000;
+			range.max_nwid = 0x01FF;
+			
+			range.num_channels = range.num_frequency = 0;
+			
+			range.sensitivity = 0x3F;
+			range.max_qual.qual = 255;
+			range.max_qual.level = 255;
+			range.max_qual.noise = 0;
+			
+			/* Copy structure to the user buffer */
+			copy_to_user(wrq->u.data.pointer, &range,
+				     sizeof(struct iw_range));
+		}
+		break;
+	case SIOCGIWPRIV:
+		/* Basic checking... */
+#if 0
+		if (wrq->u.data.pointer != (caddr_t) 0) {
+			struct iw_priv_args	priv[] =
+			{	/* cmd,		set_args,	get_args,	name */
+				{ SIOCGIPSNAP, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 0, 
+				  sizeof(struct site_survey), 
+				  "getsitesurvey" },
+			};
+			
+			/* Verify the user buffer */
+			ret = verify_area(VERIFY_WRITE, wrq->u.data.pointer,
+					  sizeof(priv));
+			if (ret)
+				break;
+			
+			/* Set the number of ioctl available */
+			wrq->u.data.length = 1;
+			
+			/* Copy structure to the user buffer */
+			copy_to_user(wrq->u.data.pointer, (u_char *) priv,
+				     sizeof(priv));
+		}
+#endif 
+		break;
+#endif
+	default:
+		ret = -EOPNOTSUPP;
+	}
+	
+	restore_flags(flags);
+	
+	return ret;
+}
+
 /*
  * Function irda_device_transmit_finished (void)
  *
@@ -451,7 +590,7 @@
  *    device. Maybe we should use: q->q.qlen == 0.
  *
  */
-int irda_device_txqueue_empty( struct irda_device *self)
+int irda_device_txqueue_empty(struct irda_device *self)
 {
 	ASSERT(self != NULL, return -1;);
 	ASSERT(self->magic == IRDA_DEVICE_MAGIC, return -1;);	
@@ -463,6 +602,117 @@
 }
 
 /*
+ * Function irda_device_init_dongle (self, type)
+ *
+ *    Initialize attached dongle. Warning, must be called with a process
+ *    context!
+ */
+void irda_device_init_dongle(struct irda_device *self, int type)
+{
+	struct dongle_q *node;
+
+	ASSERT(self != NULL, return;);
+	ASSERT(self->magic == IRDA_DEVICE_MAGIC, return;);
+
+#ifdef CONFIG_KMOD
+	/* Try to load the module needed */
+	switch (type) {
+	case ESI_DONGLE:
+		MESSAGE("IrDA: Initializing ESI dongle!\n");
+		request_module("esi");
+		break;
+	case TEKRAM_DONGLE:
+		MESSAGE("IrDA: Initializing Tekram dongle!\n");
+		request_module("tekram");
+		break;
+	case ACTISYS_DONGLE:     /* FALLTHROUGH */
+	case ACTISYS_PLUS_DONGLE:
+		MESSAGE("IrDA: Initializing ACTiSYS dongle!\n");
+		request_module("actisys");
+		break;
+	case GIRBIL_DONGLE:
+		MESSAGE("IrDA: Initializing GIrBIL dongle!\n");
+		request_module("girbil");
+		break;
+	case LITELINK_DONGLE:
+		MESSAGE("IrDA: Initializing Litelink dongle!\n");
+		request_module("litelink");
+		break;
+	default:
+		ERROR("Unknown dongle type!\n");
+		return;
+	}
+#endif /* CONFIG_KMOD */
+
+	node = hashbin_find(dongles, type, NULL);
+	if (!node) {
+		ERROR("IrDA: Unable to find requested dongle\n");
+		return;
+	}
+
+	/* Set the dongle to be used by this driver */
+	self->dongle = node->dongle;
+
+	/* Now initialize the dongle!  */
+	node->dongle->open(self, type);
+	node->dongle->qos_init(self, &self->qos);
+	
+	/* Reset dongle */
+	node->dongle->reset(self, 0);
+
+	/* Set to default baudrate */
+	irda_device_change_speed(self, 9600);
+}
+
+/*
+ * Function irda_device_register_dongle (dongle)
+ *
+ *    
+ *
+ */
+int irda_device_register_dongle(struct dongle *dongle)
+{
+	struct dongle_q *new;
+	
+	/* Check if this dongle has been registred before */
+	if (hashbin_find(dongles, dongle->type, NULL)) {
+		MESSAGE(__FUNCTION__ "(), Dongle already registered\n");
+                return 0;
+        }
+	
+	/* Make new IrDA dongle */
+        new = (struct dongle_q *) kmalloc(sizeof(struct dongle_q), GFP_KERNEL);
+        if (new == NULL)
+                return -1;
+		
+	memset(new, 0, sizeof( struct dongle_q));
+        new->dongle = dongle;
+
+	/* Insert IrDA dongle into hashbin */
+	hashbin_insert(dongles, (QUEUE *) new, dongle->type, NULL);
+	
+        return 0;
+}
+
+/*
+ * Function irda_device_unregister_dongle (dongle)
+ *
+ *    
+ *
+ */
+void irda_device_unregister_dongle(struct dongle *dongle)
+{
+	struct dongle_q *node;
+
+	node = hashbin_remove(dongles, dongle->type, NULL);
+	if (!node) {
+		ERROR(__FUNCTION__ "(), dongle not found!\n");
+		return;
+	}
+	kfree(node);
+}
+
+/*
  * Function setup_dma (idev, buffer, count, mode)
  *
  *    Setup the DMA channel
@@ -536,7 +786,7 @@
 
 	self = (struct irda_device *) hashbin_get_first(irda_device);
 	while ( self != NULL) {
-		len += sprintf(buf+len, "%s,", self->name);
+		len += sprintf(buf+len, "\n%s,", self->name);
 		len += sprintf(buf+len, "\tbinding: %s\n", 
 			       self->description);
 		
diff -u --recursive --new-file v2.3.3/linux/net/irda/iriap.c linux/net/irda/iriap.c
--- v2.3.3/linux/net/irda/iriap.c	Sat Apr 24 17:50:06 1999
+++ linux/net/irda/iriap.c	Sun May 30 10:27:04 1999
@@ -6,10 +6,10 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Thu Aug 21 00:02:07 1997
- * Modified at:   Fri Apr 23 09:57:12 1999
+ * Modified at:   Sun May  9 15:59:05 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>, 
+ *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, 
  *     All Rights Reserved.
  *     
  *     This program is free software; you can redistribute it and/or 
@@ -62,12 +62,17 @@
 extern char *lmp_reasons[];
 
 static struct iriap_cb *iriap_open( __u8 slsap, int mode);
-static void __iriap_close( struct iriap_cb *self);
+static void __iriap_close(struct iriap_cb *self);
 static void iriap_disconnect_indication(void *instance, void *sap, 
 					LM_REASON reason, struct sk_buff *skb);
 static void iriap_connect_indication(void *instance, void *sap, 
 				     struct qos_info *qos, __u32 max_sdu_size,
+				     __u8 max_header_size, 
 				     struct sk_buff *skb);
+static void iriap_connect_confirm(void *instance, void *sap, 
+				  struct qos_info *qos, 
+				  __u32 max_sdu_size, __u8 max_header_size,
+				  struct sk_buff *skb);
 static int iriap_data_indication(void *instance, void *sap, 
 				 struct sk_buff *skb);
 
@@ -181,7 +186,7 @@
 	self->slsap_sel = slsap_sel;
 	self->mode = mode;
 
-	init_timer( &self->watchdog_timer);
+	init_timer(&self->watchdog_timer);
 
 	hashbin_insert( iriap, (QUEUE*) self, slsap_sel, NULL);
 	
@@ -206,7 +211,7 @@
 	ASSERT( self != NULL, return;);
 	ASSERT( self->magic == IAS_MAGIC, return;);
 
-	del_timer( &self->watchdog_timer);
+	del_timer(&self->watchdog_timer);
 
 	self->magic = 0;
 
@@ -260,7 +265,7 @@
 
 	ASSERT( iriap != NULL, return;);
 
-	del_timer( &self->watchdog_timer);
+	del_timer(&self->watchdog_timer);
 
 	if ( self->mode == IAS_CLIENT) {
 		DEBUG( 4, __FUNCTION__ "(), disconnect as client\n");
@@ -284,9 +289,8 @@
 				       NULL);
 	}
 
-	if ( userdata) {
+	if (userdata)
 		dev_kfree_skb( userdata);
-	}
 }
 
 /*
@@ -295,28 +299,28 @@
  *    
  *
  */
-void iriap_disconnect_request( struct iriap_cb *self)
+void iriap_disconnect_request(struct iriap_cb *self)
 {
 	struct sk_buff *skb;
 
-	DEBUG( 4, __FUNCTION__ "()\n");
+	DEBUG(4, __FUNCTION__ "()\n");
 
-	ASSERT( self != NULL, return;);
-	ASSERT( self->magic == IAS_MAGIC, return;);
+	ASSERT(self != NULL, return;);
+	ASSERT(self->magic == IAS_MAGIC, return;);
 
-	skb = dev_alloc_skb( 64);
+	skb = dev_alloc_skb(64);
 	if (skb == NULL) {
-		DEBUG( 0, __FUNCTION__
-		       "(), Could not allocate an sk_buff of length %d\n", 64);
+		DEBUG(0, __FUNCTION__
+		      "(), Could not allocate an sk_buff of length %d\n", 64);
 		return;
 	}
 
 	/* 
-	 *  Reserve space for MUX and LAP header 
+	 *  Reserve space for MUX control and LAP header 
 	 */
- 	skb_reserve( skb, LMP_CONTROL_HEADER+LAP_HEADER);
+ 	skb_reserve(skb, LMP_MAX_HEADER);
 
-	irlmp_disconnect_request( self->lsap, skb);
+	irlmp_disconnect_request(self->lsap, skb);
 }
 
 void iriap_getinfobasedetails_request(void) 
@@ -381,7 +385,7 @@
 	/* Give ourselves 10 secs to finish this operation */
 	iriap_start_watchdog_timer(self, 10*HZ);
 	
-	skb = dev_alloc_skb( 64);
+	skb = dev_alloc_skb(64);
 	if (!skb)
 		return;
 
@@ -389,7 +393,7 @@
 	attr_len = strlen(attr);
 
 	/* Reserve space for MUX and LAP header */
- 	skb_reserve(skb, LMP_CONTROL_HEADER+LAP_HEADER);
+ 	skb_reserve(skb, self->max_header_size);
 	skb_put(skb, 3+name_len+attr_len);
 	frame = skb->data;
 
@@ -535,13 +539,13 @@
 	 *  value. We add 9 bytes because of the 6 bytes for the frame and
 	 *  max 3 bytes for the value coding.
 	 */
-	skb = dev_alloc_skb(value->len + LMP_HEADER + LAP_HEADER + 9);
+	skb = dev_alloc_skb(value->len + self->max_header_size + 9);
 	if (!skb)
 		return;
 
 	/* Reserve space for MUX and LAP header */
- 	skb_reserve( skb, LMP_HEADER+LAP_HEADER);
-	skb_put( skb, 6);
+ 	skb_reserve(skb, self->max_header_size);
+	skb_put(skb, 6);
 	
 	fp = skb->data;
 
@@ -666,7 +670,7 @@
 /*
  * Function iriap_send_ack (void)
  *
- *    
+ *    Currently not used
  *
  */
 void iriap_send_ack( struct iriap_cb *self) 
@@ -679,13 +683,13 @@
 	ASSERT( self != NULL, return;);
 	ASSERT( self->magic == IAS_MAGIC, return;);
 
-	skb = dev_alloc_skb( 64);
+	skb = dev_alloc_skb(64);
 	if (!skb)
 		return;
 
 	/* Reserve space for MUX and LAP header */
- 	skb_reserve( skb, 4);
-	skb_put( skb, 3);
+ 	skb_reserve(skb, self->max_header_size);
+	skb_put(skb, 1);
 	frame = skb->data;
 
 	/* Build frame */
@@ -698,8 +702,10 @@
  *    LSAP connection confirmed!
  *
  */
-void iriap_connect_confirm(void *instance, void *sap, struct qos_info *qos, 
-			   __u32 max_sdu_size, struct sk_buff *userdata)
+static void iriap_connect_confirm(void *instance, void *sap, 
+				  struct qos_info *qos, 
+				  __u32 max_sdu_size, __u8 header_size, 
+				  struct sk_buff *userdata)
 {
 	struct iriap_cb *self;
 	
@@ -711,7 +717,7 @@
 	
 	DEBUG(4, __FUNCTION__ "()\n");
 	
-	/* del_timer( &self->watchdog_timer); */
+	del_timer(&self->watchdog_timer);
 
 	iriap_do_client_event(self, IAP_LM_CONNECT_CONFIRM, userdata);
 }
@@ -724,19 +730,17 @@
  */
 static void iriap_connect_indication(void *instance, void *sap, 
 				     struct qos_info *qos, __u32 max_sdu_size,
+				     __u8 header_size, 
 				     struct sk_buff *userdata)
 {
 	struct iriap_cb *self;
 
-	DEBUG( 4, __FUNCTION__ "()\n");
-
-	self = ( struct iriap_cb *) instance;
+	self = (struct iriap_cb *) instance;
 
-	ASSERT( self != NULL, return;);
-	ASSERT( self->magic == IAS_MAGIC, return;);
-	ASSERT( self->mode == IAS_SERVER, return;);
+	ASSERT(self != NULL, return;);
+	ASSERT(self->magic == IAS_MAGIC, return;);
 
-	iriap_do_server_event( self, IAP_LM_CONNECT_INDICATION, userdata);
+	iriap_do_server_event(self, IAP_LM_CONNECT_INDICATION, userdata);
 }
  
 /*
@@ -856,7 +860,7 @@
 	}
 	opcode &= 0x7f; /* Mask away LST bit */
 	
-	switch( opcode) {
+	switch (opcode) {
 	case GET_INFO_BASE:
 		DEBUG( 0, "IrLMP GetInfoBaseDetails not implemented!\n");
 		break;
diff -u --recursive --new-file v2.3.3/linux/net/irda/iriap_event.c linux/net/irda/iriap_event.c
--- v2.3.3/linux/net/irda/iriap_event.c	Sun Mar  7 15:26:44 1999
+++ linux/net/irda/iriap_event.c	Sun May 30 10:27:04 1999
@@ -6,10 +6,10 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Thu Aug 21 00:02:07 1997
- * Modified at:   Tue Jan 26 12:29:36 1999
+ * Modified at:   Sun May  9 11:01:47 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1997 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved.
+ *     Copyright (c) 1997, 1999 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved.
  *     
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
@@ -387,9 +387,9 @@
 		}
 
 		/* Reserve space for MUX_CONTROL and LAP header */
-		skb_reserve( tx_skb, LMP_CONTROL_HEADER+LAP_HEADER);
+		skb_reserve(tx_skb, LMP_MAX_HEADER);
 		
-		irlmp_connect_response( self->lsap, tx_skb);
+		irlmp_connect_response(self->lsap, tx_skb);
 		/*LM_Idle_request(idle); */
 		
 		iriap_next_server_state( self, R_CALL);
diff -u --recursive --new-file v2.3.3/linux/net/irda/irlan/irlan_client.c linux/net/irda/irlan/irlan_client.c
--- v2.3.3/linux/net/irda/irlan/irlan_client.c	Sat Apr 24 17:50:06 1999
+++ linux/net/irda/irlan/irlan_client.c	Sun May 30 10:27:04 1999
@@ -6,13 +6,14 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Sun Aug 31 20:14:37 1997
- * Modified at:   Thu Apr 22 23:03:55 1999
+ * Modified at:   Tue May 11 00:22:39 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * Sources:       skeleton.c by Donald Becker <becker@CESDIS.gsfc.nasa.gov>
  *                slip.c by Laurence Culhane, <loz@holmes.demon.co.uk>
  *                          Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
  * 
- *     Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved.
+ *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, 
+ *     All Rights Reserved.
  *     
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
@@ -61,6 +62,7 @@
 static void irlan_client_ctrl_connect_confirm(void *instance, void *sap, 
 					      struct qos_info *qos, 
 					      __u32 max_sdu_size,
+					      __u8 max_header_size,
 					      struct sk_buff *);
 static void irlan_check_response_param(struct irlan_cb *self, char *param, 
 				       char *value, int val_len);
@@ -79,7 +81,7 @@
 	 * indication it needs to make progress. If the client is still in 
 	 * IDLE state, we must kick it to, but only if the provider is not IDLE
  	 */
-	if ((self->access_type == ACCESS_PEER) && 
+	if ((self->provider.access_type == ACCESS_PEER) && 
 	    (self->client.state == IRLAN_IDLE) &&
 	    (self->provider.state != IRLAN_IDLE)) {
 		irlan_client_wakeup(self, self->saddr, self->daddr);
@@ -105,23 +107,29 @@
 {
 	struct irmanager_event mgr_event;
 
-	DEBUG(0, __FUNCTION__ "()\n");
+	DEBUG(1, __FUNCTION__ "()\n");
 
 	ASSERT(self != NULL, return;);
 	ASSERT(self->magic == IRLAN_MAGIC, return;);
 
-	/* Check if we are already awake */
-	if (self->client.state != IRLAN_IDLE)
+	/* 
+	 * Check if we are already awake, or if we are a provider in direct
+	 * mode (in that case we must leave the client idle
+	 */
+	if ((self->client.state != IRLAN_IDLE) || 
+	    (self->provider.access_type == ACCESS_DIRECT))
 		return;
 
 	/* saddr may have changed! */
 	self->saddr = saddr;
 	
-	/* Check if network device is up */
+	/* Before we try to connect, we check if network device is up. If it
+	 * is up, that means that the "user" really wants to connect. If not
+	 * we notify the user about the possibility of an IrLAN connection
+	 */
 	if (self->dev.start) {
 		/* Open TSAPs */
 		irlan_client_open_ctrl_tsap(self);
- 		irlan_provider_open_ctrl_tsap(self);
  		irlan_open_data_tsap(self);
 		
 		irlan_do_client_event(self, IRLAN_DISCOVERY_INDICATION, NULL);
@@ -161,7 +169,7 @@
 	struct irlan_cb *self, *entry;
 	__u32 saddr, daddr;
 	
-	DEBUG(0, __FUNCTION__"()\n");
+	DEBUG(1, __FUNCTION__"()\n");
 
 	ASSERT(irlan != NULL, return;);
 	ASSERT(discovery != NULL, return;);
@@ -176,7 +184,8 @@
       	if (self) {
 		ASSERT(self->magic == IRLAN_MAGIC, return;);
 
-		DEBUG(2, __FUNCTION__ "(), Found instance!\n");
+		DEBUG(1, __FUNCTION__ "(), Found instance (%08x)!\n",
+		      daddr);
 		
 		irlan_client_wakeup(self, saddr, daddr);
 
@@ -184,30 +193,13 @@
 	}
 	
 	/* 
-	 * We have no instance for daddr, so try and find an unused one
+	 * We have no instance for daddr, so start a new one
 	 */
-	self = hashbin_find(irlan, DEV_ADDR_ANY, NULL);
-	if (self) {
-		DEBUG(0, __FUNCTION__ "(), Found instance with DEV_ADDR_ANY!\n");
-		/*
-		 * Rehash instance, now we have a client (daddr) to serve.
-		 */
-		entry = hashbin_remove(irlan, self->daddr, NULL);
-		ASSERT(entry == self, return;);
-
-		self->daddr = daddr;
-		self->saddr = saddr;
+	DEBUG(1, __FUNCTION__ "(), starting new instance!\n");
+	self = irlan_open(saddr, daddr, TRUE);
 
-		DEBUG(0, __FUNCTION__ "(), daddr=%08x\n", self->daddr);
-		hashbin_insert(irlan, (QUEUE*) self, self->daddr, NULL);
-		
-		/* Check if network device has been registered */
-		if (!self->netdev_registered)
-			irlan_register_netdev(self);
-		
-		/* Restart watchdog timer */
-		irlan_start_watchdog_timer(self, IRLAN_TIMEOUT);
-	}
+	/* Restart watchdog timer */
+	irlan_start_watchdog_timer(self, IRLAN_TIMEOUT);
 }
 	
 /*
@@ -302,6 +294,7 @@
 static void irlan_client_ctrl_connect_confirm(void *instance, void *sap, 
 					      struct qos_info *qos, 
 					      __u32 max_sdu_size,
+					      __u8 max_header_size,
 					      struct sk_buff *skb) 
 {
 	struct irlan_cb *self;
@@ -313,6 +306,9 @@
 	ASSERT(self != NULL, return;);
 	ASSERT(self->magic == IRLAN_MAGIC, return;);
 
+	self->client.max_sdu_size = max_sdu_size;
+	self->client.max_header_size = max_header_size;
+
 	/* TODO: we could set the MTU depending on the max_sdu_size */
 
 	irlan_do_client_event(self, IRLAN_CONNECT_COMPLETE, NULL);
@@ -339,7 +335,7 @@
 		return;
 
 	/* Reserve space for TTP, LMP, and LAP header */
-	skb_reserve(skb, TTP_HEADER+LMP_HEADER+LAP_HEADER);
+	skb_reserve(skb, self->max_header_size);
 	skb_put(skb, 2);
 	
 	frame = skb->data;
@@ -410,11 +406,11 @@
 	/* For all parameters */
  	for (i=0; i<count;i++) {
 		ret = irlan_extract_param(ptr, name, value, &val_len);
-		if (ret == -1) {
+		if (ret < 0) {
 			DEBUG(2, __FUNCTION__ "(), IrLAN, Error!\n");
 			break;
 		}
-		ptr+=ret;
+		ptr += ret;
 		irlan_check_response_param(self, name, value, val_len);
  	}
 	/* Cleanup */
@@ -423,9 +419,9 @@
 }
 
 /*
- * Function check_param (param, value)
+ * Function irlan_check_response_param (self, param, value, val_len)
  *
- *    Check which parameter is received and update local variables
+ *     Check which parameter is received and update local variables
  *
  */
 static void irlan_check_response_param(struct irlan_cb *self, char *param, 
@@ -469,11 +465,11 @@
 	}
 	if (strcmp(param, "ACCESS_TYPE") == 0) {
 		if (strcmp(value, "DIRECT") == 0)
-			self->access_type = ACCESS_DIRECT;
+			self->client.access_type = ACCESS_DIRECT;
 		else if (strcmp(value, "PEER") == 0)
-			self->access_type = ACCESS_PEER;
+			self->client.access_type = ACCESS_PEER;
 		else if (strcmp(value, "HOSTED") == 0)
-			self->access_type = ACCESS_HOSTED;
+			self->client.access_type = ACCESS_HOSTED;
 		else {
 			DEBUG(2, __FUNCTION__ "(), unknown access type!\n");
 		}
diff -u --recursive --new-file v2.3.3/linux/net/irda/irlan/irlan_client_event.c linux/net/irda/irlan/irlan_client_event.c
--- v2.3.3/linux/net/irda/irlan/irlan_client_event.c	Sat Apr 24 17:50:06 1999
+++ linux/net/irda/irlan/irlan_client_event.c	Sun May 30 10:27:04 1999
@@ -6,10 +6,10 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Sun Aug 31 20:14:37 1997
- * Modified at:   Thu Apr 22 12:23:22 1999
+ * Modified at:   Thu May  6 13:42:38 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>, 
+ *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, 
  *     All Rights Reserved.
  *     
  *     This program is free software; you can redistribute it and/or 
@@ -97,7 +97,7 @@
 	ASSERT(self != NULL, return -1;);
 	ASSERT(self->magic == IRLAN_MAGIC, return -1;);
 	
-	switch(event) {
+	switch (event) {
 	case IRLAN_DISCOVERY_INDICATION:
 		/* Get some values from peer IAS */
 		iriap_getvaluebyclass_request(
@@ -152,7 +152,7 @@
 		irlan_next_client_state(self, IRLAN_IDLE);
 
 		/* Give the client a kick! */
-		if ((self->access_type == ACCESS_PEER) && 
+		if ((self->provider.access_type == ACCESS_PEER) && 
 		    (self->provider.state != IRLAN_IDLE))
 			irlan_client_wakeup(self, self->saddr, self->daddr);
 		break;
@@ -222,7 +222,7 @@
 
 	ASSERT(self != NULL, return -1;);
 	
-	switch(event) {
+	switch (event) {
 	case IRLAN_DATA_INDICATION:
 		ASSERT(skb != NULL, return -1;);
 	
@@ -314,7 +314,7 @@
 	  	ASSERT(self->dtsap_sel_data != 0, return -1;);
 
 		/* Check which access type we are dealing with */
-		switch(self->access_type) {
+		switch (self->client.access_type) {
 		case ACCESS_PEER:
 		    if (self->provider.state == IRLAN_OPEN) {
 			    
diff -u --recursive --new-file v2.3.3/linux/net/irda/irlan/irlan_common.c linux/net/irda/irlan/irlan_common.c
--- v2.3.3/linux/net/irda/irlan/irlan_common.c	Thu May  6 16:40:53 1999
+++ linux/net/irda/irlan/irlan_common.c	Sun May 30 10:27:04 1999
@@ -6,10 +6,11 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Sun Aug 31 20:14:37 1997
- * Modified at:   Thu Apr 22 23:13:47 1999
+ * Modified at:   Sun May  9 11:48:49 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1997 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved.
+ *     Copyright (c) 1997, 1999 Dag Brattli <dagb@cs.uit.no>, 
+ *     All Rights Reserved.
  *     
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
@@ -93,19 +94,25 @@
 static int __irlan_insert_param(struct sk_buff *skb, char *param, int type, 
 				__u8 value_byte, __u16 value_short, 
 				__u8 *value_array, __u16 value_len);
-static void irlan_close_tsaps(struct irlan_cb *self);
+void irlan_close_tsaps(struct irlan_cb *self);
 
 #ifdef CONFIG_PROC_FS
 static int irlan_proc_read(char *buf, char **start, off_t offset, int len, 
 			   int unused);
 
 extern struct proc_dir_entry *proc_irda;
-#endif
+#endif /* CONFIG_PROC_FS */
 
+/*
+ * Function irlan_watchdog_timer_expired (data)
+ *
+ *    
+ *
+ */
 void irlan_watchdog_timer_expired(unsigned long data)
 {
 	struct irmanager_event mgr_event;
-	struct irlan_cb *self, *entry;
+	struct irlan_cb *self;
 	
 	DEBUG(0, __FUNCTION__ "()\n");
 
@@ -116,6 +123,7 @@
 
 	/* Check if device still configured */
 	if (self->dev.start) {
+		DEBUG(0, __FUNCTION__ "(), notifying irmanager to stop irlan!\n");
 		mgr_event.event = EVENT_IRLAN_STOP;
 		sprintf(mgr_event.devname, "%s", self->ifname);
 		irmanager_notify(&mgr_event);
@@ -128,22 +136,13 @@
 		 */
 		self->notify_irmanager = FALSE;
 	} else {
-		DEBUG(0, __FUNCTION__ "(), recycling instance!\n");
+		DEBUG(0, __FUNCTION__ "(), closing instance!\n");
 		if (self->netdev_registered) {
 			DEBUG(0, __FUNCTION__ "(), removing netdev!\n");
 			unregister_netdev(&self->dev);
 			self->netdev_registered = FALSE;
 		}
-
-		/* Unbind from daddr */
-		entry = hashbin_remove(irlan, self->daddr, NULL);
-		ASSERT(entry == self, return;);
-
-		self->daddr = DEV_ADDR_ANY;
-		self->saddr = DEV_ADDR_ANY;
-
-		DEBUG(2, __FUNCTION__ "(), daddr=%08x\n", self->daddr);
-		hashbin_insert(irlan, (QUEUE*) self, self->daddr, NULL);
+		irlan_close(self);
 	}
 }
 
@@ -195,12 +194,12 @@
 	/* Register with IrLMP as a service */
  	skey = irlmp_register_service(hints);
 
-	/* Start the first IrLAN instance */
+	/* Start the master IrLAN instance */
  	new = irlan_open(DEV_ADDR_ANY, DEV_ADDR_ANY, FALSE);
 
-	irlan_open_data_tsap(new);
- 	irlan_client_open_ctrl_tsap(new);
+	/* The master will only open its (listen) control TSAP */
 	irlan_provider_open_ctrl_tsap(new);
+	new->master = TRUE;
 
 	/* Do some fast discovery! */
 	irlmp_discovery_request(DISCOVERY_DEFAULT_SLOTS);
@@ -293,7 +292,7 @@
 	self->daddr = daddr;
 
 	/* Provider access can only be PEER, DIRECT, or HOSTED */
-	self->access_type = access;
+	self->provider.access_type = access;
 	self->media = MEDIA_802_3;
 
 	self->notify_irmanager = TRUE;
@@ -359,8 +358,11 @@
 
 	/* Check if device is still configured */
 	if (self->dev.start) {
-		DEBUG(2, __FUNCTION__ 
+		DEBUG(0, __FUNCTION__ 
 		       "(), Device still configured, closing later!\n");
+
+		/* Give it a chance to reconnect */
+		irlan_start_watchdog_timer(self, IRLAN_TIMEOUT);
 		return;
 	}
 	DEBUG(2, __FUNCTION__ "(), daddr=%08x\n", self->daddr);
@@ -371,8 +373,15 @@
         __irlan_close(self);
 }
 
+/*
+ * Function irlan_connect_indication (instance, sap, qos, max_sdu_size, skb)
+ *
+ *    Here we receive the connect indication for the data channel
+ *
+ */
 void irlan_connect_indication(void *instance, void *sap, struct qos_info *qos,
-			      __u32 max_sdu_size, struct sk_buff *skb)
+			      __u32 max_sdu_size, __u8 max_header_size, 
+			      struct sk_buff *skb)
 {
 	struct irlan_cb *self;
 	struct tsap_cb *tsap;
@@ -386,13 +395,17 @@
 	ASSERT(self->magic == IRLAN_MAGIC, return;);
 	ASSERT(tsap == self->tsap_data,return;);
 
-	DEBUG(2, "IrLAN, We are now connected!\n");
+	self->max_sdu_size = max_sdu_size;
+	self->max_header_size = max_header_size;
+
+	DEBUG(0, "IrLAN, We are now connected!\n");
+
 	del_timer(&self->watchdog_timer);
 
 	irlan_do_provider_event(self, IRLAN_DATA_CONNECT_INDICATION, skb);
 	irlan_do_client_event(self, IRLAN_DATA_CONNECT_INDICATION, skb);
 
-	if (self->access_type == ACCESS_PEER) {
+	if (self->provider.access_type == ACCESS_PEER) {
 		/* 
 		 * Data channel is open, so we are now allowed to
 		 * configure the remote filter 
@@ -400,12 +413,13 @@
 		irlan_get_unicast_addr(self);
 		irlan_open_unicast_addr(self);
 	}
-	/* Ready to transfer Ethernet frames */
+	/* Ready to transfer Ethernet frames (at last) */
 	self->dev.tbusy = 0;
 }
 
 void irlan_connect_confirm(void *instance, void *sap, struct qos_info *qos, 
-			   __u32 max_sdu_size, struct sk_buff *skb) 
+			   __u32 max_sdu_size, __u8 max_header_size, 
+			   struct sk_buff *skb) 
 {
 	struct irlan_cb *self;
 
@@ -416,6 +430,9 @@
 	ASSERT(self != NULL, return;);
 	ASSERT(self->magic == IRLAN_MAGIC, return;);
 
+	self->max_sdu_size = max_sdu_size;
+	self->max_header_size = max_header_size;
+
 	/* TODO: we could set the MTU depending on the max_sdu_size */
 
 	DEBUG(2, "IrLAN, We are now connected!\n");
@@ -444,7 +461,7 @@
 	struct irlan_cb *self;
 	struct tsap_cb *tsap;
 
-	DEBUG(2, __FUNCTION__ "(), reason=%d\n", reason);
+	DEBUG(0, __FUNCTION__ "(), reason=%d\n", reason);
 	
 	self = (struct irlan_cb *) instance;
 	tsap = (struct tsap_cb *) sap;
@@ -460,7 +477,7 @@
 
 	switch(reason) {
 	case LM_USER_REQUEST: /* User request */
-		//irlan_close(self);
+		irlan_close(self);
 		break;
 	case LM_LAP_DISCONNECT: /* Unexpected IrLAP disconnect */
 		irlan_start_watchdog_timer(self, IRLAN_TIMEOUT);
@@ -490,7 +507,7 @@
 	struct notify_t notify;
 	struct tsap_cb *tsap;
 
-	DEBUG(4, __FUNCTION__ "()\n");
+	DEBUG(0, __FUNCTION__ "()\n");
 
 	ASSERT(self != NULL, return;);
 	ASSERT(self->magic == IRLAN_MAGIC, return;);
@@ -500,7 +517,7 @@
 		return;
 
 	irda_notify_init(&notify);
-
+	
 	notify.data_indication       = irlan_eth_receive;
 	notify.udata_indication      = irlan_eth_receive;
 	notify.connect_indication    = irlan_connect_indication;
@@ -620,7 +637,7 @@
 		return;
 
 	/* Reserve space for TTP, LMP, and LAP header */
-	skb_reserve(skb, TTP_HEADER+LMP_HEADER+LAP_HEADER);
+	skb_reserve(skb, self->client.max_header_size);
 	skb_put(skb, 2);
 	
 	frame = skb->data;
@@ -651,7 +668,7 @@
 	if (!skb)
 		return;
 
-	skb_reserve(skb, TTP_HEADER+LMP_HEADER+LAP_HEADER);
+	skb_reserve(skb, self->client.max_header_size);
 	skb_put(skb, 2);
 	
 	frame = skb->data;
@@ -683,7 +700,7 @@
 	if (!skb)
 		return;
 
-	skb_reserve(skb, TTP_HEADER+LMP_HEADER+LAP_HEADER);
+	skb_reserve(skb, self->client.max_header_size);
 	skb_put(skb, 2);
 	
 	frame = skb->data;
@@ -719,7 +736,7 @@
 		return;
 
 	/* Reserve space for TTP, LMP, and LAP header */
-	skb_reserve(skb, TTP_HEADER+LMP_HEADER+LAP_HEADER);
+	skb_reserve(skb, self->max_header_size);
 	skb_put(skb, 2);
 	
 	frame = skb->data;
@@ -757,7 +774,7 @@
 		return;
 
 	/* Reserve space for TTP, LMP, and LAP header */
-	skb_reserve(skb, TTP_HEADER+LMP_HEADER+LAP_HEADER);
+	skb_reserve(skb, self->client.max_header_size);
 	skb_put(skb, 2);
 	
 	frame = skb->data;
@@ -796,7 +813,7 @@
 		return;
 	
 	/* Reserve space for TTP, LMP, and LAP header */
-	skb_reserve(skb, TTP_HEADER+LMP_HEADER+LAP_HEADER);
+	skb_reserve(skb, self->client.max_header_size);
 	skb_put(skb, 2);
 	
 	frame = skb->data;
@@ -836,7 +853,7 @@
 		return;
 
 	/* Reserve space for TTP, LMP, and LAP header */
-	skb_reserve(skb, TTP_HEADER+LMP_HEADER+LAP_HEADER);
+	skb_reserve(skb, self->client.max_header_size);
 	skb_put(skb, 2);
 	
 	frame = skb->data;
@@ -871,7 +888,7 @@
 		return;
 
 	/* Reserve space for TTP, LMP, and LAP header */
-	skb_reserve(skb, TTP_HEADER+LMP_HEADER+LAP_HEADER);
+	skb_reserve(skb, self->client.max_header_size);
 	skb_put(skb, 2);
 	
 	frame = skb->data;
@@ -1033,7 +1050,7 @@
 	
 	/* get parameter name */
 	memcpy(name, buf+n, name_len);
-	name[ name_len] = '\0';
+	name[name_len] = '\0';
 	n+=name_len;
 	
 	/*  
@@ -1051,7 +1068,7 @@
 
 	/* get parameter value */
 	memcpy(value, buf+n, val_len);
-	value[ val_len] = '\0';
+	value[val_len] = '\0';
 	n+=val_len;
 	
 	DEBUG(4, "Parameter: %s ", name); 
@@ -1085,31 +1102,35 @@
 	while (self != NULL) {
 		ASSERT(self->magic == IRLAN_MAGIC, return len;);
 		
-		len += sprintf(buf+len, "ifname: %s,\n",
-			       self->ifname);
-		len += sprintf(buf+len, "client state: %s, ",
-			       irlan_state[ self->client.state]);
-		len += sprintf(buf+len, "provider state: %s,\n",
-			       irlan_state[ self->provider.state]);
-		len += sprintf(buf+len, "saddr: %#08x, ",
-			       self->saddr);
-		len += sprintf(buf+len, "daddr: %#08x\n",
-			       self->daddr);
-		len += sprintf(buf+len, "version: %d.%d,\n",
-			       self->version[1], self->version[0]);
-		len += sprintf(buf+len, "access type: %s\n", 
-			       irlan_access[ self->access_type]);
-		len += sprintf(buf+len, "media: %s\n", 
-			       irlan_media[ self->media]);
-
-		len += sprintf(buf+len, "local filter:\n");
-		len += sprintf(buf+len, "remote filter: ");
-		len += irlan_print_filter(self->client.filter_type, buf+len);
-
-		len += sprintf(buf+len, "tx busy: %s\n", self->dev.tbusy ? 
-			       "TRUE" : "FALSE");
-
-		len += sprintf(buf+len, "\n");
+		/* Don't display the master server */
+		if (self->master == 0) {
+			len += sprintf(buf+len, "ifname: %s,\n",
+				       self->ifname);
+			len += sprintf(buf+len, "client state: %s, ",
+				       irlan_state[ self->client.state]);
+			len += sprintf(buf+len, "provider state: %s,\n",
+				       irlan_state[ self->provider.state]);
+			len += sprintf(buf+len, "saddr: %#08x, ",
+				       self->saddr);
+			len += sprintf(buf+len, "daddr: %#08x\n",
+				       self->daddr);
+			len += sprintf(buf+len, "version: %d.%d,\n",
+				       self->version[1], self->version[0]);
+			len += sprintf(buf+len, "access type: %s\n", 
+				       irlan_access[self->client.access_type]);
+			len += sprintf(buf+len, "media: %s\n", 
+				       irlan_media[self->media]);
+			
+			len += sprintf(buf+len, "local filter:\n");
+			len += sprintf(buf+len, "remote filter: ");
+			len += irlan_print_filter(self->client.filter_type, 
+						  buf+len);
+			
+			len += sprintf(buf+len, "tx busy: %s\n", 
+				       self->dev.tbusy ? "TRUE" : "FALSE");
+			
+			len += sprintf(buf+len, "\n");
+		}
 
 		self = (struct irlan_cb *) hashbin_get_next(irlan);
  	} 
diff -u --recursive --new-file v2.3.3/linux/net/irda/irlan/irlan_eth.c linux/net/irda/irlan/irlan_eth.c
--- v2.3.3/linux/net/irda/irlan/irlan_eth.c	Sat Apr 24 17:50:06 1999
+++ linux/net/irda/irlan/irlan_eth.c	Sun May 30 10:27:04 1999
@@ -6,13 +6,13 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Thu Oct 15 08:37:58 1998
- * Modified at:   Thu Apr 22 14:26:39 1999
+ * Modified at:   Mon May 10 20:23:49 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * Sources:       skeleton.c by Donald Becker <becker@CESDIS.gsfc.nasa.gov>
  *                slip.c by Laurence Culhane,   <loz@holmes.demon.co.uk>
  *                          Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
  * 
- *     Copyright (c) 1998 Dag Brattli, All Rights Reserved.
+ *     Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
  *      
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
@@ -29,6 +29,7 @@
 #include <linux/etherdevice.h>
 #include <linux/inetdevice.h>
 #include <linux/if_arp.h>
+#include <linux/random.h>
 #include <net/arp.h>
 
 #include <net/irda/irda.h>
@@ -67,19 +68,19 @@
 	
 	dev->tx_queue_len = TTP_MAX_QUEUE;
 
-#if 0
-	/*  
-	 *  OK, since we are emulating an IrLAN sever we will have to give
-	 *  ourself an ethernet address!
-	 *  FIXME: this must be more dynamically
-	 */
-	dev->dev_addr[0] = 0x40;
-	dev->dev_addr[1] = 0x00;
-	dev->dev_addr[2] = 0x00;
-	dev->dev_addr[3] = 0x00;
-	dev->dev_addr[4] = 0x23;
-	dev->dev_addr[5] = 0x45;
-#endif
+	if (self->provider.access_type == ACCESS_DIRECT) {
+		/*  
+		 * Since we are emulating an IrLAN sever we will have to
+		 * give ourself an ethernet address!  
+		 */
+		dev->dev_addr[0] = 0x40;
+		dev->dev_addr[1] = 0x00;
+		dev->dev_addr[2] = 0x00;
+		dev->dev_addr[3] = 0x00;
+		get_random_bytes(dev->dev_addr+4, 1);
+		get_random_bytes(dev->dev_addr+5, 1);
+	}
+
 	/* 
 	 * Network device has now been registered, so tell irmanager about
 	 * it, so it can be configured with network parameters
@@ -180,8 +181,6 @@
 {
 	struct irlan_cb *self;
 
-	DEBUG(4, __FUNCTION__ "()\n");
-	
 	self = (struct irlan_cb *) dev->priv;
 
 	ASSERT(self != NULL, return 0;);
@@ -202,19 +201,19 @@
  		dev->trans_start = jiffies;
 	}
 	
-	DEBUG(4, "Room left at head: %d\n", skb_headroom(skb));
-	DEBUG(4, "Room left at tail: %d\n", skb_tailroom(skb));
-	DEBUG(4, "Required room: %d\n", IRLAN_MAX_HEADER);
-	
-	/* skb headroom large enough to contain IR-headers? */
-	if ((skb_headroom(skb) < IRLAN_MAX_HEADER) || (skb_shared(skb))) {
+	/* skb headroom large enough to contain all IrDA-headers? */
+	if ((skb_headroom(skb) < self->max_header_size) || (skb_shared(skb))) {
 		struct sk_buff *new_skb = 
-			skb_realloc_headroom(skb, IRLAN_MAX_HEADER);
-		ASSERT(new_skb != NULL, return 0;);
-		ASSERT(skb_headroom(new_skb) >= IRLAN_MAX_HEADER, return 0;);
+			skb_realloc_headroom(skb, self->max_header_size);
 
-		/*  Free original skb, and use the new one */
+		/*  We have to free the original skb anyway */
 		dev_kfree_skb(skb);
+
+		/* Did the realloc succeed? */
+		if (new_skb == NULL)
+			return 0;
+
+		/* Use the new skb instead */
 		skb = new_skb;
 	} 
 
@@ -222,31 +221,26 @@
 	self->stats.tx_packets++;
 	self->stats.tx_bytes += skb->len; 
 
-	/*
-	 *  Now queue the packet in the transport layer
-	 *  FIXME: clean up the code below! DB
-	 */
-	if (self->use_udata) {
+	/* Now queue the packet in the transport layer */
+	if (self->use_udata)
 		irttp_udata_request(self->tsap_data, skb);
-		dev->tbusy = 0;
-	
-		return 0;
-	}
-
-	if (irttp_data_request(self->tsap_data, skb) == -1) {
-		/*  
-		 *  IrTTPs tx queue is full, so we just have to drop the
-		 *  frame! You might think that we should just return -1
-		 *  and don't deallocate the frame, but that is dangerous
-		 *  since it's possible that we have replaced the original
-		 *  skb with a new one with larger headroom, and that would
-		 *  really confuse do_dev_queue_xmit() in dev.c! I have
-		 *  tried :-) DB
-		 */
-		dev_kfree_skb(skb);
-		++self->stats.tx_dropped;
+	else {
+		if (irttp_data_request(self->tsap_data, skb) < 0) {
+			/*   
+			 * IrTTPs tx queue is full, so we just have to
+			 * drop the frame! You might think that we should
+			 * just return -1 and don't deallocate the frame,
+			 * but that is dangerous since it's possible that
+			 * we have replaced the original skb with a new
+			 * one with larger headroom, and that would really
+			 * confuse do_dev_queue_xmit() in dev.c! I have
+			 * tried :-) DB 
+			 */
+			dev_kfree_skb(skb);
+			++self->stats.tx_dropped;
 		
-		return 0;
+			return 0;
+		}
 	}
 	dev->tbusy = 0; /* Finished! */
 	
@@ -314,26 +308,16 @@
 	
 	switch (flow) {
 	case FLOW_STOP:
-		DEBUG(4, "IrLAN, stopping Ethernet layer\n");
-
 		dev->tbusy = 1;
 		break;
 	case FLOW_START:
-		/* 
-		 *  Tell upper layers that its time to transmit frames again
-		 */
-		DEBUG(4, "IrLAN, starting Ethernet layer\n");
-
+	default:
+		/* Tell upper layers that its time to transmit frames again */
 		dev->tbusy = 0;
 
-		/* 
-		 *  Ready to receive more frames, so schedule the network
-		 *  layer
-		 */
+		/* Schedule network layer */
 		mark_bh(NET_BH);		
 		break;
-	default:
-		DEBUG(0, __FUNCTION__ "(), Unknown flow command!\n");
 	}
 }
 
@@ -373,7 +357,7 @@
 	in_dev = dev->ip_ptr;
 	arp_send(ARPOP_REQUEST, ETH_P_ARP, 
 		 in_dev->ifa_list->ifa_address,
-		 &dev, 
+		 dev, 
 		 in_dev->ifa_list->ifa_address,
 		 NULL, dev->dev_addr, NULL);
 }
diff -u --recursive --new-file v2.3.3/linux/net/irda/irlan/irlan_event.c linux/net/irda/irlan/irlan_event.c
--- v2.3.3/linux/net/irda/irlan/irlan_event.c	Sun Mar  7 15:26:44 1999
+++ linux/net/irda/irlan/irlan_event.c	Sun May 30 10:27:04 1999
@@ -6,10 +6,10 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Tue Oct 20 09:10:16 1998
- * Modified at:   Wed Feb  3 21:42:27 1999
+ * Modified at:   Sun May  9 21:17:44 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1998 Dag Brattli, All Rights Reserved.
+ *     Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
  *      
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
@@ -38,22 +38,22 @@
 	"IRLAN_SYNC",
 };
 
-void irlan_next_client_state( struct irlan_cb *self, IRLAN_STATE state) 
+void irlan_next_client_state(struct irlan_cb *self, IRLAN_STATE state) 
 {
 	DEBUG(2, __FUNCTION__"(), %s\n", irlan_state[state]);
 
-	ASSERT( self != NULL, return;);
-	ASSERT( self->magic == IRLAN_MAGIC, return;);
+	ASSERT(self != NULL, return;);
+	ASSERT(self->magic == IRLAN_MAGIC, return;);
 
 	self->client.state = state;
 }
 
-void irlan_next_provider_state( struct irlan_cb *self, IRLAN_STATE state) 
+void irlan_next_provider_state(struct irlan_cb *self, IRLAN_STATE state) 
 {
 	DEBUG(2, __FUNCTION__"(), %s\n", irlan_state[state]);
 
-	ASSERT( self != NULL, return;);
-	ASSERT( self->magic == IRLAN_MAGIC, return;);
+	ASSERT(self != NULL, return;);
+	ASSERT(self->magic == IRLAN_MAGIC, return;);
 
 	self->provider.state = state;
 }
diff -u --recursive --new-file v2.3.3/linux/net/irda/irlan/irlan_filter.c linux/net/irda/irlan/irlan_filter.c
--- v2.3.3/linux/net/irda/irlan/irlan_filter.c	Thu Apr 15 05:42:42 1999
+++ linux/net/irda/irlan/irlan_filter.c	Sun May 30 10:27:04 1999
@@ -6,10 +6,10 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Fri Jan 29 11:16:38 1999
- * Modified at:   Thu Feb 25 15:10:54 1999
+ * Modified at:   Sat May  8 15:25:23 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1998 Dag Brattli, All Rights Reserved.
+ *     Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
  *      
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
@@ -41,29 +41,29 @@
 	    (self->provider.filter_operation == DYNAMIC))
 	{
 		DEBUG(0, "Giving peer a dynamic Ethernet address\n");
-
 		self->provider.mac_address[0] = 0x40;
 		self->provider.mac_address[1] = 0x00;
 		self->provider.mac_address[2] = 0x00;
 		self->provider.mac_address[3] = 0x00;
 		
 		/* Use arbitration value to generate MAC address */
-		if (self->access_type == ACCESS_PEER) {
+		if (self->provider.access_type == ACCESS_PEER) {
 			self->provider.mac_address[4] = 
 				self->provider.send_arb_val & 0xff;
 			self->provider.mac_address[5] = 
 				(self->provider.send_arb_val >> 8) & 0xff;;
 		} else {
 			/* Just generate something for now */
-			self->provider.mac_address[4] = jiffies & 0xff;
-			self->provider.mac_address[5] = (jiffies >> 8) & 0xff;
+			get_random_bytes(self->provider.mac_address+4, 1);
+			get_random_bytes(self->provider.mac_address+5, 1);
 		}
 
 		skb->data[0] = 0x00; /* Success */
 		skb->data[1] = 0x03;
 		irlan_insert_string_param(skb, "FILTER_MODE", "NONE");
 		irlan_insert_short_param(skb, "MAX_ENTRY", 0x0001);
-		irlan_insert_array_param(skb, "FILTER_ENTRY", self->provider.mac_address, 6);
+		irlan_insert_array_param(skb, "FILTER_ENTRY", 
+					 self->provider.mac_address, 6);
 		return;
 	}
 	
@@ -138,8 +138,7 @@
  *    Check parameters in request from peer device
  *
  */
-void irlan_check_command_param(struct irlan_cb *self, char *param, 
-			       char *value)
+void irlan_check_command_param(struct irlan_cb *self, char *param, char *value)
 {
 	__u8 *bytes;
 
@@ -210,6 +209,12 @@
 	}
 }
 
+/*
+ * Function irlan_print_filter (filter_type, buf)
+ *
+ *    Print status of filter. Used by /proc file system
+ *
+ */
 int irlan_print_filter(int filter_type, char *buf)
 {
 	int len = 0;
diff -u --recursive --new-file v2.3.3/linux/net/irda/irlan/irlan_provider.c linux/net/irda/irlan/irlan_provider.c
--- v2.3.3/linux/net/irda/irlan/irlan_provider.c	Sat Apr 24 17:50:06 1999
+++ linux/net/irda/irlan/irlan_provider.c	Sun May 30 10:27:04 1999
@@ -6,13 +6,14 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Sun Aug 31 20:14:37 1997
- * Modified at:   Thu Apr 22 14:28:52 1999
+ * Modified at:   Sun May  9 12:22:56 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * Sources:       skeleton.c by Donald Becker <becker@CESDIS.gsfc.nasa.gov>
  *                slip.c by Laurence Culhane,   <loz@holmes.demon.co.uk>
  *                          Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
  * 
- *     Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved.
+ *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, 
+ *     All Rights Reserved.
  *     
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
@@ -31,6 +32,7 @@
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/init.h>
+#include <linux/random.h>
 
 #include <asm/system.h>
 #include <asm/bitops.h>
@@ -50,14 +52,20 @@
 #include <net/irda/irlan_filter.h>
 #include <net/irda/irlan_client.h>
 
+static void irlan_provider_connect_indication(void *instance, void *sap, 
+					      struct qos_info *qos, 
+					      __u32 max_sdu_size,
+					      __u8 max_header_size,
+					      struct sk_buff *skb);
+
 /*
  * Function irlan_provider_control_data_indication (handle, skb)
  *
  *    This function gets the data that is received on the control channel
  *
  */
-int irlan_provider_data_indication(void *instance, void *sap, 
-				   struct sk_buff *skb) 
+static int irlan_provider_data_indication(void *instance, void *sap, 
+					  struct sk_buff *skb) 
 {
 	struct irlan_cb *self;
 	__u8 code;
@@ -111,14 +119,17 @@
  *    Got connection from peer IrLAN layer
  *
  */
-void irlan_provider_connect_indication(void *instance, void *sap, 
-				       struct qos_info *qos,
-				       __u32 max_sdu_size, struct sk_buff *skb)
+static void irlan_provider_connect_indication(void *instance, void *sap, 
+					      struct qos_info *qos,
+					      __u32 max_sdu_size, 
+					      __u8 max_header_size,
+					       struct sk_buff *skb)
 {
-	struct irlan_cb *self, *entry, *new;
+	struct irlan_cb *self, *new;
 	struct tsap_cb *tsap;
+	__u32 saddr, daddr;
 
-	DEBUG(2, __FUNCTION__ "()\n");
+	DEBUG(0, __FUNCTION__ "()\n");
 	
 	self = (struct irlan_cb *) instance;
 	tsap = (struct tsap_cb *) sap;
@@ -126,34 +137,69 @@
 	ASSERT(self != NULL, return;);
 	ASSERT(self->magic == IRLAN_MAGIC, return;);
 	
+	self->provider.max_sdu_size = max_sdu_size;
+	self->provider.max_header_size = max_header_size;
+
 	ASSERT(tsap == self->provider.tsap_ctrl,return;);
 	ASSERT(self->provider.state == IRLAN_IDLE, return;);
 
-	/* Check if this provider is currently unused */
-	if (self->daddr == DEV_ADDR_ANY) {
-		/*
-		 * Rehash instance, now we have a client (daddr) to serve.
-		 */
-		entry = hashbin_remove(irlan, self->daddr, NULL);
-		ASSERT( entry == self, return;);
-		
-		self->daddr = irttp_get_daddr(tsap);
-		DEBUG(2, __FUNCTION__ "(), daddr=%08x\n", self->daddr);
-		hashbin_insert(irlan, (QUEUE*) self, self->daddr, NULL);
+	daddr = irttp_get_daddr(tsap);
+	saddr = irttp_get_saddr(tsap);
+
+	/* Check if we already dealing with this client or peer */
+	new = (struct irlan_cb *) hashbin_find(irlan, daddr, NULL);
+      	if (new) {
+		ASSERT(new->magic == IRLAN_MAGIC, return;);
+		DEBUG(0, __FUNCTION__ "(), found instance!\n");
+
+		/* Update saddr, since client may have moved to a new link */
+		new->saddr = saddr;
+		DEBUG(2, __FUNCTION__ "(), saddr=%08x\n", new->saddr);
+
+		/* Make sure that any old provider control TSAP is removed */
+		if ((new != self) && new->provider.tsap_ctrl) {
+			irttp_disconnect_request(new->provider.tsap_ctrl, 
+						 NULL, P_NORMAL);
+			irttp_close_tsap(new->provider.tsap_ctrl);
+			new->provider.tsap_ctrl = NULL;
+		}
 	} else {
-		/*
-		 * If we already have the daddr set, this means that the
-		 * client must already have started (peer mode). We must
-		 * make sure that this connection attempt is from the same
-		 * device as the client is dealing with!  
+		/* This must be the master instance, so start a new instance */
+		DEBUG(0, __FUNCTION__ "(), starting new provider!\n");
+
+		new = irlan_open(saddr, daddr, TRUE); 
+	}
+
+	/*  
+	 * Check if the connection came in on the master server, or the
+	 * slave server. If it came on the slave, then everything is
+	 * really, OK (reconnect), if not we need to dup the connection and
+	 * hand it over to the slave.  
+	 */
+	if (new != self) {
+				
+		/* Now attach up the new "socket" */
+		new->provider.tsap_ctrl = irttp_dup(self->provider.tsap_ctrl, 
+						    new);
+		if (!new->provider.tsap_ctrl) {
+			DEBUG(0, __FUNCTION__ "(), dup failed!\n");
+			return;
+		}
+		
+		/* new->stsap_sel = new->tsap->stsap_sel; */
+		new->dtsap_sel_ctrl = new->provider.tsap_ctrl->dtsap_sel;
+
+		/* Clean up the original one to keep it in listen state */
+		self->provider.tsap_ctrl->dtsap_sel = LSAP_ANY;
+		self->provider.tsap_ctrl->lsap->dlsap_sel = LSAP_ANY;
+		self->provider.tsap_ctrl->lsap->lsap_state = LSAP_DISCONNECTED;
+		
+		/* 
+		 * Use the new instance from here instead of the master
+		 * struct! 
 		 */
-		ASSERT(self->daddr == irttp_get_daddr(tsap), return;);
+		self = new;
 	}
-	
-	/* Update saddr, since client may have moved to a new link */
-	self->saddr = irttp_get_saddr(tsap);
-	DEBUG(2, __FUNCTION__ "(), saddr=%08x\n", self->saddr);
-	
 	/* Check if network device has been registered */
 	if (!self->netdev_registered)
 		irlan_register_netdev(self);
@@ -165,9 +211,10 @@
 	 * indication it needs to make progress. If the client is still in 
 	 * IDLE state, we must kick it to 
 	 */
-	if ((self->access_type == ACCESS_PEER) && 
-	    (self->client.state == IRLAN_IDLE))
+	if ((self->provider.access_type == ACCESS_PEER) && 
+	    (self->client.state == IRLAN_IDLE)) {
 		irlan_client_wakeup(self, self->saddr, self->daddr);
+	}
 }
 
 /*
@@ -225,6 +272,9 @@
 	
 	ret = irlan_provider_parse_command(self, CMD_OPEN_DATA_CHANNEL, skb);
 
+	/* Open data channel */
+	irlan_open_data_tsap(self);
+
 	return ret;
 }
 
@@ -314,7 +364,7 @@
 		return;
 
 	/* Reserve space for TTP, LMP, and LAP header */
-	skb_reserve(skb, TTP_HEADER+LMP_HEADER+LAP_HEADER);
+	skb_reserve(skb, self->provider.max_header_size);
 	skb_put(skb, 2);
        
 	switch (command) {
@@ -334,6 +384,7 @@
 		}
 		irlan_insert_short_param(skb, "IRLAN_VER", 0x0101);
 		break;
+
 	case CMD_GET_MEDIA_CHAR:
 		skb->data[0] = 0x00; /* Success */
 		skb->data[1] = 0x05; /* 5 parameters */
@@ -341,7 +392,7 @@
 		irlan_insert_string_param(skb, "FILTER_TYPE", "BROADCAST");
 		irlan_insert_string_param(skb, "FILTER_TYPE", "MULTICAST");
 
-		switch(self->access_type) {
+		switch (self->provider.access_type) {
 		case ACCESS_DIRECT:
 			irlan_insert_string_param(skb, "ACCESS_TYPE", "DIRECT");
 			break;
diff -u --recursive --new-file v2.3.3/linux/net/irda/irlan/irlan_provider_event.c linux/net/irda/irlan/irlan_provider_event.c
--- v2.3.3/linux/net/irda/irlan/irlan_provider_event.c	Sat Apr 24 17:50:06 1999
+++ linux/net/irda/irlan/irlan_provider_event.c	Sun May 30 10:27:04 1999
@@ -6,10 +6,10 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Sun Aug 31 20:14:37 1997
- * Modified at:   Thu Apr 22 10:46:28 1999
+ * Modified at:   Fri May  7 10:53:58 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved.
+ *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved.
  *     
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
@@ -108,7 +108,7 @@
 	switch(event) {
 	case IRLAN_GET_INFO_CMD:
 		/* Be sure to use 802.3 in case of peer mode */
-		if (self->access_type == ACCESS_PEER) {
+		if (self->provider.access_type == ACCESS_PEER) {
 			self->media = MEDIA_802_3;
 			
 			/* Check if client has started yet */
@@ -129,7 +129,7 @@
 		break;		
 	case IRLAN_OPEN_DATA_CMD:
 		ret = irlan_parse_open_data_cmd(self, skb);
-		if (self->access_type == ACCESS_PEER) {
+		if (self->provider.access_type == ACCESS_PEER) {
 			/* FIXME: make use of random functions! */
 			self->provider.send_arb_val = (jiffies & 0xffff);
 		}
@@ -205,8 +205,6 @@
 static int irlan_provider_state_data(struct irlan_cb *self, IRLAN_EVENT event, 
 				     struct sk_buff *skb) 
 {
-	struct irmanager_event mgr_event;
-
 	DEBUG(4, __FUNCTION__ "()\n");
 
 	ASSERT(self != NULL, return -1;);
@@ -220,10 +218,6 @@
 		break;
 	case IRLAN_LMP_DISCONNECT: /* FALLTHROUGH */
 	case IRLAN_LAP_DISCONNECT:
-		mgr_event.event = EVENT_IRLAN_STOP;
-		sprintf(mgr_event.devname, "%s", self->ifname);
-		irmanager_notify(&mgr_event);
-
 		irlan_next_provider_state(self, IRLAN_IDLE);
 		break;
 	default:
diff -u --recursive --new-file v2.3.3/linux/net/irda/irlap_comp.c linux/net/irda/irlap_comp.c
--- v2.3.3/linux/net/irda/irlap_comp.c	Sun Mar  7 15:26:44 1999
+++ linux/net/irda/irlap_comp.c	Sun May 30 10:27:04 1999
@@ -6,11 +6,11 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Fri Oct  9 09:18:07 1998
- * Modified at:   Mon Feb  8 01:23:52 1999
+ * Modified at:   Sun May  9 11:37:06 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * Sources:       ppp.c, isdn_ppp.c
  * 
- *     Copyright (c) 1998 Dag Brattli, All Rights Reserved.
+ *     Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
  *      
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
@@ -255,11 +255,11 @@
 	}
 
 	/* FIXME: Find out what is the max overhead (not 10) */
-	new_skb = dev_alloc_skb( skb->len+LAP_HEADER+10);
+	new_skb = dev_alloc_skb( skb->len+LAP_MAX_HEADER+10);
 	if(!new_skb)
 		return skb;
 
-	skb_reserve( new_skb, LAP_HEADER);
+	skb_reserve( new_skb, LAP_MAX_HEADER);
 	skb_put( new_skb, skb->len+10);
 	
 	count = (self->compressor.cp->compress)( self->compressor.state, 
diff -u --recursive --new-file v2.3.3/linux/net/irda/irlap_event.c linux/net/irda/irlap_event.c
--- v2.3.3/linux/net/irda/irlap_event.c	Sat Apr 24 17:50:06 1999
+++ linux/net/irda/irlap_event.c	Sun May 30 10:27:04 1999
@@ -6,10 +6,10 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Sat Aug 16 00:59:29 1997
- * Modified at:   Fri Apr 23 11:55:12 1999
+ * Modified at:   Sun May  9 22:44:32 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>,
+ *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>,
  *                        Thomas Davis <ratbert@radiks.net>
  *     All Rights Reserved.
  *     
@@ -274,22 +274,22 @@
  *    Switches state and provides debug information
  *
  */
-void irlap_next_state( struct irlap_cb *self, IRLAP_STATE state) 
+void irlap_next_state(struct irlap_cb *self, IRLAP_STATE state) 
 {	
-	if ( !self || self->magic != LAP_MAGIC)
+	if (!self || self->magic != LAP_MAGIC)
 		return;
 	
-	DEBUG( 4, "next LAP state = %s\n", irlap_state[ state]);
+	DEBUG(4, "next LAP state = %s\n", irlap_state[ state]);
 
 	self->state = state;
 
 	/*
 	 *  If we are swithing away from a XMIT state then we are allowed to 
 	 *  transmit a maximum number of bytes again when we enter the XMIT 
-	 *  state again. Since its possible to "switch" from XMIT to XMIT and
+	 *  state again. Since its possible to "switch" from XMIT to XMIT,
 	 *  we cannot do this when swithing into the XMIT state :-)
 	 */
-	if (( state != LAP_XMIT_P) && ( state != LAP_XMIT_S))
+	if ((state != LAP_XMIT_P) && (state != LAP_XMIT_S))
 		self->bytes_left = self->window_bytes;
 }
 
@@ -310,7 +310,7 @@
 	ASSERT( self != NULL, return -1;);
 	ASSERT( self->magic == LAP_MAGIC, return -1;);
 
-	switch( event) {
+	switch(event) {
 	case CONNECT_REQUEST:
 		ASSERT( self->irdev != NULL, return -1;);
 
@@ -393,7 +393,6 @@
 			irlap_start_query_timer( self, QUERY_TIMEOUT);
 			irlap_next_state( self, LAP_REPLY);
 		}
-
 		dev_kfree_skb(skb);
 		break;
 
@@ -530,7 +529,7 @@
 			irlap_send_discovery_xid_frame(self, info->S,
 						       self->slot, FALSE,
 						       discovery_rsp);
-
+			
 			self->frame_sent = TRUE;
 			irlap_next_state(self, LAP_REPLY);
 		}
@@ -568,27 +567,28 @@
 
 	switch (event) {
 	case CONNECT_RESPONSE:
-		skb_pull( skb, 11);
+		/* skb_pull(skb, 11); */
+		skb_pull(skb, sizeof(struct snrm_frame));
 
-		ASSERT( self->irdev != NULL, return -1;);
-		irda_qos_negotiate( &self->qos_rx, &self->qos_tx, skb);
+		ASSERT(self->irdev != NULL, return -1;);
+		irda_qos_negotiate(&self->qos_rx, &self->qos_tx, skb);
 
 		irlap_initiate_connection_state( self);
 
 		/*
 		 * We are allowed to send two frames!
 		 */
-		irlap_send_ua_response_frame( self, &self->qos_rx);
-		irlap_send_ua_response_frame( self, &self->qos_rx);
+		irlap_send_ua_response_frame(self, &self->qos_rx);
+		irlap_send_ua_response_frame(self, &self->qos_rx);
 		
-		irlap_apply_connection_parameters( self, &self->qos_tx);
+		irlap_apply_connection_parameters(self, &self->qos_tx);
 
 		/*
 		 *  The WD-timer could be set to the duration of the P-timer 
-		 *  for this case, but it is recommomended to use twice the 
+		 *  for this case, but it is recommended to use twice the 
 		 *  value (note 3 IrLAP p. 60). 
 		 */
-		irlap_start_wd_timer( self, self->wd_timeout);
+		irlap_start_wd_timer(self, self->wd_timeout);
 		irlap_next_state( self, LAP_NRM_S);
 		break;
 
@@ -669,28 +669,30 @@
 		 *  The device with the largest device address wins the battle
 		 *  (both have sent a SNRM command!)
 		 */
-		if ( info->daddr > self->saddr) {
-			del_timer( &self->final_timer);
-			irlap_initiate_connection_state( self);
+		if (info->daddr > self->saddr) {
+			del_timer(&self->final_timer);
+			irlap_initiate_connection_state(self);
 
-			ASSERT( self->irdev != NULL, return -1;);
-			irda_qos_negotiate( &self->qos_rx, &self->qos_tx, skb);
+			ASSERT(self->irdev != NULL, return -1;);
+			/* skb_pull(skb, 11); */
+			skb_pull(skb, sizeof(struct snrm_frame));
+			irda_qos_negotiate(&self->qos_rx, &self->qos_tx, skb);
 			
 			irlap_send_ua_response_frame(self, &self->qos_rx);
-			irlap_apply_connection_parameters( self, &self->qos_tx);
-			irlap_connect_confirm( self, skb);
+			irlap_apply_connection_parameters(self, &self->qos_tx);
+			irlap_connect_confirm(self, skb);
 			
 			/* 
 			 *  The WD-timer could be set to the duration of the
-			 *  P-timer for this case, but it is recommomended
+			 *  P-timer for this case, but it is recommended
 			 *  to use twice the value (note 3 IrLAP p. 60).  
 			 */
-			irlap_start_wd_timer( self, self->wd_timeout);
+			irlap_start_wd_timer(self, self->wd_timeout);
 			
-			irlap_next_state( self, LAP_NRM_S);
+			irlap_next_state(self, LAP_NRM_S);
 		} else {
 			/* We just ignore the other device! */
-			irlap_next_state( self, LAP_SETUP);
+			irlap_next_state(self, LAP_SETUP);
 		}
 		break;
 	case RECV_UA_RSP:
@@ -702,9 +704,10 @@
 
 		/* Negotiate connection parameters */
 		ASSERT( skb->len > 10, return -1;);
-		skb_pull( skb, 10);
+		/* skb_pull(skb, 10); */
+		skb_pull(skb, sizeof(struct ua_frame));
 
-		ASSERT( self->irdev != NULL, return -1;);
+		ASSERT(self->irdev != NULL, return -1;);
 		irda_qos_negotiate( &self->qos_rx, &self->qos_tx, skb);
 
 		irlap_apply_connection_parameters( self, &self->qos_tx); 
@@ -1570,7 +1573,7 @@
 			/* 
 			 *  poll bit cleared?
 			 */
-			if ( !info->pf) {
+			if (!info->pf) {
 				self->vr = (self->vr + 1) % 8;
 				
 				/* Update Nr received */
@@ -1600,27 +1603,32 @@
 				 *  also before changing to XMIT_S
 				 *  state. (note 1, IrLAP p. 82) 
 				 */
-				irlap_wait_min_turn_around( self, &self->qos_tx);
-				/*
-				 *  Any pending data requests?
+				irlap_wait_min_turn_around(self, &self->qos_tx);
+
+				/*  
+				 * Give higher layers a chance to
+				 * immediately reply with some data before
+				 * we decide if we should send a RR frame
+				 * or not
 				 */
-				if (( skb_queue_len( &self->tx_list) > 0) && 
-				    ( self->window > 0)) 
+				irlap_data_indication(self, skb);
+
+				/* Any pending data requests?  */
+				if ((skb_queue_len(&self->tx_list) > 0) && 
+				    (self->window > 0)) 
 				{
 					self->ack_required = TRUE;
 					
-					del_timer( &self->wd_timer);
+					del_timer(&self->wd_timer);
 					
-					irlap_next_state( self, LAP_XMIT_S);
+					irlap_next_state(self, LAP_XMIT_S);
 				} else {
-					irlap_send_rr_frame( self, RSP_FRAME);
-					irlap_start_wd_timer( self, self->wd_timeout);
+					irlap_send_rr_frame(self, RSP_FRAME);
+					irlap_start_wd_timer(self, self->wd_timeout);
 
 					/* Keep the state */
-					irlap_next_state( self, LAP_NRM_S);
+					irlap_next_state(self, LAP_NRM_S);
 				}
-				irlap_data_indication( self, skb);
-
 				break;
 			}
 		}
diff -u --recursive --new-file v2.3.3/linux/net/irda/irlap_frame.c linux/net/irda/irlap_frame.c
--- v2.3.3/linux/net/irda/irlap_frame.c	Sat Apr 24 17:50:06 1999
+++ linux/net/irda/irlap_frame.c	Sun May 30 10:27:04 1999
@@ -6,10 +6,10 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Tue Aug 19 10:27:26 1997
- * Modified at:   Fri Apr 23 09:30:42 1999
+ * Modified at:   Sun May  9 22:55:11 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>, All Rights Resrved.
+ *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, All Rights Resrved.
  *     
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
@@ -1056,8 +1056,8 @@
  *    Receive and parse an Unnumbered Information (UI) frame
  *
  */
-static void irlap_recv_ui_frame( struct irlap_cb *self, struct sk_buff *skb, 
-				 struct irlap_info *info)
+static void irlap_recv_ui_frame(struct irlap_cb *self, struct sk_buff *skb, 
+				struct irlap_info *info)
 {
 	__u8 *frame;
 
@@ -1254,7 +1254,7 @@
 		 *  Received S(upervisory) frame, check which frame type it is
 		 *  only the first nibble is of interest
 		 */
-		switch(control & 0x0f) {
+		switch (control & 0x0f) {
 		case RR:
 			irlap_recv_rr_frame( self, skb, &info, command);
 			self->stats.rx_packets++;
@@ -1279,7 +1279,7 @@
 	/* 
 	 *  This must be a C(ontrol) frame 
 	 */
-	switch(control) {
+	switch (control) {
 	case XID_RSP:
 		irlap_recv_discovery_xid_rsp(self, skb, &info);
 		break;
diff -u --recursive --new-file v2.3.3/linux/net/irda/irlmp.c linux/net/irda/irlmp.c
--- v2.3.3/linux/net/irda/irlmp.c	Sat Apr 24 17:50:06 1999
+++ linux/net/irda/irlmp.c	Sun May 30 10:27:04 1999
@@ -6,10 +6,10 @@
  * Status:        Stable.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Sun Aug 17 20:54:32 1997
- * Modified at:   Fri Apr 23 09:13:24 1999
+ * Modified at:   Sun May  9 22:45:06 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>, 
+ *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, 
  *     All Rights Reserved.
  *     
  *     This program is free software; you can redistribute it and/or 
@@ -197,7 +197,7 @@
 }
 
 /*
- * Function irlmp_close_lsap (self)
+ * Function __irlmp_close_lsap (self)
  *
  *    Remove an instance of LSAP
  */
@@ -369,11 +369,11 @@
 		if (!skb)
 			return -ENOMEM;
 
-		skb_reserve(skb, LMP_CONTROL_HEADER+LAP_HEADER);
+		skb_reserve(skb, LMP_MAX_HEADER);
 	} else
 		skb = userdata;
 	
-	/* Make room for MUX control header ( 3 bytes) */
+	/* Make room for MUX control header (3 bytes) */
 	ASSERT(skb_headroom(skb) >= LMP_CONTROL_HEADER, return -1;);
 	skb_push(skb, LMP_CONTROL_HEADER);
 
@@ -443,25 +443,36 @@
 void irlmp_connect_indication(struct lsap_cb *self, struct sk_buff *skb) 
 {
 	int max_seg_size;
-
-	DEBUG(3, __FUNCTION__ "()\n");
+	int lap_header_size;
+	int max_header_size;
 	
 	ASSERT(self != NULL, return;);
 	ASSERT(self->magic == LMP_LSAP_MAGIC, return;);
 	ASSERT(skb != NULL, return;);
 	ASSERT(self->lap != NULL, return;);
 
+	DEBUG(0, __FUNCTION__ "(), slsap_sel=%02x, dlsap_sel=%02x\n", 
+	      self->slsap_sel, self->dlsap_sel);
+
 	self->qos = *self->lap->qos;
 
-	max_seg_size = self->lap->qos->data_size.value;
-	DEBUG(4, __FUNCTION__ "(), max_seg_size=%d\n", max_seg_size);
+	lap_header_size = irlap_get_header_size(self->lap->irlap);
+
+	max_seg_size = self->lap->qos->data_size.value-LMP_HEADER-
+		lap_header_size;
+	DEBUG(2, __FUNCTION__ "(), max_seg_size=%d\n", max_seg_size);
 	
+	max_header_size = LMP_HEADER + lap_header_size;
+
+	DEBUG(2, __FUNCTION__ "(), max_header_size=%d\n", max_header_size);
+
 	/* Hide LMP_CONTROL_HEADER header from layer above */
 	skb_pull(skb, LMP_CONTROL_HEADER);
 
 	if (self->notify.connect_indication)
 		self->notify.connect_indication(self->notify.instance, self, 
-						&self->qos, max_seg_size, skb);
+						&self->qos, max_seg_size, 
+						max_header_size, skb);
 }
 
 /*
@@ -470,24 +481,22 @@
  *    Service user is accepting connection
  *
  */
-void irlmp_connect_response( struct lsap_cb *self, struct sk_buff *userdata) 
+void irlmp_connect_response(struct lsap_cb *self, struct sk_buff *userdata) 
 {
-	DEBUG(3, __FUNCTION__ "()\n");
-	
-	ASSERT( self != NULL, return;);
-	ASSERT( self->magic == LMP_LSAP_MAGIC, return;);
-	ASSERT( userdata != NULL, return;);
+	ASSERT(self != NULL, return;);
+	ASSERT(self->magic == LMP_LSAP_MAGIC, return;);
+	ASSERT(userdata != NULL, return;);
 
 	self->connected = TRUE;
 
-	DEBUG( 4, "irlmp_connect_response: slsap_sel=%02x, dlsap_sel=%02x\n", 
-	       self->slsap_sel, self->dlsap_sel);
+	DEBUG(2, __FUNCTION__ "(), slsap_sel=%02x, dlsap_sel=%02x\n", 
+	      self->slsap_sel, self->dlsap_sel);
 
 	/* Make room for MUX control header ( 3 bytes) */
-	ASSERT( skb_headroom( userdata) >= LMP_CONTROL_HEADER, return;);
-	skb_push( userdata, LMP_CONTROL_HEADER);
+	ASSERT(skb_headroom(userdata) >= LMP_CONTROL_HEADER, return;);
+	skb_push(userdata, LMP_CONTROL_HEADER);
 	
-	irlmp_do_lsap_event( self, LM_CONNECT_RESPONSE, userdata);
+	irlmp_do_lsap_event(self, LM_CONNECT_RESPONSE, userdata);
 }
 
 /*
@@ -498,25 +507,35 @@
 void irlmp_connect_confirm(struct lsap_cb *self, struct sk_buff *skb) 
 {
 	int max_seg_size;
+	int max_header_size;
+	int lap_header_size;
 
 	DEBUG(3, __FUNCTION__ "()\n");
 	
-	ASSERT( skb != NULL, return;);
-	ASSERT( self != NULL, return;);
-	ASSERT( self->magic == LMP_LSAP_MAGIC, return;);
+	ASSERT(skb != NULL, return;);
+	ASSERT(self != NULL, return;);
+	ASSERT(self->magic == LMP_LSAP_MAGIC, return;);
 	
-	ASSERT( self->lap != NULL, return;);
+	ASSERT(self->lap != NULL, return;);
 	self->qos = *self->lap->qos;
 
-	max_seg_size = self->qos.data_size.value;
-	DEBUG( 4, __FUNCTION__ "(), max_seg_size=%d\n", max_seg_size);
+	lap_header_size = irlap_get_header_size(self->lap->irlap);
+
+	max_seg_size = self->lap->qos->data_size.value-LMP_HEADER-
+		lap_header_size;
+	DEBUG(2, __FUNCTION__ "(), max_seg_size=%d\n", max_seg_size);
 	
+	max_header_size = LMP_HEADER + lap_header_size;
+
+	DEBUG(2, __FUNCTION__ "(), max_header_size=%d\n", max_header_size);
+
 	/* Hide LMP_CONTROL_HEADER header from layer above */
-	skb_pull( skb, LMP_CONTROL_HEADER);
+	skb_pull(skb, LMP_CONTROL_HEADER);
 
-	if ( self->notify.connect_confirm) {
-		self->notify.connect_confirm( self->notify.instance, self,
-					      &self->qos, max_seg_size, skb);
+	if (self->notify.connect_confirm) {
+		self->notify.connect_confirm(self->notify.instance, self,
+					     &self->qos, max_seg_size, 
+					     max_header_size, skb);
 	}
 }
 
@@ -620,8 +639,8 @@
  *
  *    LSAP is being closed!
  */
-void irlmp_disconnect_indication( struct lsap_cb *self, LM_REASON reason, 
-				  struct sk_buff *userdata) 
+void irlmp_disconnect_indication(struct lsap_cb *self, LM_REASON reason, 
+				 struct sk_buff *userdata) 
 {
 	struct lsap_cb *lsap;
 
@@ -637,6 +656,10 @@
 	self->connected = FALSE;
 	self->dlsap_sel = LSAP_ANY;
 
+#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
+	irlmp->cache.valid = FALSE;
+#endif
+
 	/* 
 	 *  Remove association between this LSAP and the link it used 
 	 */
@@ -975,7 +998,7 @@
 	DEBUG( 1, "irlmp_status_request(), Not implemented\n");
 }
 
-void irlmp_status_indication( LINK_STATUS link, LOCK_STATUS lock) 
+void irlmp_status_indication(LINK_STATUS link, LOCK_STATUS lock) 
 {
 	DEBUG( 4, "irlmp_status_indication(), Not implemented\n");
 }
@@ -1418,14 +1441,14 @@
  *    Give some info to the /proc file system
  *
  */
-int irlmp_proc_read( char *buf, char **start, off_t offset, int len, 
-		     int unused)
+int irlmp_proc_read(char *buf, char **start, off_t offset, int len, 
+		    int unused)
 {
 	struct lsap_cb *self;
 	struct lap_cb *lap;
 	unsigned long flags;
 
-	ASSERT( irlmp != NULL, return 0;);
+	ASSERT(irlmp != NULL, return 0;);
 	
 	save_flags( flags);
 	cli();
@@ -1449,35 +1472,34 @@
  	} 
 
 	len += sprintf( buf+len, "\nRegistred Link Layers:\n");
-	lap = (struct lap_cb *) hashbin_get_first( irlmp->links);
-	while ( lap != NULL) {
-		ASSERT( lap->magic == LMP_LAP_MAGIC, return 0;);
 
-		len += sprintf( buf+len, "lap state: %s, ", 
-				irlmp_state[ lap->lap_state]);
+	lap = (struct lap_cb *) hashbin_get_first(irlmp->links);
+	while (lap != NULL) {
+		len += sprintf(buf+len, "lap state: %s, ", 
+			       irlmp_state[lap->lap_state]);
 
-		len += sprintf( buf+len, "saddr: %#08x, daddr: %#08x, ",
-				lap->saddr, lap->daddr); 
-		len += sprintf( buf+len, "\n");
+		len += sprintf(buf+len, "saddr: %#08x, daddr: %#08x, ",
+			       lap->saddr, lap->daddr); 
+		len += sprintf(buf+len, "\n");
 
 		len += sprintf( buf+len, "\nConnected LSAPs:\n");
 		self = (struct lsap_cb *) hashbin_get_first( lap->lsaps);
-		while ( self != NULL) {
-			ASSERT( self->magic == LMP_LSAP_MAGIC, return 0;);
-			len += sprintf( buf+len, "lsap state: %s, ", 
-					irlsap_state[ self->lsap_state]);
-			len += sprintf( buf+len, 
-					"slsap_sel: %#02x, dlsap_sel: %#02x, ",
-					self->slsap_sel, self->dlsap_sel);
-			len += sprintf( buf+len, "(%s)", self->notify.name);
-			len += sprintf( buf+len, "\n");
+		while (self != NULL) {
+			ASSERT(self->magic == LMP_LSAP_MAGIC, return 0;);
+			len += sprintf(buf+len, "lsap state: %s, ", 
+				       irlsap_state[ self->lsap_state]);
+			len += sprintf(buf+len, 
+				       "slsap_sel: %#02x, dlsap_sel: %#02x, ",
+				       self->slsap_sel, self->dlsap_sel);
+			len += sprintf(buf+len, "(%s)", self->notify.name);
+			len += sprintf(buf+len, "\n");
 			
-			self = ( struct lsap_cb *) hashbin_get_next( 
+			self = (struct lsap_cb *) hashbin_get_next( 
 				lap->lsaps);
 		} 
+		len += sprintf(buf+len, "\n");
 
-		lap = ( struct lap_cb *) hashbin_get_next( 
-			irlmp->links);
+		lap = (struct lap_cb *) hashbin_get_next(irlmp->links);
  	} 
 
 	restore_flags( flags);
diff -u --recursive --new-file v2.3.3/linux/net/irda/irlmp_frame.c linux/net/irda/irlmp_frame.c
--- v2.3.3/linux/net/irda/irlmp_frame.c	Sat Apr 24 17:50:06 1999
+++ linux/net/irda/irlmp_frame.c	Sun May 30 10:27:04 1999
@@ -1,15 +1,15 @@
 /*********************************************************************
  *                
  * Filename:      irlmp_frame.c
- * Version:       0.8
+ * Version:       0.9
  * Description:   IrLMP frame implementation
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Tue Aug 19 02:09:59 1997
- * Modified at:   Fri Apr 23 09:12:23 1999
+ * Modified at:   Sun May  9 21:00:05 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>
+ *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>
  *     All Rights Reserved.
  *     
  *     This program is free software; you can redistribute it and/or 
@@ -59,16 +59,16 @@
  *
  *    Send Link Control Frame to IrLAP
  */
-void irlmp_send_lcf_pdu( struct lap_cb *self, __u8 dlsap, __u8 slsap,
-			 __u8 opcode, struct sk_buff *skb) 
+void irlmp_send_lcf_pdu(struct lap_cb *self, __u8 dlsap, __u8 slsap,
+			__u8 opcode, struct sk_buff *skb) 
 {
 	__u8 *frame;
 	
-	DEBUG( 4, __FUNCTION__ "()\n");
+	DEBUG(4, __FUNCTION__ "()\n");
 	
-	ASSERT( self != NULL, return;);
-	ASSERT( self->magic == LMP_LAP_MAGIC, return;);
-	ASSERT( skb != NULL, return;);
+	ASSERT(self != NULL, return;);
+	ASSERT(self->magic == LMP_LAP_MAGIC, return;);
+	ASSERT(skb != NULL, return;);
 	
 	frame = skb->data;
 	
@@ -82,8 +82,8 @@
 	else
 		frame[3] = 0x00; /* rsvd */
 
-	ASSERT( self->irlap != NULL, return;);
-	irlap_data_request( self->irlap, skb, TRUE);
+	ASSERT(self->irlap != NULL, return;);
+	irlap_data_request(self->irlap, skb, TRUE);
 }
 
 /*
@@ -112,7 +112,7 @@
 	 */
 	slsap_sel = fp[0] & LSAP_MASK; 
 	dlsap_sel = fp[1];
-	
+
 	/*
 	 *  Check if this is an incoming connection, since we must deal with
 	 *  it in a different way than other established connections.
@@ -224,11 +224,11 @@
  *    Incoming LAP connection!
  *
  */
-void irlmp_link_connect_indication( struct lap_cb *self, __u32 saddr, 
-				    __u32 daddr, struct qos_info *qos,
-				    struct sk_buff *skb) 
+void irlmp_link_connect_indication(struct lap_cb *self, __u32 saddr, 
+				   __u32 daddr, struct qos_info *qos,
+				   struct sk_buff *skb) 
 {
-	DEBUG( 4, __FUNCTION__ "()\n");
+	DEBUG(4, __FUNCTION__ "()\n");
 
 	/* Copy QoS settings for this session */
 	self->qos = qos;
@@ -237,7 +237,7 @@
 	self->daddr = daddr;
 	ASSERT(self->saddr == saddr, return;);
 
-	irlmp_do_lap_event( self, LM_LAP_CONNECT_INDICATION, skb);
+	irlmp_do_lap_event(self, LM_LAP_CONNECT_INDICATION, skb);
 }
 
 /*
@@ -246,19 +246,19 @@
  *    LAP connection confirmed!
  *
  */
-void irlmp_link_connect_confirm( struct lap_cb *self, struct qos_info *qos, 
-				 struct sk_buff *userdata)
+void irlmp_link_connect_confirm(struct lap_cb *self, struct qos_info *qos, 
+				struct sk_buff *userdata)
 {
-	DEBUG( 4, "irlmp_link_connect_confirm()\n");
+	DEBUG(4, __FUNCTION__ "()\n");
 
-	ASSERT( self != NULL, return;);
-	ASSERT( self->magic == LMP_LAP_MAGIC, return;);
-	ASSERT( qos != NULL, return;);
+	ASSERT(self != NULL, return;);
+	ASSERT(self->magic == LMP_LAP_MAGIC, return;);
+	ASSERT(qos != NULL, return;);
 
 	/* Copy QoS settings for this session */
 	self->qos = qos;
 
-	irlmp_do_lap_event( self, LM_LAP_CONNECT_CONFIRM, NULL);
+	irlmp_do_lap_event(self, LM_LAP_CONNECT_CONFIRM, NULL);
 }
 
 /*
@@ -276,7 +276,9 @@
 	irlmp_add_discovery(irlmp->cachelog, discovery);
 
 	/* Just handle it the same way as a discovery confirm */
+#if 0
 	irlmp_do_lap_event(self, LM_LAP_DISCOVERY_CONFIRM, NULL);
+#endif
 }
 
 /*
@@ -365,7 +367,7 @@
 #endif
 			return lsap;
 		}
-		lsap = ( struct lsap_cb *) hashbin_get_next(queue);
+		lsap = (struct lsap_cb *) hashbin_get_next(queue);
 	}
 
 	/* Sorry not found! */
diff -u --recursive --new-file v2.3.3/linux/net/irda/irlpt/irlpt_cli.c linux/net/irda/irlpt/irlpt_cli.c
--- v2.3.3/linux/net/irda/irlpt/irlpt_cli.c	Thu May  6 16:40:53 1999
+++ linux/net/irda/irlpt/irlpt_cli.c	Sun May 30 10:27:04 1999
@@ -51,10 +51,11 @@
 static void irlpt_client_connect_confirm(void *instance, void *sap, 
 					 struct qos_info *qos, 
 					 __u32 max_seg_size, 
+					 __u8 max_header_size,
 					 struct sk_buff *skb);
-static void irlpt_client_disconnect_indication( void *instance, void *sap, 
-						LM_REASON reason,
-						struct sk_buff *userdata);
+static void irlpt_client_disconnect_indication(void *instance, void *sap, 
+					       LM_REASON reason,
+					       struct sk_buff *userdata);
 static void irlpt_client_expired(unsigned long data);
 
 #if 0
@@ -187,7 +188,7 @@
 
 #ifdef CONFIG_PROC_FS
 	create_proc_entry("irlpt_client", 0, proc_irda)->get_info
-			= irlpt_client_proc_read;
+		= irlpt_client_proc_read;
 #endif /* CONFIG_PROC_FS */
 
 	DEBUG( irlpt_client_debug, __FUNCTION__ " -->\n");
@@ -215,7 +216,6 @@
 #ifdef CONFIG_PROC_FS
 	remove_proc_entry("irlpt_client", proc_irda);
 #endif
-
 	DEBUG( irlpt_client_debug, __FUNCTION__ " -->\n");
 }
 #endif /* MODULE */
@@ -403,9 +403,8 @@
 
 	irlpt_client_do_event( self, LMP_DISCONNECT, NULL, NULL);
 
-	if (skb) {
+	if (skb)
 		dev_kfree_skb( skb);
-	}
 
 	DEBUG( irlpt_client_debug, __FUNCTION__ " -->\n");
 }
@@ -417,7 +416,8 @@
  */
 static void irlpt_client_connect_confirm(void *instance, void *sap, 
 					 struct qos_info *qos, 
-					 __u32 max_sdu_size,
+					 __u32 max_seg_size,
+					 __u8 max_header_size,
 					 struct sk_buff *skb)
 {
 	struct irlpt_info info;
@@ -443,14 +443,14 @@
 	}
 #endif
 
-	self->irlap_data_size = (qos->data_size.value - IRLPT_MAX_HEADER);
+	self->max_data_size = max_seg_size;
+	self->max_header_size = max_header_size;
 	self->connected = TRUE;
 	
 	irlpt_client_do_event( self, LMP_CONNECT, NULL, NULL);
 
-	if (skb) {
+	if (skb)
 		dev_kfree_skb( skb);
-	}
 
 	DEBUG( irlpt_client_debug, __FUNCTION__ " -->\n");
 }
@@ -603,7 +603,7 @@
 			return;
 		}
 
-		skb_reserve( skb, LMP_CONTROL_HEADER+LAP_HEADER);
+		skb_reserve(skb, LMP_MAX_HEADER);
 		irlmp_disconnect_request(self->lsap, skb);
 		DEBUG(irlpt_client_debug, __FUNCTION__
 		      ": irlmp_close_slap(self->lsap)\n");
diff -u --recursive --new-file v2.3.3/linux/net/irda/irlpt/irlpt_cli_fsm.c linux/net/irda/irlpt/irlpt_cli_fsm.c
--- v2.3.3/linux/net/irda/irlpt/irlpt_cli_fsm.c	Sun Mar  7 15:26:44 1999
+++ linux/net/irda/irlpt/irlpt_cli_fsm.c	Sun May 30 10:27:04 1999
@@ -6,10 +6,10 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Tue Jan 12 11:06:00 1999
- * Modified at:   Tue Jan 26 12:02:31 1999
+ * Modified at:   Sun May  9 13:36:13 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1998, Thomas Davis, <ratbert@radiks.net>
+ *     Copyright (c) 1998-1999, Thomas Davis, <ratbert@radiks.net>
  *     Copyright (c) 1998, Dag Brattli, <dagb@cs.uit.no>
  *     All Rights Reserved.
  *      
@@ -43,10 +43,10 @@
 				      IRLPT_EVENT event,
 				      struct sk_buff *skb, 
 				      struct irlpt_info *info);
-static int irlpt_client_state_ready  ( struct irlpt_cb *self, 
-				       IRLPT_EVENT event, 
-				       struct sk_buff *skb, 
-				       struct irlpt_info *info);
+static int irlpt_client_state_ready ( struct irlpt_cb *self, 
+				      IRLPT_EVENT event, 
+				      struct sk_buff *skb, 
+				      struct irlpt_info *info);
 static int irlpt_client_state_waiti ( struct irlpt_cb *self, 
 				      IRLPT_EVENT event,
 				      struct sk_buff *skb, 
diff -u --recursive --new-file v2.3.3/linux/net/irda/irlpt/irlpt_common.c linux/net/irda/irlpt/irlpt_common.c
--- v2.3.3/linux/net/irda/irlpt/irlpt_common.c	Sun Mar  7 15:26:44 1999
+++ linux/net/irda/irlpt/irlpt_common.c	Sun May 30 10:27:04 1999
@@ -251,18 +251,18 @@
 	}
 
 	DEBUG( irlpt_common_debug, __FUNCTION__ 
-	       ": count = %d, irlap_data_size = %d, IRLPT_MAX_HEADER = %d\n",
-		count, self->irlap_data_size, IRLPT_MAX_HEADER);
+	       ": count = %d, max_data_size = %d, IRLPT_MAX_HEADER = %d\n",
+		count, self->max_data_size, IRLPT_MAX_HEADER);
 
- 	if (count > (self->irlap_data_size - IRLPT_MAX_HEADER)) {
- 		count = (self->irlap_data_size - IRLPT_MAX_HEADER);
+ 	if (count > self->max_data_size) {
+ 		count = self->max_data_size;
  		DEBUG(irlpt_common_debug, __FUNCTION__ 
 		      ": setting count to %d\n", count);
  	}
 
 	DEBUG( irlpt_common_debug, __FUNCTION__ ": count = %d\n", count);
 
-	skb = dev_alloc_skb(count + IRLPT_MAX_HEADER);
+	skb = dev_alloc_skb(count + self->max_header_size);
 	if ( skb == NULL) {
 		printk( KERN_INFO 
 			__FUNCTION__ ": couldn't allocate skbuff!\n");
@@ -417,7 +417,7 @@
 			return 0;
 		}
 
-		skb_reserve( skb, LMP_CONTROL_HEADER+LAP_HEADER);
+		skb_reserve( skb, LMP_MAX_HEADER);
 		irlmp_disconnect_request(self->lsap, skb);
 		DEBUG(irlpt_common_debug, __FUNCTION__
 		      ": irlmp_close_slap(self->lsap)\n");
diff -u --recursive --new-file v2.3.3/linux/net/irda/irlpt/irlpt_srvr.c linux/net/irda/irlpt/irlpt_srvr.c
--- v2.3.3/linux/net/irda/irlpt/irlpt_srvr.c	Thu May  6 16:40:53 1999
+++ linux/net/irda/irlpt/irlpt_srvr.c	Sun May 30 10:27:04 1999
@@ -51,15 +51,21 @@
 static void irlpt_server_disconnect_indication(void *instance, void *sap, 
 						LM_REASON reason,
 						struct sk_buff *skb);
+
+#if 0
 static void irlpt_server_connect_confirm(void *instance, void *sap, 
 					 struct qos_info *qos,  
 					 __u32 max_seg_size,
+					 __u8 max_header_size,
 					 struct sk_buff *skb);
 static void irlpt_server_connect_indication(void *instance, 
 					    void *sap, 
 					    struct qos_info *qos, 
 					    __u32 max_seg_size,
+					    __u8 max_header_size,
 					    struct sk_buff *skb);
+#endif
+
 static int irlpt_server_data_indication(void *instance, void *sap, 
 					struct sk_buff *skb);
 static void register_irlpt_server(void);
@@ -161,7 +167,6 @@
 }
 
 extern struct proc_dir_entry *proc_irda;
-
 #endif /* CONFIG_PROC_FS */
 
 /*
@@ -171,9 +176,9 @@
  *
  */
 
-/*int irlpt_init( struct device *dev) {*/
 __initfunc(int irlpt_server_init(void))
 {
+	struct irmanager_event mgr_event;
 	__u16 hints;
 
 	DEBUG( irlpt_server_debug, "--> " __FUNCTION__ "\n");
@@ -212,6 +217,10 @@
 		= irlpt_server_proc_read;
 #endif /* CONFIG_PROC_FS */
 
+	mgr_event.event = EVENT_IRLPT_START;
+        sprintf(mgr_event.devname, "%s", irlpt_server->ifname);
+        irmanager_notify(&mgr_event);
+
 	DEBUG( irlpt_server_debug, __FUNCTION__ " -->\n");
 
 	return 0;
@@ -225,6 +234,7 @@
  */
 static void irlpt_server_cleanup(void)
 {
+	struct irmanager_event mgr_event;
 	struct sk_buff *skb;
 
 	DEBUG( irlpt_server_debug, "--> " __FUNCTION__ "\n");
@@ -245,6 +255,10 @@
 	remove_proc_entry("irlpt_server", proc_irda);
 #endif
 
+        mgr_event.event = EVENT_IRLPT_STOP;
+        sprintf( mgr_event.devname, "%s", irlpt_server->ifname);
+        irmanager_notify( &mgr_event);
+ 
 	DEBUG( irlpt_server_debug, __FUNCTION__ " -->\n");
 }
 
@@ -304,6 +318,7 @@
 					 void *sap, 
 					 struct qos_info *qos,
 					 __u32 max_seg_size,
+					 __u8 max_header_size,
 					 struct sk_buff *skb)
 {
 	struct irlpt_cb *self;
@@ -314,6 +329,9 @@
 	ASSERT( self != NULL, return;);
 	ASSERT( self->magic == IRLPT_MAGIC, return;);
 
+	self->max_data_size = max_seg_size;
+	self->max_header_size = max_header_size;
+
 	self->connected = TRUE;
 
 	irlpt_server_do_event( self, LMP_CONNECT, NULL, NULL);
@@ -329,6 +347,7 @@
 					    void *sap, 
 					    struct qos_info *qos, 
 					    __u32 max_seg_size,
+					    __u8 max_header_size,
 					    struct sk_buff *skb)
 {
 	struct irlpt_cb *self;
@@ -343,14 +362,16 @@
 	ASSERT( self != NULL, return;);
 	ASSERT( self->magic == IRLPT_MAGIC, return;);
 
+	self->max_data_size = max_seg_size;
+	self->max_header_size = max_header_size;
+
 	self->connected = IRLPT_CONNECTED;
 	self->eof = FALSE;
 
 	irlpt_server_do_event( self, LMP_CONNECT, NULL, &info);
 
-	if (skb) {
+	if (skb)
 		dev_kfree_skb( skb);
-	}
 
 	DEBUG( irlpt_server_debug, __FUNCTION__ " -->\n");
 }
diff -u --recursive --new-file v2.3.3/linux/net/irda/irmod.c linux/net/irda/irmod.c
--- v2.3.3/linux/net/irda/irmod.c	Sat Apr 24 17:50:06 1999
+++ linux/net/irda/irmod.c	Sun May 30 10:27:04 1999
@@ -6,10 +6,10 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Mon Dec 15 13:55:39 1997
- * Modified at:   Mon Apr 12 11:31:01 1999
+ * Modified at:   Mon May 10 15:28:49 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1997 Dag Brattli, All Rights Reserved.
+ *     Copyright (c) 1997, 1999 Dag Brattli, All Rights Reserved.
  *      
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
@@ -110,6 +110,7 @@
 EXPORT_SYMBOL(irttp_flow_request);
 EXPORT_SYMBOL(irttp_connect_request);
 EXPORT_SYMBOL(irttp_udata_request);
+EXPORT_SYMBOL(irttp_dup);
 
 /* Main IrDA module */
 #ifdef CONFIG_IRDA_DEBUG
@@ -151,6 +152,7 @@
 EXPORT_SYMBOL(irlmp_disconnect_request);
 EXPORT_SYMBOL(irlmp_get_daddr);
 EXPORT_SYMBOL(irlmp_get_saddr);
+EXPORT_SYMBOL(irlmp_dup);
 EXPORT_SYMBOL(lmp_reasons);
 
 /* Queue */
@@ -174,10 +176,15 @@
 EXPORT_SYMBOL(irda_device_setup);
 EXPORT_SYMBOL(irda_device_set_media_busy);
 EXPORT_SYMBOL(irda_device_txqueue_empty);
+
+EXPORT_SYMBOL(irda_device_init_dongle);
+EXPORT_SYMBOL(irda_device_register_dongle);
+EXPORT_SYMBOL(irda_device_unregister_dongle);
+
 EXPORT_SYMBOL(async_wrap_skb);
 EXPORT_SYMBOL(async_unwrap_char);
 EXPORT_SYMBOL(irda_start_timer);
-EXPORT_SYMBOL(irda_get_mtt);
+/* EXPORT_SYMBOL(irda_get_mtt); */
 EXPORT_SYMBOL(setup_dma);
 
 #ifdef CONFIG_IRTTY
@@ -505,19 +512,28 @@
 #endif
 }
 
-#ifdef MODULE
-#ifdef CONFIG_PROC_FS
+/*
+ * Function irda_proc_modcount (inode, fill)
+ *
+ *    Use by the proc file system functions to prevent the irda module
+ *    being removed while the use is standing in the net/irda directory
+ */
 void irda_proc_modcount(struct inode *inode, int fill)
 {
+#ifdef MODULE
+#ifdef CONFIG_PROC_FS
 	if (fill)
 		MOD_INC_USE_COUNT;
 	else
 		MOD_DEC_USE_COUNT;
-}
 #endif /* CONFIG_PROC_FS */
+#endif /* MODULE */
+}
+
+#ifdef MODULE
 
 MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
-MODULE_DESCRIPTION("The Linux IrDA protocol subsystem"); 
+MODULE_DESCRIPTION("The Linux IrDA Protocol Subsystem"); 
 MODULE_PARM(irda_debug, "1l");
 
 /*
diff -u --recursive --new-file v2.3.3/linux/net/irda/irproc.c linux/net/irda/irproc.c
--- v2.3.3/linux/net/irda/irproc.c	Mon May 10 13:01:21 1999
+++ linux/net/irda/irproc.c	Sun May 30 10:27:04 1999
@@ -6,10 +6,10 @@
  * Status:        Experimental.
  * Author:        Thomas Davis, <ratbert@radiks.net>
  * Created at:    Sat Feb 21 21:33:24 1998
- * Modified at:   Tue Apr  6 19:07:06 1999
+ * Modified at:   Fri May  7 08:06:49 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1998, Thomas Davis, <ratbert@radiks.net>, 
+ *     Copyright (c) 1998-1999, Thomas Davis, <ratbert@radiks.net>, 
  *     All Rights Reserved.
  *      
  *     This program is free software; you can redistribute it and/or 
@@ -20,8 +20,6 @@
  *     I, Thomas Davis, provide no warranty for any of this software. 
  *     This material is provided "AS-IS" and at no charge. 
  *     
- *     Portions lifted from the linux/fs/procfs/ files.
- *
  ********************************************************************/
 
 #include <linux/miscdevice.h>
@@ -44,28 +42,27 @@
 			   int unused);
 extern int discovery_proc_read(char *buf, char **start, off_t offset, int len, 
 			       int unused);
+static int proc_discovery_read(char *buf, char **start, off_t offset, int len,
+			       int unused);
 
-enum irda_directory_inos {
-	PROC_IRDA_LAP = 1,
-	PROC_IRDA_LMP,
-	PROC_IRDA_TTP,
-	PROC_IRDA_LPT,
-	PROC_IRDA_COMM,
- 	PROC_IRDA_IRDA_DEVICE,
-	PROC_IRDA_IRIAS
-};
+/* enum irda_directory_inos { */
+/* 	PROC_IRDA_LAP = 1, */
+/* 	PROC_IRDA_LMP, */
+/* 	PROC_IRDA_TTP, */
+/* 	PROC_IRDA_LPT, */
+/* 	PROC_IRDA_COMM, */
+/*  	PROC_IRDA_IRDA_DEVICE, */
+/* 	PROC_IRDA_IRIAS */
+/* }; */
 
 struct irda_entry {
 	char *name;
-	int (*fn)(char*,char**,off_t,int,int);
+	int (*fn)(char*, char**, off_t, int, int);
 };
 
 struct proc_dir_entry *proc_irda;
-
+ 
 static struct irda_entry dir[] = {
-#if 0 
-	{"lpt", irlpt_proc_read},
-#endif
 	{"discovery",	discovery_proc_read},
 	{"irda_device",	irda_device_proc_read},
 	{"irttp",	irttp_proc_read},
@@ -75,19 +72,22 @@
 };
 
 #define IRDA_ENTRIES_NUM (sizeof(dir)/sizeof(dir[0]))
-
+ 
 /*
  * Function irda_proc_register (void)
  *
  *    Register irda entry in /proc file system
  *
  */
-void irda_proc_register(void) {
+void irda_proc_register(void) 
+{
 	int i;
+
 	proc_irda = create_proc_entry("net/irda", S_IFDIR, NULL);
 #ifdef MODULE
 	proc_irda->fill_inode = &irda_proc_modcount;
 #endif /* MODULE */
+
 	for (i=0;i<IRDA_ENTRIES_NUM;i++)
 		create_proc_entry(dir[i].name,0,proc_irda)->get_info=dir[i].fn;
 }
@@ -98,9 +98,14 @@
  *    Unregister irda entry in /proc file system
  *
  */
-void irda_proc_unregister(void) {
+void irda_proc_unregister(void) 
+{
 	int i;
+
 	for (i=0;i<IRDA_ENTRIES_NUM;i++)
 		remove_proc_entry(dir[i].name, proc_irda);
+
 	remove_proc_entry("net/irda", NULL);
 }
+
+
diff -u --recursive --new-file v2.3.3/linux/net/irda/irsysctl.c linux/net/irda/irsysctl.c
--- v2.3.3/linux/net/irda/irsysctl.c	Sat Apr 24 17:50:06 1999
+++ linux/net/irda/irsysctl.c	Sun May 30 10:27:04 1999
@@ -1,15 +1,15 @@
 /*********************************************************************
  *                
  * Filename:      irsysctl.c
- * Version:       
- * Description:   
+ * Version:       1.0
+ * Description:   Sysctl interface for IrDA
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Sun May 24 22:12:06 1998
- * Modified at:   Fri Apr 23 09:46:38 1999
+ * Modified at:   Thu May  6 21:32:46 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1997 Dag Brattli, All Rights Reserved.
+ *     Copyright (c) 1997, 1999 Dag Brattli, All Rights Reserved.
  *      
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
diff -u --recursive --new-file v2.3.3/linux/net/irda/irttp.c linux/net/irda/irttp.c
--- v2.3.3/linux/net/irda/irttp.c	Sat Apr 24 17:50:06 1999
+++ linux/net/irda/irttp.c	Sun May 30 10:27:04 1999
@@ -6,10 +6,10 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Sun Aug 31 20:14:31 1997
- * Modified at:   Sat Apr 10 10:32:21 1999
+ * Modified at:   Mon May 10 17:12:53 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>, 
+ *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, 
  *     All Rights Reserved.
  *     
  *     This program is free software; you can redistribute it and/or 
@@ -48,8 +48,10 @@
 					struct sk_buff *);
 static void irttp_connect_indication(void *instance, void *sap, 
 				     struct qos_info *qos, __u32 max_sdu_size,
-				     struct sk_buff *skb);
-
+				     __u8 header_size, struct sk_buff *skb);
+static void irttp_connect_confirm(void *instance, void *sap, 
+				  struct qos_info *qos, __u32 max_sdu_size, 
+				  __u8 header_size, struct sk_buff *skb);
 static void irttp_run_tx_queue(struct tsap_cb *self);
 static void irttp_run_rx_queue(struct tsap_cb *self);
 
@@ -337,6 +339,7 @@
 	/* Queue frame, or queue frame segments */
 	if ((self->tx_max_sdu_size == 0) || (skb->len < self->max_seg_size)) {
 		/* Queue frame */
+		ASSERT(skb_headroom(skb) >= TTP_HEADER, return -1;);
 		frame = skb_push(skb, TTP_HEADER);
 		frame[0] = 0x00; /* Clear more bit */
 		
@@ -360,8 +363,8 @@
 		self->tx_sdu_busy = TRUE;
 		
 	 	if (self->notify.flow_indication) {
- 			self->notify.flow_indication(
-				self->notify.instance, self, FLOW_STOP);
+ 			self->notify.flow_indication(self->notify.instance, 
+						     self, FLOW_STOP);
 		}
  	}
 	
@@ -472,7 +475,7 @@
 		return;
 
 	/* Reserve space for LMP, and LAP header */
-	skb_reserve(tx_skb, LMP_HEADER+LAP_HEADER);
+	skb_reserve(tx_skb, self->max_header_size);
 
 	/*
 	 *  Since we can transmit and receive frames concurrently, 
@@ -655,15 +658,14 @@
 			return -ENOMEM;
 		
 		/* Reserve space for MUX_CONTROL and LAP header */
-		skb_reserve(skb, (TTP_HEADER+LMP_CONTROL_HEADER+LAP_HEADER));
+		skb_reserve(skb, TTP_MAX_HEADER);
 	} else {
 		skb = userdata;
 		/*  
 		 *  Check that the client has reserved enough space for 
 		 *  headers
 		 */
-		ASSERT(skb_headroom(userdata) >= 
-		       (TTP_HEADER+LMP_CONTROL_HEADER+LAP_HEADER), return -1;);
+		ASSERT(skb_headroom(userdata) >= TTP_MAX_HEADER, return -1;);
 	}
 
 	/* Initialize connection parameters */
@@ -691,12 +693,11 @@
 
 	/* SAR enabled? */
 	if (max_sdu_size > 0) {
-		ASSERT(skb_headroom(skb) >= 
-			(TTP_HEADER_WITH_SAR+LMP_CONTROL_HEADER+LAP_HEADER), 
-			return -1;);
+		ASSERT(skb_headroom(skb) >= (TTP_MAX_HEADER + TTP_SAR_HEADER), 
+		       return -1;);
 
 		/* Insert SAR parameters */
-		frame = skb_push(skb, TTP_HEADER_WITH_SAR);
+		frame = skb_push(skb, TTP_HEADER+TTP_SAR_HEADER);
 		
 		frame[0] = TTP_PARAMETERS | n; 
 		frame[1] = 0x04; /* Length */
@@ -724,8 +725,10 @@
  *    Sevice user confirms TSAP connection with peer. 
  *
  */
-void irttp_connect_confirm(void *instance, void *sap, struct qos_info *qos,
-			   __u32 max_seg_size, struct sk_buff *skb) 
+static void irttp_connect_confirm(void *instance, void *sap, 
+				  struct qos_info *qos,
+				  __u32 max_seg_size, __u8 max_header_size,
+				  struct sk_buff *skb) 
 {
 	struct tsap_cb *self;
 	int parameters;
@@ -741,7 +744,8 @@
 	ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
 	ASSERT(skb != NULL, return;);
 
-	self->max_seg_size = max_seg_size-LMP_HEADER-LAP_HEADER;
+	self->max_seg_size = max_seg_size;
+	self->max_header_size = max_header_size + TTP_HEADER;
 
 	/*
 	 *  Check if we have got some QoS parameters back! This should be the
@@ -797,9 +801,9 @@
 	skb_pull(skb, TTP_HEADER);
 
 	if (self->notify.connect_confirm) {
-		self->notify.connect_confirm(self->notify.instance, self, 
-					     qos, self->tx_max_sdu_size, 
-					     skb);
+		self->notify.connect_confirm(self->notify.instance, self, qos,
+					     self->tx_max_sdu_size, 
+					     self->max_header_size, skb);
 	}
 }
 
@@ -809,8 +813,8 @@
  *    Some other device is connecting to this TSAP
  *
  */
-void irttp_connect_indication(void *instance, void *sap, 
-			      struct qos_info *qos, __u32 max_seg_size, 
+void irttp_connect_indication(void *instance, void *sap, struct qos_info *qos,
+			      __u32 max_seg_size,  __u8 max_header_size, 
 			      struct sk_buff *skb) 
 {
 	struct tsap_cb *self;
@@ -828,7 +832,9 @@
 
 	lsap = (struct lsap_cb *) sap;
 
-	self->max_seg_size = max_seg_size-LMP_HEADER-LAP_HEADER;
+	self->max_seg_size = max_seg_size;
+
+	self->max_header_size = max_header_size+TTP_HEADER;
 
 	DEBUG(4, __FUNCTION__ "(), TSAP sel=%02x\n", self->stsap_sel);
 
@@ -850,7 +856,7 @@
 
 		switch (pl) {
 		case 1:
-			self->tx_max_sdu_size = *(frame+4);
+			self->tx_max_sdu_size = frame[4];
 			break;
 		case 2:
 			self->tx_max_sdu_size = 
@@ -878,7 +884,7 @@
 	if (self->notify.connect_indication) {
 		self->notify.connect_indication(self->notify.instance, self, 
 						qos, self->rx_max_sdu_size, 
-						skb);
+						self->max_header_size, skb);
 	}
 }
 
@@ -909,15 +915,14 @@
 			return;
 
 		/* Reserve space for MUX_CONTROL and LAP header */
-		skb_reserve(skb, (TTP_HEADER+LMP_CONTROL_HEADER+LAP_HEADER));
+		skb_reserve(skb, TTP_MAX_HEADER);
 	} else {
 		skb = userdata;
 		/*  
 		 *  Check that the client has reserved enough space for 
 		 *  headers
 		 */
-		ASSERT(skb_headroom(skb) >= 
-			(TTP_HEADER+LMP_CONTROL_HEADER+LAP_HEADER), return;);
+		ASSERT(skb_headroom(skb) >= TTP_MAX_HEADER, return;);
 	}
 	
 	self->avail_credit = 0;
@@ -939,12 +944,11 @@
 
 	/* SAR enabled? */
 	if (max_sdu_size > 0) {
-		ASSERT(skb_headroom(skb) >= 
-		       (TTP_HEADER_WITH_SAR+LMP_CONTROL_HEADER+LAP_HEADER), 
+		ASSERT(skb_headroom(skb) >= (TTP_MAX_HEADER+TTP_SAR_HEADER), 
 		       return;);
 		
 		/* Insert TTP header with SAR parameters */
-		frame = skb_push(skb, TTP_HEADER_WITH_SAR);
+		frame = skb_push(skb, TTP_HEADER+TTP_SAR_HEADER);
 		
 		frame[0] = TTP_PARAMETERS | n;
 		frame[1] = 0x04; /* Length */
@@ -1079,7 +1083,7 @@
 		/* 
 		 *  Reserve space for MUX and LAP header 
 		 */
-		skb_reserve(skb, LMP_CONTROL_HEADER+LAP_HEADER);
+		skb_reserve(skb, TTP_MAX_HEADER);
 		
 		userdata = skb;
 	}
@@ -1357,13 +1361,11 @@
 		}
 		
 		/* Make new segment */
-		frag = dev_alloc_skb(self->max_seg_size+
-				      TTP_HEADER+LMP_HEADER+
-				      LAP_HEADER);
+		frag = dev_alloc_skb(self->max_seg_size+self->max_header_size);
 		if (!frag)
 			return;
 
-		skb_reserve(frag, LMP_HEADER+LAP_HEADER);
+		skb_reserve(frag, self->max_header_size);
 
 		/*
 		 *  Copy data from the original skb into this fragment. We
diff -u --recursive --new-file v2.3.3/linux/net/irda/qos.c linux/net/irda/qos.c
--- v2.3.3/linux/net/irda/qos.c	Sat Apr 24 17:50:06 1999
+++ linux/net/irda/qos.c	Sun May 30 10:27:04 1999
@@ -6,10 +6,11 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Tue Sep  9 00:00:26 1997
- * Modified at:   Mon Apr 12 11:49:24 1999
+ * Modified at:   Mon May  3 21:15:08 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved.
+ *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, 
+ *     All Rights Reserved.
  *     
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
@@ -52,10 +53,10 @@
  *    Compute the intersection of the old QoS capabilites with new ones
  *
  */
-void irda_qos_compute_intersection( struct qos_info *qos, struct qos_info *new)
+void irda_qos_compute_intersection(struct qos_info *qos, struct qos_info *new)
 {
-	ASSERT( qos != NULL, return;);
-	ASSERT( new != NULL, return;);
+	ASSERT(qos != NULL, return;);
+	ASSERT(new != NULL, return;);
 
 	/* Apply */
 	qos->baud_rate.bits       &= new->baud_rate.bits;
diff -u --recursive --new-file v2.3.3/linux/net/irda/wrapper.c linux/net/irda/wrapper.c
--- v2.3.3/linux/net/irda/wrapper.c	Sat Apr 24 17:50:06 1999
+++ linux/net/irda/wrapper.c	Sun May 30 10:27:04 1999
@@ -1,15 +1,15 @@
 /*********************************************************************
  *                
  * Filename:      wrapper.c
- * Version:       1.1
- * Description:   SIR wrapper layer
+ * Version:       1.2
+ * Description:   IrDA SIR async wrapper layer
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Mon Aug  4 20:40:53 1997
- * Modified at:   Wed Apr 21 12:45:55 1999
+ * Modified at:   Sun May  2 21:58:00 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>, 
+ *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, 
  *     All Rights Reserved.
  *     
  *     This program is free software; you can redistribute it and/or 
@@ -34,7 +34,20 @@
 #include <net/irda/irlap_frame.h>
 #include <net/irda/irda_device.h>
 
-inline static int stuff_byte(__u8 byte, __u8 *buf);
+static inline int stuff_byte(__u8 byte, __u8 *buf);
+
+static void state_outside_frame(struct irda_device *idev, __u8 byte);
+static void state_begin_frame(struct irda_device *idev, __u8 byte);
+static void state_link_escape(struct irda_device *idev, __u8 byte);
+static void state_inside_frame(struct irda_device *idev, __u8 byte);
+
+static void (*state[])(struct irda_device *idev, __u8 byte) = 
+{ 
+	state_outside_frame,
+	state_begin_frame,
+	state_link_escape,
+	state_inside_frame,
+};
 
 /*
  * Function async_wrap (skb, *tx_buff)
@@ -52,8 +65,6 @@
 		__u8 bytes[2];
 	} fcs;
 
-	ASSERT(skb != NULL, return 0;);
-
 	/* Initialize variables */
 	fcs.value = INIT_FCS;
 	n = 0;
@@ -74,13 +85,9 @@
 	} else
 		xbofs = ((struct irlap_skb_cb *)(skb->cb))->xbofs;
 
-#if 0
- 	for (i=0; i<xbofs; i++)
-  		tx_buff[n++] = XBOF;
-#else
 	memset(tx_buff+n, XBOF, xbofs);
 	n += xbofs;
-#endif
+
 	/* Start of packet character BOF */
 	tx_buff[n++] = BOF;
 
@@ -94,7 +101,7 @@
 		ASSERT(n < (buffsize-5), return n;);
 
 		n += stuff_byte(skb->data[i], tx_buff+n);
-		fcs.value = IR_FCS(fcs.value, skb->data[i]);
+		fcs.value = irda_fcs(fcs.value, skb->data[i]);
 	}
 	
 	/* Insert CRC in little endian format (LSB first) */
@@ -108,15 +115,6 @@
 #endif
 	tx_buff[n++] = EOF;
 
-#if 0
-	{
-		int i;
-		
-		for (i=0;i<n;i++)
-			printk("%02x", tx_buff[i]);
-		printk("\n");
-	}
-#endif
 	return n;
 }
 
@@ -155,147 +153,13 @@
 }
 
 /*
- * Function async_unwrap (skb)
- *
- *    Parse and de-stuff frame received from the IR-port
- *
- */
-void async_unwrap_char(struct irda_device *idev, __u8 byte) 
-{
-	/* State machine for receiving frames */	   
-	switch (idev->rx_buff.state) {
-	case OUTSIDE_FRAME:
-		switch(byte) {
-		case BOF:
-			idev->rx_buff.state = BEGIN_FRAME;
-			idev->rx_buff.in_frame = TRUE;
-			break;
-		case XBOF:
-			/* idev->xbofs++; */
-			break;
-		case EOF:
-			irda_device_set_media_busy( idev, TRUE);
-			break;
-		default:
-			break;
-		}
-		break;
-	case BEGIN_FRAME:
-		switch (byte) {
-		case BOF:
-			/* Continue */
-			break;
-		case CE:
-			/* Stuffed byte */
-			idev->rx_buff.state = LINK_ESCAPE;
-			break;
-		case EOF:
-			/* Abort frame */
-			idev->rx_buff.state = OUTSIDE_FRAME;
-
-			idev->stats.rx_errors++;
-			idev->stats.rx_frame_errors++;
-			break;
-		default:
-			/* Got first byte of frame */
-			idev->rx_buff.data = idev->rx_buff.head;
-			idev->rx_buff.len = 0;
-
-			idev->rx_buff.data[idev->rx_buff.len++] = byte;
-			
-			idev->rx_buff.fcs = IR_FCS(INIT_FCS, byte);
-			idev->rx_buff.state = INSIDE_FRAME;
-			break;
-		}
-		break;
-	case LINK_ESCAPE:
-		switch (byte) {
-		case BOF:
-			/* New frame? */
-			idev->rx_buff.state = BEGIN_FRAME;
-			irda_device_set_media_busy(idev, TRUE);
-			break;
-		case CE:
-			DEBUG(4, "WARNING: State not defined\n");
-			break;
-		case EOF:
-			/* Abort frame */
-			idev->rx_buff.state = OUTSIDE_FRAME;
-			break;
-		default:
-			/* 
-			 *  Stuffed char, complement bit 5 of byte 
-			 *  following CE, IrLAP p.114 
-			 */
-			byte ^= IR_TRANS;
-			if (idev->rx_buff.len < idev->rx_buff.truesize)  {
-				idev->rx_buff.data[idev->rx_buff.len++] = byte;
-				idev->rx_buff.fcs = IR_FCS(idev->rx_buff.fcs,
-							   byte);
-				idev->rx_buff.state = INSIDE_FRAME;
-			} else {
-				DEBUG(1, __FUNCTION__ 
-				       "(), Rx buffer overflow, aborting\n");
-				idev->rx_buff.state = OUTSIDE_FRAME;
-			}
-			break;
-		}
-		break;
-	case INSIDE_FRAME:
-		switch (byte) {
-		case BOF:
-			/* New frame? */
-			idev->rx_buff.state = BEGIN_FRAME;
-			irda_device_set_media_busy(idev, TRUE);
-			break;
-		case CE:
-			/* Stuffed char */
-			idev->rx_buff.state = LINK_ESCAPE;
-			break;
-		case EOF:
-			/* End of frame */
-			idev->rx_buff.state = OUTSIDE_FRAME;
-			idev->rx_buff.in_frame = FALSE;
-			
-			/* 
-			 *  Test FCS and deliver frame if it's good
-			 */			
-			if (idev->rx_buff.fcs == GOOD_FCS) {
-				async_bump(idev, idev->rx_buff.data, 
-					   idev->rx_buff.len);
-			} else {
-				/* Wrong CRC, discard frame!  */
-				irda_device_set_media_busy(idev, TRUE); 
-
-				idev->stats.rx_errors++;
-				idev->stats.rx_crc_errors++;
-			}			
-			break;
-		default:
-			/* Next byte of frame */
-			if (idev->rx_buff.len < idev->rx_buff.truesize)  {
-				idev->rx_buff.data[idev->rx_buff.len++] = byte;
-				idev->rx_buff.fcs = IR_FCS(idev->rx_buff.fcs,
-							   byte);
-			} else {
-				DEBUG(1, __FUNCTION__ 
-				      "(), Rx buffer overflow, aborting\n");
-				idev->rx_buff.state = OUTSIDE_FRAME;
-			}
-			break;
-		}
-		break;
-	}
-}
- 
-/*
  * Function stuff_byte (byte, buf)
  *
  *    Byte stuff one single byte and put the result in buffer pointed to by
  *    buf. The buffer must at all times be able to have two bytes inserted.
  * 
  */
-inline static int stuff_byte(__u8 byte, __u8 *buf) 
+static inline int stuff_byte(__u8 byte, __u8 *buf) 
 {
 	switch (byte) {
 	case BOF: /* FALLTHROUGH */
@@ -303,7 +167,7 @@
 	case CE:
 		/* Insert transparently coded */
 		buf[0] = CE;               /* Send link escape */
-		buf[1] = byte^IR_TRANS;    /* Complement bit 5 */
+		buf[1] = byte^IRDA_TRANS;    /* Complement bit 5 */
 		return 2;
 		/* break; */
 	default:
@@ -313,7 +177,159 @@
 		/* break; */
 	}
 }
+
+/*
+ * Function async_unwrap (skb)
+ *
+ *    Parse and de-stuff frame received from the IrDA-port
+ *
+ */
+inline void async_unwrap_char(struct irda_device *idev, __u8 byte)
+{
+	(*state[idev->rx_buff.state]) (idev, byte);
+}
 	 
+/*
+ * Function state_outside_frame (idev, byte)
+ *
+ *    
+ *
+ */
+static void state_outside_frame(struct irda_device *idev, __u8 byte)
+{
+	switch (byte) {
+	case BOF:
+		idev->rx_buff.state = BEGIN_FRAME;
+		idev->rx_buff.in_frame = TRUE;
+		break;
+	case XBOF:
+		/* idev->xbofs++; */
+		break;
+	case EOF:
+		irda_device_set_media_busy( idev, TRUE);
+		break;
+	default:
+		break;
+	}
+}
+
+/*
+ * Function state_begin_frame (idev, byte)
+ *
+ *    
+ *
+ */
+static void state_begin_frame(struct irda_device *idev, __u8 byte)
+{
+	switch (byte) {
+	case BOF:
+		/* Continue */
+		break;
+	case CE:
+		/* Stuffed byte */
+		idev->rx_buff.state = LINK_ESCAPE;
+		break;
+	case EOF:
+		/* Abort frame */
+		idev->rx_buff.state = OUTSIDE_FRAME;
+		
+		idev->stats.rx_errors++;
+		idev->stats.rx_frame_errors++;
+		break;
+	default:
+		/* Got first byte of frame */
+		idev->rx_buff.data = idev->rx_buff.head;
+		idev->rx_buff.len = 0;
+		
+		idev->rx_buff.data[idev->rx_buff.len++] = byte;
+		
+		idev->rx_buff.fcs = irda_fcs(INIT_FCS, byte);
+		idev->rx_buff.state = INSIDE_FRAME;
+		break;
+	}
+}
 
+/*
+ * Function state_link_escape (idev, byte)
+ *
+ *    
+ *
+ */
+static void state_link_escape(struct irda_device *idev, __u8 byte)
+{
+	switch (byte) {
+	case BOF: /* New frame? */
+		idev->rx_buff.state = BEGIN_FRAME;
+		irda_device_set_media_busy(idev, TRUE);
+		break;
+	case CE:
+		DEBUG(4, "WARNING: State not defined\n");
+		break;
+	case EOF: /* Abort frame */
+		idev->rx_buff.state = OUTSIDE_FRAME;
+		break;
+	default:
+		/* 
+		 *  Stuffed char, complement bit 5 of byte 
+		 *  following CE, IrLAP p.114 
+		 */
+		byte ^= IRDA_TRANS;
+		if (idev->rx_buff.len < idev->rx_buff.truesize)  {
+			idev->rx_buff.data[idev->rx_buff.len++] = byte;
+			idev->rx_buff.fcs = irda_fcs(idev->rx_buff.fcs, byte);
+			idev->rx_buff.state = INSIDE_FRAME;
+		} else {
+			DEBUG(1, __FUNCTION__ 
+			      "(), Rx buffer overflow, aborting\n");
+			idev->rx_buff.state = OUTSIDE_FRAME;
+		}
+		break;
+	}
+}
+
+/*
+ * Function state_inside_frame (idev, byte)
+ *
+ *    
+ *
+ */
+static void state_inside_frame(struct irda_device *idev, __u8 byte)
+{
+	switch (byte) {
+	case BOF: /* New frame? */
+		idev->rx_buff.state = BEGIN_FRAME;
+		irda_device_set_media_busy(idev, TRUE);
+		break;
+	case CE: /* Stuffed char */
+		idev->rx_buff.state = LINK_ESCAPE;
+		break;
+	case EOF: /* End of frame */
+		idev->rx_buff.state = OUTSIDE_FRAME;
+		idev->rx_buff.in_frame = FALSE;
+		
+		/* Test FCS and deliver frame if it's good */
+		if (idev->rx_buff.fcs == GOOD_FCS) {
+			async_bump(idev, idev->rx_buff.data, 
+				   idev->rx_buff.len);
+		} else {
+			/* Wrong CRC, discard frame!  */
+			irda_device_set_media_busy(idev, TRUE); 
+			
+			idev->stats.rx_errors++;
+			idev->stats.rx_crc_errors++;
+		}			
+		break;
+	default: /* Must be the next byte of the frame */
+		if (idev->rx_buff.len < idev->rx_buff.truesize)  {
+			idev->rx_buff.data[idev->rx_buff.len++] = byte;
+			idev->rx_buff.fcs = irda_fcs(idev->rx_buff.fcs, byte);
+		} else {
+			DEBUG(1, __FUNCTION__ 
+			      "(), Rx buffer overflow, aborting\n");
+			idev->rx_buff.state = OUTSIDE_FRAME;
+		}
+		break;
+	}
+}
 
 
diff -u --recursive --new-file v2.3.3/linux/net/netrom/nr_route.c linux/net/netrom/nr_route.c
--- v2.3.3/linux/net/netrom/nr_route.c	Fri Oct  9 11:56:59 1998
+++ linux/net/netrom/nr_route.c	Tue May 25 13:06:35 1999
@@ -564,10 +564,13 @@
 {
 	struct device *dev, *first = NULL;
 
-	for (dev = dev_base; dev != NULL; dev = dev->next)
+	read_lock_bh(&dev_base_lock);
+	for (dev = dev_base; dev != NULL; dev = dev->next) {
 		if ((dev->flags & IFF_UP) && dev->type == ARPHRD_NETROM)
 			if (first == NULL || strncmp(dev->name, first->name, 3) < 0)
 				first = dev;
+	}
+	read_unlock_bh(&dev_base_lock);
 
 	return first;
 }
@@ -579,11 +582,14 @@
 {
 	struct device *dev;
 
-	for (dev = dev_base; dev != NULL; dev = dev->next)
+	read_lock_bh(&dev_base_lock);
+	for (dev = dev_base; dev != NULL; dev = dev->next) {
 		if ((dev->flags & IFF_UP) && dev->type == ARPHRD_NETROM && ax25cmp(addr, (ax25_address *)dev->dev_addr) == 0)
-			return dev;
-
-	return NULL;
+			goto out;
+	}
+out:
+	read_unlock_bh(&dev_base_lock);
+	return dev;
 }
 
 static ax25_digi *nr_call_to_digi(int ndigis, ax25_address *digipeaters)
diff -u --recursive --new-file v2.3.3/linux/net/netsyms.c linux/net/netsyms.c
--- v2.3.3/linux/net/netsyms.c	Sat Apr 24 17:51:48 1999
+++ linux/net/netsyms.c	Mon May 31 22:07:43 1999
@@ -106,10 +106,17 @@
 EXPORT_SYMBOL(skb_over_panic);
 EXPORT_SYMBOL(skb_under_panic);
 
+/* Socket layer global data */
+EXPORT_SYMBOL(sockhash_lock);
+
 /* Socket layer registration */
 EXPORT_SYMBOL(sock_register);
 EXPORT_SYMBOL(sock_unregister);
 
+/* Socket locking */
+EXPORT_SYMBOL(lock_sock);
+EXPORT_SYMBOL(release_sock);
+
 /* Socket layer support routines */
 EXPORT_SYMBOL(memcpy_fromiovec);
 EXPORT_SYMBOL(memcpy_tokerneliovec);
@@ -156,7 +163,6 @@
 EXPORT_SYMBOL(net_families);
 EXPORT_SYMBOL(sock_kmalloc);
 EXPORT_SYMBOL(sock_kfree_s);
-EXPORT_SYMBOL(skb_queue_lock);
 
 #ifdef CONFIG_FILTER
 EXPORT_SYMBOL(sk_run_filter);
@@ -187,7 +193,6 @@
 /*	dst_entry	*/
 EXPORT_SYMBOL(dst_alloc);
 EXPORT_SYMBOL(__dst_free);
-EXPORT_SYMBOL(dst_total);
 EXPORT_SYMBOL(dst_destroy);
 
 /*	misc. support routines */
@@ -243,7 +248,6 @@
 EXPORT_SYMBOL(__ip_finish_output);
 EXPORT_SYMBOL(inet_dgram_ops);
 EXPORT_SYMBOL(ip_cmsg_recv);
-EXPORT_SYMBOL(__release_sock);
 
 /* Route manipulation */
 EXPORT_SYMBOL(ip_rt_ioctl);
@@ -279,9 +283,11 @@
 
 /* Socket demultiplexing. */
 EXPORT_SYMBOL(tcp_good_socknum);
-EXPORT_SYMBOL(tcp_established_hash);
+EXPORT_SYMBOL(tcp_ehash);
+EXPORT_SYMBOL(tcp_ehash_size);
 EXPORT_SYMBOL(tcp_listening_hash);
-EXPORT_SYMBOL(tcp_bound_hash);
+EXPORT_SYMBOL(tcp_bhash);
+EXPORT_SYMBOL(tcp_bhash_size);
 EXPORT_SYMBOL(udp_good_socknum);
 EXPORT_SYMBOL(udp_hash);
 
@@ -470,6 +476,7 @@
 EXPORT_SYMBOL(netdev_fc_xoff);
 #endif
 EXPORT_SYMBOL(dev_base);
+EXPORT_SYMBOL(dev_base_lock);
 EXPORT_SYMBOL(dev_close);
 EXPORT_SYMBOL(dev_mc_add);
 EXPORT_SYMBOL(dev_mc_delete);
diff -u --recursive --new-file v2.3.3/linux/net/rose/rose_route.c linux/net/rose/rose_route.c
--- v2.3.3/linux/net/rose/rose_route.c	Wed Oct  7 15:52:55 1998
+++ linux/net/rose/rose_route.c	Tue May 25 13:06:35 1999
@@ -543,10 +543,13 @@
 {
 	struct device *dev, *first = NULL;
 
-	for (dev = dev_base; dev != NULL; dev = dev->next)
+	read_lock_bh(&dev_base_lock);
+	for (dev = dev_base; dev != NULL; dev = dev->next) {
 		if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ROSE)
 			if (first == NULL || strncmp(dev->name, first->name, 3) < 0)
 				first = dev;
+	}
+	read_unlock_bh(&dev_base_lock);
 
 	return first;
 }
@@ -558,11 +561,14 @@
 {
 	struct device *dev;
 
-	for (dev = dev_base; dev != NULL; dev = dev->next)
+	read_lock_bh(&dev_base_lock);
+	for (dev = dev_base; dev != NULL; dev = dev->next) {
 		if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ROSE && rosecmp(addr, (rose_address *)dev->dev_addr) == 0)
-			return dev;
-
-	return NULL;
+			goto out;
+	}
+out:
+	read_unlock_bh(&dev_base_lock);
+	return dev;
 }
 
 struct rose_route *rose_route_free_lci(unsigned int lci, struct rose_neigh *neigh)
diff -u --recursive --new-file v2.3.3/linux/net/sched/sch_api.c linux/net/sched/sch_api.c
--- v2.3.3/linux/net/sched/sch_api.c	Sun Mar 21 07:22:00 1999
+++ linux/net/sched/sch_api.c	Tue May 25 13:06:35 1999
@@ -713,6 +713,7 @@
 
 	s_idx = cb->args[0];
 	s_q_idx = q_idx = cb->args[1];
+	read_lock_bh(&dev_base_lock);
 	for (dev=dev_base, idx=0; dev; dev = dev->next, idx++) {
 		if (idx < s_idx)
 			continue;
@@ -729,6 +730,8 @@
 	}
 
 done:
+	read_unlock_bh(&dev_base_lock);
+
 	cb->args[0] = idx;
 	cb->args[1] = q_idx;
 
diff -u --recursive --new-file v2.3.3/linux/net/sched/sch_generic.c linux/net/sched/sch_generic.c
--- v2.3.3/linux/net/sched/sch_generic.c	Sun Mar 21 07:22:00 1999
+++ linux/net/sched/sch_generic.c	Tue May 25 13:06:35 1999
@@ -96,8 +96,10 @@
 		struct Qdisc *q = (struct Qdisc*)h;
 		struct device *dev = q->dev;
 
+		spin_lock_bh(&dev->xmit_lock);
 		while (!dev->tbusy && (res = qdisc_restart(dev)) < 0)
 			/* NOTHING */;
+		spin_unlock_bh(&dev->xmit_lock);
 
 		/* An explanation is necessary here.
 		   qdisc_restart called dev->hard_start_xmit,
@@ -131,8 +133,11 @@
 	for (h = qdisc_head.forw; h != &qdisc_head; h = h->forw) {
 		struct Qdisc *q = (struct Qdisc*)h;
 		struct device *dev = q->dev;
+
+		spin_lock_bh(&dev->xmit_lock);
 		if (dev->tbusy && jiffies - q->tx_last > q->tx_timeo)
 			qdisc_restart(dev);
+		spin_unlock_bh(&dev->xmit_lock);
 	}
 	dev_watchdog.expires = jiffies + 5*HZ;
 	add_timer(&dev_watchdog);
diff -u --recursive --new-file v2.3.3/linux/net/socket.c linux/net/socket.c
--- v2.3.3/linux/net/socket.c	Fri May 14 18:55:32 1999
+++ linux/net/socket.c	Tue May 25 13:06:35 1999
@@ -131,6 +131,11 @@
 static int sockets_in_use  = 0;
 
 /*
+ *	Socket hashing lock.
+ */
+rwlock_t sockhash_lock = RW_LOCK_UNLOCKED;
+
+/*
  *	Support routines. Move socket addresses back and forth across the kernel/user
  *	divide and look after the messy bits.
  */
@@ -561,7 +566,8 @@
 		/* fall through */
 	case 0:
 	call_kill:
-		kill_fasync(sock->fasync_list, SIGIO);
+		if(sock->fasync_list != NULL)
+			kill_fasync(sock->fasync_list, SIGIO);
 		break;
 	}
 	return 0;
diff -u --recursive --new-file v2.3.3/linux/net/unix/af_unix.c linux/net/unix/af_unix.c
--- v2.3.3/linux/net/unix/af_unix.c	Fri May 14 18:55:33 1999
+++ linux/net/unix/af_unix.c	Wed May 26 18:14:38 1999
@@ -8,7 +8,7 @@
  *		as published by the Free Software Foundation; either version
  *		2 of the License, or (at your option) any later version.
  *
- * Version:	$Id: af_unix.c,v 1.77 1999/05/12 11:25:07 davem Exp $
+ * Version:	$Id: af_unix.c,v 1.78 1999/05/27 00:38:41 davem Exp $
  *
  * Fixes:
  *		Linus Torvalds	:	Assorted bug cures.
@@ -144,19 +144,21 @@
 	return (unix_peer(osk) == NULL || unix_our_peer(sk, osk));
 }
 
+#define ulock(sk)	(&(sk->protinfo.af_unix.user_count))
+
 extern __inline__ void unix_lock(unix_socket *sk)
 {
-	atomic_inc(&sk->sock_readers);
+	atomic_inc(ulock(sk));
 }
 
 extern __inline__ void unix_unlock(unix_socket *sk)
 {
-	atomic_dec(&sk->sock_readers);
+	atomic_dec(ulock(sk));
 }
 
 extern __inline__ int unix_locked(unix_socket *sk)
 {
-	return atomic_read(&sk->sock_readers);
+	return (atomic_read(ulock(sk)) != 0);
 }
 
 extern __inline__ void unix_release_addr(struct unix_address *addr)
@@ -1511,7 +1513,7 @@
 	{
 		len+=sprintf(buffer+len,"%p: %08X %08X %08lX %04X %02X %5ld",
 			s,
-			atomic_read(&s->sock_readers),
+			atomic_read(ulock(s)),
 			0,
 			s->socket ? s->socket->flags : 0,
 			s->type,
diff -u --recursive --new-file v2.3.3/linux/net/x25/af_x25.c linux/net/x25/af_x25.c
--- v2.3.3/linux/net/x25/af_x25.c	Thu Aug 27 19:33:09 1998
+++ linux/net/x25/af_x25.c	Tue May 25 13:06:35 1999
@@ -1336,14 +1336,20 @@
 	/*
 	 *	Register any pre existing devices.
 	 */
-	for (dev = dev_base; dev != NULL; dev = dev->next)
+	read_lock_bh(&dev_base_lock);
+	for (dev = dev_base; dev != NULL; dev = dev->next) {
 		if ((dev->flags & IFF_UP) && (dev->type == ARPHRD_X25
 #if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE)
 					   || dev->type == ARPHRD_ETHER
 #endif
-									))
-		x25_link_device_up(dev);
-	
+			)) {
+			read_unlock_bh(&dev_base_lock);
+			x25_link_device_up(dev);
+			read_lock_bh(&dev_base_lock);
+		}
+	}
+	read_unlock_bh(&dev_base_lock);
+
 	return 0;
 }
 
